Payments
Connect Lemon Squeezy
Create licenses automatically from Lemon Squeezy orders and subscriptions, with offline certificates.
Lemon Squeezy can issue its own license keys, validated online through its API. Connecting it to Keyrail instead gives you signed certificates verified offline, per-product machine fingerprints, the same SDKs and API as with Paddle, and a single place for all your licenses.
1. Create the integration#
In the dashboard: your product, then Integrations → Connect Lemon Squeezy. Or with the vendor API:
curl https://api.keyrail.tech/v1/integrations \
-H "Authorization: Bearer $KEYRAIL_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "lemonsqueezy",
"product": "prd_7k2m9q4x1c8d3f6h0j5n",
"priceMap": { "412345": "pro", "412346": "lifetime" },
"webhookSecret": "a-long-random-string-you-choose"
}'priceMap keys are Lemon Squeezy variant IDs, values are plan codes. The response contains the webhookUrl: https://api.keyrail.tech/v1/webhooks/lemonsqueezy/int_….
2. Add the webhook in Lemon Squeezy#
In Lemon Squeezy, Settings → Webhooks → +:
- Callback URL: the
webhookUrl. - Signing secret: the same
webhookSecretyou sent to Keyrail. - Events:
order_created,order_refunded,subscription_created,subscription_updated,subscription_payment_success.
3. Test#
Enable test mode in Lemon Squeezy and buy with a test card. The license appears in the dashboard and the customer receives the key.
Test-mode events (meta.test_mode: true) are only processed by an integration whose environment is sandbox; a live integration acknowledges them and ignores them (the audit log says test mode event on a live integration), so test orders never create real licenses. Likewise a sandbox integration ignores live events. Create one integration per environment, each with the signing secret of the matching webhook.
What each event does#
| Lemon Squeezy event | Effect on the license |
|---|---|
order_created with a non-subscription variant | A perpetual license. |
subscription_created, subscription_payment_success | paidUntil = renews_at; status active. |
subscription_updated | Status mapped from the subscription status (below). |
order_refunded | Status refunded. |
| Lemon Squeezy subscription status | License status |
|---|---|
active, on_trial | active |
past_due, unpaid | past_due |
cancelled | canceled |
expired | expired |
paused | suspended |
The customer is matched by email, or created. One customer with one subscription to a plan has one license, reused if they subscribe again later.
Signature verification#
The X-Signature header must be the hex HMAC-SHA256 of the raw request body with your signing secret. Otherwise Keyrail answers 401. See webhooks.