<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mike Badger</title>
    <description>The latest articles on DEV Community by Mike Badger (@mike_badger).</description>
    <link>https://dev.to/mike_badger</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3982737%2F6eea74dd-07ec-4542-9d4f-df9aca822b65.png</url>
      <title>DEV Community: Mike Badger</title>
      <link>https://dev.to/mike_badger</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mike_badger"/>
    <language>en</language>
    <item>
      <title>Every Stripe decline code, what it actually means, and whether retrying will help</title>
      <dc:creator>Mike Badger</dc:creator>
      <pubDate>Sat, 13 Jun 2026 14:14:31 +0000</pubDate>
      <link>https://dev.to/mike_badger/every-stripe-decline-code-what-it-actually-means-and-whether-retrying-will-help-olj</link>
      <guid>https://dev.to/mike_badger/every-stripe-decline-code-what-it-actually-means-and-whether-retrying-will-help-olj</guid>
      <description>&lt;p&gt;When a card payment fails in Stripe, you get a short string back — &lt;code&gt;insufficient_funds&lt;/code&gt;, &lt;code&gt;do_not_honor&lt;/code&gt;, &lt;code&gt;authentication_required&lt;/code&gt;. Most teams glance at it and let Smart Retries hammer the card on a schedule.&lt;/p&gt;

&lt;p&gt;That's the mistake. The right move depends entirely on the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some failures &lt;strong&gt;clear themselves&lt;/strong&gt; if you retry in a few days.&lt;/li&gt;
&lt;li&gt;Some will &lt;strong&gt;never&lt;/strong&gt; clear — and retrying them can get you fined by the card networks.&lt;/li&gt;
&lt;li&gt;Many need the &lt;strong&gt;customer&lt;/strong&gt; to act, so retrying the same card is pointless.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A big slice of "failed" subscription payments are recoverable — but only if you act on the &lt;em&gt;reason&lt;/em&gt;. Here's every code that matters and what to do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  First: &lt;code&gt;code&lt;/code&gt; vs &lt;code&gt;decline_code&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Stripe returns two fields, and they're not the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;code&lt;/code&gt;&lt;/strong&gt; — the high-level category, usually &lt;code&gt;card_declined&lt;/code&gt;. Tells you almost nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;decline_code&lt;/code&gt;&lt;/strong&gt; — the granular reason from the issuing bank (&lt;code&gt;insufficient_funds&lt;/code&gt;, &lt;code&gt;lost_card&lt;/code&gt;, &lt;code&gt;do_not_honor&lt;/code&gt;). &lt;strong&gt;This is the one you act on.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Caveat: issuers don't always report the true reason — anti-fraud blocks often hide behind a vague &lt;code&gt;do_not_honor&lt;/code&gt;. The granular code is a strong hint, not gospel.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mental model: five buckets
&lt;/h2&gt;

&lt;p&gt;Every decline maps to one action: &lt;strong&gt;retry&lt;/strong&gt;, &lt;strong&gt;email the customer&lt;/strong&gt;, &lt;strong&gt;stop&lt;/strong&gt;, or &lt;strong&gt;fix your checkout&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 — Auto-recoverable (retry will likely work)
&lt;/h2&gt;

&lt;p&gt;Timing problems. The card is fine; the money wasn't there &lt;em&gt;that moment&lt;/em&gt;, or the network hiccuped.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;decline_code&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;insufficient_funds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No money right now&lt;/td&gt;
&lt;td&gt;Retry — time it near payday&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;try_again_later&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Temporary issuer issue&lt;/td&gt;
&lt;td&gt;Retry in a few hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;processing_error&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transient error&lt;/td&gt;
&lt;td&gt;Retry shortly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;issuer_not_available&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Couldn't reach the bank&lt;/td&gt;
&lt;td&gt;Retry — clears fast&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is the &lt;strong&gt;only&lt;/strong&gt; bucket where blind retries earn their keep.&lt;/p&gt;

&lt;h2&gt;
  
  
  2 — Needs customer action (retrying is pointless)
&lt;/h2&gt;

