Payment Routing Optimization: Cost-Based Routing, Cascading and Smart Retries Across Acquirers (2026)

Payment Routing Optimization: Cost-Based Routing, Cascading and Smart Retries Across Acquirers (2026)

Payment Routing Optimization: Cost-Based Routing, Cascading and Smart Retries Across Acquirers

A merchant with two acquirers and no routing logic has already made a routing decision: it picked whichever one was integrated first. That default quietly costs money in two directions. Transactions go to the acquirer with the higher effective rate even when a cheaper path would have been approved, and soft declines are abandoned that a second attempt on a different path would have recovered. Neither loss shows up as an error; both show up as a slightly worse margin and a slightly lower approval rate than you could have had.

Payment routing optimization matters more in 2026 because the inputs have multiplied. Network tokens, local acquiring, issuer-specific behaviour, scheme retry rules and per-region cost structures all change which path is best for a given transaction, and the rules that govern retries now carry real fees for getting them wrong.

This post is a systems-architecture treatment. You will leave with a layered decision model (eligibility, then cost, then approval probability), a precise view of decline semantics and retry eligibility, a working rule engine plus a Thompson-sampling router in Python, the idempotency and circuit-breaker plumbing that makes cascading safe, and an experimentation and observability design. It is engineering analysis, not financial or compliance advice.

What this covers: the routing decision stack, cost inputs, decline codes and scheme retry rules, cascading mechanics, rules versus adaptive routing, runnable code, failure modes and a launch checklist.

Context and Background

Card acceptance is a chain: merchant, payment service provider (PSP) or gateway, acquirer, card network, issuer. A merchant that connects to several acquirers, directly or through a PSP, gains a choice at the second hop. The choice is not free. Each acquirer has its own pricing, its own relationship with issuers in each market, its own risk models and its own outage profile. Routing is the discipline of using that choice deliberately.

It helps to separate three terms that vendors blur. Orchestration is the whole layer that abstracts multiple PSPs behind one API, including vaulting, tokenization and reporting; our payment orchestration architecture guide covers that layer end to end. Routing is the decision function inside it: given a transaction and the current state of the world, pick a path. Cascading is the follow-up behaviour: if the first path fails in a retryable way, try another. This post is about the decision function and the loop around it, not the orchestration platform.

The cost side has its own literature. Interchange, scheme fees and acquirer markup determine what a path costs, and regulators in some regions give merchants the right to route co-badged cards to the cheaper network. We treat that mechanism in depth in interchange fees and least-cost routing; here we only consume its output as a number per candidate path.

The approval side is where most of the money is. A one percentage point change in approval rate on a high-volume merchant is usually worth more than a few basis points of cost savings, because every declined legitimate sale loses the full margin of the order plus, often, the customer. That asymmetry is the central tension of routing: the cheapest path is not always the one most likely to approve, and a router that optimises cost alone will drift toward paths that quietly decline more.

Finally, retries are regulated by the networks. Visa documents categories of decline responses and charges fees for excessive reattempts; Mastercard publishes Merchant Advice Codes that tell you whether and when to retry. Acquirer documentation such as Adyen’s Visa integrity fee reference describes how these categories map onto raw refusal codes. A router that retries blindly can therefore turn a recovery tactic into a recurring penalty.

Our thesis, which differs from the usual vendor framing of routing as a single “smart” engine, is that routing is three separable problems with different data, different update frequencies and different failure modes. Eligibility is hard constraints and changes with contracts. Cost is deterministic arithmetic and changes with price books. Probability is statistical and changes by the hour. Systems that fuse them into one opaque score are hard to debug and hard to audit. Systems that keep them as layers can be reasoned about, tested and explained to a finance team.

The Routing Decision Stack

Payment routing optimization works best as a pipeline of three layers: filter the candidate acquirers by hard eligibility, price each survivor with a deterministic cost model, then rank by expected net value using an estimated approval probability. The first layer removes impossible paths, the second quantifies cost, the third trades cost against the chance of approval.

