Skip to main content
Business & GrowthPayment Services201 lines

Lemonsqueezy

Accept payments with Lemon Squeezy as merchant of record. Use this skill when the project needs to integrate Lemon Squeezy for SaaS subscriptions, digital product sales, license key management, checkout overlay, tax compliance, or affiliate programs. Covers the Lemon Squeezy API, checkout, subscriptions, license keys, webhooks, and Lemon.js.

Quick Summary32 lines
You are a payments specialist who integrates Lemon Squeezy into projects. Lemon
Squeezy is a merchant of record that handles payments, tax, subscriptions, and
license keys. Popular with indie developers and SaaS products for its simplicity.

## Key Points

- Use `custom` data in checkouts to pass your userId for webhook correlation
- Always verify webhook signatures with HMAC
- Use the customer portal URL for subscription self-management
- Use variants for different pricing tiers (monthly/annual, plans)
- Test with test mode before going live
- Use license keys for desktop/plugin software distribution
- Store Lemon Squeezy customer IDs and subscription IDs in your database
- Granting access on checkout redirect without webhook verification
- Not storing the subscription ID — can't manage subscriptions later
- Immediately revoking access on cancellation instead of using `ends_at`
- Building custom billing UI when the customer portal handles it
- Not handling `subscription_payment_failed` — silent access loss

## Quick Example

```bash
npm install @lemonsqueezy/lemonsqueezy.js
```

```typescript
// Programmatic overlay
window.createLemonSqueezy();
window.LemonSqueezy.Url.Open(checkoutUrl);
```
skilldb get payment-services-skills/lemonsqueezyFull skill: 201 lines
Paste into your CLAUDE.md or agent config

Lemon Squeezy Payment Integration

You are a payments specialist who integrates Lemon Squeezy into projects. Lemon Squeezy is a merchant of record that handles payments, tax, subscriptions, and license keys. Popular with indie developers and SaaS products for its simplicity.

Core Philosophy

Built for digital products

Lemon Squeezy is designed for SaaS, digital downloads, and software licenses. It handles the merchant of record responsibilities (tax, invoicing, compliance) so you focus on building product.

License keys for software distribution

Unlike Stripe or Paddle, Lemon Squeezy has first-class license key generation and validation. If you sell desktop apps, plugins, or self-hosted software, license management is built in.

Simple pricing, simple API

The API surface is intentionally smaller than Stripe. Products, variants (plans), checkouts, subscriptions, and webhooks. That's most of what you need.

Setup

Install

npm install @lemonsqueezy/lemonsqueezy.js

Initialize

import { lemonSqueezySetup, getAuthenticatedUser } from '@lemonsqueezy/lemonsqueezy.js';

lemonSqueezySetup({ apiKey: process.env.LEMONSQUEEZY_API_KEY });

// Verify setup
const { data } = await getAuthenticatedUser();
console.log('Authenticated as:', data.data.attributes.name);

Key Techniques

Create checkout

import { createCheckout } from '@lemonsqueezy/lemonsqueezy.js';

const { data } = await createCheckout(storeId, variantId, {
  checkoutData: {
    email: 'user@example.com',
    custom: { userId: '123' },
  },
  productOptions: {
    redirectUrl: 'https://yourdomain.com/success',
  },
});

const checkoutUrl = data.data.attributes.url;
// Redirect user to checkoutUrl

Overlay checkout (Lemon.js)

<script src="https://app.lemonsqueezy.com/js/lemon.js" defer></script>

<a href="https://yourdomain.lemonsqueezy.com/checkout/buy/variant-id"
   class="lemonsqueezy-button">
  Subscribe
</a>
// Programmatic overlay
window.createLemonSqueezy();
window.LemonSqueezy.Url.Open(checkoutUrl);

Subscription management

import {
  getSubscription,
  updateSubscription,
  cancelSubscription,
} from '@lemonsqueezy/lemonsqueezy.js';

// Get subscription details
const { data: sub } = await getSubscription(subscriptionId);
const status = sub.data.attributes.status; // active, past_due, cancelled, etc.

// Update subscription (change plan)
await updateSubscription(subscriptionId, {
  variantId: newVariantId,
  prorationBillingMode: 'prorated_immediately',
});

// Cancel subscription (at period end)
await cancelSubscription(subscriptionId);

// Resume canceled subscription (before period ends)
await updateSubscription(subscriptionId, { cancelled: false });

License key management

import { validateLicenseKey, activateLicenseKey, deactivateLicenseKey } from '@lemonsqueezy/lemonsqueezy.js';

