Skip to content
New Webhooks added: Inventory and Order modifications. Check the changelog →
Cartly Developers

Multi-Currency Storefronts

Automatic exchange rates via OpenExchangeRates, per-currency markup and rounding, a Liquid fx_context bundle, and a zero-decimal-currency-aware rounding pipeline.

Overview

Multi-Currency lets visitors see prices in their preferred currency. Cartly fetches live rates from OpenExchangeRates every 6 hours via a Temporal cron workflow. Each currency is configured with a mode (auto or manual), a markup percentage (0-25%), and a rounding strategy.

Backend Modules

  • internal/fxrates — OpenExchangeRates provider, Temporal cron, ApplySnapshot, rounding pipeline
  • internal/markets/pricing.go — per-rate inherit/override merge for rounding strategy
  • cmd/migrate-fx-rates-nullable — legacy 0/none → NULL backfill (idempotent, already ran on prod)

Admin API

  • GET /admin/currencies — list all currencies with rates and stale flags
  • PUT /admin/currencies/:code — upsert a currency (mode, markup_pct, rounding_strategy, active)
  • DELETE /admin/currencies/:code — remove a non-base currency
  • POST /admin/currencies/refresh — force-refresh rates (rate-limited 5/min/IP)

Liquid Context

Every Liquid render receives shop.fx_context as an engine global (available inside {% render %} snippets too, not just the parent template). Shape: { display_currency, rates: { [code]: float }, supported_currencies: [{ code, rate, markup_pct, rounding_strategy }], stale: bool }.

Rounding Strategies

  • none — raw converted amount
  • nearest_99 — e.g. €18.40 → €17.99
  • nearest_95 — e.g. €18.40 → €17.95
  • nearest_whole — e.g. €18.40 → €18

A per-currency row with rounding_strategy: "none" is respected even when the shop default is nearest_99 (explicit override wins).

Zero-Decimal Currencies

JPY, KRW, HUF, ISK, CLP, VND, BIF, RWF, XAF, XOF skip the cents-math rounding step. A ¥3,714 price stays ¥3,714.

Storefront JS

boilerplate/assets/fx-converter.js exposes window.cartlyFX.convertCents(amount, rate, markup_pct, rounding_strategy, currency) and window.cartlyFX.applyMarkupAndRounding(amount, markup_pct, rounding_strategy, currency) for client-side currency pickers.

Full guide →