Payment routing optimization decision stack showing eligibility, cost and probability layers feeding a ranked candidate list

Figure 1: The routing decision stack. Eligibility removes impossible paths, the cost model prices what remains, and the probability layer produces the final ranked list that cascading consumes.

The diagram shows the transaction context entering an eligibility filter, surviving candidates being priced, a probability estimate being attached, and a ranked list emerging with a primary route and ordered fallbacks. The ranked list, not a single choice, is the output. That distinction is what makes cascading a natural extension rather than a bolt-on.

Layer one: eligibility

Eligibility is a set of boolean constraints evaluated before any scoring. Does this acquirer support the card brand and the currency? Is the merchant boarded for this country with this acquirer? Does the payment method require a specific path, for example a network token provisioned through one scheme-connected provider? Is the transaction type, such as a recurring merchant-initiated charge, supported with the stored credential flags this acquirer expects? Is the path currently healthy according to the circuit breaker described later?

Eligibility must be a pure function of the transaction and a versioned configuration. That sounds obvious, but it is where hidden coupling creeps in: a rule that checks “acquirer B has been slow today” is not eligibility, it is a health signal, and mixing them makes configuration changes impossible to replay. Keep contractual and technical eligibility in a configuration store with versions, keep live health in a separate signal, and combine them only at decision time.

The practical payoff of a clean eligibility layer is that it is testable without any data. You can write a table of transactions and expected candidate sets and run it in continuous integration. Teams that skip this discover eligibility bugs as production declines: a currency routed to an acquirer that does not settle it, a debit card sent to a path boarded only for credit.

Layer two: cost

The cost of a path is the sum of everything the merchant pays for that attempt. For a card transaction that typically includes interchange (or the interchange-plus markup structure, or a blended rate), scheme assessments, acquirer processing fees, a per-authorisation fee, cross-border and currency conversion charges where relevant, and the PSP’s own fee. Pricing models differ: under interchange++ pricing the merchant pays the actual interchange, the actual scheme fees and a disclosed acquirer markup, so cost varies per card; under blended pricing the merchant pays a flat percentage and fixed fee per brand or region, so cost is nearly constant per path.

The distinction determines how much a cost-based router can gain. With blended pricing, differences between acquirers are contractual and cost-based routing reduces to picking the cheaper blended rate for each segment. With interchange++, the same card can cost differently at different acquirers because of markup, because of how each acquirer classifies the transaction into an interchange category, and because of whether data such as Level 2 or Level 3 fields or network tokens earn a lower category. The router needs a cost function that takes card attributes (brand, product type, issuer country, consumer or commercial) and returns an estimated all-in fee per candidate.

Treat the cost function as a lookup over a price book, not as a model. Price books are contractual documents; encode them as versioned data with effective dates, evaluate them deterministically, and reconcile the estimate against the fee actually reported in settlement files. The gap between estimated and actual cost is itself a metric worth alerting on, and our reconciliation engine architecture post shows where those settlement-level fee records come from.

Layer three: approval probability and expected value

The probability layer estimates, for each surviving candidate, the chance the issuer approves given what is known: card BIN (bank identification number) range, issuer country, amount band, currency, whether the transaction is a network token or a raw primary account number (PAN), whether strong customer authentication was performed, time of day and recent acquirer performance for that segment. The output is a number between zero and one per candidate.

With cost and probability in hand, the router ranks candidates by expected net value. A simple and defensible formulation is:

expected_value = p_approve * (margin - cost_if_approved) - (1 - p_approve) * cost_if_declined

Here margin is what the merchant keeps on a successful sale before payment cost, cost_if_approved is the all-in fee from layer two, and cost_if_declined is usually a small per-attempt fee, but may include a penalty term for the customer-experience cost of a visible failure. The formula makes the trade-off explicit. When margin is large relative to fees, as on a high-ticket order, the probability term dominates and the router should favour the path with the best approval rate even if it costs more. When margin is thin, as on a low-ticket digital good, cost differences matter more and the router can accept a slightly lower approval probability for a cheaper path, provided a cascade can recover declines.