// Validate a license key
const { data } = await validateLicenseKey(licenseKey);
const isValid = data.valid;
const activationLimit = data.meta.activation_limit;

// Activate license for a device
const activation = await activateLicenseKey(licenseKey, 'device-fingerprint');

// Deactivate
await deactivateLicenseKey(licenseKey, activation.data.id);

Customer portal

import { getSubscription } from '@lemonsqueezy/lemonsqueezy.js';

const { data: sub } = await getSubscription(subscriptionId);
const portalUrl = sub.data.attributes.urls.customer_portal;
// Redirect user to portalUrl

Webhook Processing

EventAction
order_createdGrant access, store customer ID
subscription_createdSet up subscription record
subscription_updatedUpdate plan/status
subscription_cancelledSchedule access revocation
subscription_resumedRestore access
subscription_expiredRevoke access
subscription_payment_failedNotify user
license_key_createdStore license key mapping
import crypto from 'crypto';

export async function POST(req: Request) {
  const rawBody = await req.text();
  const signature = req.headers.get('x-signature');
  const secret = process.env.LEMONSQUEEZY_WEBHOOK_SECRET;

  const hmac = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  if (hmac !== signature) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  const eventName = event.meta.event_name;
  const userId = event.meta.custom_data?.userId;

  switch (eventName) {
    case 'order_created':
      await grantAccess(userId, event.data.attributes);
      break;
    case 'subscription_cancelled':
      const endsAt = event.data.attributes.ends_at;
      await scheduleRevocation(userId, endsAt);
      break;
    case 'subscription_payment_failed':
      await notifyPaymentFailed(userId);
      break;
  }

  return new Response('OK');
}

Best Practices

  • Use custom data in checkouts to pass your userId for webhook correlation
  • Always verify webhook signatures with HMAC
  • Use the customer portal URL for subscription self-management
  • Use variants for different pricing tiers (monthly/annual, plans)
  • Test with test mode before going live
  • Use license keys for desktop/plugin software distribution
  • Store Lemon Squeezy customer IDs and subscription IDs in your database

Anti-Patterns

  • Granting access on checkout redirect without webhook verification
  • Not storing the subscription ID — can't manage subscriptions later
  • Immediately revoking access on cancellation instead of using ends_at
  • Building custom billing UI when the customer portal handles it
  • Not handling subscription_payment_failed — silent access loss
  • Using webhooks without signature verification

Install this skill directly: skilldb add payment-services-skills

Get CLI access →

Related Skills

Mollie

Accept payments with Mollie. Use this skill when the project needs to integrate Mollie for European payments, iDEAL, Bancontact, SEPA, Klarna, subscriptions, payment links, or multi-currency checkout. Covers the Mollie API, payments, subscriptions, mandates, refunds, and webhooks. Popular in Netherlands, Belgium, Germany, and across Europe.

Payment Services170L

Paddle

Accept payments with Paddle as merchant of record. Use this skill when the project needs to integrate Paddle for subscription billing, one-time payments, checkout overlay, tax compliance, invoicing, or global payment processing where Paddle handles tax, compliance, and payouts. Covers Paddle Billing API, checkout, subscriptions, transactions, webhooks, and Paddle.js.

Payment Services213L

Paypal

Accept payments with PayPal. Use this skill when the project needs to integrate PayPal for checkout, subscriptions, one-time payments, PayPal buttons, venmo, invoicing, payouts, or PayPal REST API. Covers the PayPal JavaScript SDK, REST API v2, Orders, Subscriptions, Webhooks, and Smart Payment Buttons.

Payment Services224L

Razorpay

Accept payments with Razorpay. Use this skill when the project needs to integrate Razorpay for payments in India and international markets, UPI, subscriptions, payment links, invoicing, route (split payments), or Razorpay Checkout. Covers the Razorpay API, Checkout.js, orders, subscriptions, webhooks, and UPI.

Payment Services223L

Square

Accept payments with Square. Use this skill when the project needs to integrate Square for online payments, in-person POS, invoicing, subscriptions, catalog management, or omnichannel commerce. Covers the Square API, Web Payments SDK, Orders, Subscriptions, Invoices, Catalog, and webhooks.

Payment Services218L

Stripe

Accept payments and manage subscriptions with Stripe. Use this skill when the project needs to integrate Stripe for one-time payments, recurring subscriptions, checkout sessions, payment intents, customer management, invoicing, webhook processing, billing portal, metered billing, or payment method management. Covers the Stripe API, Stripe.js, Checkout, Elements, Billing, and webhooks.

Payment Services260L