Billing

Stripe Subscriptions

End-to-end subscription lifecycle with checkout, webhooks, and entitlement updates.

Problem this solves

Subscription logic breaks when billing events and app entitlements are not kept in sync.

When to use it

  • You sell recurring plans and need reliable billing state.
  • You need immediate entitlement updates after checkout or cancellation.
  • You want webhook-driven source-of-truth billing.

Code snippet

typescript
// app/api/webhooks/stripe/route.ts
import Stripe from 'stripe';
import { headers } from 'next/headers';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: Request) {
  const body = await req.text();
  const signature = (await headers()).get('stripe-signature')!;

  const event = stripe.webhooks.constructEvent(
    body,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET!
  );

  if (event.type === 'customer.subscription.updated') {
    const subscription = event.data.object as Stripe.Subscription;
    await syncEntitlements(subscription);
  }

  return new Response(null, { status: 200 });
}

Integration guide

  1. Store customer and subscription IDs in your database.
  2. Use Checkout for plan upgrades and a billing portal for self-service changes.
  3. Handle webhook events idempotently and update entitlements immediately.
  4. Run periodic reconciliation jobs to correct drift.

Next step

Explore the full documentation and variants for this pattern.

Open full docs