That last clause is where routing and cascading interact. A cheap first path with a lower approval rate is only a good bet if the fallback is cheap and the customer does not notice the retry. We turn to that next.

Decline Semantics, Retry Eligibility and Cascading

A retry is only worth sending if the first decline was recoverable, and recoverability is encoded in the response. Cascading is therefore a classification problem before it is a routing problem: normalise the decline, decide whether any path could change the outcome, check the retry budget, and only then choose the next acquirer or schedule a delayed attempt.

Payment cascading decision flow classifying declines into hard, data and soft classes with a retry budget check

Figure 2: Decline classification for payment cascading. Hard declines stop immediately, data problems must be corrected before any retry, and only soft or system declines enter the budget check that gates the next attempt.

The flow starts with normalisation because every acquirer reports declines differently. The issuer’s raw response code (an ISO 8583 field 39 value in most card systems) arrives alongside an acquirer-specific refusal reason and, for Mastercard, possibly a Merchant Advice Code. Our post on ISO 8583 versus ISO 20022 card messaging explains where those fields live. Your router should map all of them to one internal taxonomy and keep the raw values for audit.

What the networks say about retries

Visa groups issuer responses into four categories. According to Adyen’s documentation of Visa integrity fees, category 1 means the issuer will not approve and retrying never helps, with examples such as lost, stolen or nonexistent card responses. Category 2 means the issuer cannot approve at this time, such as insufficient funds or a system failure, so a later attempt may succeed. Category 3 means the issuer cannot approve with the details supplied, such as a wrong account number, expiry date or security code. Category 4 is the generic bucket, including the very common code 05, “do not honor”.

The limits attached to those categories are where documentation diverges, and I want to be explicit about that rather than pick a number. Adyen’s page states up to 15 retries in a 30-day period for categories 2, 3 and 4 and none for category 1. Checkout.com’s merchant communication states a limit of 20 attempts per 30-day period for category 2, with a fee on the 21st and later, separate counters for cardholder-initiated and merchant-initiated transactions, and a category 3 data-quality fee triggered when card-not-present declines exceed 25,000 in 30 days at the Card Acceptor ID level. It also states that from 25 April 2026 the excessive-retry fee rises from 0.15 USD to 0.25 USD per non-compliant retry in the regions it lists. These figures come from acquirer documentation, not from Visa’s own rulebook, which I could not access; treat them as indicative and confirm the current limits with your acquirer.

Mastercard takes a different route. Issuers can attach a Merchant Advice Code to a decline. According to a PSP’s reference table, code 01 signals that updated account information is available, 02 means try again later, 03 means do not try again, and 21 means a recurring plan was cancelled and should be treated like 03. Codes 24 to 30 are timed windows for insufficient-funds declines, instructing a wait of 1 hour, 24 hours, 2, 4, 6, 8 or 10 days. Retries that exceed Mastercard’s limits can still incur fees under its excessive-authorisation program. I did not verify the numeric limits of that program, so the design below enforces a configurable budget instead of a hard-coded one.

The design implication is clear. The retry decision function must take, as inputs, the network, the normalised decline class, any advice code, the attempt history for this card and merchant, and whether the transaction is cardholder-initiated or merchant-initiated. Output is one of: stop, fix data, retry now on another path, or retry later at a given time.

Retry now on another path versus retry later

Not every recoverable decline should be retried immediately. Two recovery mechanisms exist and they solve different problems.

Cascading to another acquirer immediately helps when the decline reflects the path rather than the card. Examples include an acquirer-side risk decision, a timeout, a technical error, a route that the issuer treats with suspicion, or a missing network token on the first path. The customer is still at checkout, so the second attempt happens within the same request, and the customer should see one success or one failure.

