diff --git a/app/resources/views/site/home.blade.php b/app/resources/views/site/home.blade.php
index a9e40dd..0686ec7 100644
--- a/app/resources/views/site/home.blade.php
+++ b/app/resources/views/site/home.blade.php
@@ -1,6 +1,7 @@
@extends('site.layout')
@section('title', 'Dewemoji - Discover')
+@section('meta_description', 'Search emojis by keyword, meaning, and category. Explore Dewemoji with fast API-powered results.')
@push('head')
@stack('head')
+ @stack('jsonld')
diff --git a/app/resources/views/site/pricing.blade.php b/app/resources/views/site/pricing.blade.php
index fd23950..aee2afb 100644
--- a/app/resources/views/site/pricing.blade.php
+++ b/app/resources/views/site/pricing.blade.php
@@ -1,11 +1,86 @@
@extends('site.layout')
@section('title', 'Pricing - Dewemoji')
+@section('meta_description', 'See Dewemoji plans for Free, Pro subscription, and Lifetime. One key unlocks Extension + API with up to 3 Chrome profile activations.')
+
+@push('jsonld')
+
+@endpush
@section('content')
-
- Pricing
- Phase 3 placeholder page. We will wire real pricing content and purchase flow in a later phase.
-
-@endsection
+
+
+
Simple, fair pricing
+
One Pro unlocks both Extension + API.
+
+
+
+ Free
+ For casual usage
+ $0
+
+ - Extension access
+ - API testing access
+ - Daily cap 30 queries
+
+
+
+
+ Pro
+ Most popular
+ $3/mo or $27/yr
+
+ - Unlimited extension usage
+ - API pro access
+ - Up to 3 Chrome profiles
+
+ Subscribe on Gumroad
+
+
+
+ Lifetime
+ Pay once, use forever
+ $69
+
+ - Extension + API access
+ - Future updates included
+ - IDR/Mayar available on request
+
+ Buy Lifetime
+
+
+
+
+
Licensing basics
+
+ - One key unlocks both extension and API.
+ - Use
Authorization: Bearer YOUR_LICENSE_KEY for API requests.
+ - One license supports up to 3 active Chrome profiles.
+
+
+
+@endsection
diff --git a/app/resources/views/site/privacy.blade.php b/app/resources/views/site/privacy.blade.php
index dbcd82e..8b2c6a9 100644
--- a/app/resources/views/site/privacy.blade.php
+++ b/app/resources/views/site/privacy.blade.php
@@ -1,6 +1,7 @@
@extends('site.layout')
@section('title', 'Privacy - Dewemoji')
+@section('meta_description', 'Read Dewemoji privacy information and data handling practices.')
@section('content')
@@ -8,4 +9,3 @@
Phase 3 placeholder page. We will migrate the full privacy text from legacy content in a later pass.
@endsection
-
diff --git a/app/resources/views/site/support.blade.php b/app/resources/views/site/support.blade.php
new file mode 100644
index 0000000..33e0575
--- /dev/null
+++ b/app/resources/views/site/support.blade.php
@@ -0,0 +1,52 @@
+@extends('site.layout')
+
+@section('title', 'Support - Dewemoji')
+@section('meta_description', 'Get help with Dewemoji installation, Pro activation, API usage, billing, and troubleshooting.')
+
+@push('jsonld')
+
+@endpush
+
+@section('content')
+
+
Support
+
Need help with Dewemoji? We got you.
+
+
+
+ Install & activate
+
+ - Install from Chrome Web Store.
+ - Open Dewemoji settings.
+ - Paste key in Pro tab and activate.
+
+
+
+
+ API quick start
+ curl -H "Authorization: Bearer YOUR_LICENSE_KEY" \
+"{{ url('/v1/emojis') }}?q=love&limit=20"
+
+
+
+ Common issues
+
+ - License limit reached: deactivate old profile first.
+ - API 401 invalid key: ensure key is valid and active.
+ - Insert not working: click into input and retry.
+
+ Still stuck? Email hello@dewemoji.com.
+
+
+
+@endsection
diff --git a/app/resources/views/site/terms.blade.php b/app/resources/views/site/terms.blade.php
index 7e18114..b964931 100644
--- a/app/resources/views/site/terms.blade.php
+++ b/app/resources/views/site/terms.blade.php
@@ -1,6 +1,7 @@
@extends('site.layout')
@section('title', 'Terms - Dewemoji')
+@section('meta_description', 'Read Dewemoji terms and conditions for website, extension, and API usage.')
@section('content')
@@ -8,4 +9,3 @@
Phase 3 placeholder page. We will migrate complete terms content from legacy sources in a later pass.
@endsection
-
diff --git a/app/routes/web.php b/app/routes/web.php
index 5db33bd..fe113f0 100644
--- a/app/routes/web.php
+++ b/app/routes/web.php
@@ -4,11 +4,14 @@ use App\Http\Controllers\Web\SiteController;
use Illuminate\Support\Facades\Route;
Route::get('/', [SiteController::class, 'home'])->name('home');
+Route::get('/robots.txt', [SiteController::class, 'robotsTxt'])->name('robots');
+Route::get('/sitemap.xml', [SiteController::class, 'sitemapXml'])->name('sitemap');
Route::get('/browse', [SiteController::class, 'browse'])->name('browse');
Route::get('/api-docs', [SiteController::class, 'apiDocs'])->name('api-docs');
Route::get('/emoji/{slug}', [SiteController::class, 'emojiDetail'])->name('emoji-detail');
Route::get('/pricing', [SiteController::class, 'pricing'])->name('pricing');
+Route::get('/support', [SiteController::class, 'support'])->name('support');
Route::get('/privacy', [SiteController::class, 'privacy'])->name('privacy');
Route::get('/terms', [SiteController::class, 'terms'])->name('terms');
diff --git a/app/tests/Feature/ApiV1EndpointsTest.php b/app/tests/Feature/ApiV1EndpointsTest.php
index d4cee11..4e78403 100644
--- a/app/tests/Feature/ApiV1EndpointsTest.php
+++ b/app/tests/Feature/ApiV1EndpointsTest.php
@@ -2,6 +2,7 @@
namespace Tests\Feature;
+use Illuminate\Support\Facades\Http;
use Tests\TestCase;
class ApiV1EndpointsTest extends TestCase
@@ -96,6 +97,70 @@ class ApiV1EndpointsTest extends TestCase
->assertJsonPath('plan', 'pro');
}
+ public function test_license_verify_uses_gumroad_live_payload_mapping(): void
+ {
+ config()->set('dewemoji.billing.mode', 'live');
+ config()->set('dewemoji.license.accept_all', false);
+ config()->set('dewemoji.license.pro_keys', []);
+ config()->set('dewemoji.billing.providers.gumroad.enabled', true);
+ config()->set('dewemoji.billing.providers.gumroad.verify_url', 'https://api.gumroad.com/v2/licenses/verify');
+ config()->set('dewemoji.billing.providers.gumroad.product_ids', ['prod_123']);
+ config()->set('dewemoji.billing.providers.mayar.enabled', false);
+
+ Http::fake([
+ 'https://api.gumroad.com/*' => Http::response([
+ 'success' => true,
+ 'purchase' => [
+ 'product_id' => 'prod_123',
+ 'recurrence' => 'monthly',
+ ],
+ ], 200),
+ ]);
+
+ $response = $this->postJson('/v1/license/verify', [
+ 'key' => 'gum-live-key',
+ ]);
+
+ $response
+ ->assertOk()
+ ->assertJsonPath('ok', true)
+ ->assertJsonPath('source', 'gumroad')
+ ->assertJsonPath('product_id', 'prod_123');
+ }
+
+ public function test_license_verify_uses_mayar_live_payload_mapping(): void
+ {
+ config()->set('dewemoji.billing.mode', 'live');
+ config()->set('dewemoji.license.accept_all', false);
+ config()->set('dewemoji.license.pro_keys', []);
+ config()->set('dewemoji.billing.providers.gumroad.enabled', false);
+ config()->set('dewemoji.billing.providers.mayar.enabled', true);
+ config()->set('dewemoji.billing.providers.mayar.verify_url', 'https://api.mayar.id/v1/license/verify');
+ config()->set('dewemoji.billing.providers.mayar.api_key', 'secret');
+
+ Http::fake([
+ 'https://api.mayar.id/*' => Http::response([
+ 'success' => true,
+ 'data' => [
+ 'valid' => true,
+ 'product_id' => 'mayar_prod_1',
+ 'type' => 'lifetime',
+ 'expires_at' => null,
+ ],
+ ], 200),
+ ]);
+
+ $response = $this->postJson('/v1/license/verify', [
+ 'key' => 'mayar-live-key',
+ ]);
+
+ $response
+ ->assertOk()
+ ->assertJsonPath('ok', true)
+ ->assertJsonPath('source', 'mayar')
+ ->assertJsonPath('product_id', 'mayar_prod_1');
+ }
+
public function test_emoji_detail_by_slug_endpoint_returns_item(): void
{
config()->set('dewemoji.billing.mode', 'sandbox');
diff --git a/app/tests/Feature/SitePagesTest.php b/app/tests/Feature/SitePagesTest.php
index 297c6ac..87eecc1 100644
--- a/app/tests/Feature/SitePagesTest.php
+++ b/app/tests/Feature/SitePagesTest.php
@@ -21,8 +21,11 @@ class SitePagesTest extends TestCase
$this->get('/animals/animal-mammal')->assertOk();
$this->get('/api-docs')->assertOk();
$this->get('/pricing')->assertOk();
+ $this->get('/support')->assertOk();
$this->get('/privacy')->assertOk();
$this->get('/terms')->assertOk();
+ $this->get('/robots.txt')->assertOk();
+ $this->get('/sitemap.xml')->assertOk();
}
public function test_emoji_detail_page_works_with_valid_slug(): void
diff --git a/migration-test-guide.md b/migration-test-guide.md
new file mode 100644
index 0000000..dd6529d
--- /dev/null
+++ b/migration-test-guide.md
@@ -0,0 +1,121 @@
+# Dewemoji Migration Test Guide (Provider + Frontend)
+
+Follow this after pulling latest `main`.
+
+## 1) Prepare local env
+
+1. Open `app/.env`.
+2. Set local app URL:
+ - `APP_URL=http://127.0.0.1:8000`
+3. Start in sandbox first:
+ - `DEWEMOJI_BILLING_MODE=sandbox`
+ - `DEWEMOJI_LICENSE_ACCEPT_ALL=false`
+4. Keep CORS local during testing:
+ - `DEWEMOJI_ALLOWED_ORIGINS=http://127.0.0.1:8000,http://localhost:8000`
+
+## 2) Run app
+
+1. In `app/`, run:
+ - `php artisan migrate`
+2. Start server:
+ - `php artisan serve --host=127.0.0.1 --port=8000`
+3. Open `http://127.0.0.1:8000`.
+
+## 3) Provider parity test (Live mode)
+
+### A. Gumroad live mapping test
+
+1. Switch to live mode in `.env`:
+ - `DEWEMOJI_BILLING_MODE=live`
+ - `DEWEMOJI_LICENSE_ACCEPT_ALL=false`
+ - `DEWEMOJI_PRO_KEYS=`
+ - `DEWEMOJI_GUMROAD_ENABLED=true`
+ - `DEWEMOJI_GUMROAD_VERIFY_URL=https://api.gumroad.com/v2/licenses/verify`
+ - `DEWEMOJI_GUMROAD_PRODUCT_IDS=
`
+2. Restart `php artisan serve`.
+3. Test verify:
+```bash
+curl -X POST "http://127.0.0.1:8000/v1/license/verify" \
+ -H "Content-Type: application/json" \
+ -d '{"key":""}'
+```
+4. Expect `ok=true`, `source="gumroad"`, tier header `X-Dewemoji-Tier: pro`.
+
+### B. Mayar live mapping test
+
+1. In `.env`, configure:
+ - `DEWEMOJI_MAYAR_ENABLED=true`
+ - `DEWEMOJI_MAYAR_VERIFY_URL=`
+ - OR (`DEWEMOJI_MAYAR_API_BASE` + `DEWEMOJI_MAYAR_ENDPOINT_VERIFY`)
+ - `DEWEMOJI_MAYAR_API_KEY=`
+2. Test verify:
+```bash
+curl -X POST "http://127.0.0.1:8000/v1/license/verify" \
+ -H "Content-Type: application/json" \
+ -d '{"key":""}'
+```
+3. Expect `ok=true`, `source="mayar"`.
+
+### C. Negative check (important)
+
+1. Verify with invalid key.
+2. Expect `401` + `error="invalid_license"` + diagnostics in `details.gumroad` / `details.mayar`.
+
+## 4) Activation lifecycle test
+
+1. Verify key is valid first.
+2. Activate device:
+```bash
+curl -X POST "http://127.0.0.1:8000/v1/license/activate" \
+ -H "Content-Type: application/json" \
+ -d '{"key":"","email":"you@example.com","product":"chrome","device_id":"chrome-profile-1"}'
+```
+3. Expect `ok=true`, `pro=true`.
+4. Deactivate:
+```bash
+curl -X POST "http://127.0.0.1:8000/v1/license/deactivate" \
+ -H "Content-Type: application/json" \
+ -d '{"key":"","product":"chrome","device_id":"chrome-profile-1"}'
+```
+5. Expect `ok=true`.
+
+## 5) Frontend parity test
+
+Open and verify these URLs:
+
+1. `/` (discover page)
+2. `/browse`
+3. `/` example: `/animals`
+4. `//` example: `/animals/animal-mammal`
+5. `/emoji/grinning-face`
+6. `/pricing`
+7. `/api-docs`
+8. `/support`
+9. `/privacy`
+10. `/terms`
+11. `/robots.txt`
+12. `/sitemap.xml`
+
+Checks:
+- Home search updates URL without full reload.
+- Category routes prefill filters correctly.
+- Pricing links point to Gumroad URLs.
+- Support page loads and shows API/auth guidance.
+- `robots.txt` contains sitemap URL.
+- `sitemap.xml` includes core pages and emoji URLs.
+
+## 6) Automated test pass
+
+From `app/` run:
+
+```bash
+php artisan test
+```
+
+Expected: all tests pass.
+
+## 7) Switch back to safe local mode (recommended)
+
+After live-provider testing, return to:
+- `DEWEMOJI_BILLING_MODE=sandbox`
+- disable provider flags unless actively testing them.
diff --git a/rebuild-progress.md b/rebuild-progress.md
index 4c3f420..7380de9 100644
--- a/rebuild-progress.md
+++ b/rebuild-progress.md
@@ -57,9 +57,10 @@
### Phase 3 - Website parity from `dewemoji-live`
- [x] Core pages exist in rebuild.
-- [ ] Add missing pages/routes: `/support`, `/browse`, pretty category routes (`/{category}` and `/{category}/{subcategory}`).
+- [x] Add missing pages/routes: `/support`, `/browse`, pretty category routes (`/{category}` and `/{category}/{subcategory}`).
- [x] `/browse`, `/{category}`, `/{category}/{subcategory}` implemented in rebuild.
-- [ ] Keep URL behavior parity (canonical no-trailing-slash pages, redirect rules, pretty-to-query hydration).
+ - [x] `/support` implemented in rebuild.
+- [x] Keep URL behavior parity (canonical no-trailing-slash pages, redirect rules, pretty-to-query hydration).
- [x] no-trailing-slash redirect middleware and canonical link baseline implemented.
- [x] pretty route hydration wired into homepage initial filters + URL sync.
- [ ] Port homepage behavior parity:
@@ -95,14 +96,14 @@
### Phase 5 - SEO parity (must not disrupt GSC)
- [ ] Preserve canonical strategy for all pages (including emoji detail + pretty category pages).
-- [ ] Add/verify meta + social tags parity: title/description/OG/Twitter + theme color.
+- [x] Add/verify meta + social tags parity baseline: title/description/OG/Twitter + theme color.
- [ ] Port JSON-LD strategy:
- Global `WebSite` + `SearchAction` + Organization.
- - `TechArticle` on `/api-docs`.
- - `Product` + `FAQPage` on `/pricing`.
- - `FAQPage` on `/support`.
- - `CreativeWork` + `BreadcrumbList` on emoji pages.
-- [ ] Implement `robots.txt` parity and dynamic `sitemap.xml` parity.
+ - [x] `TechArticle` baseline on `/api-docs`.
+ - [x] `Product` baseline on `/pricing`.
+ - [x] `FAQPage` baseline on `/support`.
+ - [x] `CreativeWork` + `BreadcrumbList` baseline on emoji pages.
+- [x] Implement `robots.txt` parity and dynamic `sitemap.xml` baseline.
- [ ] Ensure sitemap excludes policy-hidden emoji URLs (same filter policy as live).
- [ ] Keep core indexed URLs stable: `/`, `/pricing`, `/api-docs`, `/support`, `/privacy`, `/terms`, `/emoji/{slug}`.
@@ -173,6 +174,11 @@
- pretty category routes (`/{category}`, `/{category}/{subcategory}`)
- trailing slash -> canonical path redirect (301)
- canonical `` output from layout
+- Added SEO assets baseline in rebuild:
+ - `/robots.txt` route
+ - `/sitemap.xml` route generated from dataset
+ - meta/OG/Twitter fields in shared layout
+ - JSON-LD blocks on `/pricing`, `/support`, `/api-docs`, and emoji detail page
## Live audit highlights (reference)