Multi-currency & markets gotchas
How money-based rules convert across currencies and markets, and the international-store gotchas merchants hit most.
If you sell in more than one currency or run multiple Shopify markets, money-based rules behave in ways that surprise a lot of merchants. This page explains exactly how thresholds convert across currencies, how market targeting differs from currency, and how to fix the handful of issues that come up again and again.
Every subtotal and price threshold you type — in conditions, cart restrictions, and tier discounts — is defined in your store's base currency. At evaluation time, that base-currency amount is converted into each shopper's presentment currency (the currency they're actually checking out in) before it's compared to their cart total. Almost every "my threshold fired at the wrong amount" report comes from this conversion step, not from a bug.
How money thresholds are evaluated across currencies
You set a money threshold once, in your base currency. You do not type a separate number for every currency you sell in. The app handles the per-currency conversion for you.
At checkout, the checkout validation extension reads the shopper's presentment currency rate — a live rate that Shopify provides for the currency the shopper is buying in — and multiplies your base-currency threshold by that rate before comparing it to the cart total. So the comparison always happens in the shopper's own currency, using Shopify's current rate.
Worked example
Say your base currency is USD and you build a rule "subtotal greater than $50".
- A shopper checking out in USD triggers the rule at
$50, exactly as typed. - A shopper checking out in EUR has that
$50converted by their presentment rate. If the live rate makes$50worth about€46, the rule effectively triggers at roughly €46-equivalent, not a flat€50.
That last point is the crux of it: the EUR shopper's trigger is the converted value of your base amount, not the same number with a euro sign in front. The number looks "off" only because you're comparing it to the digits you typed, which were always USD.
A threshold typed as $50 will almost never trigger at a clean round number like 50 in another currency. The effective trigger floats with Shopify's live exchange rate, so it can shift slightly day to day. This is expected behavior — it keeps the value of your threshold consistent across currencies instead of locking it to one set of digits.
Storefront widget prices vs base prices
The same idea applies to the prices shown inside your storefront widgets and offers, but through a different source. Storefront widget prices are converted using Shopify's published currency rates (the same rates Shopify serves from currencies.js on its CDN), so the offer prices a shopper sees match the currency they've selected on your storefront.
This keeps displayed prices consistent with the rest of your store — but converted prices are rarely "clean."
| Base price (USD) | Example converted price (EUR) |
|---|---|
9.99 | 9.43 |
25.00 | 23.60 |
50.00 | 47.20 |
After conversion you'll often see prices like 9.43 instead of a tidy 9.99. That's normal — multiplying a round number by an exchange rate produces an uneven result.
You control how those converted prices are displayed with the Price Decimal Scale setting in Settings → Global settings:
- Enforce 2 Decimal Places always shows two digits (for example
47.2is shown as47.20). - Trim Unnecessary Zeros removes trailing zeros (for example
47.20is shown as47.2).
See Cart & checkout settings for the full rundown. Note that this setting changes only the display of the number — it does not change the converted amount itself or how thresholds are compared.
Markets targeting vs currency
It's easy to conflate the two, but market targeting and currency are not the same thing. A campaign's market filter is about which Shopify market the shopper is browsing in. It has nothing to do with which currency they've selected — currency follows the shopper's market/currency selector on your storefront, and selecting a market in your campaign targeting does not change pricing.
The market-list behavior has a sharp edge that trips people up:
| Market list state | Who the campaign reaches |
|---|---|
| No market list set (the default) | All markets |
| A list with one or more markets | Only shoppers in those markets |
| An empty list (exists but contains no markets) | No one |
An empty market list is not the same as no market list. No list means "everyone." An empty list means "nobody," and the campaign list shows a warning icon next to a campaign in that state because it can't reach a single shopper. If you open the market selector, you must leave at least one market in it — or clear it back to unset.
For the full breakdown of how market targeting layers on top of schedule and customer-tag filters, see Targeting by schedule, market & customer tags.
Per-region offers recipe
Because market targeting decides who sees a campaign and base-currency thresholds auto-convert per shopper, the cleanest way to run region-specific offers is one campaign per market, each with its own market list, combined with a currency-aware threshold you type once.
Build your US campaign
Create a campaign — for example "Free gift over $75" — and set its market list to your US market. Type the $75 threshold in your base currency, as normal.
Clone it for the EU market
Duplicate the campaign and change the market list to your EU market. Leave the threshold at $75 in base currency — do not retype it in euros. The app converts $75 to each EU shopper's presentment currency automatically at checkout.
Confirm each campaign targets one market
Make sure the US campaign's market list contains only the US market and the EU clone's contains only the EU market, so shoppers see exactly one offer for their region. Watch for the empty-list warning icon on either campaign.
The result: US shoppers see and trigger the offer at $75, EU shoppers trigger it at the converted euro-equivalent of $75 — same value, one number to maintain.
Gotchas
Testing across currencies
To confirm a money rule behaves correctly for international shoppers, reproduce a real shopper's context rather than testing only in your base currency.
Switch currency on the storefront
Use the country/currency selector on your storefront to switch into the currency you want to test. Widget prices should update to the converted values.
Test in an incognito window
Open your store in an incognito or private window so you're not carrying over a cached market, currency, or logged-in customer state from earlier testing.
Confirm the market assignment
Check that the storefront has placed you in the market you expect. Market targeting keys off the market, so if you're in the wrong one, a market-targeted campaign won't appear regardless of currency.
Build the cart to the converted trigger
Remember the trigger is the converted value of your base-currency threshold, not the same digits. Add items until the cart reaches that converted amount, then confirm the offer fires.
If a rule looks like it fires at the "wrong" amount during testing, compare it against the converted value of your base threshold for that currency, not the number you typed. They will differ by the exchange rate.
Related
Targeting by schedule, market & customer tags
How market targeting layers on top of schedule and customer-tag filters, including the empty-vs-unset gotcha.
Tier (volume) discounts
How minimum-subtotal tiers and fixed-amount discounts convert per currency.
Cart & checkout settings
Set Price Decimal Scale to control how converted prices are displayed.
Restriction or limiter not blocking
When a just-saved rule hasn't taken effect yet — metafield sync lag and how to force a Sync.
Theme compatibility & cart type
Find out whether your theme supports Llama Upsells, how the app grades theme support, and how your cart type changes behavior.
Why changes take time to appear
Why a change you saved in the admin can still show the old version on the storefront or checkout, and how to force a refresh.
