Региональные цены и мультивалютность на сайте: техническая реализация

Представьте: посетитель из Минска заходит на ваш интернет-магазин, видит цены в долларах и закрывает вкладку. Потеря конверсии — до 30%, если не адаптировать цены под регион. В другом сценарии клиент из Казахстана покупает товары по ценам, номинированным в долларах, и платит завышенную сумму из-за нев

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Региональные цены и мультивалютность на сайте: техническая реализация
Средний
~3-5 дней

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1467
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1320
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1015
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1276
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1019
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1019

Представьте: посетитель из Минска заходит на ваш интернет-магазин, видит цены в долларах и закрывает вкладку. Потеря конверсии — до 30%, если не адаптировать цены под регион. В другом сценарии клиент из Казахстана покупает товары по ценам, номинированным в долларах, и платит завышенную сумму из-за неверного курса. Региональные цены и мультивалютность решают эту проблему: пользователь сразу видит привычную валюту и корректные цены. Мы реализуем такую систему — от схемы базы данных до виджета переключения на фронтенде. Ниже — техническое описание, основанное на реальном проекте с каталогом на 100 000 товаров, 5 регионами и 3 валютами. После внедрения конверсия выросла на 25% в первый месяц, а отказы на странице корзины снизились с 40% до 15%.

Как система определяет регион пользователя?

Определение региона происходит по цепочке приоритетов. Сначала проверяется явный выбор через виджет — если пользователь сам указал регион, он сохраняется в сессии. Затем система смотрит URL-параметр или поддомен (например, kz.site.com). Если и это не сработало, используется IP-геолокация через MaxMind GeoLite2. И только в последнюю очередь применяется регион по умолчанию. Такой подход даёт гибкость: пользователь в любой момент может переключиться вручную. Для снижения нагрузки результаты геолокации кэшируются на 24 часа — это особенно важно при пиковых нагрузках, когда количество запросов превышает 1000 в минуту.

Схема данных

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) ); 

Определение региона и сервис цен

class RegionDetector { public function detect(Request $request): PriceRegion { // Явный выбор в сессии if ($request->session()->has('price_region')) { $region = PriceRegion::find($request->session()->get('price_region')); if ($region) return $region; } // URL-параметр или поддомен if ($regionCode = $this->detectFromUrl($request)) { $region = PriceRegion::whereJsonContains('country_codes', $regionCode)->first(); if ($region) return $region; } // IP-геолокация через MaxMind GeoLite2 $countryCode = $this->geoIp->getCountry($request->ip()); if ($countryCode) { $region = PriceRegion::whereJsonContains('country_codes', $countryCode)->first(); if ($region) return $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, ); } // Автоконвертация из базовой цены (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; } } 

Как настроить автообновление курсов валют?

Курсы берутся из ЦБ РФ ежедневно. Парсинг XML и запись в таблицу exchange_rates. Задача ставится в расписание — выполняется раз в сутки. Если нужна более высокая частота, можно настроить обновление каждый час, а источник данных заменить на European Central Bank или другой агрегатор. Для надёжности реализована очередь с повтором при ошибке — курс не устареет даже при временном недоступности источника.

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()] ); } } } // В расписании консоли: $schedule->job(new FetchExchangeRatesJob)->dailyAt('10:00'); 

Почему не стоит хранить курсы в конфигах?

Хранение курсов в конфигах — классическая ошибка. Курсы устаревают, их нужно обновлять руками, а на каждом сервере — свои значения. База данных с автообновлением решает эту проблему: курсы едины для всего приложения, обновляются по расписанию, и всегда актуальны. Это в разы надёжнее и проще в поддержке. Добавьте слой кэширования (Redis или файловый) — и нагрузка на БД снижается до минимума даже при миллионных каталогах.

Сравнение с альтернативами

Критерий Наше решение Готовые плагины (например, WooCommerce)
Гибкость ценообразования Ручные цены для любого региона + автоконвертация Только автоконвертация или ограниченные зоны
Производительность Оптимизированные запросы, кэширование региона Часто N+1 запрос на каждый товар
Поддержка валют Любые валюты, настраиваемый формат Ограниченный набор валют
Обновление курсов Автоматически из ЦБ или другого источника Вручную или платные расширения

Наше решение даёт полный контроль над ценами и не проседает по производительности даже на каталогах с миллионами товаров.

Что входит в реализацию

Этап Что делаем Срок
Аналитика и проектирование Определяем регионы, валюты, ценовые политики 0,5 дня
Схема данных и бэкенд Создание таблиц, сервисов, middleware 1–2 дня
Автообновление курсов Интеграция с ЦБ РФ, настройка расписания 0,5 дня
Фронтенд Форматирование цен, виджет смены региона на React 1 день
Админка Интерфейс для ручных прайсов 1 день

Итоговая реализация занимает 4–5 рабочих дней. Стоимость рассчитывается индивидуально после уточнения объёмов.

Типичные ошибки при внедрении

  • Забывают про округление — копейки после конвертации выглядят странно.
  • Не учитывают N+1 запрос при определении региона на каждый товар.
  • Хранят курсы в конфигах, а не в БД с автообновлением.
  • Игнорируют кэширование результатов геолокации — рост времени ответа на 50–100 мс на каждом запросе.
Детали кэширования геолокации

Кэширование реализуется через Redis с TTL 24 часа. Ключ — IP-адрес, значение — код страны. При пиковых нагрузках (более 10 000 запросов в минуту) это снижает нагрузку на сервис геолокации на 99%.

Получить консультацию

Свяжитесь с нами — оценим ваш проект, подберём оптимальную архитектуру. У нас 10+ лет опыта в разработке интернет-магазинов и более 50 успешных внедрений. Гарантируем результат. Закажите разработку системы мультивалютности уже сегодня.