Design a Multi-Currency Checkout

Hard45 min
1 / 30
understanding•10 min read

Problem Statement: Money Is a Distributed Systems Problem

Frames multi-currency checkout as a correctness-first pipeline, not a UI nicety, and separates display, conversion, capture, and settlement.

Problem statement

Design a checkout that lets a buyer pay in their local currency while the merchant may settle in a different one. The platform must detect or let the user choose a currency, fetch exchange rates that are fresh enough to trust, convert every line item and the order total with consistent rounding, apply the right tax per jurisdiction, localize the receipt, route the payment to a gateway that supports that currency and region, and survive the rate provider going down mid-session.

This is not a formatting problem. It is a correctness problem that touches money. A rounding inconsistency between the cart, the quote, and the gateway can cause a capture to fail reconciliation, a customer to be charged a different amount than displayed, or a merchant to absorb an unbounded FX loss. The design therefore separates four distinct amounts that beginners collapse into one: the display currency (what the buyer sees), the presentment currency (what the buyer is charged), the settlement currency (what the merchant receives), and the ledger currency (the accounting unit of record).

Why the problem is distinctive

A content platform can retry a render. A checkout cannot retry a charge. The design splits eventual progress from monetary invariants. Progress—rendering prices, showing rates, queuing webhooks—may degrade and retry. Invariants—exactly one successful capture per order, presented amount equals charged amount, double-entry ledger balances per currency, rounding is deterministic and reversible—must fail closed.

The problem requires currency selection or automatic detection, real-time or daily rate fetching, final price conversion with consistent rounding, and payment gateway integration per region, plus resilience when the rate provider fails, FX rate caching, localization of receipts and tax, and country-specific compliance. Every one of those is designed explicitly below.

Public operating baseline versus design assumptions

Public evidence shows this is operationally real at enormous scale. Stripe reports supporting 135+ currencies and dozens of payment methods and processing on the order of $1.4 trillion in payment volume per year. Adyen reported roughly €565 billion processed in 2023 across 150+ currencies and 250+ payment methods on a single platform. Shopify Markets presents storefronts in 130+ currencies, and Shopify reported hundreds of billions of dollars of annual GMV. Wise reports moving several billion pounds across borders every month using the mid-market rate. These are cited company figures used for context, not requirements for our fictional system.

For capacity planning this answer explicitly assumes a mature global merchant platform with 500,000 checkout sessions per hour at peak, 8% converting to paid orders, 60% of sessions involving a currency conversion, and a 5x event multiplier during flash sales. Unless a number is tied to a citation, it is a stated design assumption, target, or budget.

The four architectural planes

  1. Presentation plane: region/currency detection, localized formatting, tax-inclusive display, price guarantees.
  2. Conversion plane: the FX rate store, quote creation, quote locking with TTL, and the rounding engine.
  3. Payment plane: gateway routing, authorization, capture, refunds, webhooks, and idempotent retries.
  4. Ledger & compliance plane: double-entry accounting per currency, tax remittance, receipts, and regulatory evidence.

A strong answer keeps these planes separate. It allows the presentation plane to degrade to a cached rate while the payment plane refuses to capture on a stale or mismatched quote, and it lets the ledger plane reconcile asynchronously without blocking the buyer.

Key Highlights

  • •Separate display, presentment, settlement, and ledger currency; collapsing them is the root of most FX reconciliation bugs.
  • •Money invariants fail closed; rendering and progress may degrade and retry.
  • •Public figures from Stripe, Adyen, Shopify, and Wise provide context; every uncited scale number here is an explicit assumption.
  • •The architecture has four planes: presentation, conversion, payment, and ledger/compliance.
  • •Presented amount must equal charged amount, and the ledger must balance per currency.
Lead With the Money Boundary
State in the first two minutes that presented amount must equal charged amount and the ledger must balance per currency. This instantly distinguishes a money-grade design from a formatting exercise.
Do Not Use Floats for Money
Representing prices as floating point and rounding ad hoc is an automatic red flag. Use integer minor units plus an ISO 4217 currency code, or a fixed-scale decimal.

Section Rescue Kit

Buzzwords to use:

ISO 4217Presentment Currency

Safe statements:

  • "I will separate display, presentment, settlement, and ledger currency before choosing any storage."
  • "Let me define which decisions are allowed to degrade and which monetary invariants must fail closed."
Design a Multi-Currency Checkout - System Design | WinJob | WinJob