Multi-Currency Checkout: Architecting Cards, Mobile Money, and Cross-Border Settlement
Accepting cards from a customer in London, mobile money from a customer in Nairobi, and a bank transfer from a customer in Dubai, through one checkout, into one reconciled ledger, is an architecture problem, not a plugin problem. This guide covers the pattern that works, the pricing discipline that keeps it secure, and the reconciliation model that most implementations discover too late.
We run this architecture on our own site, and offer it as a standalone payment gateway integration: prices are quoted in US dollars and settled through a regional gateway in Kenyan shillings, with the rate resolved on the server. What follows is the reasoning behind that design, including the parts we would do differently at larger scale.
ContextWhy one provider is rarely enough
Global processors handle international cards excellently and regional payment methods poorly or not at all. Regional gateways handle local cards and mobile money excellently, with narrower international card coverage and different settlement terms. If your customers span both worlds, you need both, and the customer must never see that seam.
There is a second reason, less discussed: provider concentration is an operational risk. A single processor holding a freeze on your account is an existential event for a business with no alternative rail. Multi-provider architecture is partly a resilience decision.
ArchitectureThe payment abstraction layer
The core pattern is a single internal interface that your application talks to, with provider-specific adapters behind it. Your checkout code never mentions a provider by name.
| Layer | Responsibility | What must never live here |
|---|---|---|
| Checkout UI | Collect intent: what is being bought, by whom, in which display currency. | Prices, rates, or any amount the server has not authorised. |
| Pricing service | Resolve the authoritative amount from a server-side catalogue. | Any value derived from the request body or query string. |
| Payment interface | One method: initiate a charge for an order. Returns a provider-agnostic result. | Provider-specific branching in calling code. |
| Provider adapters | Translate to and from each provider's API, including currency and unit conventions. | Business rules, adapters translate, they do not decide. |
| Ledger | Record every order, attempt, rate used, fee, and settlement. | Nothing. This is the source of truth and it must be complete. |
Routing lives in one place: a function that takes the customer's country, chosen method, and currency, and returns which adapter to use. When you add a provider, you write an adapter and add one routing rule. Nothing else changes.
SecurityServer-side pricing, without exception
This is the most important rule in the entire architecture, and the one most frequently broken.
The client sends selections. The server determines the price. A checkout request should say "the customer wants plan X with add-ons Y and Z", never "charge $1,249". If any amount arrives from the browser and is used to create a charge, a customer can pay whatever they like for whatever they like, and someone eventually will.
This applies to every derived value: quantities, discount codes, tax, shipping, and currency conversion. Each must be recomputed server-side from data the server controls. A discount code is a code to look up, not a percentage to accept.
Grep your checkout for the price
Search your codebase for where the charge amount originates. Trace it backwards. If the path reaches a request body, a query string, a form field, or a hidden input without passing through a server-side lookup keyed on an identifier, you have a live pricing vulnerability. It is a one-hour audit and it is the highest-value hour in an e-commerce codebase.
Handling exchange rates correctly
Quoting and settling in different currencies is entirely reasonable, most cross-border businesses do it. Five rules keep it clean:
- Resolve rates server-side. Never fetch a rate in the browser and send the converted amount back.
- Fix the rate for the session. The price a customer sees must be the price they are charged, even if the market moves mid-checkout. Fix it when checkout begins and hold it for a stated window.
- Add an explicit margin, and disclose it. Rates move and settlement takes days. A small, stated buffer is normal commercial practice; an undisclosed one is a complaint waiting to happen.
- Store the rate on the order. The rate used, the source, and the timestamp. Without this, a refund in four months is guesswork and your accounts will not reconcile.
- Round in the settlement currency, last. Carry full precision through the calculation and round only at the final charge amount, using the convention the provider expects.
Note the unit conventions carefully in each adapter. Some providers expect minor units, cents, or in some markets the smallest local unit, and some expect whole units. Getting this wrong produces charges wrong by a factor of one hundred, and it is one of the most common integration bugs in payments.
RegionalWhat mobile money changes
Mobile money is not a card with a different label. Four differences reshape your flow:
- The customer acts on their handset. Authorisation happens outside your interface, on a prompt you do not control. Your checkout must present a clear waiting state that survives a page refresh.
- Confirmation is asynchronous and can be slow. Seconds usually, minutes sometimes. Never block order creation on a synchronous response; create a pending order and resolve it on confirmation.
- Transaction limits are lower. Per-transaction and daily caps mean high-value orders may need a card path or a split arrangement. Detect this before the customer hits the wall.
- Reversals work differently. Refund mechanics, timelines, and fees differ substantially from card chargebacks. Model your refund policy around what the rail actually supports, not around what card processors do.
Webhooks and the idempotency problem
Every provider confirms payment asynchronously, and every provider will occasionally send the same confirmation twice. Four rules:
- Verify the signature on every incoming webhook before doing anything else. An unverified webhook endpoint is an open instruction to mark orders paid.
- Make handlers idempotent. Key on the provider's transaction reference and make processing the same event twice a no-op. Duplicate fulfilment is the most expensive webhook bug.
- Acknowledge fast, process after. Return success immediately, then do the work, otherwise a slow handler triggers retries and compounds the problem.
- Reconcile independently. Webhooks get lost. Run a scheduled job that queries each provider for recent transactions and repairs any order whose state does not match.
The reconciliation model
This is where multi-provider implementations usually fail, and it is invisible until the first month-end. Each provider settles on a different schedule, deducts fees differently, and converts currency at its own rate. Without deliberate design, your ledger and your bank account diverge quietly and nobody notices for a quarter.
Record five things against every transaction: the gross amount in the display currency, the rate applied, the gross in settlement currency, the provider fee, and the net actually settled. Then run a monthly reconciliation that matches settled batches to orders and reports every discrepancy. Build this at the same time as the checkout, retrofitting it means reconstructing history you did not record.
LaunchPre-launch checklist
- No charge amount anywhere in the system originates from client input.
- Webhook signatures verified; handlers idempotent and tested with duplicate events.
- Exchange rate, source, and timestamp stored on every order.
- Pending, failed, expired, and partially-paid states all have a defined user experience.
- A scheduled reconciliation job exists and has been run against real data.
- Refund path tested end to end on every rail, including mobile money.
- Provider outage behaviour defined, what the customer sees, and whether traffic reroutes.
- Currency unit conventions confirmed per adapter, with a test asserting the exact charged amount.
$100 for a one-hour session, billed at $25 per 15 minutes with a one-hour minimum. You choose your time on Calendly immediately after checkout. Pay by card or M-Pesa.
FAQFrequently asked questions
Can one checkout accept cards, mobile money, and multiple currencies?
Yes, but not through a single provider in most markets. The workable pattern is a payment abstraction layer in your own application that routes each transaction to the right provider, a card processor for international customers, a regional gateway for local cards and mobile money, while presenting one consistent checkout to the customer.
Should I quote prices in USD or in local currency?
Quote in the currency your customer thinks in, and settle in the currency your business banks in. These are different decisions. Display prices in USD for international buyers to make comparison easy, then convert at the moment of charge using a rate you control and disclose.
What is the hardest part of multi-currency payments?
Reconciliation, not collection. Taking the money is comparatively easy; matching settlements to orders across providers with different fee structures, settlement delays, and currency conversion is where most implementations fail. Design the reconciliation model before writing the checkout.
How should exchange rates be handled at checkout?
Never price from a live rate fetched in the browser. Resolve the price server-side, fix the rate for the duration of the checkout session, store the rate used against the order, and disclose it to the customer. Storing the rate is what makes refunds and accounting possible months later.
You might also like
Generative Engine Optimisation: Getting Your Business Cited by AI Answers
A practical guide to structuring content so AI answer engines can extract, attribute, and cite it, what actually changes from traditional SEO, what stays the same, and how to measure results when the click never happens.
Read more →How to Migrate Off WordPress Without Losing Your Rankings
A phase-by-phase migration plan for replatforming without losing organic traffic, building a complete URL inventory, mapping redirects properly, preserving content depth, and diagnosing a drop if one happens.
Read more →