Overview

Lemon Squeezy is a merchant of record service that allows you to accept payments from your customers by integrating with its API. It's a perfect service for subscription cases. Here's a full checkout flow:

Setup

You need to add more environment variables to your project by following these steps:

  1. Create API Key

    Please create an account and get the API key from Settings > API Keys.

  2. Add Webhook

    Then, add the webhook URL to your project from Settings > Webhooks. For subscription cases, you need to listen to the subscription_created and subscription_updated events.

    code

    https://example.com/api/webhooks/lemonsqueezy
  3. Create Product

    Create a product from the Products menu. After filling in the form, you will have a shareable URL and the product ID.

  4. Update Environment Variables

    Add the following environment variables to .env file with the API key and product ID:

    .env

    LS_KEY= LS_PRODUCT_ID=

Done! You may need to restart the server to apply the changes. And, you can apply for store activation when your landing page is ready.

Client Side

Create a checkout method in the pricing page (app/(landing)/pricing/page.tsx) to hit the order API and redirect the user to the payment page.

page.tsx

'use client' import { LemonSqueezy } from '@/lib/payments/lemonsqueezy' import { hit } from '@/lib/web/hit' import { toast } from 'sonner' export default function Pricing() { // Update this based on your needs const checkout = async (variant: string) => { // Add user authentication check, if needed // if (!user) { // r.push('/auth?r=/pricing') // return // } const resp = await hit('/api/orders', { method: 'POST', body: JSON.stringify({ name: 'John Doe', email: '[email protected]', variant }) }) const order = await resp.json() // Get it from the product page const productShareableURL = 'https://example.lemonsqueezy.com/buy/xxx-xxx-xxxx-xxxx' window.location.href = LemonSqueezy.checkout(order, productShareableURL) } }

Make sure to append the checkout[custom][order_id] parameter to the shareable URL to identify the order ID.

API Endpoint

Here's an example of the API route to create an order in the app/api/orders/route.ts:

route.ts

import { db } from '@/lib/server/db' import { parsePayload } from '@/lib/server/payload' import { NextRequest, NextResponse } from 'next/server' import { z } from 'zod' export const POST = async (req: NextRequest) => { const body = parsePayload( z.object({ name: z.string(), email: z.string().email(), variant: z.string() }).strict(), await req.json() ) if (body instanceof Error) { return NextResponse.json({ error: body.message }, { status: 400 }) } // You may need to create a user if it is not an authenticated request const user = await db.user.create({ data: { name: body.name, email: body.email } }) // Finally, create an order const data = await db.order.create({ data: { status: 'created', variant: body.variant, amount: 1000, integration: 'lemonsqueezy', currency: 'USD', userId: user.id } }) return NextResponse.json(data) }

Webhook Handler

Lastly, you need to create a webhook handler in the app/api/webhooks/lemonsqueezy/route.ts to listen to the events. Here's an example of how to handle the subscription events:

route.ts

import { LemonSqueezy, type LemonSqueezyPayload, type LemonSqueezySubscription } from '@/lib/payments/lemonsqueezy' import { NextRequest, NextResponse } from 'next/server' export const POST = async (req: NextRequest) => { // Parse the payload const body = await req.json() as LemonSqueezyPayload<LemonSqueezySubscription> const lmsqueezy = new LemonSqueezy() // Check if the subscription is new, // return `{ order, subscription, invoice }` object or null const dataNewSub = await lmsqueezy.onSubscriptionCreated(body) // Check if the subscription is updated, // return `{ subscription }` object or null if (!dataNewSub) { const dataUpdateSub = await lmsqueezy.onSubscriptionUpdated(body) } return NextResponse.json({}) }

The other handlers from the LemonSqueezy class are:

  • onOrderCreated: handler for an order is created
  • onLicenseKeyCreated: handler for a license key activation by webhook
  • activate: activate a license key with manual user input