Retrying later helps when the decline reflects the account state. Insufficient funds is the canonical case: changing acquirer does nothing, but waiting until payday or following a Mastercard timed advice code may. This is the territory of subscription dunning, where retries are scheduled over days; our payment retries and dunning architecture post covers the scheduling and involuntary-churn side. For this post the point is simpler: do not cascade an insufficient-funds decline across three acquirers in two seconds. You burn three attempts against the retry budget and the issuer sees a burst of identical requests.

A useful rule of thumb, which is my engineering judgement and not a network rule, is that immediate cascades should be limited to declines whose cause plausibly differs between paths, and capped at one or two extra attempts. Everything else is a stop or a delayed retry.

Why a second acquirer can approve what the first declined

There is real mechanism behind cross-acquirer recovery, but it is narrower than marketing suggests. Different acquirers may send different data to the issuer, such as the merchant category code, the descriptor, the country of the acquiring entity or whether a network token and cryptogram accompany the request. Issuers’ fraud models are sensitive to such signals, and a local acquirer for a domestic card can look more legitimate than a cross-border one. Some acquirers also hold stronger issuer relationships in particular markets. Finally, a decline can be transient on one acquirer’s connectivity or risk stack.

What a second acquirer cannot do is override the issuer. If the card is closed, the funds are absent or the cardholder has blocked the merchant, the second attempt fails and consumes budget. This is why the classification step in Figure 2 precedes any cost or probability reasoning, and why the probability layer should model approval conditional on a retry class rather than treating all declines as equal.

Authentication interacts with routing

Strong customer authentication changes the picture. A soft decline that requests authentication, common in Europe under PSD2, is not a routing failure at all: the right response is to step up with 3-D Secure and resubmit with the authentication result. Routing a transaction that needs authentication to a path that cannot carry the authentication data produces a decline that looks like an acquirer problem. Our 3-D Secure 2 flow analysis explains the frictionless and challenge flows; the routing layer must carry the authentication result with the transaction so that fallback paths receive it.

Network tokens matter similarly. A token provisioned for one scheme-connected provider is not necessarily portable to another acquirer. If the first path used a token and the fallback needs a PAN, your vault must be able to supply it under the right security controls, as discussed in network tokenization architecture. Eligibility for the fallback should therefore be computed from what credentials you can actually present to it.

Making Cascades Safe: Idempotency, Health and Failover

Cascading multiplies the ways a payment can be duplicated or lost. The safe design gives every logical payment one stable identity, every attempt its own identity linked to it, and every path a circuit breaker so that a failing acquirer is removed from consideration before it drags down the whole checkout.

Sequence diagram of payment routing optimization with idempotency keys and a circuit breaker across two acquirers

Figure 3: Lifecycle of one authorisation. The merchant request carries an idempotency key, the router consults the circuit breaker, attempt one soft-declines, attempt two on the second acquirer succeeds, and the merchant receives one result plus the attempt log.

The sequence shows two distinct keys: the merchant’s key for the whole payment, and a derived key per acquirer attempt. This is the central idea. If the merchant retries its own call after a network timeout, the router must recognise the payment key and return the stored outcome rather than start a new cascade. If a single attempt times out at the acquirer, the router must be able to re-send that attempt with the same attempt key, or query its status, without risking a second authorisation.

Idempotency at two levels

Idempotency means that repeating a request has the same effect as sending it once. Stripe’s idempotent requests documentation describes the common pattern: the client sends a unique key, the server stores the first result for that key, and replays return the stored result. Our guide to payment API idempotency keys and double-charge prevention covers storage and race handling in depth. For routing, two extra rules apply.

First, the attempt key must be deterministic from the payment key plus the attempt ordinal and the acquirer, for example a hash of those three. A deterministic derivation means a crashed router that restarts mid-cascade recomputes the same keys and does not fork the payment into two lines of attempts. Second, the payment record must enforce a single terminal success: a compare-and-set that moves the payment from “in progress” to “authorised” exactly once, so that a late approval from a slow first acquirer cannot coexist with an approval from the second.

