RG
RemoteGeek Hub
Growth

Add Stripe Subscription Billing

Cursor prompt to add Stripe Checkout subscriptions to a Next.js SaaS — Customer Portal, webhooks, and entitlement checks that match your AI limits.

Problem this prompt solves

Billing bugs are trust bugs: users pay and never get access, or cancel and keep generating forever. MVP Stripe integrations need Checkout, webhooks as source of truth, a customer portal, and server-side entitlement checks on AI routes — not only a “Upgrade” button that sets is_pro=true in the client.

When to use it

  • Your core AI workflow works and you are ready to charge for usage or a Pro plan.
  • You need Stripe Checkout + Customer Portal rather than a custom card form.
  • You must gate AI generations or seats by plan with webhook-synced status.
  • You already have Supabase auth users to attach as Stripe customers.

Cursor prompt

Add Stripe subscription billing to this Next.js App Router + Supabase AI SaaS.

Product defaults (adjust if README already defines plans):
- Free tier: limited AI runs
- Pro tier: higher limits (and optionally stronger model) via Stripe subscription

Implement:
1) Stripe SDK server-side only (STRIPE_SECRET_KEY). Use STRIPE_WEBHOOK_SECRET for webhooks. Publishable key only where required for Checkout redirect patterns you choose.
2) Database: store billing state on profiles or a subscriptions table: stripe_customer_id, stripe_subscription_id, plan status (active, canceled, past_due, trialing), plan price id / plan name, current_period_end. RLS: users can read their own billing fields; only service role / webhook updates them.
3) Checkout: authenticated Route Handler creates a Checkout Session in subscription mode with client_reference_id or metadata.user_id = Supabase user id. Success/cancel URLs back to /app/billing.
4) Customer Portal: Route Handler creates a portal session for the logged-in customer.
5) Webhook route: verify signature; handle at least checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, invoice.payment_failed. Sync status to the database with the service role. Make handlers idempotent.
6) Entitlements: server helper getEntitlements(user) used by AI routes to enforce free vs pro limits. Never trust a client-sent “plan” field.
7) UI: /app/billing showing current plan, upgrade button, manage billing button, and plain-language limit explanations.
8) .env.example + README: products/prices to create in Stripe, webhook forwarding with Stripe CLI for local dev.

Analytics (if helpers exist): fire billing_checkout_started and billing_subscription_active without card details.

Constraints:
- Webhook is source of truth for access — do not grant Pro only from the success URL redirect
- Do not store full card numbers
- Keep to one Pro price for MVP unless plans already exist
- Document how AI rate limits differ by plan

Expected result

Working Checkout + Customer Portal, signature-verified webhooks syncing subscription status to Supabase, and AI routes that enforce plan entitlements without trusting the client.

Implementation notes

  • Test with Stripe test mode and the Stripe CLI before touching live keys.
  • Map Stripe customer ↔ user id in metadata early; recovery is painful later.
  • Grant entitlements only after webhook confirmation; the success page can say “processing” if needed.
  • Align Pro limits with OpenAI unit economics — a $20 plan with unlimited GPT-4o is a hobby.
  • Handle past_due: decide whether to soft-lock AI or allow a grace period; document the choice.
  • Customer Portal reduces support load for cancellations and payment method updates — ship it with Checkout.

Testing checklist

  • Authenticated user can start Checkout in test mode and complete a subscription.
  • Webhook marks the user Pro; AI entitlement helper returns Pro limits.
  • Success URL alone (without webhook) does not permanently grant Pro in your design — webhook sync required.
  • Customer Portal opens and can cancel; webhook moves status to canceled and limits revert.
  • invoice.payment_failed path sets past_due (or your chosen behaviour) and AI gating responds accordingly.
  • User B cannot open a portal session for user A’s customer id.
  • Replay the same webhook event — no duplicate rows / crashes (idempotent).
  • No Stripe secret key in client bundles.

Common mistakes

  • Setting is_pro=true in the success redirect handler without webhooks.
  • Trusting price id or plan from the request body on AI routes.
  • Forgetting webhook signature verification — anyone can POST “access.”
  • Creating a new Stripe customer on every Checkout click — duplicates forever.
  • No portal — you become human billing support for card updates.
  • Unlimited AI on Pro with no rate limit — Stripe revenue becomes OpenAI donate.

Check unit economics

Make sure your Stripe price covers OpenAI cost at expected usage.

Continue

Building something real?

If you’ve moved beyond experimenting and need help defining or building your MVP, RemoteGeek can help turn the idea into a focused implementation plan.

Related resources

Next recommended guide

Add Analytics

Cursor prompt to add privacy-conscious product analytics for an AI SaaS MVP — signup, activation, and generation events that actually inform decisions.

RemoteGeek Builder Notes

One practical lesson each week. No hype.

AI building, automation, and technology-risk notes for professionals and solo builders. Signing up stores your email for follow-up — automated newsletter delivery may be connected later.