A payment orchestration layer is the software that sits between your checkout and your payment providers. It owns every decision about a payment except the money itself: which provider gets it, what the answer means, what state the payment is in, and who gets told about the change. Your code talks to one API. The layer talks to all the acquirers, wallets and bank rails below it.

Note the word layer. It names a position in your stack, not a product. You can buy that position from a vendor or build it yourself and staff it. The parts inside are the same either way, which is why it pays to know what they are.

The practice itself — running several providers behind rules — is covered in what payment orchestration is. This guide is about the box: where it sits, what is inside, what it takes off your plate, and what it will never do for you.

What is a payment orchestration layer?

A layer is defined by what sits above it and below it.

Above: your checkout, your back office, your subscription biller, your finance reports. Below: acquirers, wallet schemes, bank transfer rails, risk vendors. The layer turns many below into one above. That sentence is the whole idea, and everything else in this article is a consequence of it.

Think of travelling with one plug. Every country has its own socket, so you carry an adapter. You never learn the local wiring. The adapter does, and it exposes the shape your device already knows.

The layer is not a provider. It holds no acquiring licence, it does not settle money into your bank account, and it does not carry the risk of a chargeback. It sells you a shape, not a settlement. If you want the line between the layer and a plain gateway drawn properly, that is payment orchestration vs payment gateway.

Layer, platform, or provider: which word do you need?

People use three words for overlapping things, and the confusion costs whole meetings.

WordWhat it really means
LayerA place in the architecture. It exists even when a developer built it by hand with an if statement
PlatformA product that ships that place ready-made, with connectors, rules and reporting
ProviderThe company that actually moves the money and holds the licence

A payment orchestration platform is a bought layer. Your own routing code is a built layer. Both sit in the same slot, and both have to solve the same list of problems below.

Where the layer sits in your stack

Draw four tiers and the picture stops being abstract.

TierExamplesWhat it must never hold
AboveCheckout, app, back office, billing, analyticsProvider quirks, signing keys, decline codes
The layerRouting, connectors, payment state, events outRaw card numbers, business policy that nobody owns
BesideCard vault, ledger, reporting storeAnything the hot payment path has to wait for
BelowAcquirers, wallets, bank rails, risk vendorsYour reporting vocabulary

The tier that surprises people is beside. The card vault is not inside the layer, and this is deliberate.

In our own platform the routing service never sees a card number. It holds a UUID token. The number lives in a separate service that encrypts it through HashiCorp Vault Transit with an aes256-gcm96 key set to rotate every 2160 hours — 90 days. The routing service can ask for a payment to be made with token X. It cannot read X back into a card number for its own purposes.

That split is boring architecture and it has a very unboring payoff. Card data is the part of your system that regulators, auditors and attackers care about most. Keeping it out of the busiest, most-changed service in the stack means your routing code can be deployed twice a week without dragging the vault along for the ride.

What a payment orchestration layer is made of

Eight parts do the work. The first one is the one buyers skip and engineers lose weeks to.

1. The edge. Authentication, request signing, idempotency, validation. This is the contract your code lives against, and it is the part you cannot change later without breaking every integration you have.

2. The router. Rules plus live signals, producing one answer per payment: who gets this one, and who gets it if the first one says no.

3. The connectors. One per provider. Five acquirers in our platform sign their requests four different ways — RSA SHA-512, HMAC-SHA1 twice over, SHA1 with Base64, plain SHA1. The connector is where that zoo is absorbed so your checkout never learns any of it.

4. The state machine. One vocabulary for every provider. Ours has twelve payment statuses, of which five are terminal: declined, expired, voided, refunded, error. Only one status allows a settle (on_hold), three allow a refund, four allow a void.

5. The money-out path. Refunds, voids, payouts. Each has its own lifecycle and its own failure modes, and each is a different API call at every provider.

6. Events out. Webhooks to your systems. The most underrated part of the box, and the one with the best war story further down.

7. The config plane. Routing rules, provider credentials, tariffs, sandbox and production accounts. It also needs a dry run: ours can simulate a routing decision and return the chosen provider, the fallback, the cost and the rules that fired, without creating a transaction. Ask for that feature by name.

