<?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: Paul Hartness</title>
    <description>The latest articles on DEV Community by Paul Hartness (@paulhartness).</description>
    <link>https://dev.to/paulhartness</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4194064%2Ff9e2c668-3af6-40bc-9105-98f1bd838be5.png</url>
      <title>DEV Community: Paul Hartness</title>
      <link>https://dev.to/paulhartness</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paulhartness"/>
    <language>en</language>
    <item>
      <title>How to Reduce Soft Declines (and When a Retry Will Actually Work)</title>
      <dc:creator>Paul Hartness</dc:creator>
      <pubDate>Mon, 19 Oct 2026 12:06:24 +0000</pubDate>
      <link>https://dev.to/paulhartness/how-to-reduce-soft-declines-and-when-a-retry-will-actually-work-3ia4</link>
      <guid>https://dev.to/paulhartness/how-to-reduce-soft-declines-and-when-a-retry-will-actually-work-3ia4</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://retryfix.com/blog/how-to-reduce-soft-declines" rel="noopener noreferrer"&gt;RetryFix blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A soft decline&lt;/strong&gt; is a refused card charge where the issuer has not said the card itself is finished. The charge failed this time. The same card might still pay later, or the customer might need to do one specific thing first: add funds, authenticate, or use another payment method. A hard decline is the other case. This card cannot complete the charge, and another attempt on it will not collect.&lt;/p&gt;

&lt;p&gt;This page is the practical half: how to have fewer soft declines in the first place, and when a retry is worth running. Which codes are which, and the slice of failed payments a retry will never collect, is in &lt;a href="https://retryfix.com/blog/hard-declines-vs-soft-declines" rel="noopener noreferrer"&gt;hard declines vs soft declines&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a soft decline means
&lt;/h2&gt;

&lt;p&gt;Stripe's &lt;a href="https://docs.stripe.com/declines/codes" rel="noopener noreferrer"&gt;decline codes&lt;/a&gt; page does not group codes under a heading called "soft declines." Subscription operators use the term for the declines that are not a finished card. On a renewal, the codes this page treats as soft declines are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;insufficient_funds&lt;/code&gt;.&lt;/strong&gt; The card did not have enough for this charge. Stripe's next step is that the customer needs another payment method. The code is not on the list Stripe refuses to retry, so a later attempt is still allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;do_not_honor&lt;/code&gt; and &lt;code&gt;generic_decline&lt;/code&gt;.&lt;/strong&gt; The issuer declined and did not say why. Stripe's next step for both is that the customer contacts their card issuer. These are also not on Stripe's do-not-retry list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;processing_error&lt;/code&gt;, &lt;code&gt;issuer_not_available&lt;/code&gt;, and &lt;code&gt;reenter_transaction&lt;/code&gt;.&lt;/strong&gt; Something in the path failed. Stripe's next step is to attempt the payment again, and to involve the customer if it still will not go through.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;authentication_required&lt;/code&gt;.&lt;/strong&gt; The issuer wants the customer to authenticate, often with 3D Secure. Stripe's decline-code page uses "soft decline" for the issuer response that, on Stripe's front ends, in most cases starts an authentication flow. &lt;code&gt;authentication_required&lt;/code&gt; is the code when that step is still outstanding, and it is on the &lt;a href="https://docs.stripe.com/billing/revenue-recovery/smart-retries" rel="noopener noreferrer"&gt;list of codes&lt;/a&gt; Stripe will not run a live automatic retry against until a new payment method is attached.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sharper signal, when the issuer sends one, is the advice code on the charge, not the decline name alone. Stripe documents three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;try_again_later&lt;/code&gt;.&lt;/strong&gt; You can retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;do_not_try_again&lt;/code&gt;.&lt;/strong&gt; Do not use this card again for the same transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;confirm_card_data&lt;/code&gt;.&lt;/strong&gt; Some of the card data was wrong. Fix it before you charge again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A retry only helps if something can still change. That is what &lt;a href="https://retryfix.com/blog/what-is-payment-retry-logic" rel="noopener noreferrer"&gt;payment retry logic&lt;/a&gt; is deciding: whether to charge again, when, how many times, and when to stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to reduce soft declines before the renewal
&lt;/h2&gt;

&lt;p&gt;A retry recovers a charge that already failed. The cheaper result is a renewal that never declines. The steps below are the ones Stripe documents. A longer retry list is not one of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let the network update the card
&lt;/h3&gt;

&lt;p&gt;When an issuer replaces a card, the number and expiry you have saved can be wrong before the customer tells you. Stripe works with the card networks and &lt;a href="https://docs.stripe.com/payments/cards/overview#automatic-card-updates" rel="noopener noreferrer"&gt;automatically attempts to update saved card details&lt;/a&gt; when the customer receives a new card, including a replacement for one that expired or was reported lost or stolen. The next renewal can then use the new details instead of failing.&lt;/p&gt;

