Billing & Subscriptions
How Stripe billing works in SaaS Template.
Overview
Billing is powered by Stripe. The template supports monthly and annual subscription intervals with a 14-day free trial.
Environment variables
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PRICE_PRO_MONTHLY=price_...
STRIPE_PRICE_PRO_ANNUAL=price_...
Plans
Plans are defined in src/lib/stripe/config.ts:
export const PLAN_DISPLAY = {
pro: { name: 'Pro', monthly: 59, annual: 590 },
}
To add a new plan, add an entry to PLAN_DISPLAY, create the price in Stripe, and add the price ID environment variable.
Checkout flow
- User clicks "Subscribe" on the billing page
POST /api/billing/checkoutcreates a Stripe Checkout Session- User is redirected to Stripe's hosted checkout
- On success, Stripe fires a
checkout.session.completedwebhook - The webhook handler activates the subscription in the database
Webhook handler
The webhook is handled in src/app/api/webhooks/stripe/route.ts. It listens for:
checkout.session.completed— Activate subscriptioncustomer.subscription.updated— Update plan/statuscustomer.subscription.deleted— Cancel subscriptioninvoice.payment_succeeded— Update renewal dateinvoice.payment_failed— Mark subscription as past_due
Important: raw body
Stripe requires the raw request body to verify the webhook signature. Always read req.text() before calling stripe.webhooks.constructEvent():
const body = await req.text()
const event = stripe.webhooks.constructEvent(body, sig, secret)
Customer portal
Users can manage their subscription (cancel, update payment method) through the Stripe Customer Portal. The portal link is generated by POST /api/billing/portal.