Stripe billing
The kit ships Stripe billing built in. /app/billing shows the current plan, payment method, and
invoice history; the pricing page and landing plan grid have checkout buttons. Until you add the
Stripe keys, everything runs in demo mode — the pricing buttons fall back to their demo links
(/register · /contact) and /app/billing shows mock data. This page turns it on.
What you get once it is wired:
- Checkout — pricing CTAs open Stripe Checkout for the selected plan.
- Customer Portal — the "Manage subscription" button opens Stripe's hosted portal (change card, upgrade/downgrade, cancel, download invoices).
- Live billing state — current plan, payment method, and invoices read from Stripe.
- Webhook — subscription changes sync back to the app.
1. Install + keys
Stripe ships with the kit, so there is nothing to install. Create a Stripe account, then copy this
file's keys into .env:
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxVITE_STRIPE_PUBLISHABLE_KEYis the browser's signal that billing is live — when it is unset, every pricing CTA stays in demo mode.STRIPE_SECRET_KEYandSTRIPE_WEBHOOK_SECRETare server-only; never expose them to the client.
Find both keys in the Stripe dashboard under Developers → API keys. Use the test mode keys while developing.
2. Create prices and map them
In the Stripe dashboard, open Product catalog and create a recurring Price for each plan
you sell, then copy each price_… id into .env. The key format is
STRIPE_PRICE_<PLAN>_<INTERVAL>:
STRIPE_PRICE_STARTER_MONTHLY=price_xxxxxxxxxxxx
STRIPE_PRICE_PRO_MONTHLY=price_xxxxxxxxxxxx
STRIPE_PRICE_PRO_YEARLY=price_xxxxxxxxxxxx
STRIPE_PRICE_TEAM_MONTHLY=price_xxxxxxxxxxxx
STRIPE_PRICE_BUSINESS_MONTHLY=price_xxxxxxxxxxxx
STRIPE_PRICE_BUSINESS_YEARLY=price_xxxxxxxxxxxxThe pricing page tiers (Starter / Pro / Team) use the *_MONTHLY ids; the landing plan grid
(Pro / Business) uses MONTHLY or YEARLY based on its monthly/yearly toggle. Only set the keys
for plans you actually offer — extras can stay unset.
3. Set up the webhook
The kit exposes a webhook route at POST /api/stripe/webhook. It verifies the signature with
STRIPE_WEBHOOK_SECRET and keeps the user's plan in sync when a subscription is created, updated,
or canceled.
Locally, forward events with the Stripe CLI and use the signing secret it prints:
stripe listen --forward-to localhost:5173/api/stripe/webhookIn production, add an endpoint in Developers → Webhooks pointing at
https://yourdomain.com/api/stripe/webhook, subscribe to checkout.session.completed and
customer.subscription.updated / customer.subscription.deleted, then copy that endpoint's
signing secret into STRIPE_WEBHOOK_SECRET.
4. Test the setup
- Restart the dev server after editing
.env. - Open
/pricing— the plan buttons now start a real Stripe Checkout instead of the demo link. - Subscribe with the test card
4242 4242 4242 4242(any future expiry, any CVC). - After checkout,
/app/billingshows the live plan, payment method, and invoice. - Click Manage subscription — the Stripe Customer Portal opens; cancel or switch plans there.
Production checklist
Before going live:
- Swap the test keys (
pk_test_…/sk_test_…) for live keys (pk_live_…/sk_live_…). - Recreate your prices in live mode and update the
STRIPE_PRICE_*ids. - Add the production webhook endpoint and set
STRIPE_WEBHOOK_SECRETto its signing secret. - Confirm the Customer Portal is enabled in Settings → Billing → Customer portal.
Useful docs:
Other providers
The billing UI is provider-shaped (hosted checkout, customer portal, subscription state, invoices,
webhooks). If you prefer Lemon Squeezy, Polar, or Paddle, keep the same /app/billing screen and
map that provider's hosted checkout, portal, and webhook onto the same seams.