Reduced fees and delays: caching courses and routing payments
Full article
1) Purpose: Pay faster and cheaper without losing control of risk
Speed: less than "cold" expectations (3-DS, batchy bank, network confirmation).
Cost: Minimize PSP/bank/network fees and conversion spread.
Reliability: zero-loss invariants: one debit/credit, predictable rate.
Compliance: regional rules, sledge screening, RG/AML before submission.
2) Course service (FX) with caching: how to arrange it correctly
2. 1 Sources and calibration
Price-feeds: liquidity providers/FX-API/PSP quotes/cash registers.
Normalization: single format 'pair/base/quote/scale', mid-rate + margin ('markup _ bps'), time to get 'as _ of'.
Calibration on the PSP: the PSP part returns the "effective heading" in the response; store rate cards per method/PSP/geo.
2. 2 Cache and TTL
Hot cache (Redis) for frequent pairs (EUR↔USD, EUR↔GBP, USD↔BRL, etc.).
TTL by volatility: G10 - 30-120 c, EM - 10-30 c; stables - 60-300 s (by provider).
Grace window: tolerance for using the "rotten" course of N seconds to complete intents already started.
2. 3 Slippage protection
The client is given a temporary quote with'expires _ at'and allowed drift (for example, ± 20 bps).
When executed:- if the market has left within drift → we apply the "booked" rate;
- if you go further → re-quote (re-confirmation) or "best-effort" on brand policy.
- For conclusions - budgeted FX: conversion spending limit, transaction rejected if exceeded.
2. 4 Idempotency and atomicity
All FX transactions have an'X-Idempotency-Key 'associated with the transaction amount/pair/type.
Ledger entry and course fixing are one saga: 'quote. lock → debit/credit → quote. release`.
Rates in monetary records are stored as minor units + scale, with no floating point.
2. 5 API (reference)
Get Quote (Cached)
GET /v1/fx/quote? base=EUR"e=USD&side=buy&amount_minor=100000
→ 200 {
"pair":"EUR/USD","rate":"1. 08123","markup_bps":15, "expires_at":"2025-10-23T12:00:15Z","quote_id":"q_abc"
}
Book course and write off
POST /v1/fx/execute
Headers: X-Idempotency-Key: fx_exec_001
{
"quote_id":"q_abc","amount_minor":100000,"pair":"EUR/USD"
}
→ 200 {"status":"filled","avg_rate":"1. 08125","fx_entry_id":"fx_77"}
3) Payment routing: choose the fastest and cheapest way
3. 1 Routing factors
Geo/currency/method: map/SEPA/SWIFT/ewallet/crypto.
Fees: fix +% + FX-markup, PSP/bank hidden charges.
SLA: p95 'submit→settled', success-rate, code failure.
Cut-off and settlement windows: banks (SEPA T + 0/T + 1), crypto networks (load/gas), output.
Risks: sanctions/AML/velocity-limits/chargeback-profile.
Availability: channel/PSP status, merchant limits, treasury balances.
3. 2 Cascades and bandit strategies
Deterministic rules for "hard" requirements (jurisdiction, license, amount).
Top - ε -greedy/UCB to choose between several suitable PSPs: minimize 'cost _ per _ success' and delay.
Warm-up to new channels with reduced traffic + quick stop on degradation.
3. 3 "Smart" Retrai
We repeat only retryable codes (timeouts, transient, bank offline).
Hedged-requests (rarely, carefully): parallel launch on 2 PSPs with loser cancellation - for VIP/critical amounts, if legally permissible.
Always with idempotent PSP tokens to avoid double scrapping.
3. 4 Schedule accounting
Cut-off calendar (SEPA, SWIFT, local schemes), "holidays" by country.
Scheduler: if 3. 5 Crypto routing Network selection by 'fee _ per _ byte/throughput/ETA', white lists of addresses and travel rules (Travel Rule between VASP). Dynamic gas: calculation maxFee/maxPriority, pause of low-priority outputs at gas spikes. 3. 6 API (reference) 4) Netting, butching and on-us optimization On-us (within the ecosystem): settlement without external PSP → zero commission, instant SLA. Netting: summarize small cashouts into one external transfer (banks/crypto) with detailed posting in Ledger. Butching: SEPA files, crypto butches (UTXO consolidation/stablecoin butch) - reduce cost due to wholesale gas/commissions. 5) Cost and speed oriented telemetry and SLO 6) Observability and audit Structured JSON logs: 'trace _ id', 'route _ id', 'payment _ id', 'psp _ ref', 'fx _ quote _ id', error codes. OpenTelemetry: spans'route. select → fx. quote → wallet. debit → psp. submit → webhook. settled`. WORM audit: changes in FX margin, routing rules, priorities, cut-off calendar. 7) Treasury and balances Reserves per channel/PSP/network, automatic rebalancing taking into account traffic forecasts. Minimum balance policies and "no new payouts" stop mode when underfunded. Hedge: Auto swap stables/fiat on plan to capture spread. 8) Reconciliation and reports FX register: quote_id ↔ execute_id ↔ payment ↔ conductings of Ledger. Daily PSP/bank/network reports ↔ Ledger: categories' match/timing/missing/amount _ mismatch '. Separate FX-PnL report: realized spread, cost of quotations, re-quote losses. 9) Architectural blocks (microservices) fx-service: collecting feeds, cache, slippage policies, quote/execute API. router-service: rules, bandit logic, cut-off calendar, channel status. treasury-service: balances, rebalance, budgets. payments-orchestrator: sagas, idempotency, webhooks, DLQ. Везде — outbox/CDC, partition-keys по `tenant/brand/region`. 10) Checklists 11) Red flags (anti-patterns) Conversion at the "market" rate without fixing 'quote _ id' and validity period. The lack of slippage restrictions → a negative surprise to the player. Routing is "hard-wired" into code without telemetry and A/B. Retrains to the same PSP codes that are not retracted (for example, hard decline). Ignoring cut-off/calendars → systematic delays T + 1 +. Netting/butching is disabled at high commissions. Unsigned webhooks/validity windows → replays. No outbox/CDC → "lost" events and discrepancies in reports. 12) Withdrawal Reducing fees and delays is not a "magic" one switch, but a system of solutions: cached and managed courses with slip protection, smart routing with cascades and bandit logic, accounting for schedules and balances, plus netting and butching. Add idempotency, outbox/CDC, SLO telemetry and WORM auditing - and your payments start to go faster, cheaper and once, while remaining transparent to the player, finances and regulator.Route selection
POST /v1/payments/route
{
"amount":{"minor_units":5000,"currency":"EUR"}, "method":"card","geo":"DE","direction":"payout"
}
→ 200 {
"route_id":"rt_001", "candidates":[
{"psp":"A","eta_sec":420,"cost_minor":85,"success_rate":0. 91}, {"psp":"B","eta_sec":360,"cost_minor":120,"success_rate":0. 96}
], "recommend":"B"
}Execution (idempotent)
POST /v1/payments/submit
Headers: X-Idempotency-Key: pay_001
{ "route_id":"rt_001","psp":"B", "fx_quote_id":"q_abc" }
→ 202 {"payment_id":"pm_777","status":"SUBMITTED"}
Platform/Operator
Integrations/PSPs/FX Providers