DEV Community

Taocarts跨境
Taocarts跨境

Posted on AI-assisted

How Taocarts Turns Messy Reverse-Taobao Orders into One Clean Shipment

How Taocarts Handles Multi-Currency Checkout Without Losing Cents

Taocarts, a reverse-taobao shopping and consolidation service, lets overseas buyers purchase from Taobao, 1688 and a few other Chinese marketplaces, then ships everything as one consolidated parcel. The moment you serve customers in six currencies, "just store a price" stops working. This post is a short engineering retrospective on the checkout math we had to get right.

The rounding trap

Our first naive model kept every price in CNY and converted at display time. That looked fine until refunds and partial shipments entered the picture. A customer pays in USD, we buy in CNY, and when one item in a five-item order is out of stock, we refund a fraction. If we rounded the USD total first and then derived the refund, the summed refunds drifted from the original charge by a few cents every time. Multiply that by thousands of orders and the ledger no longer reconciles.

The rule we landed on: never round the customer-facing currency until the very last step, and never derive a sub-amount by rounding a parent amount. We store the original paid amount as an integer in the smallest unit (e.g. cents) per currency, and every refund, adjustment, and fee is computed in CNY first, then converted with a single rounding pass at the boundary.

Where the rate lives

Exchange rates are cached in Redis with a short TTL and a source timestamp. The tricky part is not fetching the rate, it is deciding which rate a given operation should use. A checkout locks the rate at the moment the order is created; a refund weeks later must use the original rate, not today's. So the rate snapshot is written into the order row, not looked up on demand. This sounds obvious, but it took one painful reconciliation incident to make it non-negotiable.

Multi-currency in the data model

We kept the domain currency as CNY for all sourcing and purchasing logic, and treated buyer currencies as a presentation + settlement layer. The Express middleware that talks to payment gateways converts only at the settlement edge. Laravel's money handling stays in integers; the front end receives already-formatted strings and is never trusted to do arithmetic.

Why this matters for a consolidation service

Consolidation means one cart can contain items from different sources, different weights, and different shipping quotes. The checkout has to show a single clean total in the buyer's currency while internally tracking each line in CNY. Getting the boundary right is what lets us show "one clean shipment, one clean price" instead of a confusing pile of conversions.

One more practical note: we turn currency drift into an observable metric. A nightly job sums all settled buyer amounts in their own currencies and compares that against the CNY costs converted at each order's locked rate, then alerts if the gap exceeds a small threshold. This has caught two rate-feed outages that would otherwise have silently eaten margin. In a consolidation business where one cart can span several sources and currencies, observability is far cheaper than trust-and-hope.

The lesson: multi-currency is not a formatting problem, it is a where-do-you-round problem. Draw the boundary once, store integers, and your finance team will stop hunting ghosts in the ledger.

Top comments (0)