Add download page and wire site navigation
This commit is contained in:
@@ -262,6 +262,11 @@ class SiteController extends Controller
|
||||
return view('site.support');
|
||||
}
|
||||
|
||||
public function download(): View
|
||||
{
|
||||
return view('site.download');
|
||||
}
|
||||
|
||||
public function privacy(): View
|
||||
{
|
||||
return view('site.privacy');
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
<a href="{{ route('home') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="layout-grid" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Discover</span></a>
|
||||
<a href="{{ route('api-docs') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl bg-white/10 text-brand-sun border border-white/5 transition-all"><i data-lucide="book-open" class="w-5 h-5"></i><span class="text-sm hidden lg:block">API Docs</span></a>
|
||||
<a href="{{ route('pricing') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="badge-dollar-sign" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Pricing</span></a>
|
||||
<a href="{{ route('download') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="download" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Download</span></a>
|
||||
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="life-buoy" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Support</span></a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
123
app/resources/views/site/download.blade.php
Normal file
123
app/resources/views/site/download.blade.php
Normal file
@@ -0,0 +1,123 @@
|
||||
@extends('site.layout')
|
||||
|
||||
@section('title', 'Download - Dewemoji')
|
||||
@section('meta_description', 'Download Dewemoji for Chrome and get notified when Android app is available.')
|
||||
|
||||
@push('jsonld')
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@@context": "https://schema.org",
|
||||
"@@type": "SoftwareApplication",
|
||||
"name": "Dewemoji",
|
||||
"applicationCategory": "BrowserApplication",
|
||||
"operatingSystem": "Chrome",
|
||||
"downloadUrl": "https://chromewebstore.google.com/detail/dewemoji-emojis-made-effo/elcikbedkbpkmdhkcmfnkdaacmnpdmha"
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="flex h-screen w-full">
|
||||
<aside class="hidden lg:flex w-20 lg:w-64 h-full glass-panel flex-col justify-between p-4 z-50 shrink-0">
|
||||
<div>
|
||||
<div class="flex items-center gap-3 px-2 mb-8 mt-2">
|
||||
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-white to-gray-300 flex items-center justify-center shadow-lg shadow-white/20 shrink-0">
|
||||
<img src="/assets/logo/logo-mark.svg" alt="Dewemoji logo" class="w-7 h-7 object-contain" />
|
||||
</div>
|
||||
<h1 class="font-display font-bold text-lg tracking-tight hidden lg:block">Dewemoji</h1>
|
||||
</div>
|
||||
<nav class="space-y-1">
|
||||
<a href="{{ route('home') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="layout-grid" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Discover</span></a>
|
||||
<a href="{{ route('api-docs') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="book-open" class="w-5 h-5"></i><span class="text-sm hidden lg:block">API Docs</span></a>
|
||||
<a href="{{ route('pricing') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="badge-dollar-sign" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Pricing</span></a>
|
||||
<a href="{{ route('download') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl bg-white/10 text-brand-sun border border-white/5 transition-all"><i data-lucide="download" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Download</span></a>
|
||||
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="life-buoy" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Support</span></a>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
@auth
|
||||
<a href="{{ route('dashboard.overview') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="layout-dashboard" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Dashboard</span></a>
|
||||
@endauth
|
||||
@guest
|
||||
<a href="{{ route('login') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="log-in" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Login</span></a>
|
||||
@endguest
|
||||
<a href="{{ route('privacy') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="shield-check" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Privacy</span></a>
|
||||
<a href="{{ route('terms') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="file-text" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Terms</span></a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="flex-1 flex flex-col h-full min-w-0 relative z-10">
|
||||
<header class="glass-header px-6 py-5 shrink-0 flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-[11px] uppercase tracking-wider text-gray-500 mb-1">Public / Download</div>
|
||||
<h1 class="font-display text-2xl md:text-3xl font-bold">Get Dewemoji</h1>
|
||||
</div>
|
||||
<button id="theme-toggle" class="w-9 h-9 rounded-full theme-surface border border-white/10 shadow-lg flex items-center justify-center text-gray-300 hover:text-white transition-colors">
|
||||
<span class="sr-only">Toggle theme</span>
|
||||
<i data-lucide="moon" class="w-4 h-4" data-theme-icon="dark"></i>
|
||||
<i data-lucide="sun" class="w-4 h-4 hidden" data-theme-icon="light"></i>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="flex-1 overflow-y-auto p-6 md:p-10">
|
||||
<div class="max-w-5xl mx-auto grid gap-4 lg:grid-cols-2">
|
||||
<section class="glass-card rounded-2xl p-6">
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Available now</div>
|
||||
<h2 class="mt-2 text-2xl font-semibold">Chrome Extension</h2>
|
||||
<p class="mt-2 text-sm text-gray-300">Fast emoji search and copy workflow directly in your browser.</p>
|
||||
<a
|
||||
href="https://chromewebstore.google.com/detail/dewemoji-emojis-made-effo/elcikbedkbpkmdhkcmfnkdaacmnpdmha"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="mt-5 inline-flex items-center gap-2 rounded-full bg-brand-ocean text-white force-white px-5 py-2.5 text-sm font-semibold hover:bg-brand-oceanSoft transition-colors"
|
||||
>
|
||||
<i data-lucide="download" class="w-4 h-4"></i>
|
||||
Install for Chrome
|
||||
</a>
|
||||
<p class="mt-3 text-xs text-gray-400">Existing extension URL stays the same on updates.</p>
|
||||
</section>
|
||||
|
||||
<section class="glass-card rounded-2xl p-6">
|
||||
<div class="text-xs uppercase tracking-[0.25em] text-gray-400">Coming soon</div>
|
||||
<h2 class="mt-2 text-2xl font-semibold">Android App</h2>
|
||||
<p class="mt-2 text-sm text-gray-300">Native app release is in progress. We will launch internal testing first, then public release.</p>
|
||||
<div class="mt-5 inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-xs text-gray-300 bg-white/5">
|
||||
<i data-lucide="smartphone" class="w-4 h-4"></i>
|
||||
Android release in preparation
|
||||
</div>
|
||||
<div class="mt-4 text-xs text-gray-400">
|
||||
Recommended for now: use web dashboard + Chrome extension.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="glass-card rounded-2xl p-6 lg:col-span-2">
|
||||
<h3 class="text-lg font-semibold">Platform Status</h3>
|
||||
<div class="mt-4 grid gap-3 sm:grid-cols-3">
|
||||
<div class="rounded-xl border border-emerald-500/30 bg-emerald-500/10 p-4">
|
||||
<div class="flex items-center gap-2 text-xs uppercase tracking-[0.2em] text-emerald-300">
|
||||
<i data-lucide="chromium" class="w-4 h-4"></i>
|
||||
<span>Chrome</span>
|
||||
</div>
|
||||
<div class="mt-1 text-sm text-emerald-100">Available</div>
|
||||
</div>
|
||||
<div class="rounded-xl border border-amber-500/30 bg-amber-500/10 p-4">
|
||||
<div class="flex items-center gap-2 text-xs uppercase tracking-[0.2em] text-amber-300">
|
||||
<i data-lucide="bot" class="w-4 h-4"></i>
|
||||
<span>Android</span>
|
||||
</div>
|
||||
<div class="mt-1 text-sm text-amber-100">In progress</div>
|
||||
</div>
|
||||
<div class="rounded-xl border border-sky-500/30 bg-sky-500/10 p-4">
|
||||
<div class="flex items-center gap-2 text-xs uppercase tracking-[0.2em] text-sky-300">
|
||||
<i data-lucide="earth" class="w-4 h-4"></i>
|
||||
<span>Web</span>
|
||||
</div>
|
||||
<div class="mt-1 text-sm text-sky-100">Available</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -42,6 +42,10 @@
|
||||
<i data-lucide="badge-dollar-sign" class="w-5 h-5 group-hover:scale-110 transition-transform"></i>
|
||||
<span class="text-sm font-medium hidden lg:block">Pricing</span>
|
||||
</a>
|
||||
<a href="{{ route('download') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all group">
|
||||
<i data-lucide="download" class="w-5 h-5 group-hover:scale-110 transition-transform"></i>
|
||||
<span class="text-sm font-medium hidden lg:block">Download</span>
|
||||
</a>
|
||||
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all group">
|
||||
<i data-lucide="life-buoy" class="w-5 h-5 group-hover:scale-110 transition-transform"></i>
|
||||
<span class="text-sm font-medium hidden lg:block">Support</span>
|
||||
|
||||
@@ -406,6 +406,9 @@
|
||||
<i data-lucide="log-in" class="w-4 h-4"></i><span>Login</span>
|
||||
</a>
|
||||
@endguest
|
||||
<a href="{{ route('download') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
|
||||
<i data-lucide="download" class="w-4 h-4"></i><span>Download</span>
|
||||
</a>
|
||||
<a href="{{ route('support') }}" class="flex items-center gap-3 rounded-xl px-4 py-3 bg-white/5 hover:bg-white/10">
|
||||
<i data-lucide="life-buoy" class="w-4 h-4"></i><span>Support</span>
|
||||
</a>
|
||||
|
||||
@@ -66,6 +66,9 @@
|
||||
<a href="{{ route('pricing') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl bg-white/10 text-brand-sun border border-white/5 transition-all">
|
||||
<i data-lucide="badge-dollar-sign" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Pricing</span>
|
||||
</a>
|
||||
<a href="{{ route('download') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
|
||||
<i data-lucide="download" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Download</span>
|
||||
</a>
|
||||
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all">
|
||||
<i data-lucide="life-buoy" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Support</span>
|
||||
</a>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<a href="{{ route('home') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="layout-grid" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Discover</span></a>
|
||||
<a href="{{ route('api-docs') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="book-open" class="w-5 h-5"></i><span class="text-sm hidden lg:block">API Docs</span></a>
|
||||
<a href="{{ route('pricing') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="badge-dollar-sign" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Pricing</span></a>
|
||||
<a href="{{ route('download') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="download" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Download</span></a>
|
||||
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="life-buoy" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Support</span></a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<a href="{{ route('home') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="layout-grid" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Discover</span></a>
|
||||
<a href="{{ route('api-docs') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="book-open" class="w-5 h-5"></i><span class="text-sm hidden lg:block">API Docs</span></a>
|
||||
<a href="{{ route('pricing') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="badge-dollar-sign" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Pricing</span></a>
|
||||
<a href="{{ route('download') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="download" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Download</span></a>
|
||||
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl bg-white/10 text-brand-sun border border-white/5 transition-all"><i data-lucide="life-buoy" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Support</span></a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<a href="{{ route('home') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="layout-grid" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Discover</span></a>
|
||||
<a href="{{ route('api-docs') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="book-open" class="w-5 h-5"></i><span class="text-sm hidden lg:block">API Docs</span></a>
|
||||
<a href="{{ route('pricing') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="badge-dollar-sign" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Pricing</span></a>
|
||||
<a href="{{ route('download') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="download" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Download</span></a>
|
||||
<a href="{{ route('support') }}" class="flex items-center gap-4 px-3 py-3 rounded-xl text-gray-400 hover:text-white hover:bg-white/5 transition-all"><i data-lucide="life-buoy" class="w-5 h-5"></i><span class="text-sm hidden lg:block">Support</span></a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@ Route::get('/emoji/{slug}', [SiteController::class, 'emojiDetail'])->name('emoji
|
||||
|
||||
Route::get('/pricing', [SiteController::class, 'pricing'])->name('pricing');
|
||||
Route::post('/pricing/currency', [SiteController::class, 'setPricingCurrency'])->name('pricing.currency');
|
||||
Route::get('/download', [SiteController::class, 'download'])->name('download');
|
||||
Route::get('/support', [SiteController::class, 'support'])->name('support');
|
||||
Route::get('/privacy', [SiteController::class, 'privacy'])->name('privacy');
|
||||
Route::get('/terms', [SiteController::class, 'terms'])->name('terms');
|
||||
|
||||
201
deployment-live-walkthrough.md
Normal file
201
deployment-live-walkthrough.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Dewemoji Live Deployment Walkthrough
|
||||
|
||||
This is the production rollout checklist for the `dewemoji` app.
|
||||
|
||||
Use this in order:
|
||||
1. Prepare env
|
||||
2. Deploy code
|
||||
3. Run post-deploy commands
|
||||
4. Ensure admin access
|
||||
5. Verify billing/webhooks/search/auth
|
||||
|
||||
---
|
||||
|
||||
## 1) Pre-Deploy (Env + Infra)
|
||||
|
||||
Set these in live environment first:
|
||||
|
||||
```env
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://your-live-domain.com
|
||||
```
|
||||
|
||||
Core requirements:
|
||||
1. Database points to live DB (`DB_*`).
|
||||
2. `APP_KEY` is set and stable.
|
||||
3. `SESSION_DRIVER`, `CACHE_STORE`, `QUEUE_CONNECTION` configured.
|
||||
4. Billing provider secrets are set (PayPal/Pakasir).
|
||||
5. Allowed origins include live domain.
|
||||
|
||||
Recommended billing cooldown config:
|
||||
|
||||
```env
|
||||
DEWEMOJI_BILLING_PENDING_COOLDOWN_SECONDS=120
|
||||
```
|
||||
|
||||
Webhook URLs:
|
||||
1. PayPal webhook: `https://your-live-domain.com/v1/paypal/webhook`
|
||||
2. Pakasir webhook: `https://your-live-domain.com/webhooks/pakasir`
|
||||
|
||||
---
|
||||
|
||||
## 2) Deploy Code
|
||||
|
||||
From your server app directory (example `/var/www/html`):
|
||||
|
||||
```bash
|
||||
git fetch --all
|
||||
git checkout main
|
||||
git pull origin main
|
||||
```
|
||||
|
||||
Install dependencies if needed:
|
||||
|
||||
```bash
|
||||
composer install --no-dev --optimize-autoloader
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3) Post-Deploy Commands (Required)
|
||||
|
||||
Run in this exact sequence:
|
||||
|
||||
```bash
|
||||
php artisan optimize:clear
|
||||
php artisan migrate --force
|
||||
php artisan config:cache
|
||||
```
|
||||
|
||||
Optional but recommended:
|
||||
|
||||
```bash
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
```
|
||||
|
||||
If you use queue workers:
|
||||
|
||||
```bash
|
||||
php artisan queue:restart
|
||||
```
|
||||
|
||||
Check migration status:
|
||||
|
||||
```bash
|
||||
php artisan migrate:status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4) Ensure Admin User
|
||||
|
||||
Admin access is role-based (`users.role = admin`).
|
||||
|
||||
### Option A: Promote existing user (recommended)
|
||||
|
||||
```bash
|
||||
php artisan tinker --execute="\App\Models\User::where('email','dewemoji@gmail.com')->update(['role'=>'admin']);"
|
||||
```
|
||||
|
||||
### Option B: Create admin user if missing
|
||||
|
||||
```bash
|
||||
php artisan tinker --execute="
|
||||
\$u=\App\Models\User::firstOrCreate(
|
||||
['email'=>'dewemoji@gmail.com'],
|
||||
['name'=>'Dewemoji Admin','password'=>\Illuminate\Support\Facades\Hash::make('ChangeMeNow123!'),'tier'=>'free','email_verified_at'=>now()]
|
||||
);
|
||||
\$u->role='admin';
|
||||
\$u->save();
|
||||
"
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
php artisan tinker --execute="dump(\App\Models\User::where('email','dewemoji@gmail.com')->first(['id','email','role','tier']));"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) Smoke Test Checklist (Live)
|
||||
|
||||
### A. Core App
|
||||
1. Login/register works over HTTPS with no insecure form warnings.
|
||||
2. Dashboard loads.
|
||||
3. Discover search returns emojis.
|
||||
4. Emoji detail page loads.
|
||||
|
||||
### B. Skin Tone
|
||||
1. Discover: change skin tone selector and verify toneable emoji changes.
|
||||
2. Detail: tone chips update hero emoji and copy behavior.
|
||||
3. Refresh page: tone preference persists.
|
||||
|
||||
### C. Account + Keywords
|
||||
1. Free account can create up to active limit.
|
||||
2. Active/inactive keyword behavior reflected in search.
|
||||
3. Private keyword search appears in Discover after creation.
|
||||
|
||||
### D. Billing
|
||||
1. PayPal checkout starts and returns.
|
||||
2. Pakasir QRIS starts and modal polls.
|
||||
3. Pending payment can be resumed.
|
||||
4. Cooldown prevents immediate repeated checkout spam.
|
||||
|
||||
### E. Webhooks
|
||||
1. PayPal event recorded and payment status updates.
|
||||
2. Pakasir event recorded and payment status updates.
|
||||
|
||||
Useful checks:
|
||||
|
||||
```bash
|
||||
tail -n 200 storage/logs/laravel.log
|
||||
php artisan tinker --execute="dump(\App\Models\WebhookEvent::latest()->take(10)->get(['id','provider','event_type','status','created_at'])->toArray());"
|
||||
php artisan tinker --execute="dump(\App\Models\Payment::latest()->take(10)->get(['id','provider','plan_code','status','created_at'])->toArray());"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) Optional: PayPal Plan Sync (Admin)
|
||||
|
||||
From admin dashboard:
|
||||
1. Open pricing admin page.
|
||||
2. Click sync PayPal plans.
|
||||
3. Confirm plan IDs are written and no 500.
|
||||
|
||||
If there is failure, check:
|
||||
|
||||
```bash
|
||||
tail -n 300 storage/logs/laravel.log | grep -Ei "paypal|sync|webhook|error|exception"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7) Extension Release Order
|
||||
|
||||
Release order:
|
||||
1. Site/backend live first.
|
||||
2. Verify API/auth on live domain.
|
||||
3. Update extension default API base to live.
|
||||
4. Publish extension update.
|
||||
|
||||
This avoids extension users hitting endpoints that are not ready.
|
||||
|
||||
---
|
||||
|
||||
## 8) Rollback Strategy
|
||||
|
||||
If release is broken:
|
||||
1. Re-deploy previous known-good git commit.
|
||||
2. Run:
|
||||
|
||||
```bash
|
||||
php artisan optimize:clear
|
||||
php artisan config:cache
|
||||
php artisan queue:restart
|
||||
```
|
||||
|
||||
3. If issue is emoji dataset, use snapshot activation in admin catalog.
|
||||
|
||||
Reference in New Issue
Block a user