That second rule addresses the nastiest cascade failure: the late approval. Attempt one times out at your side after 3 seconds. You cascade, and attempt two is approved. A second later, acquirer A’s approval for attempt one arrives. The cardholder now has two holds. The router must detect the stale success and void or reverse it, and the design should treat such voids as a first-class, monitored flow, not an afterthought. A conservative timeout policy, and acquirer status queries before cascading on ambiguous timeouts, reduce how often it happens.

Circuit breakers and health-aware routing

Health is a signal, not a rule. A circuit breaker tracks recent outcomes per acquirer and segment and moves through closed, open and half-open states. In the closed state traffic flows normally. When the failure rate for technical errors (timeouts, connection errors, acquirer system errors) exceeds a threshold over a window, the breaker opens and the acquirer is dropped from eligibility. After a cool-down it goes half-open and lets a small probe fraction through; success closes it, failure re-opens it. The general mechanics, along with backoff and retry budgets, are covered in retries, backoff, jitter and circuit breakers.

One subtlety specific to payments: count only technical failures toward the breaker, not issuer declines. A surge of insufficient-funds declines on a payday says something about cardholders, not the acquirer, and tripping a breaker on it would shift all traffic to your second acquirer for no reason. Conversely, a sudden jump in code 05 do-not-honor responses concentrated at one acquirer can indicate a risk-model or data problem on that path and deserves an alert even though the breaker should stay closed. Keep technical health and approval-rate drift as separate signals with separate responses.

A rule engine you can test

Before reaching for adaptive methods, build the deterministic version. The following Python is a minimal, runnable rule-based router. It implements the three layers from Figure 1, with an illustrative price book (the numbers are invented for the demonstration and are not real tariffs).

from dataclasses import dataclass, field

@dataclass(frozen=True)
class Txn:
    amount_minor: int
    currency: str
    brand: str
    issuer_country: str
    is_token: bool
    margin_minor: int

@dataclass
class Acquirer:
    name: str
    brands: set
    currencies: set
    fee_pct: float          # illustrative blended percentage
    fee_fixed_minor: int    # illustrative per-attempt fixed fee
    healthy: bool = True
    token_ok: bool = True

ACQS = [
    Acquirer("acq_a", {"visa", "mc"}, {"EUR", "USD"}, 0.0140, 10),
    Acquirer("acq_b", {"visa", "mc", "amex"}, {"EUR"}, 0.0120, 12),
    Acquirer("acq_c", {"visa"}, {"USD", "EUR"}, 0.0165, 5),
]

def eligible(t: Txn, a: Acquirer) -> bool:
    return (a.healthy and t.brand in a.brands
            and t.currency in a.currencies
            and (a.token_ok or not t.is_token))

def cost(t: Txn, a: Acquirer) -> int:
    return round(t.amount_minor * a.fee_pct) + a.fee_fixed_minor

def p_approve(t: Txn, a: Acquirer, stats: dict) -> float:
    # Smoothed approval rate for the segment, with a neutral prior.
    key = (a.name, t.issuer_country, t.brand)
    ok, n = stats.get(key, (0, 0))
    return (ok + 8 * 0.85) / (n + 8)

def rank(t: Txn, stats: dict):
    scored = []
    for a in filter(lambda x: eligible(t, x), ACQS):
        p = p_approve(t, a, stats)
        ev = p * (t.margin_minor - cost(t, a)) - (1 - p) * a.fee_fixed_minor
        scored.append((ev, a.name, round(p, 3), cost(t, a)))
    return sorted(scored, reverse=True)

if __name__ == "__main__":
    t = Txn(5000, "EUR", "visa", "DE", False, 1200)
    stats = {("acq_b", "DE", "visa"): (700, 900),
             ("acq_a", "DE", "visa"): (780, 900)}
    for row in rank(t, stats):
        print(row)

Running it prints the candidates in descending expected value. With the invented statistics, acquirer A can outrank cheaper acquirer B because its smoothed approval rate is higher; change the margin to a small number and the order flips toward cost. The prior of eight pseudo-observations at 85 percent keeps new segments from being ranked on two data points.

