Integration guide
Recovering failed Paddle payments
Paddle acts as merchant of record, so the charge, tax handling and invoicing stay with Paddle. What Paddle does not decide is which failure deserves an immediate nudge versus a delayed retry, or which channel that nudge should use. RRLabs reads Paddle's notifications and orchestrates the outreach; the money never moves through RRLabs.
Quick answer
Paddle recovery listens to transaction.payment_failed and subscription.past_due notifications, then follows up with the customer while Paddle, as merchant of record, keeps handling the charge, tax and invoice.
Events that matter
- transaction.payment_failed
- A Paddle transaction was declined.
- subscription.past_due
- The subscription entered the dunning window.
- transaction.completed
- Payment went through — close the recovery loop.
- subscription.canceled
- Stop all outreach for this customer.
Setting it up
- 1
Create the notification destination
In Paddle Billing, add a notification destination pointing at your RRLabs webhook URL and subscribe to the transaction and subscription events above.
- 2
Verify the signature
Each payload carries a Paddle-Signature header; RRLabs rejects anything that does not verify against the notification secret.
- 3
Respect merchant-of-record rules
Paddle owns the invoice and the tax treatment. Outreach links customers back to Paddle's hosted update page rather than a custom card form.
- 4
Close on completion
transaction.completed or a renewed subscription marks the attempt resolved.
Consume Paddle Billing notifications
// Paddle Billing → Notifications → destination URL
// Verify the Paddle-Signature header against your notification secret first.
const event = JSON.parse(rawBody);
if (event.event_type === "transaction.payment_failed") {
const tx = event.data;
await rrlabs.enqueueRecovery({
provider: "paddle",
externalId: tx.id,
customerEmail: tx.customer?.email,
amountCents: Number(tx.details?.totals?.total ?? 0),
currency: tx.currency_code,
});
}
if (event.event_type === "transaction.completed") {
await rrlabs.closeRecovery({ provider: "paddle", externalId: event.data.id });
}Manual recovery vs. automated recovery
| Capability | Paddle alone | With RRLabs |
|---|---|---|
| Retry logic | Paddle's standard dunning window | Outreach timed per failure reason inside that window |
| Channels | Paddle emails | Email, WhatsApp and SMS where consented |
| Message content | Paddle-branded templates | Your brand voice, reason-specific copy |
| Attribution | Recovered totals only | Recovered transactions linked to the message that preceded them |
| Source of truth | Paddle | Paddle — it remains merchant of record |
Frequently asked questions
Can RRLabs retry the charge itself on Paddle?
No. As merchant of record Paddle owns the retry. RRLabs orchestrates the customer-facing side and points people at Paddle's own update page.
Will this conflict with Paddle's dunning emails?
It can. Most teams reduce Paddle's email cadence and let RRLabs handle the sequence, or keep RRLabs on channels Paddle does not use.
Which Paddle version is supported?
Paddle Billing notifications. Paddle Classic webhooks use different event names and need a custom mapping.