FAILED PAYMENTSSTRIPE

Stripe decline codes explained: what to do for each one

Stripe decline codes on subscription renewals: soft vs hard declines, advice codes, retry limits, and what to tell the customer for each code.

Published 8 min read

When a renewal fails on Stripe, the charge comes back with a short string such as insufficient_funds or do_not_honor. That string is the most useful piece of information you get about the failure. It tells you whether retrying can work, whether the customer has to do something, and what you are allowed to tell them.

This guide goes through the decline codes that matter most for subscriptions, grouped by what you should do about them. It sticks to what Stripe documents in its decline codes reference and card declines guide (both accessed November 2026), and says so when Stripe’s own pages leave room for interpretation.

Where the codes live

A failed card payment on Stripe can carry several fields. They answer different questions:

Field What it tells you Where to find it
decline_code Stripe’s normalized reason for the issuer decline, if the issuer gave one On the error, and on last_payment_error of the PaymentIntent
advice_code What to do next: retry, do not retry, or check the card data On the error, alongside the decline code
network_decline_code The raw code from the card network On the charge outcome
network_advice_code The network’s own advice (for Mastercard, its Merchant Advice Codes) On the charge outcome

Stripe added the network advice and decline code fields to the API in December 2024. Stripe’s docs warn that the meaning of a network_decline_code differs between card networks, so always read it together with the card brand.

For subscriptions, you will usually read these from the invoice.payment_failed webhook: fetch the invoice’s PaymentIntent and look at last_payment_error.

Advice codes come first

The advice_code is the simplest signal, and Stripe’s card declines guide documents three values:

  • try_again_later: the issuer declined, but you may retry. If repeated retries fail, the customer needs to contact their issuer.
  • do_not_try_again: do not use this card again for this transaction. Look at the decline code for the reason.
  • confirm_card_data: the issuer declined because some card information was wrong. The customer needs to check their details.

If an advice code is present, let it override any general rule of thumb. A do_not_try_again on what looks like a soft decline means stop.

Soft vs hard declines

Stripe does not label every code as soft or hard, but the distinction is useful and Stripe uses it in its retry documentation:

  • Soft declines are temporary. The card is valid; the issuer said no this time. Retrying later can work.
  • Hard declines mean the card cannot be charged as it is. Stripe’s Smart Retries page lists the codes it will not retry without a new payment method: incorrect_number, lost_card, pickup_card, stolen_card, revocation_of_authorization, revocation_of_all_authorizations, authentication_required, highest_risk_level and transaction_not_allowed.

For those hard decline codes, Stripe keeps scheduling retries on the invoice, but a charge only runs once a new payment method is attached. If nobody asks the customer, nothing will happen. We explain the retry side in detail in how Stripe Smart Retries work.

The common codes, and what to do

The table below covers the codes you will see most on subscription renewals. “Stripe’s next step” paraphrases the decline codes reference; “For a subscription” is our practical reading.

Code Stripe’s description and next step Retry? For a subscription
insufficient_funds Not enough funds; the customer should use another payment method Yes, later Retry at a better time (after a likely payday); email the customer with an update link
generic_decline Declined for an unknown reason, or blocked by Radar; customer contacts the issuer A few times Retry; email; suggest the customer call their bank or try another card
do_not_honor Generic issuer decline with no specific reason A few times Same as generic_decline
processing_error Error while processing the card; try again, then contact the issuer Yes Retry soon; email only if it keeps failing
card_velocity_exceeded Customer exceeded the balance, credit limit or transaction limit; contact the issuer Yes, later Retry later; mention a spending limit in the email
expired_card The card has expired; use another card No Check for an automatic card update first; otherwise ask for a new card
incorrect_cvc The CVC is incorrect; try again with the correct CVC No Ask the customer to re-enter their card on the update page
incorrect_number Card number is incorrect No Ask for a new card
lost_card, stolen_card Reported lost or stolen; present as a generic decline No Ask for a new card, without mentioning the reason
pickup_card The card cannot be used for this payment (possibly reported lost or stolen); contact the issuer No Ask for a new card
fraudulent Stripe suspects fraud; show the same message as a generic decline No Treat with care; do not reveal the reason
authentication_required The bank requires the customer to authenticate Not without the customer Send a link to confirm the payment; see SCA on renewals
revocation_of_authorization, stop_payment_order Stripe lists these with an unknown reason; customer contacts the issuer No Ask for a new payment method; check whether the customer meant to stop paying
transaction_not_allowed Not permitted on this card No Ask for another card or payment method

Two points are easy to miss:

  1. Some reasons must not be shown to the customer. For lost_card, stolen_card and fraudulent, Stripe’s reference says to present the decline as a generic decline. Your dunning email should say “your payment was declined”, never “your card was reported stolen”.
  2. “Contact your issuer” is a real instruction. For generic declines, a call to the bank often resolves the block. It is fine to say so in the email, briefly.

Retry limits are real

Retrying is not free, even for soft declines. Card networks limit how often a merchant can retry a declined card, and issuers can treat excessive retries as a fraud signal.

