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

  1. User clicks "Subscribe" on the billing page
  2. POST /api/billing/checkout creates a Stripe Checkout Session
  3. User is redirected to Stripe's hosted checkout
  4. On success, Stripe fires a checkout.session.completed webhook
  5. 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 subscription
  • customer.subscription.updated — Update plan/status
  • customer.subscription.deleted — Cancel subscription
  • invoice.payment_succeeded — Update renewal date
  • invoice.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.