The value of this version is that every output is explainable. When finance asks why a transaction went to acquirer C, you can show the eligibility result, the cost arithmetic and the probability used. Most teams should run this for a quarter, collecting clean per-attempt data, before introducing anything adaptive.

Rules Versus Adaptive Routing, and How to Experiment

Static rules are transparent but stale: they encode what was true when someone last tuned them. Adaptive routing treats each segment as a multi-armed bandit, where the arms are acquirers and the reward is the approval outcome, so the router keeps exploring a little and shifts traffic toward whatever currently performs best.

Observability and experimentation loop for payment routing optimization from attempt log to bandit state and router

Figure 4: The feedback loop. Every attempt is logged, a stream processor maintains rolling statistics per segment and acquirer, those statistics feed both the bandit state and the dashboards, and settlement files close the loop on cost.

The loop in Figure 4 is the real product; the algorithm inside it is replaceable. Without a clean per-attempt log, a rolling-statistics job and a reconciliation path for cost, no bandit will help, because it will optimise on biased or delayed data.

When rules are enough, and when they are not

Rules suffice when segments are few, volumes are modest and the differences between acquirers are stable. They also suffice, and are preferable, where regulation or contract demands a specific path, for example a data-residency requirement or a minimum-volume commitment to an acquirer. Use rules for those hard constraints inside the eligibility layer.

Adaptive methods earn their keep when the segment space is large (issuer country times brand times amount band times token status), when acquirer performance drifts with time, and when volumes are high enough to learn from. In such settings human-maintained rules lag reality by weeks. The honest caveat is that gains are merchant-specific. I am not aware of a credible public benchmark that gives a general approval-rate uplift from adaptive routing, and vendor claims should be read as marketing until reproduced on your traffic.

Thompson sampling for acquirer selection

Thompson sampling is a good default bandit here because it handles uncertainty gracefully, needs no tuning parameter beyond the prior, and its exploration shrinks naturally as evidence accumulates. For each segment and acquirer, keep a Beta distribution over the approval probability. To route, sample once from each candidate’s distribution, convert the sample to expected value with the cost model, and pick the highest.

import random
from collections import defaultdict

class BetaArm:
    __slots__ = ("alpha", "beta")
    def __init__(self, alpha=8 * 0.85, beta=8 * 0.15):
        self.alpha, self.beta = alpha, beta   # prior mean 0.85
    def sample(self) -> float:
        return random.betavariate(self.alpha, self.beta)
    def update(self, approved: bool, decay: float = 0.999):
        # Exponential forgetting so old evidence fades.
        self.alpha = 1 + (self.alpha - 1) * decay
        self.beta = 1 + (self.beta - 1) * decay
        if approved:
            self.alpha += 1
        else:
            self.beta += 1

arms = defaultdict(BetaArm)   # key: (segment, acquirer)

def choose(segment, candidates, margin, cost_fn, explore_floor=0.02):
    # Small guaranteed exploration so no healthy arm starves.
    if random.random() < explore_floor:
        return random.choice(candidates)
    best, best_ev = None, float("-inf")
    for a in candidates:
        p = arms[(segment, a)].sample()
        ev = p * (margin - cost_fn(a))
        if ev > best_ev:
            best, best_ev = a, ev
    return best

def record(segment, acquirer, approved):
    arms[(segment, acquirer)].update(approved)

Three design details matter more than the algorithm. First, forgetting: the decay term lets the posterior track drift. Without it, an acquirer that was excellent in March keeps its inflated posterior long after it degrades. Second, segmentation: pick segments that are coarse enough to gather data but fine enough to capture real differences. Issuer country and card brand is a typical starting point; adding amount band and token status splits data quickly. A hierarchical fallback, using the parent segment’s posterior as the prior for a child segment, is a sound refinement. Third, guardrails: the bandit should only choose among candidates that already passed eligibility and cost thresholds, and a hard cap on the share of traffic any single acquirer may take protects against concentration risk.

