Billing Setup
Configure Stripe and LemonSqueezy payment providers for your SaaS
The SaaS starter kit supports dual payment providers - Stripe AND LemonSqueezy can work simultaneously. Configure one or both based on your business needs.
Prerequisites
- Stripe account (for Stripe payments) at stripe.com
- LemonSqueezy account (for LemonSqueezy payments) at lemonsqueezy.com
- Basic understanding of webhooks and API keys
Overview
The billing system provides:
- Dual Provider Support: Use Stripe, LemonSqueezy, or both simultaneously
- Flexible Pricing: Configure any combination of monthly, yearly, or one-time payments - the system adapts automatically
- Multiple Plans: Free, Pro, and Enterprise tiers (fully customizable)
- User-Centric Billing: Each user has their own subscription (B2C model)
- Webhook Integration: Automatic subscription status updates
- Customer Portal: Users can manage their billing directly with providers
Stripe Setup
Get Stripe API Keys
Log into your Stripe Dashboard and copy your API keys:
- Navigate to Developers → API Keys
- Copy the "Publishable key" (starts with
pk_) - Copy the "Secret key" (starts with
sk_) - Add both to your
.env.local
1# Stripe Configuration2NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_test_..."3STRIPE_SECRET_KEY="sk_test_..."Security
sk_ secret key to version control. Keep it in .env.local which is gitignored.Create Stripe Products and Prices
In the Stripe Products page:
- Create a product called "Pro Plan"
- Add one or more pricing options (the system adapts automatically):
- Monthly recurring: $19/month (optional)
- Yearly recurring: $190/year (optional)
- One-time payment: $299 (optional)
- Copy each Price ID (starts with
price_) - Repeat for "Enterprise Plan"
Flexible Pricing
Configure Stripe Price IDs
Add your Stripe Price IDs to .env.local. Only add the IDs for the pricing options you created:
1# Stripe Price IDs (only add the ones you're using)2STRIPE_PRICE_PRO_MONTHLY="price_..." # Optional3STRIPE_PRICE_PRO_YEARLY="price_..." # Optional4STRIPE_PRICE_PRO_ONE_TIME="price_..." # Optional5STRIPE_PRICE_ENTERPRISE_MONTHLY="price_..." # Optional6STRIPE_PRICE_ENTERPRISE_YEARLY="price_..." # Optional7STRIPE_PRICE_ENTERPRISE_ONE_TIME="price_..." # OptionalSkip Unused Options
*_ONE_TIME variables and leave the rest empty.These IDs are automatically loaded in src/lib/billing/plans.ts
Set Up Stripe Webhook
Webhooks allow Stripe to notify your app about subscription changes:
- Go to Stripe Webhooks
- Click "Add endpoint"
- Enter your webhook URL:
https://yourdomain.com/api/billing/stripe/webhook - Select events to listen to:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deleted
- Copy the "Signing secret" (starts with
whsec_)
1# Stripe Webhook2STRIPE_WEBHOOK_SECRET="whsec_..."Test Stripe Integration
Use Stripe's test mode to verify your setup:
- Visit your pricing page:
/pricing - Click "Get Started" on any paid plan
- Use test card:
4242 4242 4242 4242 - Complete checkout and verify subscription appears in your dashboard
Testing
LemonSqueezy Setup
Get LemonSqueezy API Key
Log into your LemonSqueezy Settings:
- Navigate to Settings → API
- Create a new API key
- Copy the API key
- Find your Store ID in Settings → Stores
1# LemonSqueezy Configuration2LEMONSQUEEZY_API_KEY="..."3LEMONSQUEEZY_STORE_ID="..."Create LemonSqueezy Products
In the LemonSqueezy Products page:
- Create a product called "Pro Plan"
- Add one or more variants (the system adapts automatically):
- Monthly subscription (optional)
- Yearly subscription (optional)
- One-time payment (optional)
- Copy each Variant ID
- Repeat for "Enterprise Plan"
Flexible Variants
Configure LemonSqueezy Variant IDs
Add your LemonSqueezy Variant IDs to .env.local. Only add the IDs for the variants you created:
1# LemonSqueezy Variant IDs (only add the ones you're using)2LEMONSQUEEZY_VARIANT_PRO_MONTHLY="..." # Optional3LEMONSQUEEZY_VARIANT_PRO_YEARLY="..." # Optional4LEMONSQUEEZY_VARIANT_PRO_ONE_TIME="..." # Optional5LEMONSQUEEZY_VARIANT_ENTERPRISE_MONTHLY="..." # Optional6LEMONSQUEEZY_VARIANT_ENTERPRISE_YEARLY="..." # Optional7LEMONSQUEEZY_VARIANT_ENTERPRISE_ONE_TIME="..." # OptionalSkip Unused Variants
Set Up LemonSqueezy Webhook
Configure webhooks to receive subscription updates:
- Go to Settings → Webhooks
- Click "Create webhook"
- Enter your webhook URL:
https://yourdomain.com/api/billing/lemonsqueezy/webhook - Copy the webhook signing secret
1# LemonSqueezy Webhook2LEMONSQUEEZY_WEBHOOK_SECRET="..."Understanding the Billing System
Plan Configuration
Plans are defined in src/lib/billing/plans.ts:
1export const PLANS = {2 free: {3 id: "free",4 name: "Free",5 description: "Perfect for trying out the platform",6 limits: {},7 stripePriceMonthly: null,8 stripePriceYearly: null,9 lemonVariantMonthly: null,10 lemonVariantYearly: null,11 features: ["Basic features", "Community support"],12 isDefault: true,13 },14 pro: {15 id: "pro",16 name: "Pro",17 description: "For power users and professionals",18 limits: {},19 stripePriceMonthly: process.env.STRIPE_PRICE_PRO_MONTHLY || null,20 stripePriceYearly: process.env.STRIPE_PRICE_PRO_YEARLY || null,21 lemonVariantMonthly: process.env.LEMONSQUEEZY_VARIANT_PRO_MONTHLY || null,22 lemonVariantYearly: process.env.LEMONSQUEEZY_VARIANT_PRO_YEARLY || null,23 priceOneTime: 299,24 features: [25 "All basic features",26 "Priority support",27 "Advanced analytics",28 "Custom integrations",29 ],30 highlighted: true,31 },32 // ... enterprise plan33};Subscription Database Schema
1model Subscription {2 id String @id @default(cuid())3 userId String @unique4 5 // Provider info6 provider PaymentProvider // stripe | lemonsqueezy7 customerId String8 subscriptionId String?9 10 // Plan info11 planId String // free | pro | enterprise12 status SubscriptionStatus // active | canceled | past_due | etc.13 14 // Billing period15 currentPeriodStart DateTime?16 currentPeriodEnd DateTime?17 cancelAtPeriodEnd Boolean @default(false)18 19 user User @relation(fields: [userId], references: [id])20 21 createdAt DateTime @default(now())22 updatedAt DateTime @updatedAt23}Webhook Handling
Webhooks are processed by API routes that update the database:
src/app/api/billing/stripe/webhook/route.ts- Handles Stripe eventssrc/app/api/billing/lemonsqueezy/webhook/route.ts- Handles LemonSqueezy events
When a subscription is created, updated, or canceled, the webhook automatically updates the user's subscription record in the database.
User Flow
- User clicks "Get Started" on pricing page
- System creates/retrieves customer in payment provider
- User is redirected to checkout page (Stripe Checkout or LemonSqueezy)
- User completes payment
- Webhook receives event and creates subscription in database
- User is redirected back to app with active subscription
Troubleshooting
Checkout Not Working
- Verify all API keys are correctly set in
.env.local - Check that Price IDs / Variant IDs match your provider dashboard
- Ensure
NEXT_PUBLIC_APP_URLis set for redirect URLs - Check browser console for errors
Webhook Not Receiving Events
- Verify webhook URL is publicly accessible (not localhost)
- Use ngrok for local development testing
- Check webhook signing secret matches environment variable
- Review webhook logs in provider dashboard
Subscription Not Showing After Payment
- Check webhook was successfully processed (check API logs)
- Verify database was updated (check Prisma Studio)
- Ensure user is logged in with the same account used for payment
Customization
Add New Plans
To add a new plan, update src/lib/billing/plans.ts:
1export const PLANS = {2 // ... existing plans3 custom: {4 id: "custom",5 name: "Custom",6 description: "For special needs",7 limits: {},8 stripePriceMonthly: process.env.STRIPE_PRICE_CUSTOM_MONTHLY || null,9 lemonVariantMonthly: process.env.LEMONSQUEEZY_VARIANT_CUSTOM_MONTHLY || null,10 features: ["Custom features"],11 },12};Customize Plan Features
Edit the features array to change what's displayed on the pricing page.
Add Usage Limits
Extend the PlanLimits interface to add custom limits:
1export interface PlanLimits {2 maxStorage?: number; // in GB3 maxApiCalls?: number; // per month4 maxUsers?: number; // for team plans5}Related Guides
Next Steps
Now that you understand billing, you might want to:
- Learn about Database Schema to understand the data structure
- Explore Admin Dashboard to track revenue
- Review Production Deployment for going live