&lt;p&gt;The card can't complete the charge as-is. Email the customer with the &lt;em&gt;specific&lt;/em&gt; ask.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;decline_code&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Ask the customer to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expired_card&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Card expired&lt;/td&gt;
&lt;td&gt;Update their card&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;incorrect_cvc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wrong CVC&lt;/td&gt;
&lt;td&gt;Re-enter card details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;incorrect_number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wrong number&lt;/td&gt;
&lt;td&gt;Re-enter card details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;authentication_required&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bank needs 3DS / SCA&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Confirm the payment&lt;/strong&gt; (see below)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The fix lives with the customer, not in your retry logic. A retry-only tool fails every one of these silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 — Lost cause (stop retrying)
&lt;/h2&gt;

&lt;p&gt;Card is dead, blocked, or flagged. Retrying won't help — and retrying network-flagged cards can trigger Visa/Mastercard penalties.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;decline_code&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;lost_card&lt;/code&gt; / &lt;code&gt;stolen_card&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Reported lost or stolen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pickup_card&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bank wants it seized — never retry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fraudulent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flagged as fraud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;revocation_of_authorization&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Customer told their bank to stop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; stop. Ask for a different card, or let it churn.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 — Hard declines (the ambiguous middle)
&lt;/h2&gt;

&lt;p&gt;The bank declined without saying why — often deliberately.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;decline_code&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;do_not_honor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Catch-all decline; often anti-fraud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;generic_decline&lt;/code&gt; / &lt;code&gt;card_declined&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No reason given&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;call_issuer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Customer must call their bank&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;card_velocity_exceeded&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Too many charges, short window&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; a couple of retries max, then email: &lt;em&gt;"Your bank blocked this — a quick call to them, or a different card, usually fixes it."&lt;/em&gt; Many &lt;code&gt;do_not_honor&lt;/code&gt; blocks lift in 30 seconds — if the customer knows to ask.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 — Structural (fix checkout, not the payment)
&lt;/h2&gt;

&lt;p&gt;Not about one customer — a recurring leak.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;decline_code&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;currency_not_supported&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Card can't be charged in that currency&lt;/td&gt;
&lt;td&gt;Offer a supported currency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;card_not_supported&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Card type unsupported&lt;/td&gt;
&lt;td&gt;Check your Stripe capabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transaction_not_allowed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transaction type blocked&lt;/td&gt;
&lt;td&gt;Often regional — review where you sell&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Fix it once, recover every future customer who'd hit the same wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  The European trap: SCA and &lt;code&gt;authentication_required&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you sell in Europe, this is what most US-built dunning tools get wrong.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under PSD2 / SCA, many recurring EU payments need the customer to authenticate via 3D Secure.&lt;/li&gt;
&lt;li&gt;When they don't, Stripe returns &lt;code&gt;authentication_required&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Most tools &lt;strong&gt;retry the card&lt;/strong&gt; — which can &lt;em&gt;never&lt;/em&gt; succeed without authentication.&lt;/li&gt;
&lt;li&gt;The fix: send a &lt;strong&gt;confirm-payment link&lt;/strong&gt; (Stripe hosts the flow), not a retry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For EU-facing SaaS, &lt;code&gt;authentication_required&lt;/code&gt; is often a large, fully recoverable chunk that's silently written off.&lt;/p&gt;

&lt;h2&gt;
  
  
  One-page playbook
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bucket&lt;/th&gt;
&lt;th&gt;Do this&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auto-recoverable&lt;/td&gt;
&lt;td&gt;Time-aware retry (near payday)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needs customer action&lt;/td&gt;
&lt;td&gt;Email the specific ask; for SCA, send a confirm link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lost cause&lt;/td&gt;
&lt;td&gt;Stop retrying; ask for a new card&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hard decline&lt;/td&gt;
&lt;td&gt;A few retries, then "call your bank / try another card"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;Fix checkout config once&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Blind retries only fix &lt;strong&gt;one&lt;/strong&gt; of these five buckets. The rest need an email, a config fix, or a hard stop — so "just turn on Smart Retries" quietly leaves money on the table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try this:&lt;/strong&gt; pull your last few months of failed payments and group them by &lt;code&gt;decline_code&lt;/code&gt;. The split usually surprises people — especially how much is recoverable customer-action, not dead cards.&lt;/p&gt;

&lt;p&gt;I'm building a tool that does this bucketing and routing automatically. Got war stories about declines, or opinions on what actually drives recovery? Drop them in the comments.&lt;/p&gt;

</description>
      <category>stripe</category>
      <category>payments</category>
      <category>saas</category>
      <category>billing</category>
    </item>
  </channel>
</rss>
