Currencies and presentment prices
Major and minor units, the per-currency `prices` map, what `?currency=` does, and the zero-price trap.
Shopify Markets lets a store sell in several currencies. Mercury indexes each product's price in every presentment currency the store has, so a storefront can show local prices without a second call.
On the collection, search, product and zone routes
A rendered product carries:
| Field | Meaning |
|---|---|
min_price | The lowest variant price in the store's default currency, in major units (89.5). |
compare_at_min_price | The lowest compare-at price, major units. |
prices | A map keyed by uppercase currency code: { "AUD": { "price": …, "compare_at_price": … }, "NZD": … }. Amounts are decimal, sent as numbers or as strings. |
variants[].price | The variant's price as Shopify sends it: a decimal string. |
Send ?currency=NZD to narrow prices to that one currency: { "NZD": { … } }. The code is
case-sensitive and must be uppercase; an unknown code leaves prices empty ({}). Fall back to
min_price when the currency you asked for is missing.
const local = product.prices?.[currency];
const amount = Number(local?.price ?? product.min_price);
const label = new Intl.NumberFormat(locale, { style: 'currency', currency: local ? currency : shopCurrency }).format(amount);
The zero-price trap. Mercury stores prices in cents and divides by 100 on the way out, but a price of
0 comes back absent, not 0. Free products (samples, gifts with purchase) have no min_price field
at all. Treat a missing min_price as zero only when you know the product is free; otherwise it means
"unknown". max_price is not sent.
Which currency to ask for
Use the shopper's presentment currency, which Shopify themes have as {{ cart.currency.iso_code }} (or
Shopify.currency.active in JavaScript). Mercury doesn't convert: it serves the prices Shopify computed
for each market.