Skip to content

LemonSqueezy Setup Guide

How to configure LemonSqueezy as the payment provider for SchemaStack.


1. Create a LemonSqueezy Account

Sign up at lemonsqueezy.com and create a store.

2. Store ID

  1. Go to Settings → Stores in the LemonSqueezy dashboard
  2. Copy the Store ID (numeric, e.g. 12345)
  3. Set it in your environment:
    PAYMENT_LEMONSQUEEZY_STORE_ID=12345

3. API Key

  1. Go to Settings → API in the LemonSqueezy dashboard
  2. Click Create API Key
  3. Give it a name (e.g. schemastack-production)
  4. Copy the key — it's only shown once
  5. Set it in your environment:
    PAYMENT_LEMONSQUEEZY_API_KEY=eyJ0eXAi...

Important: LemonSqueezy has separate test mode and live mode. Test mode uses a different API key, separate products/variants, and no real charges. Toggle test mode in the dashboard header.

4. Create Products and Variants

Each subscription tier needs a Product with monthly and/or yearly Variants in LemonSqueezy.

ProductVariant (Monthly)Variant (Yearly)
Pro PlanPro MonthlyPro Yearly
EnterpriseEnterprise MonthlyEnterprise Yearly

Steps:

  1. Go to Store → Products
  2. Click New Product
  3. Set the product name (e.g. "Pro Plan")
  4. Under Pricing, select Subscription
  5. Add variants for each billing interval:
    • Pro Monthly: set monthly price
    • Pro Yearly: set yearly price
  6. Save and note the Variant IDs (visible in the URL or via API)

Getting Variant IDs

Variant IDs are visible in the LemonSqueezy dashboard URL when viewing a variant, or via the API:

bash
curl -s https://api.lemonsqueezy.com/v1/variants \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/vnd.api+json" | jq '.data[].id'

Map Variants to Plan Provider Mappings

Each variant ID must be stored in the plan_provider_mapping table:

sql
INSERT INTO plan_provider_mapping (tier, billing_interval, provider, provider_plan_id, provider_variant_id, price_cents, currency)
VALUES
  ('PRO', 'MONTHLY', 'LEMONSQUEEZY', 'product_id', 'variant_id_monthly', 1900, 'USD'),
  ('PRO', 'YEARLY', 'LEMONSQUEEZY', 'product_id', 'variant_id_yearly', 19000, 'USD'),
  ('ENTERPRISE', 'MONTHLY', 'LEMONSQUEEZY', 'product_id', 'variant_id_monthly', 4900, 'USD'),
  ('ENTERPRISE', 'YEARLY', 'LEMONSQUEEZY', 'product_id', 'variant_id_yearly', 49000, 'USD');

Replace product_id and variant_id_* with actual LemonSqueezy IDs.

5. Webhook Configuration

  1. Go to Settings → Webhooks in the LemonSqueezy dashboard

  2. Click Create Webhook (the + button)

  3. Configure:

    • URL: https://your-domain.com/api/webhooks/payment/LEMONSQUEEZY
    • Signing Secret: Choose a strong secret (6-40 characters). This is used for HMAC-SHA256 signature verification.
    • Events: Subscribe to these events:
      • subscription_created
      • subscription_updated
      • subscription_cancelled
      • subscription_expired
      • subscription_paused
      • subscription_resumed
      • subscription_unpaused
      • order_created
      • subscription_payment_success
      • subscription_payment_failed
  4. Set the signing secret in your environment:

    PAYMENT_LEMONSQUEEZY_WEBHOOK_SECRET=your-strong-secret-here

How Webhook Verification Works

LemonSqueezy sends an X-Signature header with each webhook request. The signature is an HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret. Our WebhookProcessingService verifies this before processing any event.

Custom Data in Checkouts

When SchemaStack creates a checkout URL, it includes custom_data with:

  • organisation_id — links the subscription back to the org
  • tier_name — the subscription tier being purchased
  • billing_interval — MONTHLY or YEARLY

This data is passed through by LemonSqueezy in webhook events, allowing us to associate subscriptions with organisations.

6. Environment Variable Summary

VariableDescriptionExample
PAYMENT_PROVIDERActive payment providerLEMONSQUEEZY (default)
PAYMENT_LEMONSQUEEZY_API_KEYAPI key from dashboardeyJ0eXAi...
PAYMENT_LEMONSQUEEZY_STORE_IDStore ID from Settings → Stores12345
PAYMENT_LEMONSQUEEZY_WEBHOOK_SECRETSigning secret from webhook configmy-secret

These map to application.properties:

properties
payment.provider=${PAYMENT_PROVIDER:LEMONSQUEEZY}
payment.lemonsqueezy.api-key=${PAYMENT_LEMONSQUEEZY_API_KEY:}
payment.lemonsqueezy.store-id=${PAYMENT_LEMONSQUEEZY_STORE_ID:}
payment.lemonsqueezy.webhook-secret=${PAYMENT_LEMONSQUEEZY_WEBHOOK_SECRET:}

7. Test Mode vs Live Mode

AspectTest ModeLive Mode
API KeySeparate test keySeparate live key
Products/VariantsTest products onlyLive products only
WebhooksTest webhooksLive webhooks
ChargesNo real chargesReal money
Dashboard toggleTop-right switchTop-right switch

Recommendation: Set up products, variants, and webhooks in test mode first. Once verified, recreate them in live mode with the same structure and update your environment variables.

8. Testing Webhooks Locally

LemonSqueezy can only deliver webhooks to a public URL. To receive them on your local machine, use a tunnel:

bash
# Install ngrok if you don't have it
brew install ngrok                          # Homebrew
# Or download directly: https://ngrok.com/download

# Expose local Quarkus (port 8080) to the internet
ngrok http 8080

This gives you a public URL like https://abc123.ngrok-free.app. Set your LemonSqueezy webhook URL to:

https://abc123.ngrok-free.app/api/webhooks/payment/LEMONSQUEEZY

Now any webhook events from LemonSqueezy (test mode purchases, subscription changes) will be forwarded to your local Quarkus instance. The ngrok terminal shows all incoming requests in real time.

Tip: The ngrok URL changes every time you restart it (unless you have a paid plan with a fixed domain). Update the webhook URL in the LemonSqueezy dashboard each time.

9. Verifying the Setup

  1. Start ngrok and update the webhook URL in LemonSqueezy dashboard
  2. Test webhook delivery: LemonSqueezy lets you send test webhook events from the webhook settings page
  3. Create a test checkout: Call POST /api/billing/checkout with a valid tier and interval — you should get a LemonSqueezy checkout URL
  4. Complete a test purchase: Use the checkout URL in test mode (no real charges)
  5. Verify subscription sync: After checkout, the webhook should fire and your org's subscription tier should update automatically

SchemaStack Internal Developer Documentation