Skip to content

Stripe Dashboard Setup — Client Runbook

Stripe Dashboard Setup — Client Runbook

Applies to: Projects generated with sscli new --with-payment or sscli new --with-commerce Integration type: Stripe-hosted Checkout (redirect flow — PCI compliant by default) Audience: Developers setting up their Stripe account after code injection


Overview

When you inject the payment feature, your project is pre-wired for Stripe-hosted Checkout:

  1. Your backend creates a Checkout session and returns a checkout_url
  2. Your frontend redirects the user to that URL (window.location.href = checkout_url)
  3. Stripe handles the payment page, card data, and receipt
  4. Stripe calls your /webhooks/stripe endpoint with the outcome

You do not need to build a payment form. You need to configure the Stripe dashboard to match your generated code.


Required Environment Variables

After completing this guide, you will have values for all of these:

VariableWhere usedSource
STRIPE_SECRET_KEYBackend API callsAPI keys page
STRIPE_PUBLISHABLE_KEYFrontend (future use)API keys page
STRIPE_WEBHOOK_SECRETWebhook signature validationWebhook endpoint detail page
STRIPE_PRICE_ID_PRO_ALPHAAlpha/early-access subscription (your price)Product catalog
STRIPE_PRICE_ID_PRO_MONTHLYRecurring monthly subscription (your price)Product catalog
STRIPE_PRICE_ID_PRO_ANNUALRecurring annual subscription (your price)Product catalog
STRIPE_PRICE_ID_FEATURE_UNLOCKOne-time feature unlock (your price)Product catalog
PAYMENT_SUCCESS_URLRedirect after successful paymentYour own frontend URL
PAYMENT_CANCEL_URLRedirect when user cancelsYour own frontend URL

Step 1 — Get Your API Keys

  1. Go to Developers → API keys in the Stripe Dashboard
  2. Copy Publishable key (pk_live_...) → set as STRIPE_PUBLISHABLE_KEY
  3. Reveal and copy Secret key (sk_live_...) → set as STRIPE_SECRET_KEY

Never expose sk_live_... in the frontend or commit it to git. For local development, use test keys (pk_test_... / sk_test_...) in your .env file.


Step 2 — Create Your Products in Stripe

Go to Product catalog → Add product.

Product 1 — [YOUR_PRODUCT] Pro (recurring subscription)

FieldValue
Nombre / Name[YOUR_PRODUCT] Pro
Descripción[Describe what this plan includes for your customers.]
Descripción del cargo en extracto[YOUR_SLUG] PRO
Metadatostierpro
Lista de funcionalidades[Feature 1], [Feature 2], [Feature 3]
Etiqueta de unidadleave blank

Price to add on this product:

FieldValue
Pricing modelRecurring
Billing periodMonthly
Price[YOUR_CURRENCY] [YOUR_MONTHLY_PRICE]
NicknamePro Monthly
Currency[YOUR_CURRENCY]

After saving → copy price_live_... → set as STRIPE_PRICE_ID_PRO_MONTHLY

Then add a second price on the same product for annual:

FieldValue
Pricing modelRecurring
Billing periodYearly
Price[YOUR_CURRENCY] [YOUR_ANNUAL_PRICE] (e.g. 2 months free)
NicknamePro Annual
Currency[YOUR_CURRENCY]

After saving → copy price_live_... → set as STRIPE_PRICE_ID_PRO_ANNUAL


Product 2 — [YOUR_PRODUCT] Pro (Alpha/Early Access) — separate product, locked pricing

This is a separate product for early-access clients only. Capped at [YOUR_ALPHA_SLOT_COUNT] clients. Price is locked forever for anyone who subscribes to this SKU.

FieldValue
Nombre / Name[YOUR_PRODUCT] Pro (Alpha)
DescripciónEarly-access plan. Everything in Pro. Price locked forever as a thank-you for joining early.
Descripción del cargo en extracto[YOUR_SLUG] ALPHA
Metadatostierpro, cohortalpha
Lista de funcionalidadesEverything in Pro, Price locked at [YOUR_ALPHA_PRICE] forever, Feedback sessions (first 3 months), Early-access cohort perks
Etiqueta de unidadleave blank

Price to add:

FieldValue
Pricing modelRecurring
Billing periodMonthly
Price[YOUR_CURRENCY] [YOUR_ALPHA_PRICE]
NicknamePro Alpha Monthly
Currency[YOUR_CURRENCY]

After saving → copy price_live_... → set as STRIPE_PRICE_ID_PRO_ALPHA

