Back to insights
Stripe Payments and BillingFebruary 3, 2026

The Revenue Engine: Implementing Stripe Subscriptions and Webhooks in Next.js

KG

Karl Gusta

Instructor & Founder

Turning Code into Revenue

You have a secure app, a beautiful dashboard, and a database full of potential. But a SaaS is not a business until the first dollar hits your bank account. In the modern ecosystem, Stripe is the undisputed king of payment processing, but integrating it correctly involves more than just a "Buy" button.

In this lesson, we are building a production-ready billing system. We will cover the subscription lifecycle, secure checkout sessions, and the critical world of webhooks to ensure your database always reflects the user's current payment status.

The Problem: The "Sync" Nightmare

The most common failure in SaaS billing is a de-sync between Stripe and your database.

  • Ghost Subscriptions: A user cancels in Stripe, but your app still gives them "Pro" access.
  • Failed Payments: A credit card expires, Stripe retries the payment, but your app never notifies the user.
  • Manual Billing: Trying to build your own "Invoices" page instead of using existing, secure tools.

Stripe payment integration checkout page illustration

The Shift: Stripe as the Source of Truth

In 2026, we follow the Webhook-First approach. We don't rely on the frontend to tell us a payment was successful. Instead, we treat Stripe as the master record. When something happens in Stripe—a new subscription, a cancellation, or a refund—Stripe sends a secure "Webhook" to our Next.js API. Our server listens to these events and updates MongoDB accordingly.

Deep Dive: The Billing Workflow

1. Creating a Checkout Session

When a user clicks "Upgrade," we trigger a Server Action that creates a Stripe Checkout Session. This redirects the user to a secure, Stripe-hosted page, reducing our PCI compliance burden to zero.

javascript
// src/services/stripeService.ts import { stripe } from "@/lib/stripe"; import { auth } from "@/lib/auth"; export async function createCheckout(priceId) { const session = await auth(); if (!session) throw new Error("Unauthorized"); const checkoutSession = 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?success=true`, cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`, customer_email: session.user.email, metadata: { userId: session.user.id }, }); return checkoutSession.url; }

2. Handling Stripe Webhooks in Next.js App Router

Next.js API routes (Route Handlers) are perfect for webhooks. However, we must ensure we are reading the "Raw Body" to verify the Stripe signature, preventing hackers from faking successful payments.

javascript
// src/app/api/webhooks/stripe/route.ts import { NextResponse } from "next/server"; import { stripe } from "@/lib/stripe"; import connectDB from "@/lib/mongodb"; import User from "@/models/User"; export async function POST(req) { const body = await req.text(); const signature = req.headers.get("stripe-signature"); let event; try { event = stripe.webhooks.constructEvent( body, signature, process.env.STRIPE_WEBHOOK_SECRET ); } catch (err) { return NextResponse.json({ error: "Webhook Error" }, { status: 400 }); } if (event.type === "checkout.session.completed") { const session = event.data.object; await connectDB(); await User.findByIdAndUpdate(session.metadata.userId, { stripeCustomerId: session.customer, plan: "pro", status: "active", }); } return NextResponse.json({ received: true }); }

3. "How to implement usage-based pricing in a Next.js Stripe app?"

If your Vertical SaaS charges per "Task" or per "Seat," you can send Usage Records to Stripe. Instead of charging a flat $20/month, you report activity to Stripe's Metered Billing API. Stripe then calculates the total at the end of the billing cycle, allowing you to scale your revenue as your customers grow.

Stripe Subscription Integration Next.js SaaS

Key Benefits and Learning Outcomes

  • Global Compliance: Stripe handles VAT, GST, and sales tax across different countries automatically.
  • Customer Self-Service: By integrating the Stripe Customer Portal, users can manage their own credit cards and cancellations without you writing a single line of UI code.
  • Automated Churn Reduction: Use Stripe's built-in "Smart Retries" to recover failed payments before the user even notices.

Common Mistakes

  1. Forgetting Webhook Verification: If you do not verify the signature, anyone who knows your API endpoint can "tell" your database they paid $10,000.
  2. Hardcoding Price IDs: Use environment variables for your Stripe Price IDs (e.g., STRIPE_PRO_PLAN_ID) so you can test in Sandbox mode without affecting live data.
  3. Ignoring the Trial Period: Users are 3x more likely to convert if you offer a 7-day trial. Stripe handles the trial logic automatically if you pass a subscription_data.trial_period_days parameter.

Stripe Subscriptions in Next.js 2

Pro Tips and Best Practices

  • Test with Stripe CLI: Use the Stripe CLI to trigger mock webhooks on your local machine so you don't have to deploy to Vercel just to test a "Payment Success" flow.
  • Grace Periods: When a subscription is canceled, don't revoke access immediately. Check the current_period_end date from Stripe and let the user enjoy the remaining time they paid for.
  • Tiered Pricing: Use DaisyUI "Pricing Tables" to clearly contrast your "Free" vs "Pro" features.

How This Fits Into the Zero to SaaS Journey

We have reached the "Value Exchange" part of our journey. Your app is no longer a hobby; it is a business. You have the ability to capture value from the problems you are solving for your niche. With billing in place, the only thing left is to put your app on a public URL and start the marketing engine.

Action Plan: What to Build Next

  1. Create Stripe Products: Log into your Stripe Dashboard (Test Mode) and create a "Pro" subscription product.
  2. Setup the Stripe Singleton: Initialize the Stripe SDK in /src/lib/stripe.ts.
  3. Build the Pricing Page: Use DaisyUI to show your plan and link it to your createCheckout Server Action.
  4. Deploy a Webhook: Use a tool like ngrok or the Stripe CLI to connect your local app to Stripe events.

Ready to collect your first payment? The Stripe Subscriptions in Next.js guide has everything you need to cross the finish line.


Next Lesson: Deployment and DevOps: Launching on Vercel with Zero Downtime.

Launch SaaS 14 Days Next.js

Back to all posts

Keep reading

Related articles

View all posts

Build next

Turn this into a plan.

Use the free tools to validate, price, forecast, and shape the SaaS idea before you build.

Explore free tools