Not all failed payments are the same.
When Stripe returns a decline code, it's telling you something specific about why the charge didn't go through. The problem is that most SaaS teams treat every failed payment identically: retry it a few times, send a generic email, and hope for the best.
That approach leaves money on the table. A card declined for insufficient funds needs a completely different recovery strategy than an expired card or a fraudulent flag. If you know the decline code, you know the right next move.
This guide covers every common Stripe decline code, explains what's actually happening behind the scenes, and gives you a concrete action plan for each one.
How Stripe Decline Codes Work
When a payment fails, Stripe returns a decline_code as part of the charge object. This code comes from the card's issuing bank (in most cases) or from Stripe's own fraud detection system.
There are two broad categories:
- Soft declines: Temporary issues that may resolve on their own. These are retryable.
- Hard declines: Permanent issues that require customer action. Retrying won't help.
Understanding this distinction is critical. Retrying a hard decline wastes your retry attempts and can even trigger fraud flags with the issuing bank.
The Most Common Decline Codes
These codes account for roughly 90% of all failed subscription payments on Stripe.
insufficient_funds
What it means: The cardholder's account doesn't have enough money to cover the charge.
Type: Soft decline
How common: This is the #1 decline reason, responsible for about 35-40% of all subscription payment failures.
What to do:
- Retry in 24-48 hours. Most people get paid on predictable cycles (biweekly, monthly).
- If the first retry fails, try again 3-5 days later.
- After 2-3 failed retries, notify the customer with a payment update link.
- Avoid retrying more than 3 times total for this code.
Recovery outlook: High. These customers usually have the money, just not at the exact moment you charged. Proper retry timing recovers 50-60% without the customer doing anything.
card_declined (generic)
What it means: The bank declined the charge without providing a specific reason. This is a catch-all code.
Type: Could be either soft or hard. Treat as soft for the first occurrence.
How common: 20-25% of failures. Banks use this when they don't want to disclose the specific reason.
What to do:
- Retry once after 24 hours.
- If the retry also returns
card_declined, treat it as a hard decline. - Send the customer a payment update email with a direct link.
- In your email, suggest they contact their bank if the issue persists.
Recovery outlook: Moderate. About 40% recover on retry, another 20-25% recover after customer outreach.
expired_card
What it means: The card's expiration date has passed.
Type: Hard decline
How common: 15-20% of failures. Cards typically expire every 3-4 years, but with a large customer base, expirations happen constantly.
What to do:
- Do not retry. The card will not work until the customer provides new details.
- Send an immediate email with a pre-authenticated payment update link.
- Follow up with SMS if you have their number.
- If you had caught this before the payment attempt (pre-dunning), you could have avoided the failure entirely.
Recovery outlook: Good, actually. The customer's bank almost certainly issued a replacement card. They just need to enter the new details. Recovery rates of 65-75% are normal with a proper dunning sequence.
processing_error
What it means: Something went wrong on the bank's end or the payment network. The charge couldn't be processed.
Type: Soft decline
How common: 5-8% of failures. More common during peak processing times.
What to do:
- Retry within 2-4 hours. This is often a transient infrastructure issue.
- If the retry fails, wait 24 hours and try again.
- No need to contact the customer unless it persists past 2-3 retries.
Recovery outlook: Very high. Most processing errors resolve on their own. 70-80% recover on the first or second retry.
incorrect_cvc
What it means: The CVC/CVV code provided doesn't match what the bank has on file.
Type: Hard decline
How common: 3-5% of failures. Usually happens when card details were entered incorrectly during initial setup, or the customer received a replacement card with a new CVC.
What to do:
- Do not retry. The CVC won't magically change.
- Send a clear email explaining that their card's security code needs to be re-entered.
- Provide a direct link to re-enter their full card details.
Recovery outlook: Good. The customer clearly intended to pay (they entered card details). Recovery rates around 60-70%.
authentication_required
What it means: The bank requires 3D Secure (SCA) authentication for this transaction. The customer needs to complete an additional verification step.
Type: Hard decline (requires customer action, but the card itself is fine)
How common: 5-10% of failures, and growing. European banks under PSD2/SCA regulations trigger this frequently. Increasingly common in other regions too.
What to do:
- Do not retry the same way. Retrying without authentication will fail again.
- Send the customer a Stripe-hosted payment link that includes the 3DS authentication flow.
- Make it clear in your email that their bank requires an extra verification step.
Recovery outlook: Moderate to good. The friction of 3DS authentication does cause some drop-off. Expect 50-65% recovery.
do_not_honor
What it means: The bank is refusing the charge, but won't say why. This is the bank's version of "no comment."
Type: Treat as hard decline.
How common: 3-5% of failures.
What to do:
- One retry after 48 hours is reasonable, but don't expect it to work.
- Contact the customer and suggest they call their bank to authorize future charges from your business.
- Provide an alternative: they can enter a different card.
Recovery outlook: Lower than average. 30-40% recovery. The opacity of this code makes it harder to guide the customer.
fraudulent
What it means: Stripe's fraud detection system (Radar) flagged this charge as potentially fraudulent, or the bank's own fraud system blocked it.
Type: Hard decline
How common: 2-4% of failures. More common for new customers or unusual charge amounts.
What to do:
- Do not retry. Repeated attempts on a flagged card can get your Stripe account flagged.
- If this is a legitimate customer, contact them directly and ask them to use a different card or contact their bank.
- Review the charge in Stripe Dashboard for Radar signals.
- If you're confident it's legitimate, you can add the customer to your Radar allowlist for future charges.
Recovery outlook: Variable. For actual fraud, 0%. For false positives on legitimate customers, 40-50% if you reach out personally.
lost_card / stolen_card
What it means: The card has been reported lost or stolen by the cardholder.
Type: Hard decline
How common: 1-2% of failures.
What to do:
- Do not retry under any circumstances. Charging a reported lost/stolen card can result in disputes.
- Send a friendly email explaining you need updated payment details.
- The customer almost certainly has a replacement card from their bank.
Recovery outlook: Moderate. 45-55% recovery. The customer knows their old card is gone, but they need a nudge to update your system specifically.
card_not_supported
What it means: The card doesn't support the type of transaction you're attempting (e.g., recurring charges, international transactions).
Type: Hard decline
How common: 1-2% of failures. More common with prepaid cards or cards from smaller banks.
What to do:
- Do not retry with the same card.
- Ask the customer to provide a different card that supports recurring payments.
- Be specific in your email: "Your current card doesn't support recurring charges. Could you add a card that does?"
Recovery outlook: Moderate. 40-50%. Some customers may not have an alternative card readily available.
Less Common Decline Codes
These codes appear less frequently but are still worth understanding.
| Code | Meaning | Retryable? | Action |
|---|---|---|---|
currency_not_supported |
Card can't process charges in this currency | No | Ask for different card |
duplicate_transaction |
Same amount charged twice in quick succession | No | Check for duplicate logic in your code |
incorrect_number |
Card number is wrong | No | Ask customer to re-enter card |
incorrect_zip |
Billing ZIP doesn't match bank records | No | Ask customer to update billing address |
invalid_account |
Card account is invalid or closed | No | Ask for different card |
invalid_amount |
Charge amount is not allowed by bank | No | Check charge amount, contact bank |
invalid_expiry_month |
Expiration month is invalid | No | Ask customer to re-enter card |
invalid_expiry_year |
Expiration year is invalid | No | Ask customer to re-enter card |
issuer_not_available |
Bank's system is temporarily unavailable | Yes | Retry in 2-4 hours |
new_account_information_available |
Card has been replaced with new details | No | Card updater may help, otherwise ask for new card |
pickup_card |
Card has been reported and bank wants it returned | No | Do not retry, ask for different card |
reenter_transaction |
Bank wants you to try the charge again | Yes | Retry immediately |
restricted_card |
Card is restricted from this type of purchase | No | Ask for different card |
revocation_of_all_authorizations |
Cardholder revoked all authorizations | No | Ask for different card |
revocation_of_authorization |
Cardholder revoked authorization for your charges | No | Contact customer directly |
testmode_decline |
Test card used in live mode | No | Developer error, check your integration |
withdrawal_count_exceeded |
Card has exceeded its withdrawal limit | Yes | Retry in 24-48 hours |
Quick Reference: Recovery Strategy by Decline Code
Here's the cheat sheet. Print this out or bookmark it.
| Decline Code | Retry? | When to Retry | Customer Contact? | Expected Recovery |
|---|---|---|---|---|
insufficient_funds |
Yes | 24-48 hours | After 2nd failure | 50-60% |
card_declined |
Once | 24 hours | After failed retry | 40-50% |
expired_card |
No | N/A | Immediately | 65-75% |
processing_error |
Yes | 2-4 hours | Only if persistent | 70-80% |
incorrect_cvc |
No | N/A | Immediately | 60-70% |
authentication_required |
No* | N/A | With 3DS link | 50-65% |
do_not_honor |
Once | 48 hours | After failed retry | 30-40% |
fraudulent |
No | N/A | Carefully | 0-50% |
lost_card / stolen_card |
No | N/A | Immediately | 45-55% |
card_not_supported |
No | N/A | Immediately | 40-50% |
*Requires 3DS-enabled payment flow, not a simple retry.
Building Your Decline Code Strategy
Knowing the codes is step one. Here's how to operationalize this knowledge.
1. Route Failures by Type
Don't send the same dunning email for every failure. At minimum, create two paths:
Soft declines (retryable): Start with automated retries. Only email the customer after retries are exhausted.
Hard declines (not retryable): Skip retries entirely. Send the customer a payment update link immediately. Every hour you wait on a hard decline is wasted time.
2. Customize Your Messaging
The email for an expired card should be different from an insufficient funds notification. A customer with an expired card needs to know: "Your card expired, here's a link to add your new one." A customer with insufficient funds needs a softer touch, since mentioning funds directly can feel intrusive.
3. Track Recovery Rates by Code
Most teams track one aggregate recovery rate. Break it down by decline code instead. You might find that your expired card recovery is 70% while your do_not_honor recovery is 15%. That tells you where to focus your optimization efforts.
4. Set Smart Retry Limits
Stripe allows you to configure automatic retries in your billing settings. But the default retry schedule isn't optimized for each decline type. For insufficient_funds, retrying on the 1st and 15th of the month (common paydays) outperforms fixed intervals. For processing_error, retrying within hours outperforms waiting days.
5. Monitor for Patterns
If you see a spike in fraudulent declines, check whether your Radar rules need tuning. If authentication_required is increasing, you may need to implement 3DS in your checkout flow. Decline codes are diagnostic data, not just error messages.
The Bottom Line
A failed payment is not a dead end. It's information. The decline code tells you exactly what went wrong and, by extension, what to do about it.
The difference between a 30% recovery rate and a 70% recovery rate often comes down to whether you're treating every failure the same or routing each one to the right recovery path.
Rekko reads every Stripe decline code and automatically routes failures into the right recovery sequence. Soft declines get retried at optimal intervals. Hard declines trigger immediate customer outreach with pre-authenticated payment links. No manual sorting required.
Start your free trial and put your decline code strategy on autopilot.