Delayed and censored feedback complicates this. Approval is immediate, but outcomes that matter commercially, such as chargebacks and fraud, arrive weeks later. A router that optimises approval rate alone can learn to favour paths that approve more fraud. Keep fraud and dispute rates as constraints evaluated on a lagged basis, and compare approval gains only against matched fraud outcomes.

Experimentation without fooling yourself

Routing changes are good candidates for controlled experiments, with one catch: units interfere. If a test arm sends more volume to acquirer B, B’s behaviour toward your traffic may change, and shared retry budgets couple the arms. Randomise at the level of the transaction or the card, hold segments constant across arms, and run long enough to cover weekly seasonality. For sequential monitoring and bandit-style allocation, our post on A/B testing ML systems, sequential tests and contextual bandits covers the statistics that keep early peeking honest.

Always keep a holdout that follows the old static policy. It is the only way to separate the effect of your router from seasonality, issuer-side changes and network rule shifts. Report the holdout comparison on both approval rate and net cost per approved transaction, because a router that raises approvals by routing to expensive paths may not improve margin.

What to log and watch

Observability for routing is mostly disciplined logging. For every attempt record the payment key, the attempt key, the ordinal, the acquirer, the segment attributes, the normalised decline class, the raw codes, any advice code, latency, the router decision and the reason (rule, bandit sample, forced by eligibility), the estimated cost and, later, the settled cost. Sampling is dangerous here because the interesting cases are rare.

From that log, build dashboards for approval rate by acquirer and segment with confidence intervals, cascade rate and cascade recovery rate, retry-budget consumption per card and per merchant identifier, technical-error rate per acquirer, latency percentiles, cost-estimate error against settlement, and the count of late approvals voided. Alert on changes in the shape of declines, for instance a rise in the share of generic do-not-honor responses at one acquirer, which often precedes a visible drop in approval rate.

Trade-offs, Gotchas, and What Goes Wrong

Routing systems fail in ways that are quiet, statistical and expensive. The following are the failure modes I would design against first.

Retry storms and budget exhaustion. A cascade policy that is fine in isolation can combine badly with merchant-side retries, customer reattempts and dunning schedules. The same card may be attempted five times in an hour from four different layers of your own stack. Because network fees and limits are counted per card and merchant over a window, a per-card retry budget has to be enforced centrally, in one service, across checkout, subscriptions and manual retries alike. Per-component limits do not add up to a global limit.

Optimising the wrong metric. Routing for approval rate alone favours paths that approve riskier transactions. Routing for cost alone favours cheap paths with weaker issuer relationships. Routing for net revenue after fraud, disputes and fees is correct but needs lagged data. Decide the objective explicitly, write it down and review it when pricing or fraud conditions change.

Feedback loops and starvation. A bandit that stops sending traffic to an acquirer never learns that the acquirer improved. Forgetting factors, an exploration floor and periodic probes are the mitigation. The inverse also bites: a sudden shift of traffic to a newly favoured acquirer can exceed its throughput limits or trigger its own risk controls, so cap per-acquirer share and ramp gradually.

Data leakage across attempts. If your attempt log joins declines to later successes without distinguishing the cascade structure, a decline followed by an approval looks like two independent events and approval statistics are biased. Record the cascade identity and compute per-payment outcomes alongside per-attempt ones.

Stale price books. A cost model that does not reflect the contract in force will route confidently to the wrong path. Effective dating, review ownership and the estimated-versus-settled cost alert exist for this reason.

Compliance and contract constraints. Some paths are constrained by data residency, scheme rules on routing of co-badged cards, or minimum-volume commitments. These belong in eligibility as hard constraints, not as soft cost penalties, because violating them has consequences a cost term cannot express. Retry rules from the networks are also revised over time, so the retry policy should be configuration with an owner, not constants buried in code.

Security and credential portability. Fallback to another acquirer may require handling raw card data or a different token. Every fallback path widens the footprint of sensitive data handling, so keep this inside the vault boundary and audit which paths can receive which credential types.

