Stripe for Vibecoders: From First Charge to Subscriptions
Why this matters
The gap between "my AI app works" and "my AI app makes money" is one Stripe Checkout button. Most vibecoders never cross it. They tell themselves payments are a separate, scary project. They aren't. Stripe in 2026 is one route file, one webhook handler, and a customer portal link. You can ship the whole thing in an afternoon.
Stripe for vibecoders is the minimum viable payments stack: hosted Checkout (so you don't touch card data), signed webhooks (so you don't get spoofed), and the customer portal (so users self-serve cancellations and you don't get refund support tickets). That's it.
The setup
You need:
- A Stripe account in test mode (no review needed).
- A Next.js (or similar) app deployed somewhere with a real URL — webhooks need a public endpoint.
- A user system with stable IDs (Supabase
auth.uid()works perfectly — see Supabase for vibecoders). - 30 minutes.
Step 1: Get the keys + stay in test mode
Stripe Dashboard → Developers → API keys. You get:
STRIPE_PUBLISHABLE_KEY— public, safe to ship to the client.STRIPE_SECRET_KEY— server only.- Plus a webhook signing secret (created in Step 3).
Keep test mode on until you've actually charged a fake card end-to-end. Stripe's test card 4242 4242 4242 4242 with any future date and any CVC is your friend.
# .env.local
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_... # filled in after step 3
Never ship the secret key in client code. If your env var name does not start with NEXT_PUBLIC_, it stays server-side. Cursor and Lovable sometimes get this wrong — always grep for STRIPE_SECRET in your built JS bundle before going live.
Step 2: Spin up Checkout in 20 lines
Hosted Checkout is the lowest-risk path. Stripe handles the form, the card data never touches your server, and you get Apple Pay / Google Pay / SEPA for free.
// app/api/checkout/route.ts
import Stripe from 'stripe'
import { createClient } from '@/lib/supabase/server'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST() {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) return new Response('unauthorized', { status: 401 })
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: 'price_xxx', quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/dashboard?checkout=success`,
cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing`,
customer_email: user.email,
client_reference_id: user.id, // critical for the webhook
})
return Response.json({ url: session.url })
}
Client-side, you POST and redirect:
const { url } = await fetch('/api/checkout', { method: 'POST' }).then(r => r.json())
window.location.href = url
Done. You can charge cards.
Step 3: Verify webhooks — never skip this
The single biggest Stripe mistake vibecoders make is taking webhooks at face value. Anyone can POST to your endpoint with fake JSON. The signature check is non-negotiable.
// app/api/webhooks/stripe/route.ts
import Stripe from 'stripe'
import { headers } from 'next/headers'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(req: Request) {
const body = await req.text() // raw body — do NOT JSON.parse before verify
const sig = (await headers()).get('stripe-signature')!
let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!)
} catch (err) {
return new Response('signature failed', { status: 400 })
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object
const userId = session.client_reference_id
// mark user as paid — write to Supabase, etc.
}
return new Response('ok')
}
In Stripe Dashboard → Developers → Webhooks, add an endpoint pointing at https://yourdomain.com/api/webhooks/stripe, subscribe to checkout.session.completed, customer.subscription.updated, and customer.subscription.deleted. Copy the signing secret into your env. Test with stripe listen locally.
Step 4: Add the customer portal + subscriptions
Stripe's hosted customer portal lets users update cards, cancel, view invoices — without a single line of UI from you. One route:
const session = await stripe.billingPortal.sessions.create({
customer: customerId, // stored from the checkout webhook
return_url: `${process.env.NEXT_PUBLIC_SITE_URL}/dashboard`,
})
return Response.json({ url: session.url })
Link to this from your dashboard's "Manage subscription" button. Stripe handles dunning, payment retries, proration. You handle nothing.
For pricing pages, define your prices once in the Stripe Dashboard with price_xxx IDs and reference them by ID in code. Don't hardcode amounts in your app — when you change pricing, you only change Stripe.
Common mistakes
- Skipping signature verification — Anyone can hit your webhook URL. Without verification, an attacker can mark themselves as paid.
- Parsing the request body before verifying — Stripe needs the raw body.
await req.json()first will break the signature check. - Storing card data in your DB — Don't. Use Stripe's Customer object and store the
customer_idonly. PCI compliance is Stripe's problem when you do it this way. - Hardcoding prices in code — Move prices to Stripe Dashboard, reference by
price_xxx. Future you will thank you. - Forgetting
client_reference_id— Without it, your webhook can't tie the Stripe session back to your user. Always pass it on Checkout creation.
What's next
Pair Stripe with Vercel for hosting — webhook routes Just Work on Fluid Compute — and Supabase for the user system so the client_reference_id flow is one line. Once charges work end-to-end in test mode, flip to live mode, charge yourself a real $1, refund it, and you're shipped.
What are you building?
Claim your handle and publish your app for the world to see.
Claim your handle →Related Articles
Claude Code for Beginners: Building Smarter, Not Just Vibing
Ditch random coding and level up with AI-powered development. Claude Code turns your programming from guesswork to precision engineering.
Building Your First App in Hours with Lovable: A Vibe Coder's Guide
Transform your app idea into reality in hours, not months. Discover how Lovable is revolutionizing software creation for founders.
Crafting the Perfect PRD: An AI Builder's Guide to Precise Product Requirements
Master the art of PRD creation with expert insights that bridge visionary ideas and AI development. Navigate the essential roadmap for turning concepts into reality.