Stripe Smart Retries works well for card-based subscriptions on Stripe Billing: it times retries with Stripe's ML and can pair with built-in emails and portal. We add custom retry logic when the flow includes non-card payment methods, 3D Secure re-authentication, or retry windows that must match your exact billing cadence.
Failed-payment retry and dunning automation is the system that detects a failed charge in real time, schedules safe reattempts, and communicates with the customer until payment succeeds or the account churns.
The problem it solves
A concise answer: failed payments create involuntary churn. Smart Retries handles card timing, but edge cases need control. We built a hybrid: let Stripe retry when it can, and fall back to our webhook-driven scheduler for SCA, non-card rails, and custom windows.
Teams handling this manually chase declines in inboxes, send ad hoc emails, and risk overlapping next-cycle invoices. Without rules, retries collide with billing intervals and SCA re-auth is missed.
| Task | Manual | Automated |
|---|---|---|
| Detect failures | Staff watches dashboard reports | Webhooks fire on invoice.payment_failed / payment_intent.* |
| Retry timing | Guess a day and hope | Stripe Smart Retries ML timing for cards; custom jittered windows for others |
| SCA re-auth | Copy-paste a link later | Immediate, gated email with re-auth link |
| Non-card rails | No clear plan | Custom retries and reminders (Smart Retries often skips non-card) |
| Window control | Easy to overlap cycles | Policy caps total retry window to ≤ billing interval |
| Reporting | CSV pull then reconcile | Logged outcomes per attempt, plus scheduled dashboard CSVs |
How the automation works
Short answer: webhooks drive a decision engine. If the failure is on a card and Smart Retries applies, we defer to Stripe's schedule and only handle comms. If it is a non-card method or needs SCA, we schedule our own retries and dunning with safeguards.
- Stripe webhooks: We subscribe to invoice.payment_failed and payment_intent.* so failures arrive in real time. Endpoints verify signed payloads per Stripe's webhook guidance.
- Decision engine: A small service classifies the failure: card with Smart Retries eligible, 3D Secure required, or non-card rail.
- Smart Retries path: We let Stripe's automated collections handle retry timing for cards. We send brand emails or use Stripe's built-in emails and portal to minimize friction.
- Custom retry path: We enqueue jittered attempts inside a window no longer than the subscription interval, and send dunning emails with a confirm-payment or update-card link.
- Reporting: We write attempt logs and, for finance, schedule dashboard CSV exports for monthly reconciliation.
Step-by-step: how to build it
1) Wire Stripe webhooks for failure events
Answer first: capture failures the instant they occur. Subscribe to invoice.payment_failed and payment_intent.* and verify signatures before processing.
// server/webhooks.js (Node + Express)
import express from "express";
const app = express();
// Ensure raw body is available for signature verification per Stripe docs
app.post("/webhooks/stripe", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["stripe-signature"]; // verify signature per docs
// const event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET)
const event = JSON.parse(req.body.toString()); // placeholder: replace with verified event
switch (event.type) {
case "invoice.payment_failed":
case "payment_intent.payment_failed":
// enqueue classification + dunning job
// jobQueue.add("handleFailure", { event });
break;
default:
break;
}
res.status(200).send("ok");
});Key gotcha: configure endpoints and sign payloads per Stripe's webhook docs, not just plain JSON parsing.
2) Decide: Smart Retries or custom
Answer first: if Stripe Billing automated collections can handle card retries, use it. Otherwise branch to your scheduler.
// policy/classifier.js
export function classifyFailure({ paymentMethodType, requiresSCA }) {
if (paymentMethodType === "card" && !requiresSCA) return "smart_retries";
if (paymentMethodType === "card" && requiresSCA) return "sca_custom";
return "non_card_custom"; // many non-card rails are skipped by Smart Retries
}Reference: Stripe notes Smart Retries is part of automated collections for Billing and skips many non-card methods except ACH Direct Debit.
3) Configure a safe custom retry window
Answer first: keep total retry duration less than or equal to the billing interval to avoid overlapping the next invoice.
// config/retry-policy.json
{
"billingIntervalDays": 30,
"maxRetryWindowDays": 21,
"attempts": [1, 3, 7, 14, 21],
"jitterMinutes": [15, 45, 90, 120, 180]
}Reference: Stripe support warns that retry schedules longer than the subscription period can overlap the next cycle.
4) Send SCA re-authentication emails immediately
Answer first: failed 3D Secure is not retried by schedule. Email a confirm-payment or re-auth link promptly.
Subject: Action needed to complete your payment
Hi {{name}},
Your latest payment needs a quick security confirmation. Please complete authentication here:
{{confirm_payment_link}}
If the link expires, you can also update your card on file here:
{{customer_portal_link}}
Thanks,
Billing TeamReference: Stripe support notes failed 3D Secure authorizations are not automatically retried; you must enable hosted confirm links or handle re-authentication.
5) Handle non-card rails separately
Answer first: Smart Retries often skips non-card payment methods. Route these to a non-card policy and reminder cadence.
// policy/nonCard.js
export const nonCardPolicy = {
attempts: [1, 4, 8],
instructions: "Follow provider rules; settle outside card rails; send portal/update links",
};Reference: Stripe documents that automated collections primarily target card payments and skip many non-card methods, with ACH Direct Debit as a partial exception.
6) Wire minimal API access and finance reporting
Answer first: use Stripe's API for lookups and scheduled dashboard CSV exports for finance summaries.
# Example: list recent charges using HTTP Basic with your secret key as the username
curl https://api.stripe.com/v1/charges \
-u sk_live_abc123: \
-G --data-urlencode limit=10-- dunning_attempts table (app DB), powers your ops dashboard
create table dunning_attempts (
id bigserial primary key,
customer_id text not null,
invoice_id text,
attempt_no int not null,
scheduled_at timestamptz not null,
outcome text check (outcome in ('retry_queued','succeeded','failed','skipped')),
created_at timestamptz default now()
);Reference: Stripe supports CSV exports from the Dashboard on daily, weekly, or monthly schedules.
Where it gets complicated
Non-card payment methods. Smart Retries skips many non-card rails, so you need your own schedule and comms for those flows. We gate messages by provider rules and keep attempts low.
3D Secure authentication. Failed SCA is not retried by schedule. Either enable hosted confirm-payment links or generate re-auth flows and email them immediately.
Retry window overlap. If your retry plan runs longer than the subscription interval, the next invoice can generate while you still chase the last one. Cap total retry days to within the interval.
Webhook hygiene. Verify signatures and store minimal event data. We treat webhooks as a trigger, then re-fetch details server-side before acting.
Built-in vs brand emails. Stripe's built-in emails and portal reduce engineering, but some brands need custom language or regional compliance. We often start with built-in, then graduate to brand templates once outcomes are proven.
What this actually changes
In production we shipped this hybrid model for a subscription SaaS and a professional services firm that invoices on card. Smart Retries carried most card declines with minimal code. Our custom layer handled SCA, non-card rails, and policy windows the business required. Finance received monthly CSV summaries and ops saw real-time dunning attempts in a dashboard.
One external benchmark: involuntary churn commonly represents 20, 40 percent of total churn in subscription businesses (source: https://www.profitwell.com/blog/involuntary-churn). Reducing failed-payment fallout is one of the fastest ways to cut that share.
Frequently asked questions
When should I use Stripe Smart Retries versus custom logic?
Use Smart Retries when you are on Stripe Billing, accept card payments, and want Stripe's ML timing plus built-in emails and portal. Build custom logic for non-card rails, SCA re-authentication, or when your retry windows must match strict billing policies.
What Stripe events should my webhook listen to for dunning?
Subscribe to invoice.payment_failed for subscription invoice failures and payment_intent.* for payment-intent level failures. Webhooks deliver events in real time and should be verified and processed server-side per Stripe's webhook docs.
Does Smart Retries work for non-card payment methods?
Stripe notes that automated collections and Smart Retries primarily target card payments and skip many non-card methods, with ACH Direct Debit as a partial exception. Plan on custom logic and reminders for non-card rails.
How do I handle 3D Secure failures?
Failed 3D Secure authorizations are not retried by your schedule. Enable Stripe-hosted confirm-payment links or implement your own re-authentication step and email it immediately so the customer can complete SCA.
Can retry schedules overlap the next billing cycle?
Yes, if the retry window exceeds the subscription interval. Stripe warns that long schedules can overlap the next cycle. Keep total retry duration less than or equal to the billing interval and pause further attempts when a new invoice is issued.
Do I need Zapier or Make for this?
Stripe has official integrations on Zapier and Make for lightweight flows. For production dunning, we prefer webhooks plus code so we can classify failures, respect SCA, and enforce safe retry windows, then use scheduled dashboard CSV exports for finance.
If you want us to review your current failed-payment setup, we have built both Smart-Retries-first and custom dunning systems. See our guide on Stripe invoice failed webhooks, our custom AI integration service, and if you are ready to fix this, book a call.
Want us to build this for you?
15-minute discovery call. No pitch. We tell you what to automate first.
Book a Discovery Call