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.
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 this
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.
Common questions
Why do I need webhooks for Stripe billing?
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.
Can AI app builders add Stripe automatically?
Most can scaffold a basic Stripe integration, including a pricing page that references your price IDs. The areas to verify are server-side session creation and webhook handling - the parts most often incomplete in generated code.
Where should my Stripe secret key live?
Only in server-side environment variables. The secret key must never appear in frontend code or be committed to your repository. The publishable key is the only Stripe key intended for client-side use.
Stripe fees vary by country; the current listing is on the Stripe tool page. Payment confirmation and receipts also depend on reliable transactional email, covered in Lovable email setup.