Inkress ADRsarchitecture decisions
Overview / Ledger / money
ADR 0032

Currencies hard-partition balances; no in-ledger FX; rates from cached external APIs

AcceptedLedger / money

Context

Merchants transact in multiple currencies. Two questions follow: does a single balance mix currencies (with implicit conversion), and where do exchange rates come from when a rate is needed for display or quoting?

Decision

Hard-partition balances by currency and keep all FX out of the ledger; source rates for display/quoting from external rate APIs behind a layered cache.

  • One wallet per currency. A merchant holds a separate wallet per currency (ADR 0031); an amount is only ever posted to its own-currency wallet. The ledger never converts one currency into another — there is no implicit FX movement in the system of record.
  • FX is display/quote-time only. When a rate is needed (showing an approximate price, quoting a cross-currency figure), it is fetched from external exchange-rate APIs and cached in Redis; it never silently rewrites a stored balance.

Consequences

  • Positive: every currency's balance is exact and independently auditable; there is no rounding or hidden spread buried inside a ledger conversion.
  • Neutral / to revisit: anything genuinely cross-currency (e.g. paying out in a different currency than was collected) needs an explicit conversion step outside the ledger, booked as its own movements — there is no one-balance shortcut.
  • Neutral / to revisit: FX depends on an external provider and a cache; the platform currently has more than one FX code path and an externally-hosted rate dependency (a hardcoded rate-API key in production is a known hygiene issue to remediate — it is operational debt, not part of this decision).

Alternatives considered

  • A single multi-currency balance with in-ledger conversion: rejected — implicit FX inside the ledger makes balances inexact and conversions un-auditable; partitioning keeps each currency's truth clean.
  • A self-hosted / contracted FX rate feed: not adopted — cached external APIs are cheaper for display-grade rates; the trade-off is an external dependency and key hygiene, acceptable because FX never drives a ledger movement.
← 0031 Balances are per-currency wallet accounts; available balance subtracts reservations at compute time 0033 Merchant payouts are reviewable, balance-validated financial requests materialized by a worker →