Skip to main content
Business & GrowthEcommerce Services237 lines

Lemonsqueezy

Lemon Squeezy is an all-in-one platform for selling digital products and subscriptions.

Quick Summary17 lines
You are a Lemon Squeezy integration specialist who expertly sets up and manages digital product sales and subscriptions. You configure stores, define products, orchestrate checkout flows, process payments, and leverage webhooks for real-time event handling, ensuring global tax compliance and seamless customer experiences.

## Key Points

*   **Secure API Keys**: Never expose your private API key in client-side code. Use environment variables and server-side processing for all API interactions.
*   **Verify Webhook Signatures**: Always validate the `X-Signature` header in webhook requests to ensure the payload genuinely originated from Lemon Squeezy and hasn't been tampered with.
*   **Test in Sandbox**: Develop and test your integrations thoroughly using a Lemon Squeezy sandbox store before deploying to production.

## Quick Example

```bash
npm install @lemonsqueezy/lemonsqueezy.js
# or
yarn add @lemonsqueezy/lemonsqueezy.js
```
skilldb get ecommerce-services-skills/LemonsqueezyFull skill: 237 lines
Paste into your CLAUDE.md or agent config

You are a Lemon Squeezy integration specialist who expertly sets up and manages digital product sales and subscriptions. You configure stores, define products, orchestrate checkout flows, process payments, and leverage webhooks for real-time event handling, ensuring global tax compliance and seamless customer experiences.

Core Philosophy

Lemon Squeezy's core philosophy is to simplify the complex world of selling digital products and managing subscriptions globally. It acts as a Merchant of Record, taking on the burden of sales tax, VAT, and GST calculation and remittance across various jurisdictions. This frees you from the intricate legal and financial overhead associated with international digital sales, allowing you to focus on product development and marketing.

The platform provides a comprehensive suite of tools, from storefronts and product management to robust subscription logic and affiliate programs. Its API-first design ensures that while you can use its hosted solutions out-of-the-box, you also have the flexibility to integrate deeply into custom applications, offering a bespoke experience while still benefiting from its underlying compliance and payment infrastructure. Choose Lemon Squeezy when you need a powerful, compliant, and developer-friendly solution for recurring revenue or one-time digital sales.

Setup

To integrate Lemon Squeezy, you first need an account and an API key. Obtain your API key from your Lemon Squeezy dashboard under Settings > API. You'll need a "Private API Key" for server-side interactions.

For Node.js applications, use the official Lemon Squeezy SDK:

Install

npm install @lemonsqueezy/lemonsqueezy.js
# or
yarn add @lemonsqueezy/lemonsqueezy.js

Configure

Initialize the SDK with your private API key. It's crucial to store your API key securely, preferably in environment variables.

// server.js or api-handler.js
import { LemonSqueezy } from '@lemonsqueezy/lemonsqueezy.js';
// For CommonJS: const { LemonSqueezy } = require('@lemonsqueezy/lemonsqueezy.js');

// Initialize the Lemon Squeezy client
const ls = new LemonSqueezy(process.env.LEMON_SQUEEZY_API_KEY);

export default ls; // Export for use in other modules

Key Techniques

1. Generating a Dynamic Checkout Link

Lemon Squeezy's hosted checkout pages are powerful. You can generate a checkout URL for any product with custom parameters like variants, discounts, and pre-filled customer details.

// Example: Generate a checkout link for a product with a specific variant
import ls from './ls-client'; // Assuming ls client is initialized and exported as above

async function createProductCheckout(productId, variantId, customerEmail = '') {
  try {
    const checkout = await ls.createCheckout({
      storeId: process.env.LEMON_SQUEEZY_STORE_ID, // Your store ID
      productId: productId,
      variantId: variantId,
      // Optional: Prefill customer details or apply a discount
      checkoutOptions: {
        embed: false, // Set to true for embeddable checkout
        media: false,
        buttonColor: '#36D199',
      },
      productOptions: {
        name: 'My Custom Product Name', // Override product name on checkout
        description: 'A customized description for this purchase',
        // quantity: 1, // Optional: specify quantity
        // If you need custom fields:
        // custom: {
        //   userId: 'user_123',
        //   campaign: 'launch_promo',
        // },
      },
      // You can also pass a `checkoutData` object to pre-fill customer info
      checkoutData: {
        email: customerEmail,
        // taxNumber: 'GB123456789',
        // billingAddress: { country: 'GB' },
      }
    });

    // The checkout object contains a `data.attributes.url` for the checkout link
    if (checkout.data && checkout.data.attributes && checkout.data.attributes.url) {
      return checkout.data.attributes.url;
    } else {
      throw new Error('Could not retrieve checkout URL.');
    }
  } catch (error) {
    console.error('Error creating checkout:', error.response ? error.response.data : error.message);
    throw error;
  }
}

// Usage example (e.g., in an Express route)
// app.get('/buy/:productId/:variantId', async (req, res) => {
//   try {
//     const { productId, variantId } = req.params;
//     const checkoutUrl = await createProductCheckout(parseInt(productId), parseInt(variantId), req.user?.email);
//     res.redirect(checkoutUrl);
//   } catch (error) {
//     res.status(500).send('Failed to create checkout.');
//   }
// });

2. Handling Webhook Events

Webhooks are essential for reacting to events like successful purchases, subscription updates, or refunds. Lemon Squeezy sends POST requests to your configured webhook URL. Always verify the webhook signature.

// Example: Basic Express.js webhook handler
import express from 'express';
import bodyParser from 'body-parser';
import crypto from 'crypto';

const app = express();

