The Revenue Engine: Implementing Stripe Subscriptions and Webhooks in Next.js
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.

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
- Forgetting Webhook Verification: If you do not verify the signature, anyone who knows your API endpoint can "tell" your database they paid $10,000.
- 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. - 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_daysparameter.
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_enddate 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
- Create Stripe Products: Log into your Stripe Dashboard (Test Mode) and create a "Pro" subscription product.
- Setup the Stripe Singleton: Initialize the Stripe SDK in
/src/lib/stripe.ts. - Build the Pricing Page: Use DaisyUI to show your plan and link it to your
createCheckoutServer Action. - 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.