Paddle setup
Keylight connects to Paddle Billing with a signed webhook destination and a server-side Paddle API key. Paddle remains your Merchant of Record and checkout; Keylight listens for billing events, maps each Paddle Price ID to a key type, and keeps the matching license in sync.
There is no OAuth flow and no Paddle code to add to your app.
Before you start
Section titled “Before you start”You need:
- A Paddle sandbox or live account.
- A Keylight app with at least one key type.
- One Paddle Price ID for each key type you want to sell.
- Permission to create a Paddle notification destination.
- Permission to create a Paddle API key.
1. Copy the Keylight webhook URL
Section titled “1. Copy the Keylight webhook URL”In Keylight, go to Settings → Integrations → Paddle, select Sandbox, and copy the Webhook URL.
It looks like this:
https://api.keylight.dev/webhooks/paddle/<your-tenant-id>/sandboxThe live URL ends in /live. Do not reuse the sandbox notification destination or signing secret for live events.
2. Create the notification destination in Paddle
Section titled “2. Create the notification destination in Paddle”In the matching Paddle environment:
-
Go to Developer Tools → Notifications.
-
Click New destination.
-
Enter a clear description, such as
Keylight sandbox. -
Choose Webhook and paste the URL from Keylight.
-
Set API version to 1.
-
Set Usage type to accept both platform and simulation traffic while testing.
-
Subscribe to these events:
transaction.completedsubscription.createdsubscription.updatedsubscription.activatedsubscription.trialingsubscription.past_duesubscription.pausedsubscription.resumedsubscription.canceledadjustment.createdadjustment.updated
-
Click Save destination.
-
Copy the signing secret Paddle reveals.
Back in Keylight, paste it into Webhook signing secret and save.
3. Add a Paddle API key
Section titled “3. Add a Paddle API key”Paddle transaction webhooks include a customer ID, but not the customer’s email address. Keylight uses the Paddle Customers API to resolve that email so it can attach the license to the customer and deliver the key.
In Paddle:
-
Go to Developer Tools → Authentication → API keys.
-
Click New API key.
-
Give it a recognizable name, such as
Keylight sandbox. -
Grant the minimum required permissions:
Permission When it is needed customer.readRequired for customer lookup and license delivery. transaction.writeRequired only for Keylight-generated one-time upgrade checkouts. -
Set an expiry date and save.
-
Copy the key. Paddle shows it only once.
Paste it into Keylight → Settings → Integrations → Paddle → Paddle API key for the same environment.
Sandbox keys start with pdl_sdbx_apikey_; live keys start with pdl_live_apikey_. Keylight stores the key encrypted and never exposes it in your app or SDK.
4. Map Paddle prices to Keylight key types
Section titled “4. Map Paddle prices to Keylight key types”Open the Keylight app you want to sell, choose Add payment source → Paddle, and enter one Paddle Price ID for each key type.
| Paddle Price ID | Keylight app | Key type |
|---|---|---|
pri_01h…abc | myapp | pro-monthly |
pri_01h…xyz | myapp | pro-lifetime |
The Price ID—not the Paddle Product ID—is the routing key. A Paddle product can contain several prices, but every Price ID must map to exactly one Keylight app and key type in the same environment.
Keylight fulfills a transaction only when it contains exactly one mapped Price ID. Unmapped add-ons are ignored. Paddle quantity is recorded for diagnosis but does not change the activation limit: the Keylight key type controls activations, and one transaction issues one license.
5. Test the signed webhook
Section titled “5. Test the signed webhook”Paddle uses a webhook simulator rather than a generic “Test event” button.
- Go to Paddle → Developer Tools → Simulator.
- Choose the notification destination you created for Keylight.
- Run a single
transaction.completedsimulation using a customer and Price ID that exist in the same Paddle environment. - In Paddle, open the destination’s delivery logs and confirm Keylight returned a
2xxresponse. - In Keylight, confirm the environment moves past Test required and the mapped license was created only once.
Then run the predefined Subscription created and Subscription renewed scenarios if you sell subscriptions. A renewal must extend the existing license and record another payment; it must not mint or email a second key.
6. Repeat for live
Section titled “6. Repeat for live”When the sandbox checklist is green:
- Select Live in the Keylight Paddle connection.
- Create a separate live notification destination in Paddle.
- Add a live API key with the same minimum permissions.
- Map the live Price IDs.
- Send a live simulation, then complete one low-value live purchase you can refund.
Keylight reports the Paddle integration as Ready only when the live credentials, mappings, and signed test have succeeded. Sandbox readiness is shown separately.
What Keylight does with Paddle events
Section titled “What Keylight does with Paddle events”| Paddle event | Keylight behavior |
|---|---|
Initial transaction.completed | Mints one license, records the payment, links the Paddle customer and subscription, and sends the license email. |
Recurring transaction.completed | Records the renewal and extends the existing license without minting or emailing a new key. |
| Subscription lifecycle event | Applies the newest state and billing period to the linked license. Older out-of-order events are ignored. |
| Approved full refund | Records the refund and revokes the linked license. |
| Approved partial refund | Records the partial refund but keeps the license active. |
| Pending or rejected refund | Does not revoke the license. |
Paddle can retry a webhook or deliver events out of order. Keylight deduplicates deliveries by Paddle event ID and uses the event’s occurred_at timestamp before changing license state.
Upgrades
Section titled “Upgrades”Paddle does not support arbitrary custom_data[...] values on a hosted-checkout URL. Do not append a license key to a Paddle checkout URL.
| Upgrade path | Supported |
|---|---|
| One-time license → one-time license | Yes, through a checkout created by Keylight. |
| One-time license → subscription | No. |
| Subscription → one-time license | No. |
| Subscription → subscription | No. |
For the supported path, Keylight creates a Paddle transaction through the API and places the license and target key type in the transaction’s JSON custom_data. This is why upgrade checkouts require transaction.write permission.
Refunds and chargebacks
Section titled “Refunds and chargebacks”Most live Paddle refunds begin as pending_approval. Keylight waits for an approved adjustment.updated event before revoking a fully refunded license. A rejected refund leaves access unchanged.
A partial refund is recorded without revoking access. An approved chargeback revokes the license. Chargeback reversals are left for manual review so access is never restored from an ambiguous event.
Troubleshooting
Section titled “Troubleshooting”| Symptom | What to check |
|---|---|
| Paddle delivery log shows a signature failure | Confirm the notification destination’s signing secret is saved in the same Keylight environment. Do not use the API key as the signing secret. |
| Event is accepted but no license is created | Confirm the event contains exactly one mapped Price ID. Multiple mapped prices in one transaction are unsupported. |
| License exists but has no customer or email | Confirm the API key matches the environment and includes customer.read. |
| Subscription event changes nothing | Confirm the initial transaction already created a license with the same Paddle subscription ID. Older events are intentionally ignored. |
| Refund does not revoke access | Confirm it is a full refund with action: refund and status: approved. Pending, rejected, and partial refunds do not revoke access. |
| Upgrade checkout cannot be created | Confirm both key types are one-time, the target has exactly one mapped Price ID, and the API key includes transaction.write. |
| Simulator sends nothing | Edit the notification destination so its usage type accepts simulation traffic and confirm the simulated event is subscribed. |
Paddle retries failed webhook deliveries automatically. After correcting a credential or mapping, resend the original event from Paddle’s notification logs. Keylight retains the verified event in its recovery inbox for operator replay. Do not create a replacement sale to force another webhook.
Disconnecting Paddle
Section titled “Disconnecting Paddle”Disconnecting an environment removes its webhook secret, API key, Price ID mappings, and readiness history from Keylight. Existing licenses, customers, and payment records remain unchanged. Unresolved Paddle events remain available for review but cannot be replayed until the environment is reconnected.
Disconnecting in Keylight does not remove the notification destination from Paddle. Deactivate or delete it in Paddle → Developer Tools → Notifications if you no longer want Paddle to deliver events.