In this guide
Stripe is the default way to charge money in a vibe-coded SaaS, and most AI builders can scaffold a basic integration. The parts they tend to get wrong are the parts that matter most for getting paid reliably: server-side session creation and webhook handling. This guide covers the full flow so your billing actually works in production.
You do not need to be a payments expert, but you do need to understand the shape of the integration so you can verify the AI's output rather than trusting it blindly.
Step 1: Pick a billing model first
Before touching any code, decide how you are charging. This determines which Stripe objects you need and shapes the entire integration.
- Flat subscription tiers (e.g. $9/mo Pro) — use Stripe Billing subscriptions.
- Usage-based billing — use metered subscriptions that report usage.
- One-time purchase — use a simple Checkout Session in payment mode.
Step 2: Create products and prices in Stripe
In the Stripe Dashboard (in test mode first), create a Product for each plan and attach one or more Prices to it. Each Price has an ID that your app references when starting checkout. Most AI builders can use these price IDs directly when generating a pricing page.
Tip
Always build and test in Stripe's test mode using test card numbers before switching to live keys. Test and live objects are completely separate — price IDs do not carry over.
Step 3: Create the Checkout Session server-side
Checkout Sessions must be created on the server, never directly in the browser, because creating one requires your secret key. Prompt your builder to create a backend function — a Supabase Edge Function or an API route — that creates the session and returns its URL for the browser to redirect to.
// Server-side only (e.g. Supabase Edge Function or API route)
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const session = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [{ price: "price_xxx", quantity: 1 }],
success_url: "https://yourapp.com/success",
cancel_url: "https://yourapp.com/pricing",
customer_email: user.email,
});
return { url: session.url }; // redirect the browser to thisWatch out
Your STRIPE_SECRET_KEY must live only in server-side environment variables, never in frontend code or your repository.
Step 4: Handle the webhook (the step AI skips)
This is the most frequently missing piece in AI-generated billing. The redirect back to your app after payment does not reliably tell your database that a user has paid — Stripe webhooks do. Your app needs an endpoint that listens for events like checkout.session.completed and customer.subscription.updated, and updates the user's plan in your database when they fire.
Without this, a user can pay and still appear unpaid in your app. Verify explicitly that a webhook handler exists, is subscribed to the right events, and verifies the Stripe signature before trusting the payload.
- checkout.session.completed — mark the user as subscribed.
- customer.subscription.updated — handle upgrades, downgrades, and renewals.
- customer.subscription.deleted — revoke access on cancellation.
Step 5: Add the Customer Portal
Rather than building your own UI for plan changes, cancellations, and invoice history, use Stripe's hosted Customer Portal. You generate a portal session server-side and link the user to it. This offloads a surprising amount of billing UI and edge-case handling to Stripe.
Key takeaways
- Decide your billing model before writing any integration code.
- Create products and prices in Stripe test mode first.
- Create Checkout Sessions server-side, never in the browser.
- Webhooks — not the post-payment redirect — are what update a user's plan.
- Use Stripe's hosted Customer Portal instead of building billing UI yourself.
Frequently asked questions
The redirect back to your app after checkout is not a reliable signal that payment succeeded. Stripe webhooks (such as checkout.session.completed) are the authoritative events that should update a user's subscription status in your database. AI builders frequently omit this, which leaves billing broken in subtle ways.