Back to Blog
·14 min

Stripe Subscriptions with Next.js: Complete Integration Guide 2026

Stripe subscriptions are the backbone of SaaS billing. This guide covers the complete integration with Next.js App Router, including product setup, checkout, webhooks, and the customer portal.

Setting Up Products in Stripe

Create products and prices programmatically for consistency:

import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)

async function createProductWithPrices() {
  const product = await stripe.products.create({ name: 'Pro Plan' })
  const monthly = await stripe.prices.create({
    product: product.id, unit_amount: 2900, currency: 'usd',
    recurring: { interval: 'month' },
  })
  const yearly = await stripe.prices.create({
    product: product.id, unit_amount: 29000, currency: 'usd',
    recurring: { interval: 'year' },
  })
  return { product, monthly, yearly }
}

Checkout Flow

Use Stripe Checkout for a hosted payment page:

// 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_URL}/dashboard?upgraded=true`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
    metadata: { userId },
    subscription_data: { trial_period_days: 14 },
  })
  return Response.json({ sessionId: session.id })
}

Webhook Handling

Handle essential events to keep your database in sync:

export async function POST(req: Request) {
  const body = await req.text()
  const sig = (headers()).get('stripe-signature')!
  const event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!)

  switch (event.type) {
    case 'checkout.session.completed': {
      const session = event.data.object as Stripe.Checkout.Session
      await prisma.user.update({
        where: { id: session.metadata!.userId },
        data: { stripeCustomerId: session.customer as string, plan: 'pro' },
      })
      break
    }
    case 'customer.subscription.deleted': {
      const sub = event.data.object as Stripe.Subscription
      await prisma.user.updateMany({
        where: { stripeSubscriptionId: sub.id },
        data: { plan: 'free', subscriptionStatus: 'cancelled' },
      })
      break
    }
    case 'invoice.payment_failed': {
      const invoice = event.data.object as Stripe.Invoice
      await sendPaymentFailedEmail(invoice.customer_email!)
      break
    }
  }
  return Response.json({ received: true })
}

Customer Portal

Let customers manage subscriptions through the Stripe Customer Portal with one API call.

Usage-Based Billing

For metered pricing, report usage to Stripe with subscriptionItems.createUsageRecord.

The Bottom Line

Stripe integration follows a clear pattern: products, checkout, webhooks, portal. The Ecommerce Starter from BreafIO includes pre-built Stripe integration. The SaaS Starter Kit provides complete subscription management. The Subscription Hub offers focused billing. The API Boilerplate is great for billing services, and the Landing Page Bundle helps market pricing tiers.

Ready to Build?

Get started with our production-ready starter kits and ship your project faster.

Browse Starter Kits