Cardnudge

Fix failed payments on Stripe yourself

Last reviewed 2026-10-07

You do not need a tool to recover failed payments. For a small SaaS the whole job is an afternoon: switch on what Stripe already gives you, pull your own numbers, and decide whether what is left is worth paying anyone for. This page is written so that you never have to come back.

Step 1: turn on what you already pay for

If you bill subscriptions through Stripe you are already paying for Stripe Billing, and its recovery tools are included. None of them require code. Start here, because everything after this is measuring whether these were enough.

  1. 1.Billing, then Revenue recovery, then Retries. Enable Smart Retries. The recommended default is eight attempts over two weeks.
  2. 2.Billing, then Revenue recovery, then Emails. Enable the failed-payment email, which goes out after every failed attempt with a link for the customer to update their card.
  3. 3.On the same screen, enable the card-expiring email. It fires a month before the stored default card expires, and it prevents failures rather than chasing them.
  4. 4.Settings, then Branding. These emails and the hosted update page use your branding, and the default is worse than two minutes of work.
  5. 5.Confirm automatic card updates are active, so Stripe refreshes stored card details when an issuer reissues a number.

Do these before anything else, including reading the rest of this page. A failed payment nobody told the customer about is the cheapest recovery there is, and a surprising number of accounts have the emails switched off.

Step 2: decide what happens when dunning ends

After the last retry, Stripe does whatever you told it to. The four options are not equivalent and the default is rarely the right answer.

SettingWhat happensUse when
Cancel the subscriptionStatus becomes canceled at the end of the retry windowAccess must stop and you are comfortable losing the relationship
Mark as unpaidStatus becomes unpaid, invoices keep generating as draftsYou want to keep the record and chase manually
Leave past dueStays past_due, invoices keep generating and chargingYou would rather keep trying than close the door
PauseStatus becomes paused, no new invoices until resumedSeasonal or usage-based products where a return is likely

Leaving everything past due forever inflates your active subscriber count with people who are not paying, which quietly corrupts every metric you look at. Cancelling immediately loses customers who would have come back. Pick deliberately.

Step 3: pull your own numbers

Stripe's recovery analytics already report failure rates, recovery rates and the customers whose payments recently failed. For a one-off total over a longer window, go to the invoices directly.

  1. 1.List invoices created in the window you care about. Twelve months is the useful one; anything shorter misses a whole annual cohort.
  2. 2.Keep the invoices with an attempt count above zero. Those are the ones Stripe actually tried to charge.
  3. 3.Split them three ways. Paid with more than one attempt is recovered. Open with attempts is still being retried. Uncollectible or void is lost, as is anything attached to a subscription that was cancelled while unpaid.
  4. 4.Sum the amount due on the lost pile. That is your headline number, and it is the only one of the three that should be called a loss.

Keep the three piles separate. Adding still-being-retried money to the lost pile is the single most common way these figures get overstated, usually by people with no intention of overstating anything.

Step 4: find the decline codes, which have moved

This is the part that costs an afternoon if nobody warns you, so here is the warning. On current API versions an invoice no longer carries a charge field, and it does not carry a payment_intent field either. The attempts live under payments, which has to be expanded.

  1. 1.List invoices with payments expanded, then read the last entry of the payments list on each failed invoice.
  2. 2.That entry gives you a payment intent id. The decline information lives there, in last_payment_error.
  3. 3.Read decline_code first, then fall back to the error code. They are different fields and either can be the only one populated.

The trap inside the trap: an insufficient-funds failure reports the generic card_declined as its error code with the useful insufficient_funds in decline_code, while an expired card reports expired_card as its error code and has no decline_code at all. Read only one field and you will misclassify half your failures.

Group the codes into three families and you have everything you need to decide what to do: cards that need replacing, balances that were empty, and refusals that will never clear. Our decline codes guide lists which code belongs where.

Step 5: act on what the split tells you

  • Mostly cards needing replacement: your problem is getting customers to enter new details. Stripe's failed-payment and card-expiring emails already do this. If they are on and it is still not working, the message or the update page is the thing to improve, not the retry schedule.
  • Mostly insufficient funds: your problem is timing, and Smart Retries already picks retry moments from a model. There is little left for you to do by hand, which is good news.
  • Mostly hard declines: there is nothing to recover. Spend the time on why those customers are churning instead, because the card is a symptom.

If you want to send your own emails rather than Stripe's, the constraint is deliverability, not copywriting. SPF, DKIM and DMARC have to be right or the best-written message lands in spam. Our dunning email guide covers that, including a mistake we made on our own domain.

What a paid tool does that this does not

Everything above is free and permanent. Here is what you are still not getting, stated plainly so you can decide for yourself whether it is worth a monthly fee.

  • A recovery flow that looks like your product. Stripe's emails and hosted page carry your logo and colours, but they are Stripe's pages with your branding, not your pages.
  • Retry logic beyond Stripe's. Some tools retry outside Stripe's envelope, across more attempts or with their own models.
  • In-app prompts. An email is one channel; a banner shown to a logged-in customer with a failing card is another, and no amount of Stripe configuration produces it.
  • Attribution. Which message brought the money back is genuinely hard to answer by hand and is the thing paid tools are best at.
  • Cancel flows. A save offer at the moment someone clicks cancel is a different product from dunning, and nothing free replaces it.
  • Someone else maintaining it when Stripe moves a field, as they did with the invoice charge field.

For a small SaaS, our honest read is that steps one to three recover most of what is recoverable, and a tool earns its fee when the remaining leak clearly exceeds the subscription. At a few thousand in MRR, a tool at fifty dollars a month has to bring back six hundred a year before it has done anything for you.

And since you are on our site: our audit does step three and part of step four for you, once, for free, with a read-only key. That is the whole product. If this page means you never need it, that is a perfectly good outcome.

Questions people ask

Can I recover failed payments on Stripe without a third-party tool?
Yes, and for a small SaaS you probably should start there. Smart Retries, failed-payment emails, card-expiring emails and automatic card updates are included in Stripe Billing, require no code, and cover most of what is recoverable.
Where do I find the decline reason for a failed Stripe invoice?
On the payment intent, not the invoice. Current API versions removed the invoice's charge and payment_intent fields; expand payments on the invoice, take the payment intent id from the last entry, and read last_payment_error, checking decline_code first and the error code second.
How many retries should I configure?
Stripe's recommended Smart Retries default is eight attempts over two weeks, with the timing chosen by their model. A custom schedule caps at three retries, which usually trades away recovery for control you do not need.
What should happen to a subscription when retries run out?
Choose deliberately between cancelling, marking unpaid, leaving past due, and pausing. Leaving everything past due indefinitely quietly inflates your active subscriber count with people who are not paying.
Is it worth paying for a dunning tool at small scale?
Only after the free steps are done and measured. A tool at fifty dollars a month needs to recover more than six hundred a year to break even, and Stripe's built-in retries will already have taken the easy wins.

Sources

Find out your exact number

A free, read-only audit of your own Stripe account. No signup, and the key never leaves your browser.

Run a free audit

Keep reading