// Raw body parser for signature verification
app.post('/webhook/lemonsqueezy', bodyParser.raw({ type: 'application/json' }), async (req, res) => {
  const secret = process.env.LEMON_SQUEEZY_WEBHOOK_SECRET;
  const hmac = crypto.createHHmac('sha256', secret);
  const digest = Buffer.from(hmac.update(req.body).digest('hex'), 'utf8');
  const signature = Buffer.from(req.get('X-Signature') || '', 'utf8');

  if (!crypto.timingSafeEqual(digest, signature)) {
    console.error('Webhook signature verification failed.');
    return res.status(401).send('Invalid signature.');
  }

  const payload = JSON.parse(req.body.toString());
  const eventName = payload.meta.event_name;

  console.log(`Received Lemon Squeezy event: ${eventName}`);

  try {
    switch (eventName) {
      case 'order_created':
        // Handle new purchase: fulfill digital product, send welcome email
        const order = payload.data;
        console.log(`New order #${order.id} for ${order.attributes.user_email}.`);
        // Example: Trigger product delivery
        // await fulfillOrder(order.id, order.attributes.user_email);
        break;
      case 'subscription_created':
        // Handle new subscription: grant access to premium features
        const subscription = payload.data;
        console.log(`New subscription #${subscription.id} for ${subscription.attributes.user_email}.`);
        // Example: Update user's access level in your database
        // await updateUserSubscriptionStatus(subscription.id, subscription.attributes.user_email, 'active');
        break;
      case 'subscription_updated':
        // Handle changes like plan upgrades/downgrades, payment failures
        console.log('Subscription updated:', payload.data.id, payload.data.attributes.status);
        break;
      case 'subscription_cancelled':
        // Handle cancellation: revoke access after period ends
        console.log('Subscription cancelled:', payload.data.id);
        break;
      // Add other event handlers as needed
      default:
        console.warn(`Unhandled Lemon Squeezy event: ${eventName}`);
    }
    res.status(200).send('Webhook received successfully.');
  } catch (error) {
    console.error('Error processing webhook:', error);
    res.status(500).send('Internal server error.');
  }
});

// app.listen(3000, () => console.log('Webhook server listening on port 3000'));

3. Fetching Store Data (Products, Orders)

You can retrieve various resources from your Lemon Squeezy store using the API. This is useful for building custom dashboards, product listings, or syncing data with your internal systems.

// Example: Fetching all products from your store
import ls from './ls-client'; // Assuming ls client is initialized and exported as above

async function getAllProducts() {
  try {
    const productsResponse = await ls.getProducts();
    return productsResponse.data; // Array of product objects
  } catch (error) {
    console.error('Error fetching products:', error.response ? error.response.data : error.message);
    throw error;
  }
}

// Example: Fetching a specific order by ID
async function getOrderById(orderId) {
  try {
    const orderResponse = await ls.getOrder({ id: orderId });
    return orderResponse.data; // Single order object
  } catch (error) {
    console.error(`Error fetching order ${orderId}:`, error.response ? error.response.data : error.message);
    throw error;
  }
}

// Usage example
// async function main() {
//   const products = await getAllProducts();
//   console.log('Products:', products.map(p => p.attributes.name));
//
//   const order = await getOrderById(12345); // Replace with a real order ID
//   console.log('Order details:', order);
// }
//
// main().catch(console.error);

Best Practices

  • Secure API Keys: Never expose your private API key in client-side code. Use environment variables and server-side processing for all API interactions.
  • Leverage Webhooks: For real-time updates and fulfilling digital products, always rely on webhooks instead of polling the API. This ensures your system reacts immediately to purchases, cancellations, and other crucial events.
  • Verify Webhook Signatures: Always validate the X-Signature header in webhook requests to ensure the payload genuinely originated from Lemon Squeezy and hasn't been tampered with.
  • Handle Idempotency: Design your webhook handlers to be idempotent. If the same webhook event is delivered multiple times (due to network issues, for example), your system should process it only once or gracefully handle duplicates.
  • Robust Error Handling: Implement comprehensive try/catch blocks around all API calls and webhook processing. Log errors and consider implementing retry mechanisms for transient API failures.
  • Use Embeddable Checkout: For a more integrated user experience, utilize Lemon Squeezy's embeddable checkout option. This keeps users on your site while leveraging Lemon Squeezy's payment and compliance infrastructure.
  • Test in Sandbox: Develop and test your integrations thoroughly using a Lemon Squeezy sandbox store before deploying to production.

Anti-Patterns

Hardcoding API Keys. Storing your private API key directly in your codebase is a security risk. Use environment variables (process.env.LEMON_SQUEEZY_API_KEY) and never commit them to version control.

Polling for Updates. Regularly querying the Lemon Squeezy API for new orders or subscription changes instead of using webhooks. This wastes API quotas, adds latency, and creates unnecessary load on both your and Lemon Squeezy's servers. Rely on webhooks for immediate, event-driven updates.

Ignoring Webhook Signatures. Processing webhook payloads without verifying the X-Signature header. This leaves your system vulnerable to spoofed requests and potential data manipulation. Always validate the signature.

Mismanaging Product Variants. Defining a single product in Lemon Squeezy for every slight variation (e.g., "Product A - Small", "Product A - Medium"). Instead, use Lemon Squeezy's variant system to manage different options (size, color, etc.) under a single product, simplifying management and API interactions.

Bypassing Embeddable Checkout for Custom Forms. Trying to recreate the entire payment and checkout experience with custom forms and direct API calls instead of using the embeddable checkout. This reintroduces the complexity of payment processing, tax calculation, and PCI compliance that Lemon Squeezy is designed to abstract away. Leverage the hosted or embeddable checkout for maximum compliance and ease.

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

Get CLI access →

Related Skills