&lt;p&gt;That only happens when the issuer participates. Stripe says support is wide for American Express, Visa, Mastercard, and Discover cards issued in the United States. Support in other countries varies. You cannot tell in advance which saved cards will update. If you keep your own copy of the last four and the expiry, listen for &lt;code&gt;payment_method.automatically_updated&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This prevents a decline. It is not a retry. If the update never arrives and the charge comes back &lt;code&gt;expired_card&lt;/code&gt;, the customer needs another card. That code is a hard decline, covered on the &lt;a href="https://retryfix.com/blog/hard-declines-vs-soft-declines" rel="noopener noreferrer"&gt;hard vs soft&lt;/a&gt; page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Email before a default card expires
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Send emails about expiring cards&lt;/strong&gt;, in Stripe's revenue recovery email settings, sends one email a month before a card expires, when that card is the customer's default payment method or default source. The renewal should not be the moment anyone learns the card is ending.&lt;/p&gt;

&lt;p&gt;That email does not replace the note after a charge has already failed. &lt;strong&gt;Send emails when card payments fail&lt;/strong&gt; is a separate switch, and it sends after each failed payment. If you also send your own failed-payment mail, the customer gets two threads. Pick one sender. What those notes should say is in &lt;a href="https://retryfix.com/blog/what-is-dunning" rel="noopener noreferrer"&gt;what dunning is&lt;/a&gt;, including the day 1, day 3, and day 7 reminder ladder RetryFix describes publicly. Treat that ladder as an example of the emails, not as a recovery rate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Put a second card where the retry will actually use it
&lt;/h3&gt;

&lt;p&gt;A backup card helps only if the next attempt charges it. When Stripe retries a subscription invoice, it uses one payment method, in this order: the subscription's default payment method, then the subscription's default source, then the customer's default payment method, then the customer's default source. It does not try every card saved on the customer.&lt;/p&gt;

&lt;p&gt;There is a trap in that order. If the subscription has its own default payment method, and a card update only changes the customer's invoice default, Stripe keeps retrying the subscription's card. Update the field the failed attempt used.&lt;/p&gt;

&lt;p&gt;Asking for a second card at signup, or in the product before renewal, is what makes an &lt;code&gt;insufficient_funds&lt;/code&gt; decline recoverable without waiting on a balance that may not change. Stripe's written next step for that code is an alternative payment method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a recognisable descriptor, and authenticate while the customer is present
&lt;/h3&gt;

&lt;p&gt;A statement descriptor is the text on the bank or card statement. &lt;a href="https://docs.stripe.com/get-started/account/statement-descriptors" rel="noopener noreferrer"&gt;Stripe's docs&lt;/a&gt; say a clear, accurate descriptor can reduce chargebacks and disputes, and that it should reflect the name you do business under. For card charges the account has a short prefix. A per-charge suffix is for one-time card payments. Stripe creates subscription charges itself, so you cannot set that suffix on the subscription's PaymentIntent. Subscription and invoice charges use the account descriptor unless you set one on the invoice.&lt;/p&gt;

&lt;p&gt;A clearer name will not turn an insufficient-funds decline into an approval. It is still worth setting, because a renewal the customer does not recognise is how a real charge gets queried or stopped. Most banks show the descriptor. Some do not.&lt;/p&gt;

&lt;p&gt;Authentication is the other step you can take before the decline. Some issuers, especially in Europe and India, want the customer to confirm the payment. Stripe's &lt;a href="https://docs.stripe.com/declines/card" rel="noopener noreferrer"&gt;card declines&lt;/a&gt; guide says using &lt;a href="https://docs.stripe.com/payments/3d-secure" rel="noopener noreferrer"&gt;3D Secure&lt;/a&gt; can lower decline rates in countries that support it. On Stripe's front ends, an issuer soft decline that wants authentication usually becomes a prompt while the customer is still there.&lt;/p&gt;

&lt;p&gt;A renewal is off-session. The customer is not on a page to complete a prompt. An &lt;code&gt;authentication_required&lt;/code&gt; result then needs a message, not another silent charge. Stripe can email a hosted confirmation link when &lt;strong&gt;Send a Stripe-hosted link for customers to confirm their payments when required&lt;/strong&gt; is on. While the customer is still in checkout, Stripe's front ends can turn the issuer's soft decline into an authentication step instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a retry will actually work
&lt;/h2&gt;

