Reference guide · updated September 2026

Declined transaction test cards, beyond the generic error.

Most checkout flows only test one decline path. Real checkouts fail in at least four distinct ways — and your error messaging should say something different for each one.

Not All Declines Are the Same Decline

It's tempting to build one generic "Payment failed, please try again" message and call decline handling done. The problem: a customer whose card was declined for insufficient funds needs a completely different next step (use a different card) than one caught by a fraud filter (contact their bank) or one who mistyped their CVC (fix and retry immediately). Showing the same message for all three either confuses customers or, worse, tips off actual fraudsters about exactly which control caught them.

This page covers the generic and fraud-related decline paths specifically. Insufficient funds and expired cards get their own dedicated pages, linked below, since they're common enough to deserve focused treatment.

Stripe's Generic and Fraud Decline Codes

Stripe returns a specific decline_code alongside every declined charge. The test numbers below trigger the catch-all and fraud-adjacent paths specifically.

NumberDecline codeWhat it means
4000 0000 0000 0002generic_declineBank rejected with no specific reason given
4000 0000 0000 9987lost_cardCard reported lost by the cardholder
4000 0000 0000 0341fraudulentFlagged as fraudulent by the card network
Handle fraud-related codes carefully: Stripe's own guidance is explicit that fraudulent, lost_card, and stolen_card should never be shown verbatim to the customer — surface a generic "contact your bank" message instead, since revealing the specific fraud signal can help an actual bad actor calibrate their next attempt.

The "do_not_honor" Catch-All

do_not_honor is worth understanding specifically because it's maddeningly uninformative by design: it's the card issuer's way of declining a transaction without telling the merchant (or Stripe) why. It could be a risk-scoring model, an internal policy, or something entirely account-specific — the issuing bank simply isn't obligated to say. Your UI can't do better than "please try a different payment method or contact your card issuer" for this one, and that's expected, not a bug in your integration.

Soft Declines vs. Hard Declines

Worth building into your retry logic: declines aren't all equally final. Soft declines (like insufficient_funds or the generic decline) can sometimes succeed if the customer simply retries — a temporary balance issue may have resolved. Hard declines (lost_card, stolen_card, invalid card details) won't succeed on retry and need genuinely new card details. Automatically retrying a hard decline wastes a network call and can look like card-testing fraud to the processor's own risk systems.

Generate test card numbers →

Related Guides

Frequently Asked Questions

What's the difference between a generic decline and do_not_honor?

Functionally they're similar — both mean the bank declined without a specific reason. generic_decline is Stripe's own catch-all classification; do_not_honor is the underlying network-level code some issuers return. Treat both the same way in your UI: a neutral 'try another card or contact your bank' message.

Should I retry a declined transaction automatically?

Only for soft declines (insufficient funds, generic decline), and even then sparingly — repeated rapid retries on the same card can trigger a processor's own fraud detection. Never auto-retry hard declines like lost_card or stolen_card; they require genuinely different card details.

Can I show the customer the exact decline_code Stripe returns?

For most codes, yes, if translated into plain language. For fraud-related codes specifically (fraudulent, lost_card, stolen_card), don't — showing the real reason can help an actual fraudster understand which control caught them and adjust their next attempt.