PaymentHood payments reliability

Most checkouts treat a failed payment as one event: the charge did not go through, so show an error and let the customer try again. That single response is wrong for two of the three ways a payment actually fails. Retrying the wrong failure wastes money and irritates the card networks. Not retrying the right one throws away revenue you had already earned.

The three categories are hard declines, soft declines, and technical failures. They need opposite handling, and the difference is visible in the response you already receive.

1. Hard declines: the issuer said no, and will say no again

The issuer made a permanent decision about this card. Nothing about waiting changes it.

Code Meaning
04, 07 Pick up card (the second is the fraud-flagged variant)
14 Invalid card number
15 No such issuer
41 Lost card
43 Stolen card
54 Expired card
57 Transaction not permitted to cardholder

Do not retry these. Not later, not through another provider. The correct response is to ask the customer for a different payment method — not to try the same card again with different framing.

2. Soft declines: the issuer said not now

Same card, same customer, different moment, plausibly a different answer.

Code Meaning
51 Insufficient funds
61 Exceeds withdrawal amount limit
65 Exceeds withdrawal count limit
62 Restricted card
91 Issuer or switch inoperative
05 Do not honour

Code 05 deserves its own note. It is the most common decline and the least informative — issuers use it as a catch-all when they do not want to tell you why. Treat it as soft, but cap the attempts rather than retrying it like an insufficient-funds decline.

One caveat on all of these: your gateway’s normalised codes are what you should actually branch on. Providers map the underlying network codes differently, and some collapse several into one. Build your classification against the codes your provider documents, then check it against real traffic.

3. Technical failures: nobody said anything

Timeouts, 502s, connection resets, TLS errors, a provider having a bad hour.

This category is different from the other two in a way that matters more than anything else in this article: a decline is an answer, and a timeout is not. When a provider declines, you know the customer was not charged. When a request times out, the charge may have succeeded, may have failed, or may still be in flight. You do not know, and you must not guess.

Never retry a hard decline

This is the rule that costs real money when it is broken. Card networks monitor repeated attempts on codes that cannot succeed, and excessive retries can attract fees or penalties. The exact caps and windows change, so confirm the current figures with your provider rather than trusting a number in a blog post, including this one.

The commercial argument is simpler anyway: each pointless retry may carry a per-attempt fee, and a stream of failed authorisations on the same card does your standing with the issuer no favours.

Retry timing that matches the reason

Retrying is not one behaviour with one delay. Match the schedule to what you are waiting for:

  • Technical failure — retry once, immediately, ideally through a different provider. You are not waiting for anything to change; you are routing around something broken.
  • Insufficient funds (51) — you are waiting for money to arrive in an account. Retrying five minutes later is noise. Retry on a scale of days, and where you can, aligned with local pay cycles.
  • Velocity limits (61, 65) — you are waiting for a rolling limit to reset. Hours, not minutes.
  • Issuer unavailable (91) — you are waiting for someone else’s system to come back. Minutes.

For subscriptions the same logic drives your dunning schedule: attempts spread over days for funding problems, and a much shorter cycle for technical ones.

Retries create double charges unless you stop them

Every retry mechanism needs an idempotency key — a value you send with the charge so that a repeated request returns the original result instead of creating a second payment. Without one, a timeout followed by a retry is a genuine risk of charging a real customer twice.

The subtlety that catches people out is which retries share a key:

  • A retry after a timeout is the same payment attempt. It must reuse the original key, so the provider can tell you what happened to the first request rather than performing a new charge.
  • A retry after a definitive decline is a new attempt. It needs a new key — reuse the old one and you will get the cached decline back forever and conclude, wrongly, that the card is dead.

Derive the key from the order and the attempt, never randomly per HTTP request. A key that changes on every request provides no protection at all, which is the most common way this gets implemented wrong.

When failing over to another provider helps

Failover is powerful for exactly one of the three categories.

It helps for technical failures. If a provider is timing out, another one probably is not, and the customer never needs to know.

It does nothing for hard declines. The issuer rejected the card, and the issuer is the same institution no matter which acquirer asks. Retrying a stolen card elsewhere is not a routing strategy.

It is a judgement call for soft declines. A different acquirer can present a transaction differently — local versus cross-border, a different descriptor, a different MCC — and that occasionally changes the outcome. It is worth testing with your own traffic, and it is not worth assuming.

What to tell the customer

Your error copy is part of your recovery rate. Two rules cover most of it.

Do not explain the decline. You usually do not know the real reason, and “insufficient funds” is a humiliating thing to display to someone standing at a counter when you are guessing. “That card was declined — please try another card or payment method” is accurate for every hard and soft decline.

Never tell a customer they were not charged unless you have verified it. For declines you can say it safely, because a decline is an answer. For a timeout you cannot, because the charge may have gone through. Say something honest about the uncertainty — that you are confirming and will email them — and then go and confirm it server-side.

That second rule follows from a broader one worth stating plainly: an order is paid when your server asks the provider and the provider says so. Not when the browser reached your success URL, which an attacker controls.

Measure the right number

The metric is authorisation rate: approved authorisations divided by attempted ones. The aggregate figure is nearly useless on its own — what makes it actionable is the segmentation:

  • By provider. Two providers on the same traffic rarely perform identically.
  • By country and currency. A cross-border transaction routed through a domestic acquirer often behaves very differently.
  • By payment method and card BIN. One issuer behaving badly can hide inside a healthy average.
  • By decline code, over time. A shift in the mix of codes is a signal well before the total moves. Rising 05s look like nothing; rising 91s are somebody’s outage.

Alert separately on technical failure rate. It is the only one of the three that is definitely yours to fix.

The checklist

  • Every failure is classified as hard, soft or technical before anything else happens.
  • Hard declines are never retried.
  • Retry delays match the reason, not one global backoff.
  • Every charge carries an idempotency key derived from the order and attempt.
  • Timeout retries reuse the key; post-decline retries use a new one.
  • Failover is wired for technical failures only.
  • Orders are marked paid from a server-side confirmation, never a redirect.
  • Customer-facing copy never states a reason you have not verified.
  • Authorisation rate is broken down by provider, country, method and code.

Where PaymentHood fits

Everything above is implementable yourself, and the classification table is the easy part — the work is keeping it correct across providers as each one changes its codes, plus idempotency, failover health checks and server-side confirmation for every one of them.

PaymentHood handles that layer: decline classification, retry and failover rules, idempotency and webhook signature verification across 36+ providers behind one API. If you are running WooCommerce, WHMCS, VirtueMart, Phoca Cart or J2Commerce, the free plugins mean it is a plugin install rather than a development project, and the retry and routing rules live in a dashboard instead of in your checkout code.

If the broader idea is new to you, What Is Payment Orchestration? covers the layer this sits in, and the provider directory lists what is available in your market.

One integration, every payment provider

PaymentHood gives you routing, failover, idempotency and webhook verification across 36+ providers — without rebuilding any of it per provider.