8. Reconciliation. Matching what the provider says happened against what the bank says happened. This one is usually sold as “reporting” and is usually the weakest part of any layer, bought or built.

How a payment moves through the layer, step by step

Here is the trip, with the parts that only show up in production.

Step 1. The request arrives and is proved genuine. Signature, timestamp, nonce. Our canonical signing string is eight lines: version, method, path, sorted query string, SHA-256 of the body, timestamp, nonce, idempotency key. The window for the timestamp is ±5 minutes, and each nonce is remembered for twice that.

Step 2. Idempotency is checked. If the same key came in before, the stored response is replayed instead of the payment being made twice. Ours keeps that response for 24 hours, in a cache namespace scoped to the API key.

Step 3. The environment barrier is applied. Sandbox accounts and production accounts never mix, for any operation. If the barrier empties the candidate list, the payment is declined with a reason that says so. It is never quietly swapped to the other environment.

Step 4. Routing picks a primary and a fallback. Rules first, then capability, then health, then cost. The stage-by-stage detail lives in what payment orchestration is; what matters here is that the answer is two providers, not one.

Step 5. The connector translates and calls. Your fields become their fields, their answer becomes your status.

Step 6. The payment parks, if it has to. A 3DS challenge sends the cardholder to their bank. The payment sits in waiting_auth while a human types a code into a page you do not control. The layer has to hold that state, survive the browser round trip, and cope with the provider’s callback arriving before the shopper’s browser comes back. That race is real, it happens daily, and a layer that assumes the browser wins will double-book payments.

Step 7. The status changes and the world is told. Your systems get a webhook. Your reports get a row. Your reconciliation gets an expectation to match later.

Six of those seven steps have nothing to do with routing. That is the thing most orchestration copy gets wrong: routing is the famous part, but the state, the contract and the events are where the engineering weeks actually go.

What the layer takes off your code

Put plainly: it absorbs difference so your application can stay ignorant.

  • Difference in dialect. Four signing schemes, five providers, one request shape for you.
  • Difference in vocabulary. Fifteen normalised decline reasons instead of every acquirer’s own list, so insufficient_funds means the same thing no matter who said it.
  • Difference in lifecycle. One place that knows a void is not a refund, and that a hold that expired is not a decline.
  • Difference in safety. Idempotency, replay protection and the sandbox barrier are written once, not per integration.
  • Difference in timing. Retry to the fallback provider happens inside the layer, in the same request, without your checkout knowing anything went wrong.

The value is not that any one of those is hard. It is that all of them are boring, and boring work done in five places drifts.

Where the layer breaks in practice

Every item below cost somebody real time. Most of them cost us ours.

Outbound webhooks in the payer’s request

This is the best example of a layer problem that no routing feature can save you from.

Our webhook delivery used to be synchronous, inside the request that the payer is watching. One hanging merchant endpoint blocked the thread for the full 10-second HTTP timeout. Deliveries ran one after another, so a merchant with three subscriptions added 30 seconds to the checkout response. That number was measured, not estimated. There were no retries at all: one POST, and on failure a line in the log while the notification vanished.

The fix was not clever, just correct. Delivery moved to a queue: five attempts, with pauses of 10 seconds, 1 minute, 5 minutes and 15 minutes. The payload is frozen at the moment of the event, so a retry cannot deliver a newer state and make events arrive out of order. The signature is recomputed on every attempt, because after a 15-minute pause the old timestamp would look like a replay to the merchant and be rejected. Failures land in a dead-letter store with a delivery journal instead of disappearing.

Ask any vendor where outbound webhooks live relative to the payment request. If the answer is vague, you have found the thing that will slow your checkout on your busiest day.

A signature that does not cover the path

Our first signing scheme signed the body and the timestamp only. It did not cover the method, the path, the query string or the idempotency key.

That sounds academic until you write it out: a signature for POST /payments/{A}/refund was also valid for POST /payments/{B}/void, because both have the same body. The scheme was replaced in place with the eight-line canonical string above before it ever met production traffic, and a nonce was added because a ±5 minute window on its own is not replay protection.

