Skip to content

Why Stripe subscription payments fail, and what each decline code needs

5 min read

A failed renewal rarely means a customer wants to leave. Most of the time a card expired, a balance was low on the wrong day, or a bank asked for a confirmation nobody saw. The decline code Stripe records tells you which of these happened, and each one calls for a different response.


What happens when a renewal fails

When a subscription renews, Stripe creates an invoice and tries to charge the customer's default payment method. If the bank refuses, the invoice stays open, the subscription usually moves to past_due, and Stripe records why the payment was refused: a decline code such as insufficient_funds or expired_card, sometimes with a more specific reason from the bank.

From there, what happens depends on your Billing settings. Stripe can retry the payment on a schedule, email the customer, and after the last attempt cancel the subscription, mark it unpaid or leave it past due. None of these steps looks at why the payment failed. A card that expired three months ago gets the same treatment as a balance that was short by a few francs on the 28th.

Soft declines and hard declines

The most useful distinction is between declines that can succeed on a later attempt and declines that cannot succeed until the customer does something. The first kind is often called a soft decline, the second a hard decline.

  • Soft declines: insufficient funds, temporary problems at the bank (processing_error, try_again_later), spending or frequency limits reached (card_velocity_exceeded). A later attempt can work without any action from the customer.
  • Hard declines: expired card, lost or stolen card, closed account, wrong card number. No retry will ever succeed; the customer has to provide a new payment method.
  • Authentication required: a category of its own. The card is fine, but the bank wants the cardholder to confirm the payment. Retrying does nothing; the customer has to complete a step.

Retrying a hard decline is pointless, and repeated attempts on a card that cannot be charged are something card networks watch. Retrying a soft decline at the right moment, on the other hand, often works without the customer ever noticing there was a problem.

The decline codes you will see most often

CodeWhat it usually meansWhat helps
insufficient_fundsNot enough balance or credit at that momentA retry a few days later, a short and neutral email
expired_cardThe card on file is past its expiry dateA new card from the customer; automatic card updates sometimes fix it first
generic_decline, do_not_honorThe bank refused without giving a reasonOne retry, then an email suggesting another card or a call to the bank
authentication_requiredThe bank wants a 3-D Secure confirmationA link where the customer confirms the payment
incorrect_cvc, incorrect_numberCard details are wrongAsk the customer to enter the card again
lost_card, stolen_cardThe bank blocked the cardA new payment method; never mention the reason to the customer
card_velocity_exceededToo many attempts or a limit was reachedWait before the next attempt

The list is not exhaustive and banks do not always use the codes consistently. Treat the code as a strong hint, not a certainty, and look at what actually gets paid afterwards.

Expired cards: the most predictable failure

An expiry date is known years in advance, which makes this the easiest failure to prevent. Card networks run updater services that pass new card details to merchants, and Stripe applies these updates automatically when the issuing bank takes part. When it does not, the renewal fails on the first billing date after the card expired.

Two things help: a reminder sent before the card expires, with a link to update it, and a follow-up after the failure that asks clearly for a new card instead of saying vaguely that something went wrong. We cover both in reducing card declines before they happen.

Insufficient funds: a question of timing

This is usually the most recoverable failure. The customer has a valid card and often fully intends to pay; the money simply was not there on that day. Many people are paid once a month, so an attempt a few days after the start or the end of the month has a better chance than one made at a random hour.

The email that goes with it should be short and neutral. Nobody likes being told that their account was empty. A sentence such as 'your bank declined the payment' is enough, followed by the amount and a single link to pay or to switch cards.

3-D Secure: when the bank wants a confirmation

Under strong customer authentication rules in Europe, the first payment of a subscription is usually confirmed by the cardholder. Later renewals are generally treated as payments initiated by the merchant and go through without a new confirmation, but the bank can still ask for one, for example after a change of card or for a large amount.

In that case Stripe marks the payment as requiring an action and sends the invoice.payment_action_required event. Retries alone will not help: the customer has to open a link, confirm in their banking app and come back. The faster that link reaches them, the better.

Generic declines: the hardest to read

Codes like do_not_honor or generic_decline mean the bank said no without saying why. It can be a fraud rule, a card blocked for online payments, a limit, or a temporary issue. A single retry a few days later sometimes succeeds. If it does not, the customer needs to try another card or contact their bank, and the email should say exactly that, without blaming anyone.

Turning decline codes into a process

  1. Record the decline code for every failed invoice, not just the fact that it failed.
  2. Group the codes into three families: worth retrying, needs a new card, needs authentication.
  3. For each family, decide the timing of retries and the message the customer receives.
  4. Measure what gets paid, and compare it with what would have been paid anyway. A holdout group is the only honest way to do that.

This is the logic Reclaiva follows. It reads each failed invoice through read-only access to Stripe, sends the follow-up that fits the decline in your company's name, and compares the result with a group of invoices that receive no follow-up at all. What you see is the revenue recovered on top of what Stripe recovers on its own.

All articles