Every SaaS founder knows about voluntary churn — customers who cancel because they're not seeing value. But there's a second, quieter type of churn that often goes untracked: involuntary churn from failed payments.
Industry data puts it at 30-40% of all subscription cancellations. For a $100K MRR business, that's roughly $3,500-$4,000 disappearing every month from credit cards that expired, bank fraud blocks, or exceeded limits — not because the customer wanted to leave.
Why Banks Decline Payment Attempts
Understanding why payments fail is the first step to recovering them:
| Reason | Frequency | Recoverable? |
|---|---|---|
| Insufficient funds | ~38% | Yes (retry end of month) |
| Card expired | ~25% | Yes (update request) |
| Fraud prevention block | ~18% | Yes (customer contacts bank) |
| Card reported lost/stolen | ~12% | No (new card needed) |
| Bank-side technical error | ~7% | Yes (retry within 24h) |
The key insight: most failures are temporary. A retry at the right time recovers the revenue.
The Optimal Retry Schedule
Don't retry immediately and aggressively — that triggers fraud detection and makes the situation worse. The optimal dunning schedule:
- Day 0: Immediate retry (catches temporary errors)
- Day 3: Second retry (most cards refreshed by now)
- Day 7: Third retry with email notification
- Day 15: Final attempt + escalation email
Each retry should use intelligent payment routing — try a different payment processor if the primary fails, or use card updater services to get updated card details automatically.
Building a Dunning Email Sequence
The email sequence matters as much as the retry schedule. Tone progression:
Email 1 (Day 3 — No-blame)
Subject: Quick update on your [Product] subscription
Hi {{name}},
We had trouble processing your payment for [Product]. This happens
occasionally — usually a temporary bank issue.
We'll automatically retry in a few days. No action needed.
Email 2 (Day 7 — Helpful)
Subject: Action needed: Update your payment details
Hi {{name}},
We've been unable to process your [Product] subscription payment.
Your account will remain active for 7 more days.
Update your payment details here: [LINK]
Email 3 (Day 14 — Urgency)
Subject: Your [Product] account will be suspended tomorrow
Hi {{name}},
This is a final reminder. Your account will be suspended in 24 hours
unless you update your payment details.
We'd hate to lose you — here's a one-click update link: [LINK]
The three-email sequence typically recovers 18-25% of failed payments before the account suspends.
Implementing Retry Logic with Stripe
// Stripe webhook handler for payment failures
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
const event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
if (event.type === 'invoice.payment_failed') {
const invoice = event.data.object;
const attemptCount = invoice.attempt_count;
// Schedule next retry based on attempt number
const retryDelays = [0, 3, 7, 15]; // days
const nextDelay = retryDelays[attemptCount] || null;
if (nextDelay !== null) {
await scheduleRetry(invoice.subscription, nextDelay);
await sendDunningEmail(invoice.customer, attemptCount);
} else {
await suspendSubscription(invoice.subscription);
await sendFinalNotice(invoice.customer);
}
}
res.json({ received: true });
});
Measuring Recovery Rate
Track these metrics to optimise your dunning process:
- Recovery rate: % of failed payments recovered before suspension
- Recovery revenue: total MRR recovered per month
- Time to recovery: median days from failure to successful payment
- Email-to-update CTR: click-through on payment update links
A well-implemented dunning system typically achieves 15-25% recovery rate. Tools like ChurnGuard automate the entire process — retry scheduling, email sequences, recovery tracking — with zero code integration required.
Don't let payment failures become silent cancellations. The revenue is there; you just need to ask for it at the right time.
Top comments (2)
This is such an overlooked area
A lot of teams focus on retries and dunning but still miss the user side of the story
From the dashboard it looks like churn or failed payments
But you don’t really know what the user experienced in that moment
Tools like Flidget can help add that missing context
So you can understand whether it was a payment issue confusion or actual intent to leave
Feels like combining both could significantly improve recovery rates
The decline-reason table is the most useful part of this post — especially the "Recoverable?" column, because it forces the real design question: what should happen differently for each row. Too many retry implementations treat all failures as one queue.
One refinement on the retry schedule: Day 0 immediate retries are fine for bank-side technical errors, but for insufficient_funds an immediate retry rarely helps, and a second attempt hours later can trip velocity-based fraud filters. Timing the retry to the reason matters more than the exact day count — end-of-month for funds, within 24 hours for technical errors, and stop entirely for lost/stolen.
Also worth saying about the metrics section: recovery rate on its own can mislead. A flat 20% looks fine until you split it by decline reason and discover you're recovering 60% of expired cards but 0% of fraud blocks, because nobody's telling those customers to call their bank. Segmenting recovery by reason is what turns the metric from a vanity number into an actual roadmap.
Good breakdown overall — the tone progression in the email sequence (no-blame → helpful → urgency) matches what I've seen work too.