Stripe subscriptions trip up almost every developer the first time. Here's the complete guide webhooks, customer portal, free tiers, and the edge cases that bite you in production.
Step 1: Create Products and Prices in Stripe Dashboard
Before writing any code, set up your products in the Stripe dashboard. Create a product for each tier. For each product, create a recurring price. Copy the price IDs you'll need them.
Step 2: Create a Checkout Session
// app/api/checkout/route.ts
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?success=true",
cancel_url: process.env.NEXT_PUBLIC_URL + "/pricing",
metadata: { user_id: userId },
});
return Response.json({ url: session.url });
}Step 3: Handle Webhooks
This is where most developers get it wrong. You must verify the webhook signature. Handle at minimum: checkout.session.completed, customer.subscription.updated, and customer.subscription.deleted.
Step 4: Customer Portal
Don't build your own subscription management UI. Stripe's customer portal handles plan upgrades, downgrades, cancellations, and payment method updates. It takes 10 minutes to set up.
const portalSession = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: process.env.NEXT_PUBLIC_URL + "/dashboard",
});
redirect(portalSession.url);Need Stripe integrated into your MVP? I ship it in 14 days.