Stripe dunning, by decline reason.
A low balance, an expired card and a stolen card are three different problems. Good Stripe dunning treats them differently: it reads why each payment failed, retries only what can clear, asks for a new card when nothing else will work, and shows the status in your CRM.
What Stripe dunning already does well
Before building anything, it's worth knowing how much Stripe Billing handles on its own. Under Billing, Revenue recovery, you get:
- Smart Retries. Stripe picks the retry times for you. Its recommended default is 8 tries within 2 weeks, and you can stretch the window up to 2 months.
- Hard decline protection. For codes like
stolen_card,lost_cardorincorrect_number, Stripe keeps the schedule but only charges again once there is a new payment method. - Customer emails for failed payments and cards that are about to expire, with a link to update the card.
- An end state. When retries run out, the subscription is canceled, marked unpaid, left past due or paused, whichever you chose.
If you haven't turned these on, do that first. It's free and it's the right baseline. Most of what follows is about the gaps.
Where one dunning schedule falls short
Stripe's recovery happens inside Stripe, where most of your team never looks. Sales and support don't know a customer is past due until the account is already canceled. The account manager calls a client to talk about an upsell while their card has been failing for ten days.
The emails are also the same for everyone. A customer whose card simply expired gets the same message as one whose bank flagged the charge. And a $40 monthly plan gets the same treatment as a $4,000 one, where a personal call from the owner would recover it in a day.
A low balance often clears on the next retry. An expired card fails every time until the customer updates it. A card reported stolen should never be chased with a friendly reminder. Treating all three the same way loses money on the first two and creates risk on the third.
Five steps, every time.
Stripe Payment Recovery
Stolen, lost or blocked cards and unknown reasons go straight to your team. They are never retried.
- Retry later
- Ask for a new card
- Send to your team
How each failure is handled
Every failure lands in one of four groups based on Stripe's decline_code. Each group has its own path, and you can move a code between groups if your data says otherwise.
| Group | Typical Stripe codes | What happens |
|---|---|---|
| Retryable | insufficient_funds, generic_decline, do_not_honor, try_again_later, processing_error | Short heads-up to the customer, Stripe keeps retrying, status in the CRM |
| Needs an update | expired_card, incorrect_cvc, incorrect_number, authentication_required | Ask for a new card or 3D Secure confirmation right away, no wasted retries |
| Needs review | stolen_card, lost_card, pickup_card, fraudulent, highest_risk_level | Never retried, no automatic email, your team gets an alert |
| Unknown | Anything not mapped yet | The system doesn't guess. Your team is alerted |
Retryable: low balance or a soft decline
These often clear on their own, so the system lets Stripe's retries do their job. The customer gets one short, neutral notice instead of a string of warnings, and the CRM shows "payment retrying" so nobody on your team is surprised.
Needs an update: expired card or wrong details
Retrying would fail the same way, so the customer is asked to update their card immediately, with a direct link to Stripe's hosted page. For authentication_required, the message asks them to confirm the payment with their bank instead, since the card itself is fine.
Needs review: stolen, lost or blocked card
Never retried, and the customer doesn't get an automatic "please update your card" email. Your team gets an alert with the customer, invoice and amount, and decides how to reach out.
Unknown reason
New or rare codes go to a person, and the code is logged so it can be mapped later.
One case per invoice
Stripe's own retries are tracked inside the same case, so the customer never gets duplicate emails from you and from Stripe.
Bigger accounts get a person
Above an amount you set, the account owner is alerted on the first failure. A personal message from someone they know recovers more than any template.
Recovered only when paid
A case closes only when Stripe reports the invoice paid. If it's still unpaid after the last attempt, your team is warned before the subscription is canceled or marked unpaid.
Set up Stripe first, then automate
This order saves money. A lot of teams pay for a dunning tool before using what Stripe already includes.
- Turn on Smart Retries in Billing, Revenue recovery. Start with the recommended 8 tries in 2 weeks.
- Turn on the customer emails for failed payments and expiring cards, and make sure the update link points to your customer portal.
- Decide what happens after the final attempt: cancel, mark unpaid, leave past due or pause. Write it down, because your CRM status should match it.
- Then add the layer Stripe doesn't have: routing by decline reason, CRM status, owner alerts and a review queue for risky codes.
Mistakes we see most
- Retrying hard declines on a custom schedule. Each attempt on a stolen or invalid card is wasted and can look bad to card networks.
- Two sets of emails. Stripe's emails stay on while a separate tool sends its own, so the customer gets both.
- The CRM never finds out. The subscription is canceled in Stripe, and the CRM still shows an active customer.
- The webhook is the only trigger. If one event is missed, the case is never opened. A daily check against open invoices catches what the webhook missed.
- Counting a retry as recovery. Only an invoice marked paid is recovered.
What to measure
Three numbers tell you if it works: the share of failed invoices that end up paid, the average days from failure to payment, and how many subscriptions end because of a failed payment rather than a decision to cancel. Check them monthly, before and after the change.
Works with your tools
Built on Stripe with GoHighLevel and Gmail. The status can land in HubSpot or another CRM instead. See all integrations.
What you get
- A map of your process, agreed before we build
- The system running inside your own tools
- Testing on your real data before anything reaches a customer
- A log of every action and the reason for it
- Documentation your team can read, and a fixed price with no retainer
Questions, answered.
Doesn't Stripe already handle dunning and retries?
Yes. Smart Retries and Stripe's customer emails are a good baseline, and this works alongside them. The difference is that each failure is handled by its reason, risky cards go to a person, bigger accounts get a personal touch, and the status shows up in your CRM.
Which decline reasons does it handle?
It groups Stripe's decline codes into retryable, needs update, needs review and unknown. Each group has its own path, and you can move a code between groups.
Will customers get duplicate emails?
No. There is one case per invoice, and Stripe's own retries are tracked inside it.
Where does my team see the status?
On the customer record in your CRM. Nobody needs to open Stripe to know where a customer stands.
What access do you need to our Stripe account?
A restricted API key with only the permissions the system needs, plus a webhook for payment events. We don't need your login.
How long does it take to set up?
Usually one to two weeks, including testing with Stripe's test cards for every decline type.
Failed payments sitting in an inbox?
Show us how failed payments are handled today. On a 30-minute call we'll map the paths and tell you what we'd build.

