Stripe and Lemon Squeezy
The environment variables and settings for each payment provider, and how to switch between them.
Choose the provider
Set the provider in two places so the app and the database agree:
NEXT_PUBLIC_BILLING_PROVIDER=stripeorlemon-squeezy.billing_providerin thepublic.configrow (stripeby default, fromapps/web/supabase/schemas/02-config.sql).
billing.config.ts is validated against the provider at build time.
Stripe
| Variable | Notes |
|---|---|
STRIPE_SECRET_KEY | Server only. Must start with sk_ or rk_ |
STRIPE_WEBHOOK_SECRET | Server only. Must start with whsec_ |
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Must start with pk_. Required unless you use the hosted checkout page |
STRIPE_UI_MODE | embedded_page (default) shows checkout inside your app; hosted_page sends users to Stripe's page |
STRIPE_ENABLE_TRIAL_WITHOUT_CC | true lets users start a plan's trial without entering a card |
The Stripe package checks these prefixes and stops with a message naming the variable if one is wrong.
In the Stripe dashboard:
- Create the products and prices, and copy each price id into
billing.config.ts. - Add a webhook endpoint for
https://<your-domain>/api/billing/webhook. The handler acts oncheckout.session.completed,checkout.session.async_payment_succeeded,checkout.session.async_payment_failed,customer.subscription.updated,customer.subscription.deletedandinvoice.paid. - Turn on the customer portal so users can manage their plan.
Lemon Squeezy
| Variable | Notes |
|---|---|
LEMON_SQUEEZY_SECRET_KEY | API key, server only |
LEMON_SQUEEZY_SIGNING_SECRET | The webhook signing secret |
LEMON_SQUEEZY_STORE_ID | Your store id |
In Lemon Squeezy:
- Create the products and variants, and use the variant ids as line item ids in
billing.config.ts. - Add a webhook for
https://<your-domain>/api/billing/webhookwith the signing secret above. The handler acts onorder_created,subscription_created,subscription_updated,subscription_expiredandsubscription_payment_success.
Lemon Squeezy plans can have only one line item. The config schema rejects more.
Test mode
Use test keys while you build. With Stripe test mode, the card 4242 4242 4242 4242 completes checkout. Run pnpm stripe:listen to receive webhooks locally (see Webhooks).