Hanzo
CommerceGuides

Multi-Region Commerce

Configure regions, currencies, taxes, and localized pricing

This guide covers setting up multi-region selling in Hanzo Commerce, including region configuration, currency handling, per-region tax rules, and localized product pricing.

Overview

Regions control how your store operates in different markets:

Region → Currency, Tax Rules, Payment Providers, Fulfillment Options
       → Countries (exclusive: one country per region)
       → Localized Pricing

Step 1: Create Regions

Define a region for each market you sell into:

curl -X POST https://api.hanzo.ai/region \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "North America",
    "currency": "USD",
    "taxIncluded": false,
    "countries": [
      {"code": "US"},
      {"code": "CA"}
    ],
    "paymentProviders": ["stripe"],
    "fulfillmentProviders": ["manual"]
  }'
const northAmerica = await commerce.regions.create({
  name: 'North America',
  currency: 'USD',
  taxIncluded: false,
  countries: [{ code: 'US' }, { code: 'CA' }],
  paymentProviders: ['stripe'],
  fulfillmentProviders: ['manual']
})

const europe = await commerce.regions.create({
  name: 'Europe',
  currency: 'EUR',
  taxIncluded: true,
  countries: [{ code: 'DE' }, { code: 'FR' }, { code: 'IT' }, { code: 'ES' }],
  paymentProviders: ['stripe'],
  fulfillmentProviders: ['manual', 'dhl']
})

Each country can only belong to one region. The taxIncluded flag determines whether prices displayed to customers include tax (common in Europe) or exclude tax (common in the US).

Step 2: Currency Handling

Each region has a single currency. Prices are stored in the smallest currency unit (cents):

RegionCurrency$29.99 stored as
North AmericaUSD2999
EuropeEUR2999
JapanJPY2999 (no decimals)
UKGBP2999

When querying products, pass the region to get localized prices:

curl "https://api.hanzo.ai/product?regionId=reg_europe" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Step 3: Localized Pricing

Set different prices per region for the same product:

curl -X POST https://api.hanzo.ai/product/prod_abc123/price \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "prices": [
      {
        "regionId": "reg_north_america",
        "amount": 2999,
        "currency": "USD"
      },
      {
        "regionId": "reg_europe",
        "amount": 2799,
        "currency": "EUR"
      },
      {
        "regionId": "reg_japan",
        "amount": 4500,
        "currency": "JPY"
      }
    ]
  }'
await commerce.products.setPrices('prod_abc123', {
  prices: [
    { regionId: northAmerica.id, amount: 2999, currency: 'USD' },
    { regionId: europe.id, amount: 2799, currency: 'EUR' },
    { regionId: japan.id, amount: 4500, currency: 'JPY' }
  ]
})

Step 4: Tax Configuration Per Region

US: Tax-Exclusive Pricing

For the US, taxes are calculated at checkout and added to the displayed price:

curl -X POST https://api.hanzo.ai/taxregion \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "United States",
    "countryCode": "US",
    "defaultTaxRate": 0,
    "providerId": "txprov_auto"
  }'

The automatic tax provider calculates state, county, and city taxes based on the shipping address.

EU: Tax-Inclusive Pricing (VAT)

For Europe, prices include VAT. Create tax rates for each country:

curl -X POST https://api.hanzo.ai/taxrate \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "taxRegionId": "txreg_eu",
    "name": "Germany VAT",
    "rate": 0.19,
    "code": "DE-VAT"
  }'
curl -X POST https://api.hanzo.ai/taxraterule \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "taxRateId": "txrate_de_vat",
    "type": "province",
    "reference": "DE"
  }'

Common EU VAT Rates

CountryStandard Rate
Germany19%
France20%
Italy22%
Spain21%
Netherlands21%

Associate stock locations with regions for fulfillment routing:

curl -X POST https://api.hanzo.ai/region/reg_europe/stocklocation \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "stockLocationIds": ["sloc_berlin"]
  }'

This ensures European orders are routed to the Berlin warehouse.

Detecting Customer Region

Automatically detect a customer's region from their IP or shipping address:

curl "https://api.hanzo.ai/region/detect?country=DE" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "id": "reg_europe",
  "name": "Europe",
  "currency": "EUR",
  "taxIncluded": true
}

In your storefront, detect on page load:

const region = await commerce.regions.detect({ country: customerCountry })

// Use region for all subsequent product and cart calls
const products = await commerce.products.list({ regionId: region.id })
const cart = await commerce.carts.create({ regionId: region.id })

Managing Countries

Add or remove countries from a region as your business expands:

# Add countries
curl -X POST https://api.hanzo.ai/region/reg_europe/countries \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "countries": [{"code": "PL"}, {"code": "CZ"}]
  }'

# Remove a country
curl -X DELETE https://api.hanzo.ai/region/reg_europe/countries/CZ \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
await commerce.regions.addCountries(europe.id, {
  countries: [{ code: 'PL' }, { code: 'CZ' }]
})

await commerce.regions.removeCountry(europe.id, 'CZ')

Removing a country from a region makes your store unavailable in that market. Existing orders from that country are not affected, but new orders cannot be placed.

How is this guide?

Last updated on

On this page