Skip to content
prod e051e98
Browse

1 · MUST path

Objective — walk the launch-blocking spine of Phase 8 in order — brand & core settings, transactional mail with DNS auth, a payment gateway with verified webhooks, plans & feature flags, then a full seed + verify pass — so the product is provably sellable.

The launch-blocking spine of Phase 8: do these in order and the product is sellable. Each step summarizes the MUST work and links to the deep page for the SHOULD/OPTIONAL depth. If you only have one afternoon, this is the page.

flowchart LR
A["1. Brand & settings"] --> B["2. Transactional mail"]
B --> C["3. Payment gateway"]
C --> D["4. Verify webhook signature"]
D --> E["5. Plans & feature flags"]
E --> F["6. Seed + verify every flow"]
F --> G(["Launch-ready"])

Set the visual identity and the core application values before anything downstream (mail templates, checkout branding) consumes them.

  1. Lock the brand kit. Logo light/dark, favicon, OG image, and a color palette run through a WCAG 4.5:1 contrast check, plus heading/body fonts. (Deep page: Brand kit.)

    • ✅ Logo set, favicon, OG image, and a WCAG-checked palette are ready.
  2. Enter the core values in the admin panel. App name, default locale/timezone, primary color.

    • ✅ App name, locale, timezone, and primary color saved in the admin UI.
  3. Save brand values as reproducible reference files in your project context — not just in the UI.

    • ✅ Brand values exist as versioned reference files, not only in the admin panel.

Pick one provider, authenticate the sending domain via DNS, then wire credentials as placeholders and prove a real send lands in the inbox.

  1. Wire the mail credentials as placeholders in the production environment.

    Terminal window
    # .env — placeholders only; real values come from your secrets manager
    MAIL_MAILER=smtp
    MAIL_HOST=<smtp-host>
    MAIL_PORT=587
    MAIL_USERNAME=<smtp-username>
    MAIL_PASSWORD=<smtp-api-key>
    MAIL_ENCRYPTION=tls
    MAIL_FROM_ADDRESS=noreply@<your-domain>
    MAIL_FROM_NAME="<App Name>"
    # Expected: env loads with the MAIL_* placeholders present, real values from the vault
    • ✅ The MAIL_* block is wired with placeholders; real values stay in the secrets manager.
  2. Send a real test and confirm it lands in the inbox (not spam) with authentication passing.

    Terminal window
    php artisan tinker
    >>> Mail::raw('Test '.now(), fn($m) => $m->to('you@<your-domain>')->subject('Mail test'));
    # Expected: arrives in inbox; headers show spf=pass, dkim=pass, dmarc=pass
    • ✅ The test email arrives in the inbox with spf=pass, dkim=pass, dmarc=pass.

Create the external account, generate test keys first, enter them in the admin panel (built-in module) or wire env placeholders (custom integration), then register the webhook endpoint and store its signing secret.

  1. Generate test keys and wire them — paste into the admin panel module, or set env placeholders for a custom integration.

    Terminal window
    # .env — placeholders; never commit real keys
    STRIPE_KEY=<pk_test_xxx>
    STRIPE_SECRET=<sk_test_xxx>
    STRIPE_WEBHOOK_SECRET=<whsec_xxx>
    # Expected: the three placeholders are present; real keys live in the vault
    • ✅ Test keys are in place (admin panel or env) and the webhook signing secret is stored.

4. Verify the webhook signature (critical)

Section titled “4. Verify the webhook signature (critical)”

A handler that trusts unverified POST bodies lets anyone upgrade themselves to enterprise for free. Confirm signature verification runs, then exercise it locally.

  1. Forward and trigger a webhook to confirm the signature verifies.

    Terminal window
    stripe listen --forward-to https://<app>.test/stripe/webhook
    stripe trigger checkout.session.completed
    # Expected: handler runs, signature verifies, subscription row created/updated
    • ✅ The handler runs, the signature verifies, and a subscription row is created or updated.

Define tiers, intervals, and prices in the admin plans manager (or seed them in code), and map each tier to the features it unlocks. The provider-side Products/Prices must also exist in the gateway dashboard — a plan that exists only in your DB will 500 at checkout.

  1. Define the plans in the admin plans manager, or seed them in code when no admin UI exists.

    Terminal window
    # Code-based fallback only — when no admin plans UI exists
    php artisan make:seeder PlansSeeder
    php artisan db:seed --class=PlansSeeder
    # Expected: the seeder runs and the plan rows exist
    • ✅ Tiers, intervals, and prices exist, and each tier maps to the features it unlocks.
  2. Confirm the provider-side Products/Prices exist in the gateway dashboard, matching the DB price IDs.

    • ✅ Each plan’s price maps to a real provider-side Price ID — no DB-only plans.

Prove the product end-to-end in staging before launch. This is the gate: nothing ships until every flow is proven.

  1. Exercise the upgrade and decline paths. Test card 4242 4242 4242 4242 upgrades a user; declined card 4000 0000 0000 0002 shows an error and does not upgrade.

    • ✅ The test card upgrades the user; the declined card errors and does not upgrade.
  2. Confirm /pricing renders correctly. All tiers show with correct prices, and each price maps to a real provider-side Price ID.

    • /pricing renders every tier with correct prices mapped to real Price IDs.
  3. Confirm the transactional emails arrive. Password reset and welcome emails arrive and render in Gmail, Outlook, and an iOS client.

    • ✅ Password reset and welcome emails arrive and render across Gmail, Outlook, and iOS.
  4. Run a small live payment, then refund it. A real charge confirms the live keys work end-to-end.

    • ✅ A small live payment succeeds and is refunded cleanly.

Do not mark this step done until every box below is checked.

  • 🔀 Brand applied — set in the admin panel and saved as reproducible reference files.
  • 🔀 Test mail lands — arrives in the inbox with spf/dkim/dmarc=pass.
  • 🔀 Payment gateway connected — test keys wired; webhook signature verified.
  • 👤 Plans render/pricing shows correct prices; gateway Products/Prices exist.
  • 🔀 Every flow proven in staging — upgrade, decline, emails, live charge + refund.