Files
myeasycms-v2/docs/billing/lemon-squeezy.mdoc
Giancarlo Buomprisco 7ebff31475 Next.js Supabase V3 (#463)
Version 3 of the kit:
- Radix UI replaced with Base UI (using the Shadcn UI patterns)
- next-intl replaces react-i18next
- enhanceAction deprecated; usage moved to next-safe-action
- main layout now wrapped with [locale] path segment
- Teams only mode
- Layout updates
- Zod v4
- Next.js 16.2
- Typescript 6
- All other dependencies updated
- Removed deprecated Edge CSRF
- Dynamic Github Action runner
2026-03-24 13:40:38 +08:00

267 lines
7.6 KiB
Plaintext

---
status: "published"
label: "Lemon Squeezy"
title: "Configure Lemon Squeezy Billing for Your Next.js SaaS"
order: 3
description: "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](https://lemonsqueezy.com)
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`:
```bash
LEMON_SQUEEZY_SECRET_KEY=your_api_key_here
LEMON_SQUEEZY_SIGNING_SECRET=your_webhook_signing_secret
LEMON_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 |
{% alert type="error" title="Keep secrets secure" %}
Add these to `.env.local` only. Never commit them to your repository or add them to `.env`.
{% /alert %}
## Step 2: Configure Billing Provider
Set Lemon Squeezy as your billing provider in the environment:
```bash
NEXT_PUBLIC_BILLING_PROVIDER=lemon-squeezy
```
And update the database:
```sql
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.
```tsx
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!
],
},
],
},
],
});
```
{% alert type="warning" title="Single line item only" %}
The schema validation will fail if you add multiple line items with Lemon Squeezy. This is a platform limitation.
{% /alert %}
## Step 5: Configure Webhooks
### Local Development
Lemon Squeezy requires a public URL for webhooks. Use a tunneling service like ngrok:
```bash
# 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:
```tsx
{
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:
```tsx
import { createBillingGatewayService } from '@kit/billing-gateway';
async function reportUsage(subscriptionItemId: string, quantity: number) {
const service = createBillingGatewayService('lemon-squeezy');
return service.reportUsage({
id: subscriptionItemId, // From subscription_items table
usage: {
quantity,
action: 'increment',
},
});
}
```
See the [metered usage guide](/docs/next-supabase-turbo/billing/metered-usage) 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
## Related Documentation
- [Billing Overview](/docs/next-supabase-turbo/billing/overview) - Architecture and provider comparison
- [Billing Schema](/docs/next-supabase-turbo/billing/billing-schema) - Configure your pricing
- [Webhooks](/docs/next-supabase-turbo/billing/billing-webhooks) - Custom webhook handling
- [Metered Usage](/docs/next-supabase-turbo/billing/metered-usage) - Usage-based billing implementation