Paddle and Lemon Squeezy integration
Connects Merchant of Record billing events to local customers and access without confusing provider and seller responsibilities.
paddle-lemonsqueezy-mor
See how it helpsWhen is it useful?
Use when integrating Paddle or Lemon Squeezy checkout and subscriptions under the chosen commercial arrangement.
What does it help you do?
- Maps provider products, prices and checkout metadata to internal tenant identities.
- Verifies webhook evidence and handles duplicate or reordered lifecycle events.
- Keeps buyer invoicing, local entitlements and settlement records appropriately separate.
Try asking this
Design subscription activation from verified MoR events, including repeated webhooks and refund changes without duplicate customer invoices.
Detailed documentation
Read the skill’s original instructions, examples and checks in their authored language.
Show less
Paddle & Lemon Squeezy (Merchant of Record)
Unlike standard payment processors (Stripe, Barion, Adyen) where your company is the legal seller responsible for global VAT/GST/sales-tax registration, calculation and invoicing, Paddle and Lemon Squeezy act as the Merchant of Record (MoR): they sell the product to the end customer in their own name, calculate and remit global taxes, issue the customer-facing tax invoice/receipt, and pay out net revenue to you.
1. Architectural Implications of Merchant of Record
- Do not double-issue customer tax invoices: when Paddle or Lemon Squeezy is the MoR, the MoR issues the legal buyer invoice and handles B2B VAT ID validation and reverse-charge rules. Do not simultaneously trigger Billingo or Számlázz.hu to issue a duplicate B2C/B2B invoice to the end buyer for the same transaction unless explicitly instructed for a B2B payout settlement invoice to the MoR entity itself.
- Price and Tax Display: pass customer location or use the MoR's client/server price preview APIs so inclusive vs. exclusive tax amounts (
tax_mode) match what the checkout overlay calculates.
2. Paddle Billing (v1 / Modern Paddle API)
- Catalog Hierarchy:
Product->Price(recurring or one-time) ->Discount. - Checkout Correlation (
custom_data):- When opening
Paddle.Checkout.open({ items: [{ priceId, quantity }], customData: { userId, workspaceId } })or creating a server-side transaction, always pass your internal tenant/user identifiers incustom_data. - Paddle copies
custom_dataonto the resultingtransactionandsubscriptionobjects.
- When opening
- Webhook Verification & Lifecycle:
- Verify
Paddle-Signatureheader (ts=...;h1=...HMAC-SHA256 over${ts}:${rawBody}) using the official Paddle SDK or constant-time HMAC check. - Core events:
transaction.completed: payment succeeded and invoice finalized; provision one-time purchases or confirm renewal.subscription.created,subscription.activated,subscription.updated,subscription.past_due,subscription.paused,subscription.canceled.
- Plan Changes & Proration: call
PATCH /subscriptions/{id}withitemsandproration_billing_mode(prorated_immediately,prorated_next_billing_period,full_immediately,do_not_bill). - Scheduled Changes: inspect
subscription.scheduled_change(action: 'cancel' | 'pause' | 'resume',effective_at) — do not revoke access whilestatus === 'active'and a futurescheduled_changeis pending!
- Verify
3. Lemon Squeezy Integration
- Catalog Hierarchy:
Store->Product->Variant(each billing interval/tier is a distinctvariant_id). - Checkout Creation (
POST /v1/checkouts):- Pass
checkout_data.custom: { user_id, workspace_id }and lock prefilled email/discount fields where required.
- Pass
- Webhook Verification (
X-Signature):- Verify the hex-encoded HMAC-SHA256 signature in
X-Signatureagainst the raw request body using your webhook signing secret. - Key events:
order_created,order_refunded,subscription_created,subscription_updated,subscription_payment_success,subscription_payment_failed,subscription_payment_recovered,subscription_cancelled,subscription_expired. - Note: in Lemon Squeezy,
subscription_cancelledmeans the user canceled future renewals (cancelled: true, status"cancelled"during the grace period untilends_at), whereassubscription_expiredfires when access should actually end (ends_atreached). Always checkends_atbefore revoking access!
- Verify the hex-encoded HMAC-SHA256 signature in
Included files 1
SKILL.md