When alpha slots fill ([YOUR_ALPHA_SLOT_COUNT] clients), stop sharing this product’s checkout link. Existing subscribers keep their [YOUR_ALPHA_PRICE] forever — never change or archive this price.


Product 3 — Feature Unlock (one-time)

Unlocks any single injected feature for a project (commerce, auth, etc.). Not tied to a subscription.

| Field | Value | | ---------------------- | ------------------------------------------------------------------------------------------------------ | ---- | ----- | | Nombre / Name | Feature Unlock | | Descripción | One-time purchase. Unlocks permanently one selected feature for injection for your projects (commerce | auth | etc) | | Etiqueta de unidad | leave blank |

Price to add:

FieldValue
Pricing modelOne time
Price[YOUR_CURRENCY] [YOUR_UNLOCK_PRICE]
Currency[YOUR_CURRENCY]

After saving → copy price_1T... → set as STRIPE_PRICE_ID_FEATURE_UNLOCK

Price IDs look like price_1Abc123.... Each price has its own ID — if you update the amount, create a new price and update the env var.


Step 3 — Register Your Webhook Endpoint

  1. Go to Developers → Webhooks → Add endpoint

  2. Set Endpoint URL to your backend’s webhook path:

    https://<your-backend-domain>/webhooks/stripe
  3. Under Select events, add these (minimum required by the generated code):

    EventWhy
    payment_intent.succeededUnlock fulfillment trigger
    payment_intent.payment_failedFailure handling
    checkout.session.completedPost-checkout confirmation
    customer.subscription.createdActivate Pro access
    customer.subscription.deletedRevoke Pro access
    invoice.payment_failedSubscription renewal failure
  4. Click Add endpoint

  5. On the endpoint detail page → Signing secret → click Reveal → copy whsec_...

  6. Set as STRIPE_WEBHOOK_SECRET

This secret must match exactly. Each endpoint has its own signing secret. If you create a second endpoint (e.g. for staging), it will have a different whsec_.


Step 4 — Set Your Redirect URLs

Set these to your live frontend routes:

PAYMENT_SUCCESS_URL=https://yourdomain.com/payment/success
PAYMENT_CANCEL_URL=https://yourdomain.com/payment/cancel

For local development:

PAYMENT_SUCCESS_URL=http://localhost:5173/payment/success
PAYMENT_CANCEL_URL=http://localhost:5173/payment/cancel

These are returned by the backend when creating a Checkout session — Stripe redirects the user to one of them after the payment flow completes.


Step 5 — Choose Your Integration Mode (Dashboard prompt)

When Stripe asks “How do you want to accept recurring payments?”, choose:

Formulario de compra prediseñado / Prebuilt checkout form (Checkout)

This matches the generated code. Your backend creates a Checkout session via the Stripe API, and the frontend redirects to Stripe’s hosted page. Do not select Payment Links (no API control) or Elements (requires building your own form).


Step 6 — Verify the Webhook Is Working

After deploying with the env vars set:

  1. Stripe Dashboard → Developers → Webhooks → your endpoint → Send test webhook
  2. Select event type: payment_intent.succeeded
  3. Click Send test webhook
  4. Your backend should log a 200 response

You can also check your backend logs for:

Payment success for <order_id>. Triggering fulfillment.

If you get a 401, your STRIPE_WEBHOOK_SECRET does not match the endpoint’s signing secret. If you get a 422, the webhook body failed signature validation — confirm the raw body is not being parsed before reaching the handler.


Local Testing with Stripe CLI

To test webhooks locally without deploying:

Terminal window
# Install Stripe CLI (https://stripe.com/docs/stripe-cli)
stripe login
stripe listen --forward-to http://localhost:8000/webhooks/stripe

The CLI will print a whsec_test_... key — use that as STRIPE_WEBHOOK_SECRET in your .env during local development.


Summary Checklist

  • STRIPE_SECRET_KEY set (backend)
  • STRIPE_PUBLISHABLE_KEY set
  • Pro Alpha product created → STRIPE_PRICE_ID_PRO_ALPHA set
  • Pro Monthly product created → STRIPE_PRICE_ID_PRO_MONTHLY set
  • Pro Annual product created → STRIPE_PRICE_ID_PRO_ANNUAL set
  • Feature Unlock product created → STRIPE_PRICE_ID_FEATURE_UNLOCK set
  • Webhook endpoint registered at /webhooks/stripe
  • All 6 events selected on the endpoint
  • STRIPE_WEBHOOK_SECRET (whsec_...) set
  • PAYMENT_SUCCESS_URL and PAYMENT_CANCEL_URL set to live URLs
  • Test webhook returns 200 from your backend