DEV Community

Swipe Again for Swipe Again

Posted on

Hard or Soft: The First Question Every Failed Payment Has to Answer

For developers building decline handling, the taxonomy that matters is not the full list of network codes. It is a single branch point that every failed payment hits in its first second: is this refusal final, or is it weather? Hard declines answer final. Soft declines answer weather. Almost every design mistake in a retry system comes from routing a case down the wrong side of that branch.

Hard declines carry information about the credential itself: the card is closed, reported lost or stolen, invalid, or the account behind it no longer exists. Presenting the same credential again cannot succeed, and presenting it repeatedly is worse than useless — it teaches issuer systems to associate your descriptor with noise. The only productive move on a hard decline is a conversation: the customer supplies a different payment method, or the case closes. Any retry budget spent here is spent against a locked door.

Soft declines carry information about the moment: insufficient funds, a temporary issuer caution, an authentication step that never completed, a do not honor with no reason attached. The credential may be perfectly good; the timing, the balance, or the issuer's mood was not. These cases reward patience with structure — attempts spaced across days, a check for updated credentials before each attempt, and one early, plain message to the customer. A meaningful share of soft declines resolve with no customer action at all, simply because the moment changed.

The implementation consequence is a routing table, not a retry counter. That routing is spelled out in a triage table that separates hard declines from soft ones before the first retry is scheduled: which code families belong on each side, the grey-zone codes that need a history check before routing, and the exit rules for cases that change sides mid-sequence — because they do, and a soft case that returns a hard response on its third attempt must leave the retry path immediately, not finish its scheduled attempts out of inertia.

Two grey zones deserve explicit handling in code. Authentication-required responses are soft in nature but blocked in practice: no retry succeeds until the customer completes a challenge, so the case belongs in a customer-action queue with a retry armed behind the completed step, not in either standard path. And generic responses — the shrugs — should route by history: a first-ever failure on a long-held card is weather until proven otherwise, while the same code on a brand-new signup deserves earlier human review, because the base rates behind the identical code are entirely different.

The metrics change once the branch exists. Instead of one recovery rate smeared across unlike cases, you can read the two numbers that actually diagnose a billing operation: how much of the soft pool patience recovered, and how fast hard cases reached the customer and closed. A falling soft recovery rate points at scheduling. Slow hard-case closure points at messaging. Without the split, both problems hide inside an average that moves for reasons nobody can name.

If your current handler retries everything the same number of times on the same schedule, it is not a neutral default — it is a decision to hammer locked doors and rush open ones, daily, at scale. Split the branch first. Every refinement after that is tuning; this one is correctness.

Swipe Again builds that branch into every recovery sequence it runs — hard cases straight to the customer, soft cases worked patiently — as part of its flat-fee service.

Top comments (0)