Skip to content

Waterfall & Fallback

What walks down the candidate list, what stops it, and when a connection is paused.

The waterfall is one pass down the ranked candidate list, inside the POST /payments/{id} call, while your request is still open. It is not a retry policy and not a background job. When it ends, the HTTP response is already on its way back to you.

There is no fallback chain to configure. The chain is the candidate list that Orchestration builds, and everything on this page is about how far down it Wajub goes before it gives up.

What moves the ladder on

Only one thing does: the attempt failed in a way that says nothing about the payer.

What the connection didWhat happens next
The provider could not be reachedThe next candidate is tried
The provider reported a temporary problem on its sideThe next candidate is tried
The provider answered that the operator timed out or is unavailable, that your credentials are refused, or that its float is exhaustedThe next candidate is tried, and the connection is flagged when the credentials were refused
The connection had no credentials for this channel, so nothing was sentThe next candidate is tried, and the connection is flagged

Each of these marks the attempt failed, counts against that connection's health on this channel, and moves down one line. The payer is not told anything, because from their side nothing has happened yet.

What stops it

Besides the unanswered request above, four things, and only one of them is good news.

A provider accepted. That includes accepting as pending and as requires_action. The provider owns the payment from that moment and nothing else is tried.

A provider refused for a reason that would repeat. An empty wallet, an unregistered number, an operator ceiling, a declined card, a prompt the payer let expire. Wajub answers 402 on the spot and does not touch the next candidate, because asking a second provider would ask the same person the same question. A card declined at Stripe is final, and error_code carries Stripe's own decline code.

A connection of yours is misconfigured in a way the provider does not call a refusal of your credentials. A required field absent for that channel, for example. That stops the ladder rather than skipping to the next connection, so a single broken connection at the top of your list can cost you every payment routed through it. Credentials are the exception: missing ones send nothing and rejected ones are a path failure, so in both cases the next connection is tried, and the connection is flagged, as described below.

Three providers have been tried. Wajub tries at most three providers per POST /payments/{id} call, on every account. A candidate list ten deep is still walked three lines at most, and the request ends with 402 even though eligible connections were left untried. A connection passed over for missing credentials counts as one of the three.

How long the call can take

A provider that does not answer in time is the unanswered request above: the payment stays pending and nothing else is tried. With three providers tried, POST /payments/{id} can stay open close to a minute. Give your own HTTP client at least 90 seconds on that call, so that it does not give up on a payment Wajub is still placing.

A card stays with its provider

A card the payer typed into a provider's own fields can only be charged by that provider: a Stripe.js payment method, a saved Paystack authorization. Another provider cannot read it, and the first one may already have debited the card.

So that connection is the only one tried, whatever the ranking put first, and a failure is never failed over to another connection. If that connection cannot be tried at all, because it is paused on this channel or no longer active, the payment answers the same 422 as an empty list rather than going to a provider that cannot charge the card.

A step the provider opened stays with it

Two kinds of call are made to the provider that opened them, and never fail over:

  • A provider's own payment window (client_session, the provider's hosted page or SDK sheet the payer completes): it is opened on the first provider of the ranking, and if that provider cannot open it, the call fails rather than trying the next connection.
  • A confirmation (a code, a PIN, a phone number, a birth date or an address the payer sends after the charge started): it goes to the provider that asked for it, which holds the pending charge. Another provider knows nothing of that charge.

A connection the provider refuses

When a connection has no credentials for the channel, or the provider rejects the key it holds, Wajub records it on the connection.

WhereWhat you see
Orchestration, Providers, and the MatrixA Connection refused badge on the connection
The provider pageAn alert with the error code and when it was last seen, and Fix the connection
Your team's emailOne email within about five minutes of the flag, when email notifications are on, and another only if the connection is flagged again after the flag cleared

The flag clears when you save different credentials, when a connection test passes, or on the next payment the connection takes. A payment that only found connections without credentials is not counted as a decline against the payer: the configuration was at fault, not them.

Every attempt leaves a row

