WritingCommerce
A landed cost engine that honours the duty it quoted at checkout
How to build a landed cost service whose quote survives rate changes between cart and charge: versioned inputs, a stored quote, and a charge against its id.
A customer in Sydney adds a £240 jacket to the cart of a Manchester shop and picks express delivery. Before they can pay, the checkout has to show one number that covers the goods, the £32 carrier charge, the duty the destination will levy on the goods and the import tax charged on top of all three, and it has to show that number within a second or two. The system that produces it is the landed cost engine, and its purpose is that the customer is never asked for money again when the parcel arrives.
The constraint that shapes the engine is that every input moves independently of the customer's session: the exchange rate changes every few minutes, the carrier reprices a lane, the duty table gets a new version, a product is reclassified. So the engine must never be asked to recompute at payment time. It computes once, stores the quote with the exact versions it used, and the charge references the quote id. If a recomputation would give a different figure from what the customer saw, that difference is the merchant's to reconcile, not the customer's to pay.
flowchart TD C[Checkout] --> E[Landed cost engine]:::accent E --> H[HS classifier] E --> D[Duty rate tables] E --> X[FX rate source] E --> R[Carrier rate quote] E --> Q[Quote store] Q --> C C --> P[Payment] P --> O[Order]
Checkout asks for one number
Classification and the duty table
Live rates with one fallback
Compute once, store before showing
Charge the quote, not the cart
Trade-offs
I have built cross-border procurement that buys from Amazon, Alibaba, AliExpress and 1688 and ships with DHL, FedEx, UPS and USPS, and the rule I apply to a landed cost engine is that a price shown is a price honoured, whatever that costs the merchant in variance. Each design choice below pays for that rule in a different currency.
| Choice | What it buys | What it costs |
|---|---|---|
| Store the quote before showing it | Payment charges exactly what the customer saw, by id | A write on every cart change, and a sweeper for expired rows |
| Expire quotes after 30 minutes | Bounded exposure to FX and carrier moves | A customer who returns after lunch sees a new total |
| Bind the quote to a cart and address hash | A quote for one basket cannot pay for another | Any edit, even a quantity change, invalidates the quote |
| Highest plausible rate for unclassified skus | Never under-collect on a product nobody has coded | Some customers see a higher total and leave |
| Rate card fallback when the carrier is down | Checkout keeps quoting through a carrier outage | Shipping variance that has to be booked and reviewed |
| Refuse a stale FX rate outright | No quote is ever built on a rate the ledger cannot match | An FX outage turns every quote into duties on delivery |
Failure modes
- The carrier quote times out. At 14:02 the Sydney customer reaches checkout; the engine calls the carrier for a 1.4 kg express parcel to New South Wales and gets nothing back in 1500 ms. The engine reads the rate card for that lane, which says £29 rather than the live £32, computes duty at the table's 5 percent on the goods (£12) and import tax at 10 percent on goods, shipping and duty (£28.10), and stores a quote of 30910 minor units marked carrier_source rate_card. The customer sees £309.10 and pays it. When fulfilment buys the label the carrier charges £32; reconciliation sees the rate_card marker, books £3 of shipping and 30p of tax to the variance ledger and closes the order. You observe the carrier_quote_fallback counter rising; the containment is an alert when fallbacks exceed 5 percent of quotes over 10 minutes, and a weekly rate card refresh so the fallback stays close to the live price.
- The FX rate is stale. The rate source stops publishing and the engine sees a rate stamped 18 minutes ago. It refuses to quote, checkout shows duties on delivery, and the quote_refused_stale_fx counter rises; a rate that stops moving for more than the freshness window is the trigger, and the containment is a second rate source the engine consults only when the first is stale.
- The quote store is unavailable. Every quote request returns an error and every cross-border customer gets duties on delivery. You observe quote write latency and a conversion drop on cross-border orders at the same minute; the containment is that the store is the one component with a replica and a failover, because nothing else in the engine needs to be durable.
- The quote expires between page load and the pay button. Payment finds expires_at in the past and returns quote_expired to checkout, which requests a fresh quote and shows the new total before charging. You observe the ratio of quote_expired to charges; the containment is refreshing the quote silently once the checkout page has been open for 25 minutes.
- The classification was wrong. The parcel is assessed at a different rate on arrival and the carrier's duty invoice does not match the quote. Reconciliation books the difference against the tariff code, and a code that produces variance on three orders in a row raises a classification review. The customer paid what was quoted, so nothing reaches them.
Build the quote store first, before the classifier, before the carrier integration and before any pricing logic: a row with a quote_id, a cart hash, an expiry and a snapshot of every input version. Once payment charges by quote_id, every other component can be replaced, cached or made to fall back without the customer ever seeing a second bill.