&lt;p&gt;Retry the same card when the issuer has left the door open. Stop when they have not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A later attempt on the same card can be worth it&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The advice code is &lt;code&gt;try_again_later&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The decline is &lt;code&gt;processing_error&lt;/code&gt;, &lt;code&gt;issuer_not_available&lt;/code&gt;, or &lt;code&gt;reenter_transaction&lt;/code&gt;. Stripe's next step is to attempt again.&lt;/li&gt;
&lt;li&gt;The decline is &lt;code&gt;insufficient_funds&lt;/code&gt;, &lt;code&gt;do_not_honor&lt;/code&gt;, or &lt;code&gt;generic_decline&lt;/code&gt;, and you still have a sensible number of attempts left. These codes are not on Stripe's do-not-retry list. They are also not a promise. For insufficient funds, the written next step is still another payment method. For a do-not-honor or a generic decline, the written next step is that the customer contacts the issuer. A spaced retry can catch a balance that has been topped up, or a temporary block that has cleared. It cannot make the customer call their bank.&lt;/li&gt;
&lt;li&gt;The decline is &lt;code&gt;card_velocity_exceeded&lt;/code&gt;: a balance, credit, or amount limit on the card. Stripe's next step is that the customer contacts the issuer. Waiting is reasonable. Charging again immediately is how you hit the same limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The customer has to act&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The advice code is &lt;code&gt;do_not_try_again&lt;/code&gt; or &lt;code&gt;confirm_card_data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The code is &lt;code&gt;authentication_required&lt;/code&gt;. Ask the customer to confirm the payment. Stripe keeps the retry on the schedule, and it will not run the charge until a new payment method is attached.&lt;/li&gt;
&lt;li&gt;The card cannot pay: &lt;code&gt;expired_card&lt;/code&gt;, or a code on Stripe's do-not-retry list such as &lt;code&gt;lost_card&lt;/code&gt;, &lt;code&gt;stolen_card&lt;/code&gt;, or &lt;code&gt;incorrect_number&lt;/code&gt;. Stop charging this card and ask for a new one. Stripe already withholds a live retry on that published list. &lt;code&gt;expired_card&lt;/code&gt; is not on the list, so an expired card can still be charged again unless something else in your setup stops it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://retryfix.com/blog/what-are-stripe-smart-retries" rel="noopener noreferrer"&gt;What Stripe Smart Retries are&lt;/a&gt;: a Billing setting that picks each attempt time from Stripe's network data, inside a window you choose, from 1 week up to 2 months. The recommended default is 8 tries within 2 weeks. A custom schedule is the other option: up to three retries, each a set number of days after the previous attempt. Card networks limit how often a single charge can be reattempted. Stripe recommends a maximum of eight retries for charges that permit retries, and says issuers may treat further retries as possible fraud, which can increase declines on legitimate charges.&lt;/p&gt;

&lt;p&gt;Space matters more than adding attempts. Several charges a few hours apart do not create funds on an insufficient-funds card. They use up attempts the network will tolerate. A practical shape for which soft decline to leave for a few days is in &lt;a href="https://retryfix.com/blog/how-to-retry-failed-stripe-payments" rel="noopener noreferrer"&gt;how to retry failed Stripe payments&lt;/a&gt;. Put the customer email in the gap before the next attempt, so that attempt can use an updated card.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is a soft decline?&lt;/strong&gt; A card charge the issuer refused without saying the card is finished. For a subscription renewal, that means insufficient funds, a generic or do-not-honor response, a processing error, or a request to authenticate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you reduce soft declines?&lt;/strong&gt; Keep the saved card current, with network card updates and an email a month before a default card expires. Store a second payment method on the field Stripe will actually charge. Authenticate while the customer is present. Set a statement descriptor the customer can recognise, so a real renewal is less likely to be queried or stopped. Then retry only where a later attempt is allowed, with space between tries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should you retry every soft decline?&lt;/strong&gt; No. Retry when the advice code is &lt;code&gt;try_again_later&lt;/code&gt;, or when the decline is a processing or issuer-unavailable error. For insufficient funds, do-not-honor, or a generic decline, a small number of spaced retries can be worth it, and the customer may still have to act. Do not retry when the advice code says not to. Do not keep charging a card that needs authentication or a new number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Stripe retry soft declines for you?&lt;/strong&gt; On subscription invoices, Smart Retries will retry many of them inside the window you set. It will not run a live charge for a code on its published hard-decline list, which includes &lt;code&gt;authentication_required&lt;/code&gt;. It also will not retry when no payment method is available, when the card is India-issued, or when the Connect account has been disconnected. After the final attempt, Stripe makes no further payment attempts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between a soft decline and a hard decline?&lt;/strong&gt; A soft decline can still clear later, or after the customer does one thing. A hard decline means this card cannot pay, so the path is a new payment method. How to split the codes in your own Stripe data is in &lt;a href="https://retryfix.com/blog/hard-declines-vs-soft-declines" rel="noopener noreferrer"&gt;hard declines vs soft declines&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;In the Stripe Dashboard, open &lt;strong&gt;Billing → Revenue recovery&lt;/strong&gt;. Confirm Smart Retries are on, and that the window is the one you meant rather than a long run of extra attempts. Turn on &lt;strong&gt;Send emails about expiring cards&lt;/strong&gt;. On a handful of recent failures, read the decline code and the advice code, and separate the ones that can wait from the ones that need the customer today.&lt;/p&gt;

&lt;p&gt;Then open one subscription that failed for insufficient funds. If it has its own default payment method, that is the card the next retry will charge, including when the customer has just added a different card on their profile.&lt;/p&gt;




&lt;p&gt;Try the demo: &lt;a href="https://retryfix.com/demo/start" rel="noopener noreferrer"&gt;https://retryfix.com/demo/start&lt;/a&gt;&lt;/p&gt;

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