Each attempt is listed on the payment with the provider that was tried, its response, the latency and its rank in the decision. A payment that cascaded twice shows three of them, in order.

You read them in the Dashboard on the payment itself, under the timeline, and in Konsole next to the decision that produced them. They are the only place the attempt history exists: the payment object returned by the API carries none of it.

One payment, two providers

  1. attempt 1status: failed
    12:31:40

    Rank 1 in the decision, placed there by a routing rule. The provider answered that the operator had timed out, so the failure counted against this connection on this channel and the next connection was tried.

    provider
    cinetpay
    response
    timeout
    latency
    8.0 s
  2. attempt 2status: processing
    12:31:49

    Rank 2, the first connection no rule named. The operator agreed to prompt the payer, which is an acceptance, and the ladder ended here.

    provider
    flutterwave
    response
    pending
    latency
    0.9 s

The second row is where most of the confusion about the cascade comes from.

Which failures count against a connection

Failures that come from the payer never count against a connection: an empty wallet, a wrong or unregistered number, an operator limit, a card declined by its bank or by the provider's fraud screen, a prompt the payer did not confirm. Another provider would get the same answer, and counting them would slowly push your traffic away from a perfectly healthy connection.

Failures on the provider's or the operator's side do count. They lower the connection's recent success rate on that channel, and a connection that keeps failing is paused, as described below.

The circuit breaker

When a connection keeps failing on one channel, Wajub pauses it on that channel for a few minutes, before it can cost you more payments, and brings it back automatically once it works again. Only your account and that channel are affected: a connection going bad on cm.mtn keeps serving cm.orange, and every other merchant is untouched. Payments and payouts share a connection's health on a channel.

A paused connection is not tried, which is why a connection you expected to be tried can be absent from the ranking entirely. It is not left out silently: Konsole shows it as Skipped (circuit open), and the Matrix shows the circuit on that connection's cell.

Routing decision · cm.mtn · 25 000 XAF

One connection paused, two ranked, the first one carried it.

  1. 1
    flutterwave
    accepted0.9 s
    priority 100
  2. 2
    pawapay
    never called
    priority 100
  3. cinetpay

    Skipped (circuit open). The connection was paused on this channel after repeated failures.

If that connection was your preferred one, the effect is visible and confusing without this page: a rule pointing at CinetPay looks ignored for a few minutes. It was not ignored, the connection was paused and could not be placed. If the rule names a fallback provider, that one takes CinetPay's place for as long as the pause lasts.

Two providers can never both charge

Within one call, Wajub stops at the first acceptance, so at most one attempt succeeds. The part that needs care is the call itself being repeated.

Send an Idempotency-Key header on POST /payments/{id} and a repeat of the same call within 24 hours returns the same response without running a second waterfall. Without the header there is no such protection, and a client that retries a request whose response it never saw can open a second cascade on a payment that already succeeded.

One key per payment attempt

Derive the key from something stable in your own system, such as the order reference plus the attempt number. The key is validated as up to 128 characters of A-Z a-z 0-9 . _ : -, and reusing it with a different channel, data or split within 24 hours answers 422. See Idempotency.

When the list was empty to begin with

A 422 on the channel key means no candidate survived the filters, so nothing was tried and nothing can be retried elsewhere. It is an account configuration problem, not a payer problem: the channel, the currency, a provider Wajub cannot charge through yet, or connections paused on this channel left nothing to try. A card held by one provider whose connection cannot be tried ends here too.

Response · 422 Unprocessable Content
{
"code": 422,
"status": "Unprocessable Content",
"message": "No eligible payment provider found for this transaction.",
"errors": {
"channel": [
"No eligible payment provider found for this transaction."
]
}
}

The fix is never in your request. The routing log shows the connections skipped because they were paused on the channel. For the other reasons, run the same channel, currency and amount through the routing simulator, on a Growth plan or above, which names the reason each connection was left out. A connection you paused yourself, or a provider that is not active, does not appear there at all.

What did you think of this content?