When you review someone else’s layer, ask what exactly the signature covers. “We sign the request” is not an answer. The lines of the canonical string are.

Health numbers are always slightly stale

Our provider health score is 70% success rate and 30% response time. Under one second scores full marks on the time part, ten seconds scores zero, and a provider with no recent traffic sits at a neutral 50 rather than a zero. Below 40 a provider drops out of the candidate list.

Now the part that matters architecturally. That score is computed on a five-minute schedule and cached in Redis for ten minutes, because the payment path cannot afford a SQL aggregate per transaction. So routing decides on a number that describes a window that has already closed. There is no version of this that is live. Short windows overreact to noise, long windows miss the outage that started eight minutes ago, and any cache in front of either adds its own lag.

The honest design goal is not freshness. It is that the layer keeps paying when the number is wrong: if every candidate is below the threshold, we drop the health filter entirely and route anyway. A degraded provider beats no provider.

The capability catalogue tells a comfortable lie

Our catalogue lists Click to Pay as a supported method on all five acquirers. In the code path that runs at 3am, only two of them — Hutko and OschadBank — accept a wallet cryptogram directly on our own checkout. The other three support the wallet on their own hosted page instead. Same word, two different integrations.

Routing therefore reads a separate, narrower function for that check, not the catalogue. If it read the catalogue, it would send a wallet payment to a provider that physically cannot take it, and the shopper would see a failure that no dashboard explains.

Every layer has a version of this gap. The question to ask is which source of truth the router reads, not what the feature matrix says.

Non-card rails do not fit the card state machine

A card payment is a conversation: you ask, the issuer answers, you get an authorisation. A bank transfer is not. Nobody answers. Money either lands on your account later, or it does not.

Ukraine’s IBAN+ QR standard, set by National Bank of Ukraine Resolution No. 97 of 19 August 2025 and in force since 1 November 2025, works exactly that way. The shopper scans a code, their banking app makes a credit transfer, and the layer finds out only by reading the bank statement afterwards. Format 003 caps the encoded payload at 507 bytes; the older format 001 caps at 331. There is no PAN anywhere in it, so the whole rail sits outside card data scope.

For the layer this means holding two axes at once:

  • The payment axis — what the shopper said they would do.
  • The settlement axis — what the bank says actually happened.

The two are matched later, against a statement, with duplicate protection keyed on the bank’s own transaction id. Our default window for the shopper to pay is 30 minutes; money that lands after it needs a written policy, not a shrug.

If you are choosing a layer and local bank rails are on your roadmap, this is the question that separates the products. A layer that models everything as a card authorisation will bolt transfers on as a special case, and the special case will leak into your reports.

The layer is one dependency with a throughput budget

You removed five integrations and added one thing every payment passes through. That is usually the right trade, and it is still a trade.

Budget is part of it. On a single beta host, with worker counts capped at 8 for the payment service, 4 for the vault client and 2 for Vault itself, our load tests measured roughly 30 transactions per second sustained and about 33 at the ceiling. That number describes one test rig on one day. It is not a promise about anyone’s production traffic, and it is exactly the kind of number you should ask a vendor for with the same caveats attached.

What a payment orchestration layer cannot do

Worth saying plainly, because the category is sold as if the box fixes everything.

PCI scope does not move on its own. Scope follows card data, not architecture diagrams. And some requirements never leave you at all: under PCI DSS v4.0.1, requirements 6.4.3 and 11.6.1 — script inventory and tamper detection on the payment page — became mandatory on 31 March 2025 and apply to the page in the shopper’s browser, which is yours. A layer can change how much card data touches your servers. It cannot patch your checkout page for you.

The money view stays split. The layer unifies the payment view, not the bank view. Five providers still mean five settlement files, five schedules, five ways of naming a refund and five sets of fees deducted before the money lands. Ask what the layer does after settlement, not just before authorisation.

Missing capability stays missing. If your acquirer has no coverage for a market or a method, a router in front of it does not conjure any. It only makes it cheaper to send that traffic elsewhere.

