Documentation Index
Fetch the complete documentation index at: https://mintlify.com/openshiporg/openfront/llms.txt
Use this file to discover all available pages before exploring further.
Openfront provides comprehensive multi-regional commerce capabilities, allowing you to sell in multiple countries with localized currencies, pricing, tax rates, and payment/shipping options.
Regional Architecture
The multi-regional system is built around three core concepts:
- Regions - Geographic markets with specific settings
- Countries - Individual countries assigned to regions
- Currencies - Supported currencies for pricing and payments
Region Configuration
A region represents a distinct market with its own currency, tax rules, and available services:
Region {
name: string // Display name (e.g., "North America")
currencyCode: string // ISO currency code (e.g., "usd")
taxRate: float // Default tax rate
taxCode: string // Tax jurisdiction code
giftCardsTaxable: boolean // Apply tax to gift cards
automaticTaxes: boolean // Enable automatic tax calculation
taxInclusivePricing: boolean // Prices include tax
metadata: json // Additional configuration
// Relationships
currency: → Currency
countries: [→ Country]
paymentProviders: [→ PaymentProvider]
fulfillmentProviders: [→ FulfillmentProvider]
shippingOptions: [→ ShippingOption]
orders: [→ Order]
carts: [→ Cart]
giftCards: [→ GiftCard]
discounts: [→ Discount]
taxRates: [→ TaxRate]
}
Creating Regions
Create regions in the admin dashboard or via GraphQL:
mutation CreateRegion {
createRegion(data: {
name: "Europe"
currencyCode: "eur"
taxRate: 0.20
automaticTaxes: true
taxInclusivePricing: true
countries: {
connect: [
{ id: "country_de" },
{ id: "country_fr" },
{ id: "country_es" }
]
}
currency: {
connect: { code: "EUR" }
}
}) {
id
name
currency {
code
symbol
}
countries {
name
iso2
}
}
}
Popular Region Templates
Openfront includes pre-configured region templates:
// features/platform/regions/constants/popular-regions.ts
export const POPULAR_REGIONS = [
{
name: "United States",
currencyCode: "USD",
countries: ["US"],
taxRate: 0.0,
taxInclusivePricing: false,
},
{
name: "European Union",
currencyCode: "EUR",
countries: ["DE", "FR", "IT", "ES", "NL", "BE"],
taxRate: 0.20,
taxInclusivePricing: true,
},
{
name: "United Kingdom",
currencyCode: "GBP",
countries: ["GB"],
taxRate: 0.20,
taxInclusivePricing: true,
},
{
name: "Canada",
currencyCode: "CAD",
countries: ["CA"],
taxRate: 0.13,
taxInclusivePricing: false,
},
];
Currency Management
Currencies define how prices are displayed and processed:
Currency {
code: string // ISO 4217 code (USD, EUR, JPY)
symbol: string // Display symbol ($, €, ¥)
symbolNative: string // Native symbol
name: string // Full name
includesTax: boolean // Default tax inclusion
noDivisionCurrency: boolean // No decimal places (JPY, KRW)
regions: [→ Region]
orders: [→ Order]
carts: [→ Cart]
payments: [→ Payment]
moneyAmounts: [→ MoneyAmount]
}
Creating Currencies
mutation CreateCurrency {
createCurrency(data: {
code: "EUR"
symbol: "€"
symbolNative: "€"
name: "Euro"
includesTax: true
noDivisionCurrency: false
}) {
id
code
symbol
}
}
No-Division Currencies
Some currencies don’t use decimal places (cents):
const NO_DIVISION_CURRENCIES = [
"JPY", // Japanese Yen
"KRW", // Korean Won
"VND", // Vietnamese Dong
"CLP", // Chilean Peso
"PYG", // Paraguayan Guarani
// ... and others
];
Openfront handles these automatically:
- Amounts stored as whole numbers (not cents)
- No decimal formatting in display
- Proper API formatting for payment providers
Regional Pricing
Products can have different prices in each region:
MoneyAmount {
amount: integer // Price in smallest currency unit
currencyCode: string // Currency for this price
minQuantity: integer // Bulk pricing minimum
maxQuantity: integer // Bulk pricing maximum
variant: → ProductVariant
region: → Region
currency: → Currency
priceList: → PriceList
}
Setting Regional Prices
mutation UpdateVariantPrices {
updateProductVariant(
where: { id: "variant_id" }
data: {
prices: {
create: [
{
amount: 9900
currency: { connect: { code: "USD" } }
region: { connect: { id: "region_us" } }
},
{
amount: 8900
currency: { connect: { code: "EUR" } }
region: { connect: { id: "region_eu" } }
},
{
amount: 7900
currency: { connect: { code: "GBP" } }
region: { connect: { id: "region_uk" } }
}
]
}
}
) {
id
prices {
amount
currency { code symbol }
region { name }
}
}
}
Calculated Pricing
Openfront provides virtual fields for formatted pricing:
// Product variant with calculated pricing
const variant = {
id: "variant_123",
sku: "SHIRT-BLK-M",
prices: [
{ amount: 9900, currency: "USD" },
{ amount: 8900, currency: "EUR" },
],
// Virtual field
calculatedPrice: {
calculatedAmount: 9900,
originalAmount: 9900,
currencyCode: "USD",
formattedPrice: "$99.00",
}
};
Tax Configuration
Region-Level Taxes
Configure default tax rates at the region level:
Region {
taxRate: 0.20 // 20% VAT
taxCode: "EU_VAT" // Tax jurisdiction
giftCardsTaxable: false // Exempt gift cards
automaticTaxes: true // Auto-calculate taxes
taxInclusivePricing: true // Prices include tax
}
Product-Level Tax Rates
Override taxes for specific products:
mutation CreateTaxRate {
createTaxRate(data: {
rate: 0.10
code: "REDUCED_VAT"
name: "Reduced VAT Rate"
region: { connect: { id: "region_eu" } }
products: {
connect: [{ id: "product_books" }]
}
}) {
id
rate
name
}
}
Tax Calculation
Taxes are calculated based on region settings:
// Tax-inclusive pricing (UK, EU)
const priceWithTax = 10000; // £100.00
const taxRate = 0.20; // 20% VAT
const priceBeforeTax = Math.round(priceWithTax / (1 + taxRate));
// Result: £83.33 + £16.67 VAT = £100.00
// Tax-exclusive pricing (US, CA)
const priceBeforeTax = 10000; // $100.00
const taxRate = 0.0825; // 8.25% sales tax
const taxAmount = Math.round(priceBeforeTax * taxRate);
// Result: $100.00 + $8.25 tax = $108.25
Country Configuration
Countries are assigned to regions:
Country {
iso2: string // ISO 3166-1 alpha-2 (US, GB, DE)
iso3: string // ISO 3166-1 alpha-3 (USA, GBR, DEU)
numCode: integer // ISO numeric code
name: string // Display name
displayName: string // Localized name
region: → Region
taxRates: [→ TaxRate]
}
Creating Countries
mutation CreateCountry {
createCountry(data: {
iso2: "DE"
iso3: "DEU"
numCode: 276
name: "Germany"
displayName: "Deutschland"
region: { connect: { id: "region_eu" } }
}) {
id
name
iso2
region { name }
}
}
Regional Services
Payment Provider Assignment
Assign payment providers to specific regions:
mutation AssignPaymentProvider {
updatePaymentProvider(
where: { id: "pp_stripe" }
data: {
regions: {
connect: [
{ id: "region_us" },
{ id: "region_eu" },
{ id: "region_uk" }
]
}
}
) {
id
name
regions { name }
}
}
Shipping Options by Region
Configure region-specific shipping:
mutation CreateShippingOption {
createShippingOption(data: {
name: "DHL Express"
priceType: "calculated"
region: { connect: { id: "region_eu" } }
provider: { connect: { id: "sp_shippo" } }
requirements: {
create: [
{
type: "min_subtotal"
amount: 5000 # Free shipping over €50
}
]
}
}) {
id
name
region { name }
}
}
Storefront Integration
Detect and display correct region in the storefront:
URL Structure
https://store.com/us/products/shirt
https://store.com/gb/products/shirt
https://store.com/de/products/shirt
Region Detection
// app/(storefront)/[countryCode]/layout.tsx
export default async function CountryLayout({
params,
children,
}: {
params: { countryCode: string }
children: React.ReactNode
}) {
const { countryCode } = params;
// Get region for country
const region = await getRegionByCountry(countryCode);
if (!region) {
notFound();
}
return (
<RegionProvider region={region}>
{children}
</RegionProvider>
);
}
Price Display
// Automatically show prices in correct currency
function ProductPrice({ variant, region }) {
const price = variant.prices.find(
p => p.region.id === region.id
);
if (!price) return null;
return (
<div className="text-2xl font-bold">
{formatPrice(price.amount, price.currency)}
</div>
);
}
function formatPrice(amount: number, currency: Currency) {
const value = currency.noDivisionCurrency
? amount
: amount / 100;
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency.code,
}).format(value);
}
Multi-Regional Cart Flow
Determine region from URL or IP geolocation:
const countryCode = params.countryCode; // From URL: /us/
const region = await getRegionByCountry(countryCode);
Initialize cart with correct region:
mutation {
createCart(data: {
region: { connect: { id: "region_us" } }
currency: { connect: { code: "USD" } }
}) {
id
region { name }
currency { code symbol }
}
}
Add Products with Regional Pricing
Products automatically use correct prices:
mutation {
addLineItem(cartId: "cart_id", variantId: "variant_id", quantity: 1) {
id
lineItems {
title
unitPrice # Automatically uses USD price
quantity
formattedTotal
}
}
}
Taxes calculated based on region:
query {
cart(where: { id: "cart_id" }) {
subtotal # Product total
tax # Calculated tax
total # Final total
region {
taxRate
taxInclusivePricing
}
}
}
Best Practices
Region Strategy
- Create regions based on business operations, not just geography
- Group countries with similar tax rules and shipping
- Consider currency zones (Eurozone, ASEAN, etc.)
- Plan for region-specific payment providers
Pricing Strategy
- Set prices based on local market conditions
- Consider purchasing power parity
- Account for local taxes in pricing
- Use price lists for customer-specific pricing
Tax Compliance
- Research local tax requirements
- Use tax-inclusive pricing where required (EU, UK)
- Configure correct tax codes
- Consider using tax automation services
Currency Display
- Show prices in customer’s currency
- Use proper currency formatting
- Handle no-division currencies correctly
- Display currency symbols appropriately