When a card payment fails in Stripe, you get a short string back — insufficient_funds, do_not_honor, authentication_required. Most teams glance at it and let Smart Retries hammer the card on a schedule.
That’s the mistake. The right move depends entirely on the code:
- Some failures clear themselves if you retry in a few days.
- Some will never clear — and retrying them can get you fined by the card networks.
- Many need the customer to act, so retrying the same card is pointless.
A big slice of “failed” subscription payments are recoverable — but only if you act on the reason. Here’s every code that matters and what to do about it.
First: code vs decline_code
Stripe returns two fields, and they’re not the same:
-
code— the high-level category, usuallycard_declined. Tells you almost nothing. -
decline_code— the granular reason from the issuing bank (insufficient_funds,lost_card,do_not_honor). This is the one you act on.
Caveat: issuers don’t always report the true reason — anti-fraud blocks often hide behind a vague do_not_honor. The granular code is a strong hint, not gospel.
The mental model: five buckets
Every decline maps to one action: retry, email the customer, stop, or fix your checkout.
1 — Auto-recoverable (retry will likely work)
Timing problems. The card is fine; the money wasn’t there that moment, or the network hiccuped.
decline_code
Meaning
Action
insufficient_funds
No money right now
Retry — time it near payday
try_again_later
Temporary issuer issue
Retry in a few hours
processing_error
Transient error
Retry shortly
issuer_not_available
Couldn’t reach the bank
Retry — clears fast
This is the only bucket where blind retries earn their keep.
2 — Needs customer action (retrying is pointless)
The card can’t complete the charge as-is. Email the customer with the specific ask.
decline_code
Meaning
Ask the customer to
expired_card
Card expired
Update their card
incorrect_cvc
Wrong CVC
Re-enter card details
incorrect_number
Wrong number
Re-enter card details
authentication_required
Bank needs 3DS / SCA
Confirm the payment (see below)
The fix lives with the customer, not in your retry logic. A retry-only tool fails every one of these silently.
3 — Lost cause (stop retrying)
Card is dead, blocked, or flagged. Retrying won’t help — and retrying network-flagged cards can trigger Visa/Mastercard penalties.
decline_code
Meaning
lost_card / stolen_card
Reported lost or stolen
pickup_card
Bank wants it seized — never retry
fraudulent
Flagged as fraud
revocation_of_authorization
Customer told their bank to stop
Action: stop. Ask for a different card, or let it churn.
4 — Hard declines (the ambiguous middle)
The bank declined without saying why — often deliberately.
decline_code
Meaning
do_not_honor
Catch-all decline; often anti-fraud
generic_decline / card_declined
No reason given
call_issuer
Customer must call their bank
card_velocity_exceeded
Too many charges, short window
Action: a couple of retries max, then email: “Your bank blocked this — a quick call to them, or a different card, usually fixes it.” Many do_not_honor blocks lift in 30 seconds — if the customer knows to ask.
5 — Structural (fix checkout, not the payment)
Not about one customer — a recurring leak.
decline_code
Meaning
Fix
currency_not_supported
Card can’t be charged in that currency
Offer a supported currency
card_not_supported
Card type unsupported
Check your Stripe capabilities
transaction_not_allowed
Transaction type blocked
Often regional — review where you sell
Fix it once, recover every future customer who’d hit the same wall.
The European trap: SCA and authentication_required
If you sell in Europe, this is what most US-built dunning tools get wrong.
- Under PSD2 / SCA, many recurring EU payments need the customer to authenticate via 3D Secure.
- When they don’t, Stripe returns
authentication_required. - Most tools retry the card — which can never succeed without authentication.
- The fix: send a confirm-payment link (Stripe hosts the flow), not a retry.
For EU-facing SaaS, authentication_required is often a large, fully recoverable chunk that’s silently written off.
One-page playbook
Bucket Do this Auto-recoverable Time-aware retry (near payday) Needs customer action Email the specific ask; for SCA, send a confirm link Lost cause Stop retrying; ask for a new card Hard decline A few retries, then “call your bank / try another card” Structural Fix checkout config onceTakeaway
Blind retries only fix one of these five buckets. The rest need an email, a config fix, or a hard stop — so “just turn on Smart Retries” quietly leaves money on the table.
Try this: pull your last few months of failed payments and group them by decline_code. The split usually surprises people — especially how much is recoverable customer-action, not dead cards.
I’m building a tool that does this bucketing and routing automatically. Got war stories about declines, or opinions on what actually drives recovery? Drop them in the comments.
답글 남기기
댓글을 달기 위해서는 로그인해야합니다.