Multi-currency pricing via API

Type
Concept
Applies to
Medusa v2.x
Updated

Short answer

Medusa handles multi-currency pricing through its Pricing Module: each variant has a price set with one price per currency, plus optional prices scoped by region, customer group or quantity. The Store API returns a calculated price for the shopper's region and country, with and without tax. Medusa does not convert currencies automatically.

How Medusa's price model handles multiple currencies

Medusa's Pricing Module links every product variant and shipping option to a price set: a collection of prices for one resource. Each price has a Copy to clipboardcurrency_code and an Copy to clipboardamount stored in major units, so Copy to clipboard20 means 20.00 (Pricing Concepts).

Prices can carry rules and quantity tiers:

  • Price rules limit a price to a context, such as a Copy to clipboardregion_id, a customer group or a postal code.
  • Tiers use Copy to clipboardmin_quantity and Copy to clipboardmax_quantity to change the price by quantity.
  • Price lists group prices of type Copy to clipboardsale or Copy to clipboardoverride that apply only when their rules match, optionally within a date range.
Pricing needMedusa featureStatus
One price per currencyPrices with a currency_code in the price setNative
Different price for one region in the same currencyPrice with a region_id ruleNative
Customer group or B2B pricesPrice lists with rulesNative
Quantity breaksTiered pricesNative
Automatic exchange-rate conversionNot built in; update prices from a rate feed in a scheduled jobCustom

How to set multi-currency prices with the Admin API

You send prices per currency when you create or update a variant. A price with Copy to clipboardrules applies only in that context. This body for Copy to clipboardPOST /admin/products/{id}/variants follows the Admin API schema:

{
"title": "Large",
"prices": [
{ "currency_code": "usd", "amount": 40 },
{ "currency_code": "eur", "amount": 39 },
{ "currency_code": "eur", "amount": 42, "rules": { "region_id": "reg_123" } }
]
}

Customers in region Copy to clipboardreg_123 see 42 EUR. Other euro customers see 39 EUR.

How the Store API returns calculated prices

The Store API resolves one price per variant for the shopper's context. Add Copy to clipboard*variants.calculated_price to Copy to clipboardfields on the List Products or Get Product routes, and pass at least one context parameter (Retrieve Product Variant's Prices):

  • Copy to clipboardregion_id: the region, which also sets the currency.
  • Copy to clipboardcountry_code and Copy to clipboardprovince: needed for prices with tax.
  • Copy to clipboardcart_id: uses the cart's region and shipping address instead.
const { products } = await sdk.store.product.list({
fields: "*variants.calculated_price",
region_id: region.id,
country_code: "fr",
})

Each variant gets Copy to clipboardcalculated_amount, Copy to clipboardoriginal_amount, Copy to clipboardcurrency_code, and, when both region and country are passed, Copy to clipboardcalculated_amount_with_tax and Copy to clipboardcalculated_amount_without_tax. If the request is authenticated, the customer's groups are used to select group prices automatically.

How to calculate prices in backend code

In custom API routes or workflows, you pass the currency and region directly with Query (Get Variant Prices):

const { data: products } = await query.graph({
entity: "product",
fields: ["variants.calculated_price.*"],
filters: { id: "prod_123" },
context: {
variants: {
calculated_price: QueryContext({ region_id: "reg_123", currency_code: "eur" }),
},
},
})

How Medusa selects a price

Medusa's Copy to clipboardcalculatePrices returns a calculated price and an original price for each price set (Prices Calculation):

  1. With no rules in the context, it selects the default price for the currency.
  2. If a price matches every rule in the context, it selects that price.
  3. Otherwise, it selects the price that matches the most rules.

A matching price list price becomes the calculated price. For sale price lists, the original price stays the regular price so the storefront can show a strike-through; for override price lists, the original price equals the calculated price.

Medusa compared with commercetools price selection

Both platforms store explicit prices per currency and pick one per request. They differ in which dimensions a price can be scoped by.

AspectMedusacommercetools
Price scopeCurrency plus any rules, such as region, customer group or postal codeCurrency, country, customer group and channel
Selection inputregion_id, country_code or cart_id on the Store APIpriceCurrency, priceCountry, priceCustomerGroup and priceChannel on product projections
FallbackExact match, then the price matching the most rulesA fixed order of eight scope combinations, with time-bound prices first
Quantity tiersmin_quantity and max_quantityTiers by minimum quantity per line item

commercetools facts come from its pricing overview and Product Projections docs.

When Medusa is not the right choice

  • You want prices converted from one base currency automatically. Medusa needs a price per currency or a custom sync job. Shopify converts prices at the current market exchange rate by default, and BigCommerce auto-converts prices using an exchange rate the merchant sets or updates through its API.
  • You need a search index sorted by price in many currencies with no setup. Medusa's Pricing Module calculates a price for one currency per query, so a search index needs a price field per currency (Index Prices for Multiple Currencies).

Common questions

How do I get a product's price in a specific currency from the Medusa Store API?

Pass the region_id of a region that uses that currency, with fields=*variants.calculated_price. Medusa returns each variant's calculated_price in the region's currency.

Can Medusa have two prices in the same currency for different regions?

Yes. Add a price with a region_id rule. Medusa selects it for that region and uses the default price for the currency elsewhere.

Does Medusa support customer-specific prices in multiple currencies?

Yes. Medusa price lists hold prices per currency with rules such as customer group, and the Store API applies them when the customer is authenticated.

How is Medusa's price selection different from commercetools?

commercetools scopes prices by currency, country, customer group and channel and walks a fixed fallback order. Medusa scopes prices by currency plus any rules and picks the price that matches the most rules.