Founderflow Logo

Lemon Squeezy Integration

Complete guide to integrating Lemon Squeezy payments in your Founderflow Boilerplate. Learn how to set up subscriptions, handle webhooks, and process payments with this modern payment platform.

Overview

๐Ÿ‹

Lemon Squeezy Payment Processing

Lemon Squeezy is a modern payment platform that handles subscriptions, one-time payments, and digital product sales with built-in tax compliance and global payment methods.

โœ“

Checkout Links

Simple payment links for quick setup

โœ“

Tax Compliance

Automatic tax calculation and compliance

โœ“

Customer Portal

Built-in customer management and billing

โœ“

Global Payments

Support for international payment methods

โœ“

Webhooks

Real-time payment notifications

โœ“

Analytics

Built-in analytics and reporting

Setup & Configuration

Environment Variables

Configure your Lemon Squeezy integration with these environment variables:

.env.local
# Lemon Squeezy Configuration
LEMON_SQUEEZY_API_KEY=sk_test_...
LEMON_SQUEEZY_STORE_ID=your_store_id
LEMON_SQUEEZY_WEBHOOK_SECRET=your_webhook_secret
# For production, use live keys:
# LEMON_SQUEEZY_API_KEY=sk_live_...

Lemon Squeezy Configuration

The Lemon Squeezy client is configured in lib/lemonsqueezy.ts:

lib/lemonsqueezy.ts
import { LemonSqueezy } from '@lemonsqueezy/lemonsqueezy.js';
if (!process.env.LEMON_SQUEEZY_API_KEY) {
throw new Error('LEMON_SQUEEZY_API_KEY is not set');
}
export const lemonsqueezy = new LemonSqueezy({
apiKey: process.env.LEMON_SQUEEZY_API_KEY!,
onError: (error) => console.error(error)
});
export const storeId = process.env.LEMON_SQUEEZY_STORE_ID!;

Payment Flow

Creating a Checkout

Create a checkout session for one-time or subscription payments:

app/api/lemonsqueezy/checkout/route.ts
import { lemonsqueezy, storeId } from '@/lib/lemonsqueezy';
export async function POST(request: Request) {
try {
const { variantId, userId, planId } = await request.json();
const checkout = await lemonsqueezy.createCheckout(storeId, variantId, {
checkoutOptions: {
embed: true,
media: false,
logo: true
},
checkoutData: {
custom: {
userId,
planId
}
}
});
return Response.json({ url: checkout.data.attributes.url });
} catch (error) {
return Response.json({ error: error.message }, { status: 500 });
}
}

Webhook Handling

Process Lemon Squeezy webhooks to update user subscriptions:

app/api/webhook/lemonsqueezy/route.ts
import dbConnect from '@/lib/mongodb';
import User from '@/models/User';
export async function POST(request: Request) {
const body = await request.json();
const signature = request.headers.get('x-signature');
// Verify webhook signature
if (!verifyWebhookSignature(body, signature)) {
return Response.json({ error: 'Invalid signature' }, { status: 400 });
}
if (body.meta.event_name === 'order_created') {
await dbConnect();
const order = body.data;
// Update user subscription status
await User.findOneAndUpdate(
{ id: order.attributes.custom_data.userId },
{ $set: { 'plan.isPremium': true } }
);
}
return Response.json({ received: true });
}

Frontend Integration

Lemon Squeezy Checkout Component

Create a simple checkout component for Lemon Squeezy payments:

components/payments/LemonSqueezyCheckout.tsx
'use client';
export default function LemonSqueezyCheckout({ variantId, userId }: { variantId: string; userId: string; }) {
const handleCheckout = async () => {
const response = await fetch('/api/lemonsqueezy/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ variantId, userId })
});
const { url } = await response.json();
if (url) window.location.href = url;
};
return (
<button
onClick={handleCheckout}
className="bg-yellow-500 hover:bg-yellow-600 text-white px-6 py-2 rounded-lg"
>
Subscribe with Lemon Squeezy
</button>
);
}