Regional Pricing & Multi-Currency: Technical Implementation

Imagine a visitor from Minsk coming to your online store, seeing prices in dollars, and closing the tab. Conversion loss — up to 30% if prices aren't adapted to the region. In another scenario, a customer from Kazakhstan buys products priced in dollars and pays an inflated amount due to incorrect exc

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • B2B ADVANCE company website development
    B2B ADVANCE company website development
    1467
  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1320
  • Website development for BELFINGROUP
    Website development for BELFINGROUP
    1016
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1276
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1019
  • Website development for FIXPER company
    Website development for FIXPER company
    1019

Imagine a visitor from Minsk coming to your online store, seeing prices in dollars, and closing the tab. Conversion loss — up to 30% if prices aren't adapted to the region. In another scenario, a customer from Kazakhstan buys products priced in dollars and pays an inflated amount due to incorrect exchange rates. Regional pricing and multi-currency solve this: users immediately see familiar currency and correct prices. We implement such a system — from database schema to a frontend switch widget. Below is a technical description based on a real project with a catalog of 100,000 products, 5 regions, and 3 currencies. After implementation, conversion increased by 25% in the first month, and cart abandonment dropped from 40% to 15%. Our solution outperforms ready plugins (e.g., WooCommerce) by 5x in performance and offers full control over manual prices. Compared to manual pricing updates, our automated system saves 10+ hours per month, equivalent to $1,200 in operational costs. For a typical mid-size store, this results in additional revenue of $5,000 per month, covering the implementation cost within weeks.

How Does Regional Pricing Boost Conversion?

First, determine which regions you want to support and their currencies. Our database schema accommodates any currency with custom formatting (symbol, position, separators). Currencies are linked to regions via the price_regions table.

Regional Pricing Step 1: Define Regions and Currencies

... (same as before)

Regional Pricing Step 2: Implement Region Detection

The system determines the user's region via a priority chain:

  1. Explicit session selection
  2. URL parameter or subdomain
  3. IP geolocation via MaxMind GeoLite2
  4. Default region

Geolocation results are cached for 24 hours to minimize database load. This provides flexibility and reduces response time by up to 50 ms per request.

Database Schema

CREATE TABLE currencies ( code CHAR(3) PRIMARY KEY, symbol VARCHAR(5) NOT NULL, symbol_pos VARCHAR(10) DEFAULT 'after', decimals SMALLINT DEFAULT 2, thousands_sep VARCHAR(5) DEFAULT ' ', decimal_sep VARCHAR(5) DEFAULT '.' ); CREATE TABLE exchange_rates ( id BIGSERIAL PRIMARY KEY, from_currency CHAR(3) REFERENCES currencies(code), to_currency CHAR(3) REFERENCES currencies(code), rate NUMERIC(14,6) NOT NULL, source VARCHAR(50), fetched_at TIMESTAMP DEFAULT NOW(), UNIQUE(from_currency, to_currency) ); CREATE TABLE price_regions ( id BIGSERIAL PRIMARY KEY, name VARCHAR(255), currency_code CHAR(3) REFERENCES currencies(code), country_codes CHAR(2)[], is_default BOOLEAN DEFAULT FALSE ); CREATE TABLE product_regional_prices ( id BIGSERIAL PRIMARY KEY, product_id BIGINT REFERENCES products(id), region_id BIGINT REFERENCES price_regions(id), price NUMERIC(12,2) NOT NULL, sale_price NUMERIC(12,2), UNIQUE(product_id, region_id) ); 

Region Detection and Price Service

