Integration guide
Recovering failed Stripe payments
Stripe already retries failed subscription invoices and can send its own dunning emails. What it does not do is decide which decline codes are worth retrying, vary the channel per customer, or attribute a later successful charge back to the message that caused it. That orchestration layer is what RRLabs adds — Stripe remains the source of truth for every charge.
Quick answer
Stripe recovery works by listening to invoice and payment_intent failure webhooks, classifying the decline code, then retrying and messaging the customer on a schedule that matches the reason for the failure.
Events that matter
- invoice.payment_failed
- A subscription invoice could not be collected.
- payment_intent.payment_failed
- A one-off or off-session charge was declined.
- customer.subscription.updated
- The subscription moved to past_due or unpaid.
- invoice.paid
- Recovery succeeded — used to close the attribution loop.
Setting it up
- 1
Create the endpoint
Point a Stripe webhook at your RRLabs workspace endpoint and subscribe to the invoice and payment_intent events above.
- 2
Verify signatures
Every payload is verified against the signing secret before it is processed; unverified events are rejected.
- 3
Classify the decline
The decline_code decides the plan: soft declines get timed retries, hard declines go straight to a card-update request.
- 4
Close the loop
invoice.paid marks the recovery attempt resolved so the same customer is not messaged again.
Verify and route invoice.payment_failed
// Stripe → your RRLabs endpoint
const event = stripe.webhooks.constructEvent(rawBody, sig, endpointSecret);
if (event.type === "invoice.payment_failed") {
const invoice = event.data.object;
await rrlabs.enqueueRecovery({
provider: "stripe",
externalId: invoice.id,
customerEmail: invoice.customer_email,
amountCents: invoice.amount_due,
currency: invoice.currency,
declineCode: invoice.last_finalization_error?.decline_code ?? null,
});
}
if (event.type === "invoice.paid") {
await rrlabs.closeRecovery({ provider: "stripe", externalId: event.data.object.id });
}Manual recovery vs. automated recovery
| Capability | Stripe alone | With RRLabs |
|---|---|---|
| Retry scheduling | Fixed Smart Retries schedule for all failures | Schedule chosen per decline reason |
| Channels | Email only | Email, WhatsApp and in-product prompts |
| Message content | One template for every failure | Reason-specific copy with AI drafting |
| Attribution | Not tracked | Recovered invoices linked to the attempt that preceded them |
| Source of truth | Stripe | Stripe — RRLabs never holds card data |
Frequently asked questions
Does RRLabs replace Stripe Billing?
No. Stripe stays the payment processor and the system of record. RRLabs reads failure events and orchestrates retries and outreach on top.
Will customers get duplicate emails from Stripe and RRLabs?
Turn off Stripe's built-in dunning emails when you enable RRLabs outreach, or restrict RRLabs to channels Stripe does not use.
Do you store card details?
No. Card data never leaves Stripe; RRLabs works with event metadata and customer contact details only.