According to Stripe’s support article on payments blocked due to excessive retries (accessed November 2026), Visa’s rules broadly prohibit more than 15 retries of a single payment over 30 days. For Visa transactions on integrated pricing, Stripe blocks retries after the 15th attempt when it judges the chance of success to be low. Stripe’s own retry defaults (8 tries for Smart Retries) sit well under that, but custom retry logic outside Stripe can cross it if you are not careful.

Mastercard’s equivalent signal is the Merchant Advice Code, which Stripe exposes as network_advice_code. Processor documentation such as Braintree’s merchant advice codes reference describes codes that mean “do not try again” and one that means the cardholder cancelled the recurring payment. When you see these, stop retrying that card.

Practical rules:

  • Count every attempt per invoice, including ones your own code triggers.
  • Stop immediately on a hard decline or a do_not_try_again advice.
  • Do not retry the same card several times in one day “just in case”.
  • Space soft-decline retries out, and align them with when money is likely to be available.

What to tell the customer, by group

Most teams send the same email for every failure. A few variations by decline group work better and are not much extra work.

Group Codes Email message
Funds or limits insufficient_funds, card_velocity_exceeded “We’ll try again on [date]. If you’d rather use another card, update it here.”
Unclear generic_decline, do_not_honor, processing_error “Your bank declined the payment. It’s often a temporary block; a quick call to your bank, or another card, usually fixes it.”
Card unusable expired_card, lost_card, stolen_card, pickup_card, incorrect_number “We need a new card to keep your subscription active.” No reason given.
Authentication authentication_required “Your bank asks you to confirm this payment. It takes a minute.”
Card details incorrect_cvc, confirm_card_data advice “Some card details didn’t match. Please re-enter your card.”

Ready-to-use emails for each step are in our dunning email templates.

Logging decline codes properly

You cannot improve what you do not measure. For every failed renewal, store:

  • Invoice ID, customer ID, amount and currency.
  • Attempt number and date.
  • decline_code, advice_code, network_decline_code, network_advice_code.
  • Card brand, funding type (credit, debit, prepaid) and country.
  • Outcome: recovered (and how: retry, new card, authentication), or lost.

With a few months of data, you can answer the questions that matter: which codes recover on retry and which only recover when the customer acts, whether prepaid cards fail more often (see prepaid and virtual cards), and whether your emails or your retries do most of the work.

Grouping for reports

Raw codes are too granular for a monthly report. Group them:

Report group Codes
Soft: funds insufficient_funds, card_velocity_exceeded
Soft: unclear generic_decline, do_not_honor, processing_error, try_again_later
Hard: card expired_card, lost_card, stolen_card, pickup_card, incorrect_number, incorrect_cvc
Hard: authorization revocation_of_authorization, revocation_of_all_authorizations, stop_payment_order, transaction_not_allowed
Authentication authentication_required
Risk fraudulent, highest_risk_level

Keep the raw code in your data so you can regroup later. Stripe adds and deprecates codes over time; Stripe’s reference now marks some older codes as deprecated in favor of advice codes.

Testing decline handling

Stripe provides test card numbers that trigger specific decline codes. Before going live with any recovery logic, run each group through test mode:

  • A soft decline schedules a retry and sends the first email.
  • A hard decline sends a “new card needed” email and does not retry the same card.
  • authentication_required sends a confirmation link.
  • lost_card and stolen_card produce a generic message.
  • A successful payment (invoice.paid) stops all emails and retries.
  • Each attempt is logged with its codes.

Common mistakes

  • Treating every decline the same. Retrying a stolen card eight times does not recover anything and can count against you with the network.
  • Telling the customer the raw reason. “Do not honor” means nothing to customers, and some reasons must not be disclosed at all.
  • Ignoring advice codes. They exist precisely to tell you whether to retry.
  • Only reading the first failure. The code can change between attempts; a card can go from insufficient_funds to expired_card across a long retry window.
  • No exit for hard declines. If the only path to recovery is a retry, hard declines are lost by design.

For the full picture of failed payment recovery, from retries to emails to measurement, see how to recover failed payments on Stripe.

How NoChurn helps

NoChurn reads the decline code on every failed renewal and handles each group differently. Soft declines are retried at times based on likely paydays and the customer’s local time (or you keep Stripe Smart Retries). Hard declines and authentication requests skip pointless retries and go straight to an AI-written recovery email, reviewed before sending, with a link to a hosted card update page. Sensitive reasons are never shown to the customer, and every decision is logged with its code.

It connects to Stripe in one click, and pricing depends only on MRR, with no share of recovered revenue. See failed payments and pricing.

PROMPT

Read https://www.nochurn.sh/llms.txt,
then follow https://www.nochurn.sh/docs/quickstart/
to add NoChurn to this app. Use a test key
(nc_test_…) until I say we go live.

Stop leaking revenue.

  • Free until $300 MRR
  • No card needed
  • Live in 10 minutes
  • Never a cut of what we recover