Skip to content
GatewayChanger

How routing works

One order, one question: who charges this, and with which processor? This page explains how GatewayChanger answers it, what can make an otherwise good candidate unavailable, and what you can see afterwards.

The route call

The seller shop calls POST /api/v1/route when the payment step renders, and again on finalize if the total changed. The request carries the amount in minor units, the currency, the billing and shipping country, the line items with their categories, optional metadata, and a mode_hint of embed or redirect. The shop key identifies the seller and the environment.

Nothing in that request is sensitive: at that point in the checkout no card field has been filled in, and none of them would be accepted.

Rules are an ordered list

Rules belong to an organization and an environment. They are evaluated top to bottom and the first rule whose conditions match wins — there is no scoring, no weighting between rules, no hidden precedence. Keep a catch-all rule at the bottom so that every order has an answer.

Conditions

Condition Example
Amount >= 5000 (minor units — €50.00)
Currency in [EUR, CHF]
Billing country in [DE, AT]
Shipping country != billing country
Item category contains "electronics"
Item SKU or tag any sku starts with "GWC-"
Metadata metadata.campaign == "summer"

Conditions within a rule are combined with AND. To express OR, write two rules.

Candidates

A rule lists candidates in order — each one a shop plus a processor on that shop:

1. Shop B · stripe
2. Shop C · stripe
3. Shop A (self) · test

"Self" is a normal candidate: a rule can decide that the selling shop charges with its own processor, and nothing else in the flow changes.

Strategies

  • First available (default) — walk the list, take the first candidate that passes every availability check.
  • Round robin — rotate between the available candidates to spread volume.
  • Weighted — split traffic by percentage across the available candidates; the weights must total 100.

What makes a candidate unavailable

A candidate is skipped, with the reason recorded, when any of these is true:

  • Offline. The shop has not sent a heartbeat for more than 15 minutes, or reported itself as paused.
  • Not verified. The shop has not passed the capability probe, so it cannot act as a charger.
  • Processor cannot do it. The processor is disabled at that shop, or does not support the currency or the customer's country.
  • Cap reached. The shop's daily or monthly cap for this currency would be exceeded by this amount.
  • Excluded. The seller asked us to exclude it — normally because it just failed a charge technically.
  • Wrong environment. A test shop can never be a candidate for a live decision, or the other way round.

Caps

Each charger shop can have a daily and a monthly cap per currency. Caps count captured amounts, less refunds, in the ledger. Crossing 80% raises an alert; reaching 100% simply removes the shop from routing until the window rolls over — checkouts keep working through the next candidate instead of failing.

The decision, and the grant

The answer contains the chosen candidate, up to a handful of alternatives, the consent sentence to display, a trace of the evaluation, and a grant: a token signed with our Ed25519 key that names the seller, the charger, the processor, the amount, the currency, the environment and an expiry 30 minutes out.

The grant is what makes the rest of the flow safe without the shops having to trust each other blindly. A charger verifies the signature against our published key, checks that the grant names it, that it has not expired, that the environment matches, and that the amount equals the session amount. Anything else is refused.

If the cart total changes after the decision — a coupon, shipping, tax — the seller re-routes and the session is patched with the new grant. An old grant can never be used to charge a new amount.

Failover

There are two kinds of failure and they are treated very differently.

  • A card decline (declined, insufficient_funds, incorrect_cvc, expired_card) is the customer's to solve. The iframe resets and asks for another card. Routing to a different shop would produce the same answer from the same scheme, and would risk double charges.
  • A technical failure (timeout, 5xx, processor_error, unavailable) is ours to route around. The seller asks for a new decision with the failing shop excluded and tries the next candidate — at most twice — and each attempt is written to the decision as a failed_over event.

The ledger

The charger reports the outcome — it owns the processor truth — and every later event is appended to the same transaction: captured, voided, refunded, refund failed, dispute opened, won, lost. A transaction carries both order references, the decision id, the processor reference, the card brand and last four, and the amounts.

Settlement reports roll that up for a period, per charger, seller and currency: gross, refunds, disputes, net and count. That is the figure one of your entities invoices another. GatewayChanger never holds or moves the money.

What you can see afterwards

  • The trace on every decision: which rule matched, which candidates were considered, and the reason each one was skipped.
  • Events on the decision: session created, consent accepted, charge requested, charge failed, failed over, abandoned.
  • The transaction and its events, searchable by order reference, processor reference or the last four digits.
  • Webhooks for the same events, signed with HMAC-SHA256, with retries and a delivery log you can replay.