Somebody still owns the policy. Rules encode decisions. Somebody still has to make them, watch them and change them when the market moves. Teams that buy a layer and assign nobody to it end up with a more expensive version of what they had.

Vendor checks stay yours. PCI DSS requirement 12.8.5 expects you to know which requirements the service provider manages and which stay with you. That matrix is a document you have to hold, not a logo on a website.

A fuller list of what to demand from the product, feature by feature, is in payment orchestration software: what it must do.

Who uses a payment orchestration layer?

Everyone with more than one provider. The only real question is whether they know it.

If two developers have ever argued about which acquirer gets the retry, you already run a layer. It just lives in an if statement, and its documentation is one person’s memory.

In practice, four groups end up with an explicit one: cross-border merchants who collected local providers market by market, subscription businesses whose revenue depends on renewals surviving a first decline, high-risk verticals that need a second live provider for continuity rather than optimisation, and platforms that inherit every seller’s geography at once.

Notice that size is not on the list. A small merchant selling into six countries hits this wall long before a large one selling into one.

Do you need a payment orchestration platform, or just the layer?

You need the layer either way. The question is who builds and staffs it.

Build it yourself when payments are your product, your routing logic is genuinely unusual, and you have someone whose job — not side project — is that code. Buy it when you want the boring eight parts to already exist and you would rather spend your engineers on your own domain. The honest test is one question: in three years, when whoever wrote that code has moved on, will it still have an owner?

The signal that you have outgrown the if statement is usually one of these:

  • Adding a provider takes a release, and the release scares somebody.
  • Nobody can say what your top three decline reasons were last month without asking a developer.
  • A provider outage would stop revenue, not slow it.
  • Your reports disagree with your bank and the disagreement is resolved by hand.

One of those is a normal Tuesday. Three of those is a project.

How to choose a payment orchestration provider: five questions about the layer

Feature lists are easy to fake. These five are hard to answer with a slide, because each one has a specific correct shape.

1. What exactly does your signature cover? You want the canonical string, line by line. Method and path or not? Query string? Idempotency key? Is there a nonce, or just a time window?

2. How does idempotency work? Ask for the retention period, the scope of the key, and what happens if the same key arrives with a different body. Silence on that last one is a bug waiting to be yours.

3. When was the health number computed? Any specific answer is fine. “In real time” means nobody has looked.

4. Where do outbound webhooks live in the request path? Then: how many retries, on what schedule, and can you replay a delivery from their dashboard when your endpoint was down for an hour.

5. How is a non-card rail modelled? Bank transfers, QR codes, wallets. If the answer maps everything onto authorise-and-capture, you now know where your reconciliation pain will come from.

Here is how to read the answers.

What you hearWhat it means
A named field, file or schedule you can verify laterThey have hit this problem
The right behaviour described in general termsProbably fine, ask for the doc
“Our engine handles that automatically”Nobody in the room knows

Write down the exact words for question 4. That is the answer people revise between calls.

What to do this week

Three things, none of which need a project.

Find your layer. Search the codebase for the place that chooses a provider. It exists. Read it out loud in a meeting: whoever wrote it left a comment, and the comment is your requirements document.

Time your webhook path. Send yourself a notification with a receiver that sleeps for 10 seconds and see what happens to the checkout response. If it waits, you have found the same bug we did, and now you know what it costs.

Write down one race. Pick the 3DS return. Note what your system does if the provider’s callback lands before the shopper’s browser does. If nobody knows, that is the first test to write.

Do those three and you will know more about your own layer than any comparison table will tell you. When it is time to look at who sits below it, our provider directory lists acquirers and payment methods by country and vertical, so you can start from who actually operates where you sell.


Numbers about our own platform come from the P26M payment service source, checked 1 September 2026. PCI DSS requirement numbers and the 31 March 2025 date follow PCI DSS v4.0.1 as published by the PCI Security Standards Council. The Ukrainian QR rail follows National Bank of Ukraine Resolution No. 97 of 19 August 2025.