This recipe integrates a complete payment system into an MVP — covering Stripe (subscriptions, one-time, usage-based), LemonSqueezy (merchant of record), and PayPal, with webhook handling, customer portal, and subscription lifecycle management. [src1]
sk_test_*) until go-live checklist is complete. [src1]Which payment approach?
├── SaaS with subscriptions AND want maximum control
│ └── PROVIDER A: Stripe Checkout + Billing
├── SaaS AND want zero tax/compliance burden
│ └── PROVIDER B: LemonSqueezy (merchant of record)
├── AI/API product with usage-based pricing
│ └── PROVIDER C: Stripe Meters + Billing
└── Marketplace with split payments
└── PROVIDER D: Stripe Connect
| Provider | Fee | Tax Handling | Setup Time | Payout Speed | Best For |
|---|---|---|---|---|---|
| A: Stripe | 2.9% + $0.30 | Self-managed | 45-60 min | 2 days | SaaS, full control |
| B: LemonSqueezy | 5% + $0.50 | Included (MoR) | 30 min | Net 15 | Solo founders, global |
| C: Stripe Meters | 2.9% + $0.30 | Self-managed | 60-90 min | 2 days | AI/API, metered |
| D: Stripe Connect | 2.9% + $0.30 + fee | Per-account | 2-4 hours | Varies | Marketplaces |
Duration: 10 minutes · Tool: Stripe Dashboard + CLI
Create products and prices via Stripe CLI or Dashboard. Add API keys and price IDs to .env.local. Verify products visible in Stripe Dashboard. [src1]
Verify: Products and prices visible in Stripe Product Catalog. · If failed: Check API key permissions; ensure test mode is active.
Duration: 10-15 minutes · Tool: Code editor
Create app/api/checkout/route.ts that creates a Stripe Checkout session with customer lookup/creation, subscription mode, and success/cancel URLs. For LemonSqueezy, use the Checkouts API. [src1] [src5]
Verify: "Subscribe" button redirects to checkout page; test card 4242 4242 4242 4242 works. · If failed: Check API keys and price/variant IDs.
Duration: 5-10 minutes · Tool: SQL Editor
Create customers table (links users to Stripe customer IDs) and subscriptions table (tracks status, price, period). Enable RLS. Add indexes for webhook lookups.
Verify: Tables created with RLS active. · If failed: Check auth.users reference exists.
Duration: 15-20 minutes · Tool: Code editor
Create app/api/webhooks/stripe/route.ts handling checkout.session.completed, subscription.created/updated/deleted, and invoice.payment_failed. Always verify webhook signatures with stripe.webhooks.constructEvent(). Use Stripe CLI for local forwarding: stripe listen --forward-to localhost:3000/api/webhooks/stripe. [src4] [src5]
Verify: Complete test checkout; subscription appears in database. · If failed: Check webhook signing secret; review Stripe Dashboard webhook logs.
Duration: 10 minutes · Tool: Code editor
Create portal session API route for self-service subscription management. Build server-side getUserSubscription() and requireSubscription() helpers. Enable Customer Portal in Stripe Dashboard.
Verify: Users can open portal to manage subscriptions; feature gates work. · If failed: Enable portal in Stripe Dashboard; verify customer ID is passed correctly.
Duration: 5 minutes · Tool: Stripe Dashboard
Add production webhook endpoint in Stripe Dashboard. Select events to listen for. Copy signing secret to production environment variables. Exclude webhook route from auth middleware.
Verify: Stripe shows successful deliveries. · If failed: Check webhook URL is public; update signing secret in production.
{
"output_type": "payment_system",
"format": "code + configured provider",
"columns": [
{"name": "payment_provider", "type": "string", "description": "Provider used", "required": true},
{"name": "billing_model", "type": "string", "description": "Subscription, one-time, or usage-based", "required": true},
{"name": "plans_configured", "type": "string", "description": "Plan names and price IDs", "required": true},
{"name": "webhook_endpoint", "type": "string", "description": "Webhook URL", "required": true},
{"name": "customer_portal", "type": "boolean", "description": "Self-service portal enabled", "required": true}
],
"expected_row_count": "1",
"sort_order": "N/A",
"deduplication_key": "payment_provider"
}
| Quality Metric | Minimum Acceptable | Good | Excellent |
|---|---|---|---|
| Checkout completion | Flow works end-to-end | + Error handling | + Retry logic |
| Webhook reliability | All events processed | + Idempotent handlers | + Dead letter queue |
| Subscription sync | Status synced to DB | + Plan/price synced | + Usage metrics |
| Customer portal | Users can cancel | + Upgrade/downgrade | + Invoice history |
| Security | Signature verified | + HTTPS + rate limiting | + Stripe Radar |
If below minimum: Verify webhook signature checking. Test with Stripe test cards (success, decline, 3D Secure).
| Error | Likely Cause | Recovery Action |
|---|---|---|
No such price: price_xxx | Wrong price ID or test/live mismatch | Verify price ID in Dashboard; check mode |
| Webhook signature failed | Wrong signing secret or parsed body | Use raw body for verification; update secret |
| Customer not found | Test/live mode mismatch | Ensure consistent mode; recreate customer |
| Subscription in Stripe but not in DB | Webhook failed to process | Check Stripe webhook logs; replay events |
| Webhook endpoint unreachable | Auth middleware blocking or firewall | Exclude webhook route from auth middleware |
| Component | Stripe | LemonSqueezy | Stripe + Tax |
|---|---|---|---|
| Transaction fee | 2.9% + $0.30 | 5% + $0.50 | 2.9% + $0.30 |
| Tax compliance | Self-managed | Included | + 0.5% |
| At $1,000 MRR | ~$32/mo | ~$55/mo | ~$37/mo |
| At $10,000 MRR | ~$320/mo | ~$550/mo | ~$370/mo |
| At $100,000 MRR | ~$3,200/mo | ~$5,500/mo | ~$3,700/mo |
Attackers can POST fake events to grant themselves premium access. [src4]
stripe.webhooks.constructEvent()Use raw request body and webhook signing secret. Return 400 for invalid signatures.
Client state can be manipulated to appear subscribed. [src5]
Query the subscriptions table (synced via webhooks) in server components and middleware.
Violates PCI-DSS and exposes massive liability. [src1]
Stripe handles all card data in their PCI-compliant infrastructure.
Use this recipe when a developer needs to add payment processing to an MVP with authenticated users. Requires working authentication and a database. Covers checkout, subscriptions, webhooks, customer portal, and usage-based billing — not pricing strategy or revenue analytics.