Why Stripe subscription payments fail, and what to tell your customer
Updated October 28, 2026
A failed renewal rarely means a customer wants to leave. Usually a card expired, a bank said no for its own reasons, or a payment needed one more confirmation. Here is what Stripe's decline codes mean in plain language, and a kind thing to say for each one.
Where the reason comes from
When Stripe tries to charge a saved card for a subscription renewal, the card issuer (your customer's bank) decides whether to approve it. Stripe's documentation explains that issuers' automated systems look at signals like spending habits, account balance and card details such as the expiry date and CVC. If the issuer says no, Stripe passes on what it can as a decline code.
You can see it in two places:
- In the Dashboard, on the failed payment.
- In the API, as
decline_codeon thecard_declinederror, inlast_payment_erroron the PaymentIntent, and asoutcome.reasonon the charge.
One thing to know up front: Stripe notes that issuers put most declines in the generic bucket (generic_decline), so the exact reason is often unclear. For privacy reasons, issuers only discuss the details with the cardholder. So your customer can often find out more than you can.
The codes you will see most with subscriptions
Stripe lists dozens of decline codes. For renewals charged off-session, a handful come up again and again. The "next step" column follows Stripe's own guidance.
| Code | What it means | Next step (per Stripe) |
|---|---|---|
insufficient_funds | The card doesn't have enough funds or credit for this charge. | The customer uses another payment method. |
expired_card | The card has expired. | The customer uses another card. |
generic_decline | Declined for an unknown reason, or blocked by Stripe Radar. | The customer contacts their card issuer. |
do_not_honor | Declined for an unknown reason. | The customer contacts their card issuer. |
transaction_not_allowed | Declined for an unknown reason. | The customer contacts their card issuer. |
card_velocity_exceeded | Over the card's balance, credit limit or transaction limit. | The customer contacts their card issuer. |
authentication_required | The bank wants extra authentication such as 3D Secure. | For off-session payments, ask the customer to retry so they can authenticate. |
incorrect_cvc | The CVC is wrong. | The customer tries again with the correct CVC. |
processing_error | Something went wrong while processing the card. | Try again; if it still fails, the customer contacts their issuer. |
lost_card, stolen_card, fraudulent | Reported lost or stolen, or suspected fraud. | Don't share this detail. Treat it like generic_decline. |
That last row matters. Stripe's guidance is not to tell a customer their card was flagged as lost, stolen or fraudulent. Your message should read exactly like a generic decline.
Can a retry fix it?
Some declines are temporary. Funds arrive on payday, a bank's fraud check relaxes, a processing hiccup clears. Others will fail every time until the customer gives you a different card.
Stripe calls the second group hard declines. Its retry documentation lists them: 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 these, Stripe keeps the retries on the schedule, but they only actually run once a new payment method is on file.
Stripe also returns an advice code on the charge outcome that helps you decide:
do_not_try_again: don't reuse this card for this charge.try_again_later: the issuer declined, but a retry is allowed.confirm_card_data: some card information was wrong; the customer should check it.
expired_card isn't on the hard-decline list, but retrying the same expired card won't help. The customer has to add a new one. Stripe can also email customers a month before a saved card expires, which heads off many of these. There's more on that in Smart Retries vs dunning emails.
What to tell your customer
Your customer didn't do anything wrong, and in most cases they don't know the payment failed. A good message is short and specific. It assumes good faith and gives them one link to fix it. It should never mention fraud or a lost card, and it shouldn't guess about their finances.
A few lines you can adapt:
Expired card
Hi Sam, your subscription renewed today, but the card we have on file has expired, so the payment didn't go through. You can add a new card here in about a minute: [link]. Nothing else changes. Thank you!
Insufficient funds
Don't say "insufficient funds". It's private, and the customer will know if it applies. Keep it neutral and offer another way to pay.
Hi Sam, this month's payment didn't go through. It happens. We'll try again in a few days, or if you'd like to use a different card you can update it here: [link]. If anything's off, just reply.
Generic decline, do_not_honor, lost or stolen
Hi Sam, your bank declined this month's payment and didn't tell us why. Banks only share the reason with the cardholder, so a quick call to them usually clears it up. Or you can use another card here: [link].
Authentication required
Hi Sam, your bank asked for a quick confirmation before it approves this month's payment. You can confirm it here, and it only takes a moment: [link].
Send from a real address that you read. Keep the first message friendly and practical. If you send a second or third reminder, keep the same tone. A calm reminder from a person gets a better reaction than an alarm.
Small things that prevent the next failure
- Turn on expiring-card emails in Stripe so customers update their card before it lapses.
- Update the right field. Stripe retries on the subscription's default payment method first. If you only update the customer's default, retries keep using the old card. See payment method ordering.
- Don't hammer the card. Stripe notes that card networks limit how many times you can retry a single charge. It recommends at most eight retries, because issuers can treat extra attempts as possible fraud.
- Know what happens at the end. After the last retry, your settings decide whether the subscription is canceled, marked unpaid or left past due. Our past_due guide walks through each.
See your own failed payments, for free
Calmretry reads your Stripe account with a restricted, read-only key and shows the failed subscription payments from the last 90 days: what was paid later, what is still open, and the decline reasons. It never touches funds or card data.
Sources
- Stripe Docs: Decline codes
- Stripe Docs: Card declines
- Stripe Docs: Automate payment retries
- Stripe Docs: Error codes