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

  1. Create a Lemon Squeezy account
  2. Create a Store in your Lemon Squeezy dashboard
  3. Create Products and Variants for your pricing plans
  4. Set up a webhook endpoint

Step 1: Environment Variables

Add these variables to your .env.local:

LEMON_SQUEEZY_SECRET_KEY=your_api_key_here
LEMON_SQUEEZY_SIGNING_SECRET=your_webhook_signing_secret
LEMON_SQUEEZY_STORE_ID=your_store_id
VariableDescriptionWhere to Find
LEMON_SQUEEZY_SECRET_KEYAPI key for server-side callsSettings → API
LEMON_SQUEEZY_SIGNING_SECRETWebhook signature verificationSettings → Webhooks
LEMON_SQUEEZY_STORE_IDYour store's numeric IDSettings → Stores

Step 2: Configure Billing Provider

Set Lemon Squeezy as your billing provider in the environment:

NEXT_PUBLIC_BILLING_PROVIDER=lemon-squeezy

And update the database:

UPDATE public.config SET billing_provider = 'lemon-squeezy';

Step 3: Create Products in Lemon Squeezy

  1. Go to your Lemon Squeezy Dashboard → Products

  2. Click New Product

  3. Configure your product:

    • Name: "Pro Plan", "Starter Plan", etc.
    • Pricing: Choose subscription or one-time
    • Variant: Create variants for different billing intervals
  4. 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!
],
},
],
},
],
});

Step 5: Configure Webhooks

Local Development

Lemon Squeezy requires a public URL for webhooks. Use a tunneling service like ngrok:

# Install ngrok
npm install -g ngrok
# Expose your local server
ngrok http 3000

Copy the ngrok URL (e.g., https://abc123.ngrok.io).

Create Webhook in Lemon Squeezy

  1. Go to Settings → Webhooks

  2. Click Add Webhook

  3. Configure:

    • URL: https://your-ngrok-url.ngrok.io/api/billing/webhook (dev) or https://yourdomain.com/api/billing/webhook (prod)
    • Secret: Generate a secure secret and save it as LEMON_SQUEEZY_SIGNING_SECRET
  4. Select these events:

    • order_created
    • subscription_created
    • subscription_updated
    • subscription_expired
  5. Click Save

Production Webhooks

For production, replace the ngrok URL with your actual domain:

https://yourdomain.com/api/billing/webhook

Metered 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

  1. Check that LEMON_SQUEEZY_SIGNING_SECRET matches the secret in your Lemon Squeezy webhook settings
  2. Ensure the raw request body is used for verification (not parsed JSON)
  3. Verify the webhook URL is correct

Subscription not created

  1. Check webhook logs in Lemon Squeezy dashboard
  2. Verify the order_created event is enabled
  3. 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