Recover failed PayPal payments
PayPal subscriptions fail for reasons a card processor never sees — a funding source removed, a billing agreement cancelled, or a currency the buyer's account cannot cover. Recovery here is mostly about getting the buyer back into PayPal to fix the funding source.
The PayPal events recovery depends on
- BILLING.SUBSCRIPTION.PAYMENT.FAILED
- A recurring PayPal payment failed — open a recovery case.
- PAYMENT.SALE.COMPLETED
- A subscription payment cleared. Close the case.
- BILLING.SUBSCRIPTION.CANCELLED
- The billing agreement ended.
Verifying PayPal webhooks
- Scheme
- Asymmetric — verified server-side by PayPal's API
- Signature location
- PayPal-Transmission-Sig (plus id, time and cert URL headers)
PayPal does not use a shared-secret HMAC. Post the transmission headers and event body to /v1/notifications/verify-webhook-signature and require verification_status: SUCCESS.
const res = await fetch(
"https://api-m.paypal.com/v1/notifications/verify-webhook-signature",
{
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}` },
body: JSON.stringify({
transmission_id: req.headers["paypal-transmission-id"],
transmission_time: req.headers["paypal-transmission-time"],
cert_url: req.headers["paypal-cert-url"],
auth_algo: req.headers["paypal-auth-algo"],
transmission_sig: req.headers["paypal-transmission-sig"],
webhook_id: process.env.PAYPAL_WEBHOOK_ID,
webhook_event: JSON.parse(rawBody),
}),
},
);
const { verification_status } = await res.json();
if (verification_status !== "SUCCESS") return new Response("Invalid", { status: 401 });Decline playbook for PayPal
| Failure signal | Recovery action |
|---|---|
| Funding source declined | Ask the buyer to add or reselect a funding source in PayPal, with a direct link to the agreement. |
| Billing agreement cancelled by buyer | Treat as voluntary churn — do not retry; route to a win-back message instead. |
| Temporary decline | Allow PayPal's retry to run and send a short reminder rather than a card-update request. |
What happens after a payment fails
T+0s
Payment failed
Provider webhook received and verified
T+2s
AI analysis
Decline reason classified, amount at risk scored
T+5s
Dynamic email
Copy generated for that decline code and customer
Day 2
Smart retry
Retry scheduled only when the decline code is retryable
Day 2
WhatsApp push
Second channel used when the email went unanswered
On success
Recovered
Counted only when the provider confirms the charge
Timings describe the configured workflow, not a guaranteed outcome. Every step is skipped when the decline reason makes it counter-productive.
Frequently asked questions
- Why can't PayPal webhooks be verified with a local HMAC?
- PayPal signs events asymmetrically and expects verification through its own API endpoint, so the check requires a server-side call rather than a local digest comparison.
- Is a failed PayPal payment involuntary churn?
- Usually, unless the buyer cancelled the billing agreement themselves — that event is voluntary and should not be retried.
Connect PayPal with read-only credentials
RRLabs reads failure events and orchestrates recovery. It never holds card data and never becomes the merchant of record.
Get early access