Designing a Real-Time Cross-Border Remittance Platform with Transparent Fee Calculation
A production-grade walkthrough for engineers and architects: how to move money across borders in seconds, quote fees the sender can trust before they tap confirm, and keep the whole thing correct even when a bank on the other side of the world goes offline at 2 a.m.
Introduction & History
Sending money across a border used to mean walking into a physical agent location, filling out a paper form, paying a fee nobody could explain, and telling the recipient to wait three to five business days. Behind the counter, that money was actually moving through a chain of correspondent banks, each one taking a cut, each one adding a day of settlement lag, and each one applying its own exchange rate with no visibility to the sender. This model, built on the SWIFT messaging network and bilateral correspondent banking relationships, has powered international finance since the 1970s, but it was never designed for a world where someone wants to send fifty dollars to a family member and know, before they commit, exactly how many dollars will land on the other side.
The shift toward real-time cross-border remittance began with three parallel developments. First, domestic instant payment rails — UPI in India, PIX in Brazil, FedNow and RTP in the United States, SEPA Instant in Europe — proved that settlement in seconds, not days, was technically achievable at national scale. Second, a new generation of fintech remittance companies built software layers on top of local banking rails in each corridor, pre-funding accounts in destination currencies so a transfer could be paid out locally without ever touching a slow international wire. Third, regulators worldwide pushed hard on fee transparency, requiring disclosure of the total cost of a transfer, including the hidden markup baked into exchange rates, before a customer authorizes payment.
A modern real-time remittance platform sits at the intersection of these three forces. It behaves like a domestic instant-payment app to the end user, it operates a treasury and liquidity network that pre-positions money in dozens of currencies, and it computes and discloses an all-in fee — including FX margin — before the sender ever commits. This tutorial designs such a system from first principles, covering everything from the quote engine to the compliance screening pipeline to how the platform recovers when a payout rail silently fails.
The regulatory backdrop matters as much as the technology. In the United States, the Remittance Rule under Regulation E requires providers to disclose the exact exchange rate, all fees, and the exact amount the recipient will receive, before the sender pays. The European Union’s Payment Services Directives impose similar pre-contractual disclosure obligations, and India’s Reserve Bank has pushed banks and payment operators toward similar clarity for outward remittances. This means fee transparency is not simply good product design, it is frequently a hard legal requirement, and the architecture has to guarantee that the number shown to the sender is the number that is actually charged, with no drift between the quote and the execution.
The remittance market itself is enormous and still growing. The World Bank has tracked global remittance flows to low- and middle-income countries reaching several hundred billion dollars a year, with migrant workers sending money home to support families as one of the largest sources of external finance for many developing economies, larger in many corridors than foreign direct investment. Every basis point shaved off the average fee, and every hour shaved off the average delivery time, therefore has outsized real-world impact on people who are often sending a meaningful share of their income and can least afford opaque deductions. This is the human context an architect should keep in mind when making a seemingly small decision, like how long a quote should stay valid, because that decision directly affects whether a low-income sender gets exactly what they were promised.
From a pure systems standpoint, this domain is a rich teaching example because it combines almost every hard distributed systems problem in one place: strict financial consistency requirements colliding with the need for very low latency, multi-party coordination across organizations the platform does not control, strong security and regulatory constraints, and a real-time user-facing product experience layered on top of all of it. Very few systems ask an architect to reason simultaneously about basis-point-level pricing accuracy, sanctions law, saga-based distributed transactions, and sub-second UI responsiveness — which is exactly why this is a favorite domain for senior system design interviews.
Think of the platform as an international airport. The Quote Service is the ticket counter that tells you the total price before you book. The Transfer Orchestrator is air traffic control, sequencing every step so nothing collides. The Treasury layer is the airline’s fuel reserves pre-positioned at each airport, so a plane doesn’t need to carry enough fuel for the round trip — it just needs enough locally at the destination to refuel and continue.
Q: Why can’t we just use SWIFT for everything?
A strong answer explains that SWIFT is a messaging protocol, not a settlement network — it tells banks what to do, but actual money movement still depends on each bank’s own correspondent relationships, funding, and processing windows, which is why SWIFT transfers can take one to five days and why intermediary banks can deduct undisclosed fees along the way.
Architecture & Components
At a high level, the platform is a collection of independently deployable services organized around three concerns: quoting and pricing (before the sender commits), transfer execution and compliance (after the sender commits), and treasury and payout (the actual movement of money in the destination country). Each concern scales differently, fails differently, and has different consistency requirements, which is why they are split into separate services rather than built as one monolith.
2.1 Component Responsibilities
| Component | Responsibility | Notes |
|---|---|---|
| API Gateway | Authentication, request validation, rate limiting, TLS termination, routing to backend services | Stateless, horizontally scaled, sits behind a load balancer |
| Quote Service | Produces a locked, time-bound quote combining live FX rate and computed fee | Read-heavy, must respond in well under a second |
| FX Rate Aggregator | Pulls live rates from multiple liquidity providers and market data feeds, computes a blended mid-market rate | Caches aggressively; rates refresh every few seconds |
| Fee Calculation Engine | Computes total cost: fixed fee, percentage fee, FX margin, corridor-specific surcharges | Rule-driven, versioned, fully auditable |
| Transfer Orchestrator | Coordinates the multi-step transfer as a saga: debit, compliance check, ledger entry, payout initiation | Owns the transfer state machine |
| Compliance and AML Service | Sanctions list screening, KYC verification, transaction monitoring for suspicious patterns | Can block or hold a transfer pending manual review |
| Double-Entry Ledger | System of record for every debit and credit; guarantees the books always balance | Strongly consistent, append-only |
| Treasury and Liquidity Manager | Tracks pre-funded balances in each destination currency and country | Decides whether to pay out from local liquidity or route via correspondent bank |
| Payout Rail Adapter Layer | Translates a generic “pay this person this amount” instruction into rail-specific API calls | One adapter per rail (UPI, PIX, SEPA Instant, SWIFT, ACH) |
| Reconciliation Service | Compares internal ledger state against rail and bank statements to catch mismatches | Runs continuously and in scheduled batch sweeps |
Think of the platform like an international airport. The Quote Service is the ticket counter that tells you the total price before you book. The Transfer Orchestrator is air traffic control, sequencing every step so nothing collides. The Treasury layer is the airline’s fuel reserves pre-positioned at each airport, so a plane doesn’t need to carry enough fuel for the round trip — it just needs enough locally at the destination to refuel and continue.
Internal Working
3.1 How a Live Quote is Built
When a sender opens the app and types in an amount, the client calls the Quote Service, which must answer in well under a second because this call often happens on every keystroke as the sender adjusts the amount. The Quote Service does not compute a fresh FX rate from scratch on every call — that would require a network round trip to a liquidity provider, which is too slow and too expensive to do at that frequency. Instead, the FX Rate Aggregator maintains a continuously refreshed cache of mid-market rates per currency pair, updated every one to five seconds from multiple providers, with the median or volume-weighted rate selected to avoid outlier manipulation.
The Fee Calculation Engine then applies a rule set specific to the corridor — the combination of source country, destination country, currency pair, and payment method. A rule set typically includes a fixed fee component, a percentage-of-amount component, an FX margin applied on top of the mid-market rate, and sometimes a payout-method surcharge, for example, cash pickup costing more than a bank deposit. All of these are combined into a single all-in number, and critically, the exchange rate shown to the sender already has the margin baked in, so what they see is genuinely what they get.
The resulting quote is not just a number — it is a locked, signed, time-bound object with a unique quote ID, an expiry timestamp typically thirty seconds to a few minutes out, and a cryptographic signature so the backend can later verify the quote was not tampered with client-side. When the sender confirms, the client sends back this quote ID, and the orchestrator re-validates it against the still-cached rate before locking in execution.
It is worth walking through the fee breakdown in more concrete detail, because the phrase “transparent fee” means little until it is decomposed. Suppose a sender in the United States wants to send money to a recipient in the Philippines. The Fee Calculation Engine typically assembles the following components: a fixed origination fee that covers baseline processing cost regardless of amount; a percentage fee that scales with the transfer size, often tiered so that larger transfers pay a lower percentage; an FX margin, which is the spread applied on top of the interbank mid-market rate and is usually the largest and least visible cost component in legacy remittance products; and, where applicable, a payout-method surcharge, since delivering cash to a pickup location generally costs the platform more in local partner fees than depositing directly into a bank account or e-wallet. All four numbers are computed by the same rule evaluation pass and then combined into two numbers the sender actually sees: the total amount they will pay, and the exact amount the recipient will receive, expressed in the destination currency.
The rule sets that drive this calculation are stored as versioned, corridor-specific configuration rather than hardcoded logic, because pricing changes constantly in response to competitive pressure, regulatory caps in certain countries, and changing costs from underlying liquidity providers. Each rule set carries an effective-from timestamp, and every quote records exactly which rule set version priced it, which means that if a regulator or an internal auditor asks “why was this specific sender charged this specific fee eighteen months ago,” the platform can answer precisely rather than reconstructing the answer from application logs.
There is also a subtler correctness requirement here: rounding. When converting between currencies with different minor unit conventions — for example, the Japanese yen has no decimal subdivision while most currencies use two decimal places — naive rounding can cause the sum of all fee components to not exactly match the total charged, which looks like a bug to a sharp-eyed sender even if the discrepancy is a fraction of a cent. The Fee Calculation Engine therefore performs all internal arithmetic in the smallest indivisible unit of each currency, using integer arithmetic rather than floating point, and applies a single, deterministic rounding step only at the very end when producing the final displayed numbers, guaranteeing the displayed components always sum exactly to the displayed total.
Q: What happens if the market rate moves between when the quote is shown and when the sender confirms?
The correct answer is that the platform absorbs small movements within the quote’s validity window by pre-hedging — the treasury desk buys the needed currency slightly ahead of guaranteed quotes — and if the quote expires, the client is forced to fetch a fresh one rather than letting an orchestrator silently apply a stale rate.
3.2 The Transfer Saga
Once the sender confirms, the Transfer Orchestrator begins a saga: a sequence of local transactions across multiple services, each with a compensating action if a later step fails. This pattern is necessary because a cross-border transfer touches at least four independently owned pieces of state — the sender’s source-currency balance, the compliance decision, the internal ledger, and the destination payout rail — and no single database transaction can span all of them safely.
The saga steps, in order, are: reserve and debit the sender’s funds, run compliance and fraud checks, write the double-entry ledger record marking funds as “in transit,” instruct the treasury layer to initiate payout via the appropriate rail, and on confirmed payout, mark the ledger entry as “settled” and notify both parties. If compliance flags the transfer, the saga pauses at a “held for review” state rather than rolling back automatically, since blocking outright could tip off a bad actor, while a human compliance analyst investigates. If the payout rail reports a permanent failure, for example the recipient account does not exist, the saga runs a compensating transaction that reverses the debit and refunds the sender.
Wise (formerly TransferWise) built its entire model around this local-liquidity insight: instead of moving money across a border for every single transfer, it matches senders and receivers in opposite directions within its own network wherever possible, settling both legs locally and only needing to true up the net difference between currency pools periodically. This is why Wise can offer near-instant, low-fee transfers on many corridors — the money often never actually crosses the border at all.
3.3 Why a Saga Instead of Two-Phase Commit
A natural first instinct is to ask why the platform does not simply wrap the whole transfer in a single distributed transaction using two-phase commit, so that either everything succeeds or everything rolls back cleanly. The answer is that two-phase commit requires every participant to hold locks and stay available until the coordinator says commit or abort, and at least one participant in this flow — the external payout rail — is a system the platform does not own, cannot force to hold a lock, and cannot guarantee will respond within any bounded time. A correspondent bank might take a business day to confirm a wire; a two-phase commit protocol simply cannot tolerate that kind of delay without freezing every other resource involved in the transaction.
The saga pattern accepts this reality by breaking the transfer into a sequence of independently committed local transactions, each of which is durable the moment it completes, paired with an explicit compensating transaction that can undo its effect later if a downstream step fails. This trades strict atomicity for eventual consistency with a guaranteed, well-tested recovery path, which is the only practical option once external, uncontrolled systems are part of the flow. The cost of this trade-off is that for a brief window, the system can be in a state where funds have been debited from the sender but not yet delivered to the recipient — a state the platform must represent explicitly, as “in transit,” rather than pretending it does not exist.
Every compensating action in the saga is designed to be idempotent and safe to run more than once, because a crash mid-compensation must not result in either a double refund or a lost one. This is typically achieved by having each compensating step check the current state of the ledger before acting, so that re-running “refund the sender” against a transfer that has already been refunded is a safe no-op rather than a second, erroneous credit.
Data Flow & Lifecycle
A transfer moves through a well-defined set of states from creation to completion. Modeling this explicitly as a state machine, rather than scattering status flags across tables, is what makes the system auditable and lets support teams and compliance officers understand exactly where a given transfer is at any moment.
4.1 Sequence of a Successful Transfer
4.2 Failure Recovery and Reconciliation Flow
Not every payout succeeds cleanly on the first attempt, and not every failure is reported honestly by an external rail in real time. The reconciliation flow below shows how the platform detects and recovers from a stalled or silently failed payout rather than leaving a transfer stuck indefinitely in “processing.”
4.3 Event-Driven Backbone
Every state transition emits an event onto the Kafka event bus, keyed by transfer ID to guarantee ordering per transfer. Downstream consumers — the notification service, the reconciliation service, the audit log, and any analytics pipeline — subscribe independently, which decouples them from the orchestrator’s direct call path. This means a slow notification provider can never block the transfer saga itself, and a new consumer, such as a future customer-support dashboard, can be added without touching the orchestrator’s code.
Advantages, Disadvantages & Trade-offs
Advantages
- Sender sees the true, all-in cost before committing, building trust and satisfying regulatory disclosure requirements
- Local-liquidity payout means many transfers settle in seconds rather than days
- Saga-based orchestration keeps the system consistent even though it spans many independently owned services
- Event-driven architecture allows new consumers, like fraud analytics, to be added without touching core transfer logic
Disadvantages & Trade-offs
- Maintaining pre-funded liquidity pools in dozens of currencies ties up working capital and carries FX exposure risk
- Compliance screening adds latency and occasionally holds legitimate transfers, hurting the “instant” promise
- Saga compensating transactions are complex to get right; a poorly designed rollback can itself cause fund discrepancies
- Supporting many local payout rails means maintaining many bespoke adapters, each with its own quirks and downtime windows
The central tension in this system is speed versus certainty. Instant payout rails settle fast but offer little room to reverse a mistake, while correspondent banking is slow but more forgiving of errors. A well-designed platform routes low-risk, low-value transfers through fast local rails and reserves the slower, more heavily reviewed path for higher-risk or higher-value transfers, deliberately trading some speed for safety where the stakes are higher.
Performance & Scalability
At a scale of millions of requests per minute, the quote endpoint is by far the highest-traffic component, since it is called far more often than actual transfers are confirmed — most senders check a rate before deciding to send. The Quote Service is therefore designed to be almost entirely read-driven off cache: FX rates live in Redis with sub-millisecond reads, and fee rule sets are also cached in memory per corridor, refreshed only when an administrator updates pricing. This lets a small fleet of stateless quote service instances, scaled horizontally behind a load balancer, absorb enormous read volume without touching a database on the hot path.
The transfer execution path is write-heavy but much lower volume by comparison, since only a fraction of quotes convert into confirmed transfers. Here the bottleneck shifts to the ledger, which must maintain strict consistency. The ledger is partitioned by account or by sender region using consistent hashing, so that writes for different senders land on different database shards and do not contend with each other, while each shard individually guarantees strong consistency for the accounts it owns.
Compliance screening, particularly sanctions list matching using fuzzy name matching algorithms, is CPU-intensive. This is offloaded to a dedicated, independently scaled service pool so that a burst of sanctions screening load never starves the orchestrator or the ledger of resources.
Q: How would you handle a sudden 10x spike in quote requests during a market volatility event?
A good answer covers auto-scaling the stateless quote service tier based on request rate, ensuring the Redis rate cache is itself replicated and sharded so it does not become a single bottleneck, and applying backpressure or graceful degradation, such as showing a slightly stale rate with a visible “refreshing” indicator, rather than letting the whole quote path fail.
6.1 Treasury Liquidity and FX Hedging at Scale
Performance in this domain is not only about request latency; it is also about capital efficiency. Every currency the platform pays out in requires a pre-funded balance sitting in a local bank account or with a local payment partner, and holding that balance ties up working capital while exposing the platform to currency fluctuation between the moment it acquires that currency and the moment it is paid out to recipients. The Treasury and Liquidity Manager solves this at scale by netting flows: rather than buying foreign currency for every individual transfer, it aggregates the net demand across thousands of simultaneous transfers in a corridor and executes far fewer, much larger FX trades with wholesale liquidity providers, which dramatically reduces both transaction cost and market impact compared to trading currency one retail-sized transfer at a time.
To protect the guaranteed rate shown in a locked quote, the treasury desk runs a hedging strategy that pre-buys a buffer of the destination currency ahead of aggregate expected demand, informed by historical volume patterns per corridor. When actual demand exceeds the hedge buffer, perhaps during a regional holiday when remittance volume spikes, the system automatically triggers additional wholesale purchases rather than letting quote fulfillment slow down or letting the platform take on unbounded FX risk.
6.2 Latency Budget
| Step | Target Latency | Technique |
|---|---|---|
| Quote generation | < 150 ms | Cached FX rate, cached fee rules, no external calls |
| Compliance screening | < 400 ms typical, async for edge cases | In-memory sanctions index, async escalation for fuzzy matches |
| Ledger write | < 50 ms | Sharded, indexed, append-only writes |
| Local rail payout | 1 to 15 seconds | Direct API integration with instant payment rail |
| Correspondent bank payout | Minutes to 1 business day | SWIFT messaging, async status polling and webhooks |
High Availability & Reliability
Money movement systems cannot tolerate silent data loss, so every write to the ledger is first appended to the Kafka event log before being applied, giving a durable, replayable record that can rebuild ledger state even if a database node is lost. The ledger database itself runs with synchronous replication to at least one standby in a different availability zone, so a primary failure triggers automatic failover without losing an acknowledged write.
Because payout rails in different countries have independent uptime characteristics, the Payout Rail Adapter Layer is built with circuit breakers per rail. If a specific country’s local rail starts timing out, its circuit opens, that rail is marked degraded, and new payouts for that corridor are either queued for retry or automatically rerouted to a fallback path, such as correspondent banking, rather than allowing failures to cascade back into the orchestrator and stall unrelated transfers in other corridors.
A circuit breaker per payout rail works like a household electrical breaker per room. If the kitchen circuit overloads and trips, the lights in the bedroom stay on. Without that isolation, one bad connection anywhere in the house could black out the whole building.
7.1 Idempotency and Exactly-Once Money Movement
Every external call in the payout path, from the client’s confirm request to the treasury’s instruction to a rail, carries an idempotency key. If a client retries a confirm request after a timeout, the orchestrator recognizes the same idempotency key and returns the existing transfer’s status rather than creating a duplicate saga, which is the difference between a system that occasionally sends money twice and one that never does.
Idempotency keys are generated client-side, typically as a UUID created once when the sender taps confirm and reused for every retry of that exact confirmation attempt, and are stored server-side alongside the resulting transfer ID for a rolling window well beyond any plausible client retry duration, often 24 hours. When a duplicate key arrives, the gateway returns the original response immediately without re-invoking the orchestrator at all, which also protects the compliance and ledger services from redundant load during periods of client-side network instability, such as a sender confirming a transfer on an unreliable mobile connection and their app silently retrying several times.
7.2 Disaster Recovery and Backup
Beyond regional failover for live traffic, the platform maintains point-in-time recoverability for the ledger through continuous write-ahead log shipping to a separate backup store, allowing the database to be restored to any specific second within the retention window, which matters enormously for financial systems where “restore to the most recent nightly backup” is not an acceptable answer if it means losing hours of confirmed transfers. Disaster recovery drills are run on a regular cadence, deliberately failing over a live region to its standby during a low-traffic window, to verify that failover actually works rather than assuming it does based on architecture diagrams alone. The platform defines explicit recovery time and recovery point objectives per service tier: the ledger and orchestrator, being on the critical money-movement path, target a recovery point objective measured in seconds, while lower-criticality services like analytics dashboards can tolerate a recovery point objective of several minutes without materially affecting the sender experience.
Security
Security in a remittance platform spans authentication, data protection, and financial-crime prevention, and all three are treated as first-class architectural concerns rather than bolted on afterward.
- Authentication and authorization: OAuth 2.0 with short-lived access tokens and mandatory step-up multifactor authentication for high-value transfers or new payout destinations.
- Encryption: TLS 1.3 in transit everywhere, encryption at rest for the ledger and personally identifiable information, and field-level encryption for the most sensitive data such as bank account numbers.
- Sanctions and watchlist screening: Every sender and recipient is screened against global sanctions lists such as OFAC, UN, and EU lists using fuzzy name matching, with any potential match automatically routed to a manual review queue.
- KYC and identity verification: Document verification and liveness checks at onboarding, with risk-based re-verification triggered by unusual transaction patterns.
- Transaction monitoring: Real-time and batch fraud models look for structuring, that is, splitting a large transfer into many small ones to stay under reporting thresholds, along with velocity anomalies and device or location mismatches.
- Least privilege and audit: Internal staff access to production financial data is role-scoped and every access is logged; ledger records are immutable and append-only so no one, including an administrator, can silently edit history.
Q: How do you prevent an attacker from replaying a captured transfer confirmation request to drain a sender’s account?
The answer combines short-lived, single-use quote tokens, idempotency keys tied to the original request, and server-side re-validation of the quote’s expiry and signature, so a replayed request either matches the already-completed transfer, doing nothing new, or is rejected as an expired quote.
Visa and Mastercard’s cross-border networks, along with fintechs like Remitly and Revolut, all invest heavily in real-time transaction risk scoring engines that combine device fingerprinting, behavioral biometrics, and historical corridor risk data, because cross-border remittance corridors are disproportionately targeted by money laundering and romance-scam-driven fraud.
8.1 Secrets, Data Residency, and Third-Party Risk
Beyond the sender-facing controls, the platform also has to secure the machinery that talks to banks, liquidity providers, and payout rails. Credentials for every external integration are stored in a dedicated secrets manager, never in application configuration files or environment variables checked into source control, and are rotated on a fixed schedule as well as immediately upon any suspected compromise. Because the platform integrates with dozens of third-party banks and payment partners, each with different security postures, every outbound integration is treated as a distinct trust boundary: network egress is restricted per service so that, for example, the Compliance Service cannot reach the internet at large, only the specific sanctions-list provider endpoints it needs.
Data residency compounds this further. Many countries legally require that transaction data about their citizens, and sometimes the encryption keys protecting that data, remain physically within their borders. The platform’s data architecture therefore is not a single global database but a set of regional data domains, each holding the ledger and personal data for senders and recipients whose transactions are subject to that jurisdiction, with only non-sensitive, aggregated data replicated globally for cross-region reporting and fraud pattern detection.
Monitoring, Logging & Metrics
Observability in this system needs to answer both engineering questions, such as “is the quote service healthy,” and financial questions, such as “do the books balance right now.” This means metrics span both infrastructure telemetry and business-level financial reconciliation.
| Category | What is Tracked |
|---|---|
| Golden signals | Latency, traffic, error rate, and saturation for every service, especially the Quote Service and Orchestrator |
| Business metrics | Quote-to-confirm conversion rate, average transfer completion time per corridor, compliance hold rate |
| Financial integrity | Real-time ledger balance checks: sum of all debits must equal sum of all credits at every moment |
| Rail health | Per-rail success rate, timeout rate, and circuit breaker state, alerting when any corridor degrades |
| Compliance | Sanctions screening throughput, false positive rate, average manual review resolution time |
| Distributed tracing | End-to-end trace per transfer ID across every service the saga touches, for fast root-cause analysis |
Every service emits structured logs correlated by transfer ID and trace ID, shipped to a centralized log pipeline. Dashboards built on this data separate “is the system up” from “is the money correct,” because a system can be technically healthy, with normal latency and error rates, while a subtle bug still causes ledger entries to drift out of balance, which is why automated reconciliation alerts are treated as page-worthy incidents, not background reports.
Alert thresholds in this domain are set deliberately tighter than in most consumer software, because the cost of a missed financial anomaly compounds quickly. A ledger imbalance of even a small amount, left undetected for hours, can represent thousands of individually small discrepancies across a high-volume corridor, so the reconciliation job’s mismatch threshold is typically set to alert on the very first unexplained cent, with automated triage tools that classify common, benign causes, such as a rounding difference already accounted for by a pending settlement, separately from genuine, unexplained discrepancies that require a human financial engineer to investigate immediately.
Deployment & Cloud
Each service is packaged as a container and deployed on Kubernetes, with separate node pools for latency-sensitive services like the Quote Service and heavier batch workloads like reconciliation, so that a batch job never starves an interactive request path of CPU. The platform runs across at least two cloud regions, with the ledger database’s primary region chosen per regulatory data residency requirements, since many countries require financial transaction data about their citizens to be stored within their borders.
Deployments use blue-green or canary rollout strategies for any service touching the money-movement path, so a bad deploy can be rolled back in seconds and is first exposed to only a small percentage of live traffic. Infrastructure is defined declaratively, with every environment, from staging to production, provisioned from the same versioned templates to eliminate configuration drift between environments.
Q: Why not deploy the ledger service globally across every region for lowest latency everywhere?
The answer is that financial ledgers require strong consistency, and spreading a strongly consistent database across many distant regions adds significant write latency due to consensus overhead, so most platforms instead pick a primary region per data-residency zone and accept slightly higher latency for far-away users rather than compromise consistency.
Databases, Caching & Load Balancing
11.1 Database Choices
| Store | Used For | Why |
|---|---|---|
| PostgreSQL, sharded | Double-entry ledger, account balances | Strong ACID guarantees, mature support for financial workloads |
| Redis | FX rate cache, fee rule cache, quote token store, rate limiting counters | Sub-millisecond reads, native TTL support for quote expiry |
| Kafka | Event backbone for state transitions and inter-service communication | Durable, ordered-per-key log that decouples producers from consumers |
| Document store | KYC documents, compliance case files | Flexible schema for varied document types across jurisdictions |
| Data warehouse | Analytics, regulatory reporting, historical trend analysis | Optimized for large aggregate queries, separate from operational load |
11.2 Caching Strategy
FX rates are cached with a short TTL of a few seconds and refreshed proactively by a background worker rather than on-demand, so that a spike in read traffic never causes a thundering herd against the upstream rate provider. Fee rule sets change far less often, so they are cached with a longer TTL and explicitly invalidated the moment an administrator updates pricing, guaranteeing the cache is never stale by more than the propagation delay of that invalidation event.
11.3 Load Balancing
A global anycast load balancer routes each sender to their nearest healthy regional entry point, and within a region, requests are distributed across service instances using least-outstanding-requests load balancing, which handles the uneven request cost of, for example, a quote lookup versus a heavier compliance check, better than simple round robin.
UPI in India processes tens of billions of transactions a month by relying on NPCI’s centralized switch design combined with participating banks each running their own scaled infrastructure, illustrating how a real-time payment rail balances a shared, standardized protocol with distributed execution capacity.
11.4 Choosing a Ledger Sharding Key
The choice of sharding key for the ledger deserves special attention because it determines whether the busiest operation in the system, posting a debit and credit pair for a single transfer, can be done as a single-shard local transaction or requires a more expensive cross-shard coordination step. Sharding by account ID, specifically the sender’s account, means a debit to the sender is always local to one shard, but the corresponding credit to an intermediate settlement account or to the recipient may land on a different shard if the recipient is also a platform customer in a different region. Most production ledgers resolve this by using an intermediate “in transit” clearing account per shard: the debit and the movement into the local clearing account happen atomically on the sender’s shard, and a separate, asynchronously reconciled process moves value out of the clearing account and into the recipient’s shard, which keeps every individual write fast and local while still preserving global correctness through the reconciliation service described earlier.
APIs & Microservices
The platform exposes a small set of public APIs to client applications and a much larger set of internal APIs between services. Public APIs are REST over HTTPS for simplicity and broad client compatibility, covering quote creation, transfer confirmation, transfer status lookup, and recipient management. Internal service-to-service calls that require low latency and strong typing use gRPC, while anything that can be asynchronous, such as notifying the reconciliation service of a new ledger entry, flows through Kafka instead of a direct call, reducing tight coupling between services.
12.1 Key Public Endpoints, Conceptually
- Create Quote: accepts source amount or destination amount, corridor, and payout method; returns a locked quote with full fee breakdown and expiry.
- Confirm Transfer: accepts a quote ID and idempotency key; starts the transfer saga and returns a transfer ID.
- Get Transfer Status: returns the current state-machine status and a human-readable timeline for the sender.
- Webhook Callback: notifies partner integrators of status changes, so they don’t need to poll.
POST /v1/quotes HTTP/1.1
Host: api.remit.example.com
Authorization: Bearer <access_token>
Idempotency-Key: 3d2b8f1e-6a45-4c9b-9d1c-2f8e0a11bb42
Content-Type: application/json
{
"corridor": { "from": "US", "to": "PH" },
"source_amount": { "currency": "USD", "value_minor": 20000 },
"payout_method": "bank_deposit",
"recipient_id": "rcp_01HX9J7N5V2QW3T6"
}
HTTP/1.1 200 OK
Content-Type: application/json
{
"quote_id": "qt_01HXA1P0KZ8M9F3D",
"expires_at": "2026-08-11T18:04:00Z",
"source": { "currency": "USD", "value_minor": 20000 },
"destination": { "currency": "PHP", "value_minor": 1114500 },
"effective_rate": "55.7250",
"fee_breakdown": {
"fixed_fee_minor": 199,
"percent_fee_minor": 100,
"fx_margin_minor": 285,
"payout_surcharge_minor": 0,
"total_fee_minor": 584
},
"policy_version": "ph-corridor-v42",
"signature": "ed25519:base64..."
}Each microservice owns its own database and exposes no direct database access to any other service, which preserves the ability to change a service’s internal schema without coordinating a lockstep release across the whole platform. Service boundaries are drawn around business capability, not technical layer, which is why “Compliance” is one service rather than splitting sanctions screening and fraud scoring into separate deployables that would otherwise need to share nearly identical case-management state.
Q: How do you version an API like Create Quote without breaking existing mobile clients that can’t be force-updated instantly?
A solid answer describes additive, backward-compatible changes as the default, an explicit API version in the URL or header for breaking changes, and a deprecation window with monitoring on old-version traffic before finally retiring it.
12.2 Rate Limiting and Partner API Design
The public Create Quote endpoint is the single most abused endpoint on the platform, because it requires no authentication in some product flows, letting a prospective sender check pricing before creating an account, and because competitors and price-comparison sites have every incentive to scrape it continuously. The gateway applies tiered rate limiting: a generous per-IP limit for anonymous browsing, a stricter but still comfortable per-account limit for authenticated users actively building a transfer, and a separate, much higher-throughput allowance for registered partner integrators who have signed a commercial agreement and authenticate with API keys rather than user sessions. Anomalous patterns, such as thousands of quote requests per minute from a single IP with no corresponding confirmations, are automatically throttled and flagged to the fraud and risk team, since aggressive scraping is often itself a precursor to a more targeted attack, such as probing for pricing gaps to exploit.
For business partners who embed remittance functionality into their own apps, such as a telecom operator offering money transfer as an added service, the platform exposes a slightly different integration surface: a server-to-server API secured with mutual TLS and signed requests, webhook delivery with retry and exponential backoff for status updates, and a sandbox environment that mirrors production behavior, including simulated compliance holds and simulated rail failures, so partner engineering teams can build and test their failure-handling logic before ever touching real money.
Design Patterns & Anti-patterns
13.1 Patterns Applied
Saga pattern
Coordinates the multi-step transfer across services with explicit compensating actions instead of a single distributed transaction.
Circuit breaker
Isolates failures per payout rail so one country’s outage cannot cascade platform-wide.
Event sourcing
Every balance change is derived from an immutable, ordered sequence of events, making the full history reconstructable and auditable.
Strangler pattern
When adding a new payout rail or migrating a legacy corridor integration, traffic is gradually shifted from the old adapter to the new one rather than a risky big-bang cutover.
CQRS
The high-volume quote read path is served from a denormalized, cache-optimized model, separate from the strongly consistent write model used for the ledger.
13.2 Anti-patterns to Avoid
| Anti-pattern | Why it hurts |
|---|---|
| Distributed monolith | Splitting services by technical layer instead of business capability, so that a single logical change forces coordinated deploys across five services. |
| Silent retries without idempotency | Retrying a payout call on timeout without an idempotency key, risking a duplicate payment to the recipient. |
| Showing a rate without the margin baked in | Displaying a “market rate” separately from a “fee,” which technically discloses numbers but obscures the true total cost — regulators and users alike now expect a single all-in figure. |
| Synchronous chains across every service | Making the orchestrator call compliance, then ledger, then treasury all synchronously in one long blocking chain, which turns one slow downstream service into an outage for the entire transfer path. |
| Mutable ledger rows | Allowing any process to update a historical balance entry in place instead of appending a correcting entry, which destroys auditability. |
Best Practices & Common Mistakes
Best Practices
- Lock quotes with a short, enforced expiry and re-validate server-side at confirmation time
- Treat the ledger as append-only and immutable; corrections are new entries, never edits
- Isolate each payout rail behind its own adapter and circuit breaker
- Make every write in the transfer path idempotent using client-supplied idempotency keys
- Reconcile continuously against external rail statements, not just once a day
Common Mistakes
- Computing fees client-side and trusting the client’s number at confirmation
- Treating compliance screening as an afterthought bolted onto the orchestrator instead of a first-class saga step
- Underestimating FX volatility risk between quote and settlement for large transfers
- Building one giant payout adapter that tries to handle every country’s rail with conditional logic
- Skipping chaos testing on rail failures until a real outage happens in production
One practice that separates mature remittance platforms from newer entrants is deliberate, scheduled chaos testing against payout rail integrations: periodically injecting simulated timeouts, malformed responses, and partial failures into a staging environment that mirrors production traffic patterns, so that the circuit breakers, retry logic, and reconciliation alerts are exercised regularly rather than only being tested for the first time during an actual outage. Teams that skip this step often discover, during a real incident, that a retry loop was missing a backoff cap, or that a compensating transaction assumed a precondition that does not hold when two failures happen in quick succession, which are exactly the kinds of bugs chaos testing surfaces safely ahead of time.
Real-World / Industry Examples
Several companies illustrate different points on the spectrum of this design.
Local-matching pioneer
Pioneered the local-matching model, settling both legs of a transfer domestically wherever possible and displaying the mid-market rate plus a single transparent fee, which became something of an industry benchmark for fee disclosure.
Direct rail depth
Built deep, direct integrations with local payout rails and cash pickup networks across many developing-market corridors, prioritizing payout speed and reach in regions where bank account penetration is lower.
Multi-currency accounts
Layered cross-border transfer capability on top of an existing multi-currency account architecture, letting customers hold balances in several currencies and convert at the moment of transfer rather than the moment of send.
Card-network push rails
Took a different approach, building push-payment rails on top of existing card network infrastructure to reach billions of card-linked accounts globally without needing bespoke bank integrations in every country.
Central bank-backed instant rails, such as India’s UPI, Brazil’s PIX, and the Single Euro Payments Area’s SEPA Instant, have become the backbone that many of these fintechs plug into on the payout side, since a domestic instant rail plus a smart treasury layer can deliver an experience that feels international but is, under the hood, largely a series of well-orchestrated domestic payments.
It is also worth noting the interoperability efforts underway at the central bank level, since these will shape how such platforms are architected over the next decade. The Bank for International Settlements has run experimental projects linking multiple countries’ instant payment systems directly to each other, aiming to let a payment initiated on one country’s domestic rail settle directly on another country’s domestic rail without a commercial intermediary in between. If these efforts mature into production infrastructure, the treasury and payout-adapter layers described in this tutorial would gain an additional, potentially much cheaper and faster routing option alongside local-liquidity payout and correspondent banking, which is exactly the kind of extensibility a well-designed payout adapter layer, decoupled behind a generic interface, should be able to absorb without disrupting the rest of the platform.
FAQ, Summary & Key Takeaways
Why is a saga used instead of a single distributed transaction across all services?
Because the services involved own independent databases and, in the case of external payout rails, live entirely outside the platform’s control. Distributed two-phase commit across that many boundaries would be slow, fragile, and impossible to extend to a third-party bank’s API, so the saga pattern with explicit compensating actions is the practical choice.
How is the fee kept “transparent” rather than just technically disclosed?
By collapsing the FX margin and every fee component into a single all-in number and a single effective exchange rate shown before confirmation, rather than listing a “market rate” and a separate “fee” that a sender has to do arithmetic on to understand their true cost.
What happens if a payout rail confirms success but the money never actually arrives?
This is exactly what the continuous reconciliation service exists to catch: it compares the platform’s ledger against the rail’s own settlement statements and flags any mismatch for investigation, since a rail’s real-time confirmation and its final settlement record can occasionally diverge.
How does the system stay fast while still doing heavyweight compliance screening?
Most legitimate transfers pass automated screening in a few hundred milliseconds using pre-loaded, in-memory watchlist indexes; only the small fraction that produce a fuzzy or ambiguous match are pushed to an asynchronous manual review queue, so the common case stays fast and only genuinely uncertain cases pay the latency cost of human review.
Why does the platform maintain pre-funded balances in destination currencies instead of buying currency at the moment each transfer is confirmed?
Buying currency per-transfer at retail volumes would be both slow, since it depends on an external market execution, and expensive, since small trades get worse pricing than large ones. Pre-funding, combined with netting demand across many simultaneous transfers into fewer, larger wholesale trades, is what allows the platform to guarantee an instant, locked rate to any individual sender while still managing FX risk efficiently in aggregate.
How does the system decide whether to route a payout through a local instant rail versus a slower correspondent banking path?
The Treasury and Liquidity Manager checks, in order, whether it holds sufficient pre-funded local currency and whether a direct rail integration exists and is currently healthy for that corridor and payout method; if both hold, the transfer routes through the fast local rail. If liquidity is insufficient or the local rail’s circuit breaker is open, the system falls back to correspondent banking, which is slower but does not depend on the platform’s own currency position or a single rail’s uptime.
- Split the platform into pricing, execution, and treasury domains, each with different consistency and latency needs
- Lock and sign quotes server-side, and never trust a client-supplied fee or rate at confirmation time
- Model the transfer as an explicit state machine driven by a saga, with real compensating actions for every failure mode
- Isolate each payout rail behind its own adapter and circuit breaker so one country’s outage stays contained
- Treat the ledger as the immutable source of truth, and reconcile against it continuously, not periodically
- Bake compliance and fraud screening into the saga as a first-class step, not an afterthought
A real-time cross-border remittance platform is, at its core, a locked quote plus a saga: a promise made to the sender in the pricing domain, and a disciplined, compensable sequence in the execution and treasury domains that keeps that promise, one signed quote at a time, even when a bank on the other side of the world is asleep.