Skip to main content
Markets are coming with Spree 5.4 early March

Overview

Markets let you segment a single Store into distinct geographic regions, each with its own currency, locale, and set of countries. For example, an international store might define:
  • North America — USD, English, ships to US and Canada
  • Europe — EUR, German, ships to DE, FR, AT, NL
  • United Kingdom — GBP, English, ships to GB

Market Model Diagram

Key relationships:
  • A Store has many Markets — each market defines a selling region
  • A Market has many Countries through MarketCountry join records
  • A Country can belong to multiple Markets (across different stores)
  • Markets connect to the Pricing system via Spree::Pricing::Context

Market Attributes

AttributeDescriptionExample Value
nameHuman-readable name for the market. Must be unique per store.North America
currencyISO 4217 currency code used for this market.USD
default_localeDefault locale/language for this market.en
supported_localesComma-separated list of additional locales supported by this market.en,fr
tax_inclusiveWhether prices in this market include tax.false
defaultWhether this is the default market for the store. Only one market per store can be default.true
positionSort order for market resolution priority. Lower numbers = higher priority.0
deleted_atSoft-delete timestamp. Markets are never fully removed from the database.nil

Default Market

Spree::Current.market provides per-request access to the current market. The fallback chain is:
  1. Explicitly set market (e.g., resolved from the customer’s country)
  2. The store’s default market (store.default_market)
# Spree::Current.market is available in controllers, models, and services
Spree::Current.market
# => #<Spree::Market name: "North America", currency: "USD", ...>

# Set it explicitly per-request
Spree::Current.market = eu_market
The default market for a store is determined by the default flag, falling back to the first market by position:
store.default_market
# => #<Spree::Market name: "North America", default: true, ...>

Currency and Locale

When markets are configured, Spree::Current.currency derives from the current market rather than the store:
# Fallback chain for currency:
# 1. Explicitly set currency
# 2. Current market's currency
# 3. Store's default_currency
Spree::Current.currency
# => "USD" (from Spree::Current.market.currency)
Each market tracks its supported locales. Use supported_locales_list to get all available locales (always includes the default_locale):
market = Spree::Market.find_by(name: 'Europe')
market.supported_locales_list
# => ["de", "en", "fr"]
The store aggregates currencies and locales from all its markets:
store.supported_currencies_list
# => [#<Money::Currency USD>, #<Money::Currency EUR>, #<Money::Currency GBP>]

store.supported_locales_list
# => ["de", "en", "fr"]

Finding a Market by Country

Resolve which market a country belongs to using the class method:
country = Spree::Country.find_by(iso: 'DE')
market = Spree::Market.for_country(country, store: current_store)
# => #<Spree::Market name: "Europe", currency: "EUR", ...>
Or via the store convenience method:
current_store.market_for_country(country)
# => #<Spree::Market name: "Europe", currency: "EUR", ...>
When multiple markets contain the same country, the one with the lowest position is returned.

Countries Available for Checkout

Markets determine which countries are available during checkout. The countries_available_for_checkout method on Store aggregates countries from all markets:
current_store.countries_available_for_checkout
# => [#<Spree::Country iso: "AT">, #<Spree::Country iso: "CA">, ...]

Creating Markets

# Create a market with countries
na_market = current_store.markets.create!(
  name: 'North America',
  currency: 'USD',
  default_locale: 'en',
  countries: [usa, canada],
  default: true
)

eu_market = current_store.markets.create!(
  name: 'Europe',
  currency: 'EUR',
  default_locale: 'de',
  supported_locales: 'de,en,fr',
  tax_inclusive: true,
  countries: [germany, france, austria, netherlands]
)
When you run rails db:seed, Spree automatically creates a default market for each store

Store API

The Store API provides endpoints for fetching markets and resolving which market applies to a given country. All endpoints require a publishable API key.
MethodEndpointDescription
GET/api/v3/store/marketsList all markets for the current store
GET/api/v3/store/markets/:idGet a single market by prefixed ID
GET/api/v3/store/markets/resolve?country=:isoResolve which market a country belongs to
GET/api/v3/store/markets/:market_id/countriesList countries in a market
GET/api/v3/store/markets/:market_id/countries/:isoGet a country with its states
Returns all markets for the current store with their countries.
curl -H "Authorization: Bearer spree_pk_xxx" \
  https://example.com/api/v3/store/markets
{
  "data": [
    {
      "id": "mkt_k5nR8xLq",
      "name": "North America",
      "currency": "USD",
      "default_locale": "en",
      "supported_locales": ["en"],
      "tax_inclusive": false,
      "default": true,
      "countries": [
        { "iso": "US", "name": "United States" },
        { "iso": "CA", "name": "Canada" }
      ]
    }
  ]
}
Determine which market applies for a given country ISO code. Useful for auto-selecting the correct currency and locale when a customer’s location is known.
curl -H "Authorization: Bearer spree_pk_xxx" \
  https://example.com/api/v3/store/markets/resolve?country=DE
Returns the market object on success, or 404 if no market contains that country.
Returns countries belonging to a specific market. Useful for populating address dropdowns during checkout.
curl -H "Authorization: Bearer spree_pk_xxx" \
  https://example.com/api/v3/store/markets/mkt_k5nR8xLq/countries
{
  "data": [
    { "iso": "CA", "iso3": "CAN", "name": "Canada", "states_required": true, "zipcode_required": true },
    { "iso": "US", "iso3": "USA", "name": "United States", "states_required": true, "zipcode_required": true }
  ]
}

Pricing Integration

Markets integrate with the Pricing system through the Spree::Pricing::Context. The current market is automatically included in pricing resolution, enabling market-specific Price Lists via the Market Rule.
# Market is automatically part of the pricing context
context = Spree::Pricing::Context.new(
  variant: variant,
  currency: 'EUR',
  market: eu_market
)

# Price Lists with a Market Rule targeting eu_market will apply
price = variant.price_for(context)
See Pricing — Market Rule for details on configuring market-specific price lists.
  • Stores — Multi-store setup and configuration
  • Pricing — Price Lists, Price Rules, and the Pricing Context
  • Addresses — Countries, States, and Zones
  • Internationalization — Locales, currencies, and translations