Handling Multi-Currency Rounding in a Cross-Border Store (and Why Refund Tags Matter)
-->
Handling Multi-Currency Rounding in a Cross-Border Store (and Why Refund Tags Matter)
When you run a reverse-Taobao store that sells to buyers in USD, EUR, KRW, and JPY at the same time, currency is not a display detail — it is a correctness problem. A price of ¥99 might show as $13.71, and on refund it becomes $13.72. Multiply that by thousands of orders and you either over-charge buyers or leak cents every single day.
At Taocarts (a cross-border independent-site system built for reverse-Taobao and 1688 sourcing), we treat money as integer minor units end to end. The storefront shows a friendly decimal, but every calculation — tax, shipping, coupon, refund — runs on the smallest currency unit (cents, won, yen). We never round in the middle of a chain; we round exactly once, at the edge, when presenting to the user.
The classic trap is "naive reversal". A common bug is to recompute the refund from the current exchange rate instead of replaying the original transaction. Rates move between purchase and return, so the buyer gets back a different amount than they paid. Our fix: store the original settlement amount per order in its original currency, and the refund is a reversal of that recorded value, not a fresh conversion. For a partial refund we split the recorded amount proportionally and keep the remainder as a precise fraction, never a rounded guess.
Multi-currency rounding also bites on aggregated shipments. When three items from different suppliers merge into one box, each has its own settled currency. We keep a per-line settlement ledger so the consolidated invoice still reconciles to the sum of its parts. The UI shows one total, but the ledger underneath is strictly additive from immutable line items.
Then there is the refund-reason problem. If you only log "refunded", you learn nothing. We attach structured reason tags at refund time — size, damage, late delivery, wrong item — and aggregate them per SKU. That turns a cost center into a feedback loop: the SKU with the highest "size" return rate tells you the size chart needs fixing before you reorder.
A refund endpoint must also be idempotent. Network retries can fire the request twice; if each attempt creates a new payout, you double-refund. We key the operation on a client-generated idempotency key and persist the first outcome, so a retry returns the original result. Combined with the immutable ledger, a refund is safe to retry and safe to audit.
On presentation we use half-even (banker's) rounding so that across high volume the rounding errors cancel rather than skew. It is invisible to a single buyer but matters when finance reconciles the monthly statement against the processor.
The engineering takeaway is simple: keep money as integers, replay original transactions instead of recomputing, tag every refund with a reason, and make the endpoint idempotent. Do those four things and multi-currency stops being a daily leak and becomes just another report you read on Monday.