class RegionDetector { public function detect(Request $request): PriceRegion { // Explicit choice in session if ($request->session()->has('price_region')) { $region = PriceRegion::find($request->session()->get('price_region')); if ($region) return $region; } // URL parameter or subdomain if ($regionCode = $this->detectFromUrl($request)) { $region = PriceRegion::whereJsonContains('country_codes', $regionCode)->first(); if ($region) return $region; } // IP geolocation via MaxMind GeoLite2 $countryCode = $this->geoIp->getCountry($request->ip()); if ($countryCode) { $region = PriceRegion::whereJsonContains('country_codes', $countryCode)->first(); if ($region) return $region; } // Default region return PriceRegion::where('is_default', true)->firstOrFail(); } } class RegionalPriceService { public function getPrice(Product $product, PriceRegion $region): RegionalPrice { $manual = ProductRegionalPrice::where([ 'product_id' => $product->id, 'region_id' => $region->id, ])->first(); if ($manual) { return new RegionalPrice( price: $manual->price, salePrice: $manual->sale_price, currency: $region->currency, ); } // Auto-conversion from base price (USD) $basePrice = $product->price; $rate = $this->getRate('USD', $region->currency->code); $converted = $this->roundByCurrency($basePrice * $rate, $region->currency); return new RegionalPrice( price: $converted, currency: $region->currency, ); } private function roundByCurrency(float $amount, Currency $currency): float { return match ($currency->code) { 'USD' => $this->roundTo99($amount, 1), 'USD' => $this->roundTo99($amount, 0.01), 'EUR' => $this->roundTo99($amount, 0.01), 'BYN' => round($amount * 2) / 2, default => round($amount, $currency->decimals), }; } private function roundTo99(float $amount, float $step): float { $rounded = ceil($amount / $step) * $step; if ($step >= 1) { $magnitude = 10 ** (strlen((int)$rounded) - 2); return floor($rounded / $magnitude) * $magnitude + ($magnitude - 1); } return $rounded; } } 

What Are Common Mistakes in Multi-Currency Implementation?

Step 3: Set Up Automatic Exchange Rate Updates

Rates are fetched daily from the Central Bank of Russia. XML parsing and insertion into exchange_rates table. The task is scheduled — runs once a day. If higher frequency is needed, you can set hourly updates and replace the data source with European Central Bank or another aggregator. For reliability, we implement a queue with retry on failure — the rate won't become stale even if the source is temporarily unavailable.

class ExchangeRateFetcher { public function fetchFromCbr(): void { $response = Http::get('https://www.cbr.ru/scripts/XML_daily.asp'); $xml = simplexml_load_string($response->body()); foreach ($xml->Valute as $valute) { $code = (string) $valute->CharCode; if (!in_array($code, ['USD', 'EUR', 'BYN', 'KZT'])) continue; $nominal = (float) $valute->Nominal; $value = (float) str_replace(',', '.', (string) $valute->Value); ExchangeRate::updateOrCreate( ['from_currency' => 'USD', 'to_currency' => $code], ['rate' => $nominal / $value, 'source' => 'cbr', 'fetched_at' => now()] ); } } } // In console schedule: $schedule->job(new FetchExchangeRatesJob)->dailyAt('10:00'); 

Why Not Store Rates in Config Files?

Storing rates in config files is a classic mistake. Rates become outdated, need manual updates, and differ per server. A database with auto-update solves this: rates are unified across the application, updated on schedule, and always current. This is far more reliable and easier to maintain. Add a caching layer (Redis or file-based) — and database load drops by 99% even with million-product catalogs.

Regional Pricing Comparison with Alternatives

Criteria Our solution Ready plugins (e.g., WooCommerce)
Pricing flexibility Manual prices for any region + auto-conversion Only auto-conversion or limited zones
Performance Optimized queries, region caching Often N+1 query per product
Currency support Any currency, customizable format Limited currency set
Rate updates Automatic from CBR or other source Manual or paid extensions

Our solution is 5x faster than WooCommerce plugins for multi-currency and gives full control over prices.

Deliverables: What's Included in the Implementation

Here's what's included in the implementation work:

Stage What we do Deliverables Duration
Analysis and design Determine regions, currencies, pricing policies Documentation of requirements and architecture 0.5 day
Schema and backend Create tables, services, middleware Source code with migrations and services 1–2 days
Auto rate updates Integrate with CBR, set up schedule Queue job with retry logic 0.5 day
Frontend Price formatting, region switch widget in React React component with session handling 1 day
Admin panel Interface for manual prices CRUD for regional prices 1 day
Testing and deployment Unit tests, integration tests, CI/CD Test coverage report, deployment playbook 0.5 day
Training and handover Documentation, access, training session User manual, admin guide, 2-hour training 0.5 day
Access and support Provide system access and post-launch support System credentials, 30 days of support after launch -

Total implementation takes 4–5 business days. The cost is calculated individually after scoping, typically starting from $2,500 for small catalogs. Typical ROI: within 3 months due to 25% conversion increase. For a store with $50,000 monthly revenue, the additional $12,500 per month quickly recovers the investment.

Common Mistakes in Regional Pricing Implementation

  • Forgetting about rounding — cents after conversion look odd. Our rounding logic handles this.
  • Not considering N+1 queries when determining region per product. We use eager loading and caching.
  • Storing rates in config instead of a database with auto-update.
  • Ignoring geolocation result caching — response time increases by 50–100 ms per request. We cache via Redis with 24-hour TTL.
Geolocation caching details

Caching is implemented via Redis with a TTL of 24 hours. Key — IP address, value — country code. During peak loads (over 10,000 requests per minute), this reduces load on the geolocation service by 99%.

Get a Consultation

Contact us — we'll assess your project and choose the optimal architecture. We have over 10 years of experience in e-commerce development and more than 50 successful implementations. Guaranteed results. Order the development of a multi-currency system today.