Configure Lemon Squeezy Billing for Your Next.js SaaS
Complete guide to setting up Lemon Squeezy payments in Makerkit. Lemon Squeezy is a Merchant of Record that handles global tax compliance, billing, and payments for your SaaS.
Lemon Squeezy is a Merchant of Record (MoR), meaning they handle all billing complexity for you: VAT, sales tax, invoicing, and compliance across 100+ countries. You receive payouts minus their fees.
Why Choose Lemon Squeezy?
Pros:
- Automatic global tax compliance (VAT, GST, sales tax)
- No need to register for tax collection in different countries
- Simpler setup than Stripe for international sales
- Built-in license key generation (great for desktop apps)
- Lower complexity for solo founders
Cons:
- One line item per plan (no mixing flat + metered + per-seat)
- Less flexibility than Stripe
- Higher fees than Stripe in some regions
Prerequisites
- Create a Lemon Squeezy account
- Create a Store in your Lemon Squeezy dashboard
- Create Products and Variants for your pricing plans
- Set up a webhook endpoint
Step 1: Environment Variables
Add these variables to your .env.local:
LEMON_SQUEEZY_SECRET_KEY=your_api_key_hereLEMON_SQUEEZY_SIGNING_SECRET=your_webhook_signing_secretLEMON_SQUEEZY_STORE_ID=your_store_id| Variable | Description | Where to Find |
|---|---|---|
LEMON_SQUEEZY_SECRET_KEY | API key for server-side calls | Settings → API |
LEMON_SQUEEZY_SIGNING_SECRET | Webhook signature verification | Settings → Webhooks |
LEMON_SQUEEZY_STORE_ID | Your store's numeric ID | Settings → Stores |
Keep secrets secure
Add these to .env.local only. Never commit them to your repository or add them to .env.
Step 2: Configure Billing Provider
Set Lemon Squeezy as your billing provider in the environment:
NEXT_PUBLIC_BILLING_PROVIDER=lemon-squeezyAnd update the database:
UPDATE public.config SET billing_provider = 'lemon-squeezy';Step 3: Create Products in Lemon Squeezy
Go to your Lemon Squeezy Dashboard → Products
Click New Product
Configure your product:
- Name: "Pro Plan", "Starter Plan", etc.
- Pricing: Choose subscription or one-time
- Variant: Create variants for different billing intervals
Copy the Variant ID (not Product ID) for your billing schema
The Variant ID looks like 123456 (numeric). This goes in your line item's id field.
Step 4: Update Billing Schema
Lemon Squeezy has a key limitation: one line item per plan. You cannot mix flat, per-seat, and metered billing in a single plan.
import { createBillingSchema } from '@kit/billing';export default createBillingSchema({ provider: 'lemon-squeezy', products: [ { id: 'pro', name: 'Pro', description: 'For professionals', currency: 'USD', plans: [ { id: 'pro-monthly', name: 'Pro Monthly', paymentType: 'recurring', interval: 'month', lineItems: [ { id: '123456', // Lemon Squeezy Variant ID name: 'Pro Plan', cost: 29, type: 'flat', }, // Cannot add more line items with Lemon Squeezy! ], }, ], }, ],});Single line item only
The schema validation will fail if you add multiple line items with Lemon Squeezy. This is a platform limitation.
Step 5: Configure Webhooks
Local Development
Lemon Squeezy requires a public URL for webhooks. Use a tunneling service like ngrok:
# Install ngroknpm install -g ngrok# Expose your local serverngrok http 3000Copy the ngrok URL (e.g., https://abc123.ngrok.io).
Create Webhook in Lemon Squeezy
Go to Settings → Webhooks
Click Add Webhook
Configure:
- URL:
https://your-ngrok-url.ngrok.io/api/billing/webhook(dev) orhttps://yourdomain.com/api/billing/webhook(prod) - Secret: Generate a secure secret and save it as
LEMON_SQUEEZY_SIGNING_SECRET
- URL:
Select these events:
order_createdsubscription_createdsubscription_updatedsubscription_expired
Click Save
Production Webhooks
For production, replace the ngrok URL with your actual domain:
https://yourdomain.com/api/billing/webhookMetered Usage with Lemon Squeezy
Lemon Squeezy handles metered billing differently than Stripe. Usage applies to the entire subscription, not individual line items.
Setup Fee + Metered Usage
Use the setupFee property for a flat base charge plus usage-based pricing:
{ id: 'api-monthly', name: 'API Monthly', paymentType: 'recurring', interval: 'month', lineItems: [ { id: '123456', name: 'API Access', cost: 0, type: 'metered', unit: 'requests', setupFee: 10, // $10 base fee tiers: [ { upTo: 1000, cost: 0 }, { upTo: 'unlimited', cost: 0.001 }, ], }, ],}The setup fee is charged once when the subscription is created.
Reporting Usage
Report usage using the billing API:
import { getBillingGatewayProvider } from '@kit/billing-gateway';async function reportUsage(subscriptionItemId: string, quantity: number) { const service = await getBillingGatewayProvider('lemon-squeezy'); return service.reportUsage({ id: subscriptionItemId, // From subscription_items table usage: { quantity, action: 'increment', }, });}See the metered usage guide for complete implementation details.
Testing
Test Mode
Lemon Squeezy has a test mode. Enable it in your dashboard under Settings → Test Mode.
Test mode uses separate products and variants, so create test versions of your products.
Test Cards
In test mode, use these card numbers:
- Success:
4242 4242 4242 4242 - Decline:
4000 0000 0000 0002
Any future expiry date and any 3-digit CVC will work.
Common Issues
Webhook signature verification failed
- Check that
LEMON_SQUEEZY_SIGNING_SECRETmatches the secret in your Lemon Squeezy webhook settings - Ensure the raw request body is used for verification (not parsed JSON)
- Verify the webhook URL is correct
Subscription not created
- Check webhook logs in Lemon Squeezy dashboard
- Verify the
order_createdevent is enabled - Check your application logs for errors
Multiple line items error
Lemon Squeezy only supports one line item per plan. Restructure your pricing to use a single line item, or use Stripe for more complex pricing models.
Testing Checklist
Before going live:
- [ ] Create test products in Lemon Squeezy test mode
- [ ] Test subscription checkout with test card
- [ ] Verify subscription appears in user's billing section
- [ ] Test subscription cancellation
- [ ] Verify webhook events are processed correctly
- [ ] Test with failing card to verify error handling
- [ ] Switch to production products and webhook URL
Related Documentation
- Billing Overview - Architecture and provider comparison
- Billing Schema - Configure your pricing
- Webhooks - Custom webhook handling
- Metered Usage - Usage-based billing implementation