Key Takeaways
-
Stripe payment failures fall into three distinct types — issuer declines, Stripe Radar blocks, and invalid API calls — each requiring a different response.
-
Most card declines originate with the customer's bank, not with Stripe. Stripe passes the decline message along but doesn't control the outcome.
-
Stripe Radar can block legitimate transactions. Knowing how to review and override these blocks prevents unnecessary revenue loss.
-
API-level errors typically won't appear in your Stripe Dashboard — they surface in your server logs and need to be caught there.
-
Persistently high failure rates on international transactions often point to a structural gap that cannot be fixed through retries or rule adjustments.
A "payment failed" message in Stripe doesn't tell you much on its own. The fix depends entirely on which of Stripe's three failure types triggered it — and each type requires a completely different response. Treating a Stripe Radar block the same way you'd handle an expired card won't resolve anything. This guide covers how to identify what went wrong and what to do about it.
Why Did the Stripe Payment Fail? Start Here
Before jumping to fixes, identify which failure type you're dealing with. The Stripe Dashboard's Charge outcome field tells you which category triggered the failure. Here's a quick reference:
|
What you see in Stripe
|
Root cause
|
Where to look
|
|
card_declined + decline code
|
Customer's card issuer declined the charge
|
Decline codes section below
|
|
payment blocked + Radar reason
|
Stripe Radar's fraud prevention triggered
|
Radar section below
|
|
HTTP 4xx / API error response
|
Integration or configuration error on your side
|
API errors section below
|
|
High failure rate on international cards
|
Structural coverage or 3DS friction
|
International payments section below
|
Each category needs a different response. The sections below walk through all three, with specific actions for each.
Issuer Declines: What the Bank Is Telling You
Issuer declines are the most common type of Stripe payment failure. They happen when the customer's bank or card network rejects the charge during the
payment authorization process — Stripe never gets to charge the card. Stripe receives a decline code from the issuer and passes it back to you, but has no ability to override the bank's decision.
The decline code tells you the reason. Here are the most common ones and what each means:
|
Decline Code
|
What It Means
|
What to Do
|
|
insufficient_funds
|
Card balance too low
|
Ask the customer to use a different card
|
|
expired_card
|
Card has passed its expiry date
|
Ask the customer to update their card details
|
|
do_not_honor
|
Generic decline — bank doesn't specify a reason
|
Ask the customer to call their bank
|
|
authentication_required
|
Card issuer requires 3DS verification
|
Enable 3DS in your Stripe integration
|
|
authentication_failure
|
3DS was required but the attempt failed
|
Have the customer retry; verify your 3DS setup
|
|
card_velocity_exceeded
|
Too many charge attempts on this card
|
Wait 24 hours before retrying
|
|
lost_card / stolen_card
|
Card flagged as compromised
|
Do not retry; treat as potential fraud risk
|
|
card_not_supported
|
Card type doesn't support this transaction type
|
Customer needs to use a different card or payment method
|
The
card_not_supported code is worth noting separately. Prepaid cards and some virtual cards
don't support certain types of purchases — particularly subscription billing or international transactions. If you're seeing this code frequently, it may signal a mismatch between your payment setup and the card types your customers tend to use.
What to Do When a Customer's Card Is Declined
For one-off
card declines, the most effective action is clear, specific error messaging. "Your payment didn't go through" is less useful than "Your card was declined — please try a different card or contact your bank." Customers who know what's wrong are more likely to complete the purchase.
For subscription businesses, Stripe Billing's Smart Retries uses machine learning to time retry attempts when authorization is more likely to succeed. It's enabled by default on Stripe Billing and can recover a meaningful percentage of failed recurring charges without any manual intervention.
The "do_not_honor" Code
do_not_honor is a catch-all decline that gives no specific reason. The issuing bank has decided to decline but won't say why. Common triggers include an unusual transaction pattern for the cardholder, the bank's internal fraud model flagging the charge, or a temporary system issue on the bank's side.
There's no configuration change on your end that will fix this. The only path forward is the customer calling their bank to understand the block and, if everything is legitimate, asking the bank to approve future transactions.
Stripe Radar Blocks: When Stripe Rejects the Payment
Stripe Radar is Stripe's fraud prevention layer. When it blocks a payment, it does so before contacting the issuing bank — which means the customer's card is never actually charged, and no decline code comes from the bank. The block originates entirely within Stripe.
Radar evaluates every payment against a risk score built from signals including IP address, device fingerprinting, email address, and
address verification (AVS) data. Payments that exceed a risk threshold — or match a custom rule you've configured — get blocked. You can see which outcome type and reason triggered the block in the Dashboard under the payment's
Radar outcome section.
Common reasons a legitimate payment gets flagged by Radar:
-
Customer is using a VPN, which creates an IP-country mismatch
-
First-time purchase from a location or device outside the customer's typical pattern
-
High transaction value that deviates from the card's normal spending history
-
Billing address doesn't match the card's registered address (AVS mismatch)
-
Email domain associated with disposable or high-risk accounts in Radar's training data
When Radar Blocks a Legitimate Transaction
If you review a blocked payment in the Dashboard and recognize it as legitimate, click Add to allow list. This overrides the matching rules for future attempts from that source. It does not automatically retry the payment — the customer will need to try again.
If you're seeing a pattern of Radar blocks on customers you'd expect to be legitimate, review your custom rules. Overly broad rules — for example, blocking all payments from a particular country or above a blanket dollar amount — can generate false positives at scale. Test any rule changes in Stripe's review mode before enabling them on live payments.
Invalid API Calls: Integration Errors on Your Side
Unlike issuer declines and Radar blocks, API errors don't represent a payment that was attempted and rejected — they represent a request that was malformed before Stripe could process it. These won't always appear in your Stripe Dashboard, which means they're easy to miss if you're only checking there.
The four most common categories:
-
Expired or invalid API keys — the api_key_expired error means your integration is using a rotated or deleted key. Update your server environment with the current key from the Dashboard.
-
Incorrect payment amount — amount_too_small and amount_too_large errors occur when the charge amount falls outside Stripe's permitted range for the currency. Check the amount calculation logic in your integration.
-
Missing 3DS authentication flow — for off-session recurring charges, Stripe requires a SetupIntent or a PaymentIntent with the appropriate confirmation flow. If authentication_required returns on an automated charge, the initial setup didn't capture the mandate correctly.
-
Wrong parameter types or missing fields — these produce generic 400 errors. Check the API reference for the specific endpoint and confirm the parameters your code is sending match the expected format.
How to Catch API Errors Before Customers Do
API errors surface in your server logs, not in the Stripe Dashboard. Set up error logging and alerting on your server side to catch these as they happen. Stripe's test mode provides specific test card numbers that simulate each decline and error scenario — run through them regularly when making integration changes to catch regressions before they reach production.
How to Track Your Stripe Failure Rate
Stripe shows authorization and failure rate data under Dashboard → Radar → Business overview. You can also filter the Payments list by failed status to see the volume and breakdown of failures over any period.
When reading the numbers, separate unique declines (first-attempt failures) from retry failures (failed re-attempts on already-declined charges). Mixing them inflates the apparent failure rate and makes diagnosis harder.
What to look for in the data:
-
A sudden drop in authorization rate — check for a recent Radar rule change or a new integration deployment
-
High do_not_honor rate clustered in a specific region — may indicate bank-level friction with a particular issuer or market
-
High authentication_required rate — your 3DS setup likely needs review
-
High blocked rate from Radar — custom rules may be too aggressive
Consistently tracking these metrics is the foundation for any effort to
improve payment approval rates — spikes often point directly to the source of the problem.
When Stripe Keeps Failing on International Transactions
Not every Stripe failure problem can be solved with retries, rule adjustments, or better error messaging. When failures cluster specifically around international transactions — and the decline codes don't point to fixable card-level issues — the underlying cause is often structural.
Stripe's authorization rates are not uniform across markets. In parts of Southeast Asia, Latin America, and Africa, local card-issuing banks have lower acceptance rates for international card network transactions. Cross-border payments also trigger more frequent 3DS authentication requirements, and 3DS completion rates vary significantly by region — customers unfamiliar with the flow or on older devices tend to abandon it. For B2B use cases specifically, Stripe's product is optimized for online consumer checkout, not for high-value supplier payments or multi-currency local collection.
The signal that this is the issue: your failure rate is concentrated in specific countries, the decline codes don't match anything on the customer's side, and adjusting your Stripe configuration doesn't move the numbers.
For
cross-border B2B payments — collecting from international clients, paying overseas suppliers — a platform designed specifically for that use case addresses the gap without requiring you to replace Stripe entirely.
PhotonPay is built for cross-border B2B payment flows, supporting multi-currency accounts, global payouts across 200+ countries and regions, and local payment collection in markets where card-based flows hit structural limits. Businesses that find Stripe works well for domestic or consumer-facing payments but falls short for international B2B transactions use PhotonPay for those corridors specifically.
Frequently Asked Questions
Why does Stripe say "payment failed" but the customer's card shows no charge?
When Stripe Radar blocks a payment, it does so before contacting the card issuer — so no authorization attempt reaches the bank. The customer sees no pending charge on their statement. For issuer declines, the bank may briefly show a pending authorization that reverses within a few days, but no funds are actually moved.
What does "do_not_honor" mean on Stripe?
do_not_honor is a generic decline code that means the issuing bank refused the charge without specifying a reason. It's often triggered by the bank's internal fraud scoring, an unusual transaction pattern, or a temporary system issue. There's no fix on your integration side — the customer needs to call their bank to resolve it.
Can I retry a Stripe payment that was declined?
It depends on the decline code. For card_velocity_exceeded, Stripe recommends waiting 24 hours. For do_not_honor or insufficient_funds, retrying immediately rarely succeeds. For authentication_required, the retry should route through 3DS verification. Stripe Billing's Smart Retries handles optimal timing automatically for subscription payments.
How do I lower my Stripe decline rate?
Start by identifying which failure type is driving the rate — issuer declines, Radar blocks, or API errors. For issuer declines, improve your error messaging and enable Smart Retries for subscriptions. For Radar over-blocking, review and tighten your custom rules. For API errors, add server-side error logging to catch issues before they reach customers. For persistent failures on international transactions, evaluate whether your payment infrastructure covers those markets adequately.
What are alternatives to Stripe for international business payments?
For cross-border B2B payments — paying suppliers, collecting from international clients, managing multi-currency accounts — dedicated global payment platforms cover corridors and payment methods that Stripe's consumer-focused infrastructure doesn't prioritize. PhotonPay supports global payouts and local payment collection across 200+ countries and regions, which covers markets where standard card processing sees lower authorization rates.