Full Stack 51:102025-08-28

Stripe Subscriptions in Next.js: The Complete Setup

How to implement Stripe recurring billing — checkout sessions, webhooks, customer portal, and subscription status management — in a Next.js App Router app.

Watch on YouTube

Click to open in YouTube

Video Notes & Code

Key takeaways, code snippets, and resources from this video.

Checkout Session

typescript
// app/api/checkout/route.ts
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)

export async function POST(req: Request) {
  const { priceId, userId } = await req.json()

  const session = await stripe.checkout.sessions.create({
    mode: 'subscription',
    payment_method_types: ['card'],
    line_items: [{ price: priceId, quantity: 1 }],
    success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/dashboard?success=1`,
    cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing`,
    metadata: { userId },
  })

  return Response.json({ url: session.url })
}

Webhook Handler

typescript
// app/api/webhooks/stripe/route.ts
export async function POST(req: Request) {
  const sig = req.headers.get('stripe-signature')!
  const body = await req.text()

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

  if (event.type === 'customer.subscription.updated') {
    const sub = event.data.object as Stripe.Subscription
    await db.update(users).set({
      subscriptionStatus: sub.status,
      planId: sub.items.data[0].price.id,
    }).where(eq(users.stripeCustomerId, sub.customer as string))
  }

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

Found this useful?

Subscribe for weekly content on AI engineering, SaaS building, and full stack development.

Book a Call