Engineering
A second currency is a price you typed, not a rate we applied
Multi-currency pricing was refused here twice for good reasons. It shipped when we noticed the reasons all rested on one unexamined assumption.
“A merchant cannot show €19 to a German and 39 KM to a Bosnian” had been on our list for a long time, along with everything it needs: a rate source, the rate frozen onto the order, and settlement currency tracked separately from display. All three are real work, none of it existed, and a previous decision record had written the refusal down in one line — building the override table would be building storage for a price nothing could charge.
That sentence was correct about the thing it was about. It also contained a hidden premise, and finding it is the whole of this story: it assumed the second number would be computed from a rate.
A rate source, a frozen rate and a settlement currency all follow from the first requirement. None of them follows from a number a merchant typed.
Three problems that stop existing
There is no rate, so there is nothing to freeze. A frozen rate exists to stop history being rewritten when the market moves. Here the price is frozen on the order, by the same mechanism that already freezes every other price — the unit price on an order line is a snapshot, as are the product name and the fulfilment terms. A merchant editing their euro price tomorrow does not touch a sale made today, for exactly the reason editing their dollar price does not.
There is no settlement currency to track apart from display, because we settle nothing. Money goes from the customer to the merchant’s own provider account and we hold none of it. Presentment is settlement: the order is charged in euros by a provider that takes euros, into the merchant’s own account, on that account’s terms.
And the settleability question was already answered. A function in the payments layer had been filtering a shop’s connections by adapter-supported currency since the day it was written. Stripe and PayPal answer “the merchant’s own account decides”; the bank-transfer and QR rails answer with real lists. Every caller could only ever hand it the shop’s one currency. Nothing about that function changed — it was simply given the currency actually being charged.
What it cost to keep honest
A currency prices all of a product or none of it. A percentage composes with whatever an option costs; an absolute price composes with nothing. So a currency that does not price every active variant is refused at the write boundary, rather than discovered by a customer who finds “Personal” buyable in euros and “Professional” not.
Reports had to change too. Revenue is grouped by currency and shown per currency, never summed across it — a single total over three currencies would be a number produced by a rate nobody applied, which is precisely the thing this whole design refuses to do.
The honest limitation, stated where a merchant meets it: nothing is converted, anywhere. If you want to sell in a currency, you type a price in it. We think that is the right trade — but it is a trade, and the alternative is a number our arithmetic invented and your customer’s bank disagrees with.
Drawn from ADR-0170, the decision record written before this was built. Every non-obvious choice in this product has one.
Sellstamp