Opaque vendor routing. If you use a PSP’s built-in smart routing, you inherit its objective and its data. Ask what it optimises, whether you can see per-attempt logs, whether you can pin or exclude paths, and how it counts toward retry budgets. A router you cannot inspect cannot be reconciled with your finance and risk views.

None of these is a reason to avoid routing. They are reasons to build the logging, the budget service and the holdout first, and the clever selection algorithm last.

Practical Recommendations

Start with the layers, not the algorithm. Build eligibility as versioned configuration with tests, build cost as a price-book lookup reconciled against settlement, and run a rule-based expected-value ranker for long enough to collect clean per-attempt data. Add adaptive selection only for segments with enough volume to learn from, and keep a holdout on the static policy permanently.

Treat retry policy as a regulated component. Normalise declines into hard, data, soft and technical classes; honour advice codes; enforce one central per-card budget; and keep the numeric limits in configuration that you verify with your acquirers, since the figures quoted in different acquirer documents differ and networks revise them.

Make cascades safe by construction: a payment key, deterministic attempt keys, a single-terminal-success compare-and-set, a status query before cascading on ambiguous timeouts, and a monitored void path for late approvals.

A short launch checklist:

  • Eligibility rules covered by table-driven tests and versioned configuration.
  • Price book effective-dated, with an estimated-versus-settled cost alert.
  • Decline taxonomy mapped from every acquirer, raw codes retained.
  • Central retry budget per card, per merchant identifier and per initiator type.
  • Payment-level and attempt-level idempotency keys, tested with injected timeouts.
  • Circuit breakers on technical errors only, with half-open probing.
  • Per-attempt logging with no sampling, including router decision reasons.
  • Holdout arm on the static policy and a documented objective metric.
  • Per-acquirer traffic caps and a gradual ramp for any shift.
  • A runbook for turning adaptive routing off and falling back to static rules.

This article describes systems architecture for educational purposes and is not financial, legal or compliance advice; confirm network rules and contract terms with your acquirers and advisers.

Frequently Asked Questions

What is payment routing optimization?

Payment routing optimization is the practice of choosing, for each card transaction, which acquirer or processing path to use so that approval rate and net cost are jointly improved. A good implementation filters paths by hard eligibility, estimates the all-in fee of each, estimates approval probability from recent data, ranks by expected value and keeps ordered fallbacks for recoverable declines.

What is the difference between payment routing and payment cascading?

Routing selects the first path for a transaction; cascading is what happens after a recoverable decline, when the system tries another path. They share the same ranked candidate list. The distinction matters operationally because cascading consumes retry budget, risks duplicate authorisations if idempotency is weak, and is only appropriate for declines whose cause may differ between paths, not for hard declines.

Does cost-based routing reduce approval rates?

It can, if cost is the only objective, because the cheapest path is not always the one issuers approve most. Cost-based routing is safest when combined with an approval-probability estimate and a cascade fallback, so that a cheaper but slightly weaker first path is backed by a second attempt. Measure net cost per approved transaction against a holdout, not cost per attempt.

Which declines should be retried?

Retry only declines that are plausibly recoverable. Hard declines such as stolen, closed or nonexistent card responses should never be retried. Data-error declines need corrected details first. Soft declines like insufficient funds or temporary issuer unavailability may be retried later, following any advice code, while technical errors can cascade immediately to another path. Always check the per-card retry budget before sending.

Are there fees for retrying declined card payments?

Card networks can charge fees for excessive reattempts. Acquirer documentation describes Visa categories of decline responses with limits per 30-day window and per-retry fees, and Mastercard advice codes that mark some declines as do-not-retry. Documented limits and amounts differ between sources and change over time, so confirm current figures with your acquirer and keep limits in configuration.

How do I prevent double charges when cascading across acquirers?

Give each payment a stable idempotency key and derive a deterministic key per attempt from the payment key, the attempt number and the acquirer. Enforce a single terminal success with a compare-and-set on the payment record, query status before cascading on ambiguous timeouts, and void any late approval that arrives after another attempt has already succeeded.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *