Реализация мультивалютности на сайте: цены, оплата, отчётность

Плохо реализованная мультивалютность порождает расхождения в бухгалтерии, баги с округлением и проблемы с НДС. Крупный интернет-магазин потерял 1.5 млн руб. из-за того, что цены округлялись вниз в пользу покупателя. Наш опыт позволяет избежать этих ошибок и гарантирует точность финансовых данных. Бо

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

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

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

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

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

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

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

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1414
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    982
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1241
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    994

Плохо реализованная мультивалютность порождает расхождения в бухгалтерии, баги с округлением и проблемы с НДС. Крупный интернет-магазин потерял 1.5 млн руб. из-за того, что цены округлялись вниз в пользу покупателя. Наш опыт позволяет избежать этих ошибок и гарантирует точность финансовых данных. Более 5 лет мы внедряем мультивалютность на сайтах разного масштаба — от стартапов до enterprise-решений с 30+ валютами. В этой статье разберём ключевые технические решения: схемы хранения цен, автообновление курсов, форматирование и мультивалютные платежи.

Почему мультивалютность — это не просто переключатель валют?

Мультивалютность — комплексная задача, затрагивающая базу данных, бизнес-логику и платёжные шлюзы. Ошибки на любом этапе приводят к финансовым потерям. Рассмотрим два основных подхода к хранению цен.

Как мы храним цены?

Есть два принципиально разных подхода. Первый — базовая валюта с конвертацией на лету: все цены хранятся в одной валюте, при отображении умножаются на актуальный курс. Просто в реализации, но курс меняется — покупатель видит разные цены при каждом визите. Подходит для B2B и информационных сайтов.

Второй — явные цены в каждой валюте: в базе хранится цена отдельно для каждой валюты. Менеджер управляет ценами вручную или с помощью автообновления по курсу. Покупатель видит фиксированную «красивую» цену (999 руб., а не 997,34). Это оптимально для розницы.

Явные цены лучше конвертации на лету в 3 раза по стабильности для покупателя — цена не меняется от визита к визиту.

Характеристика Базовая валюта + конвертация Явные цены в каждой валюте
Сложность реализации Низкая Средняя
Стабильность цен для покупателя Низкая (колеблется с курсом) Высокая (фиксированы)
Подходит для B2B, витрины e-commerce, розница
Управление ценами Автоматическое Ручное / полуавтомат
CREATE TABLE currencies ( code CHAR(3) PRIMARY KEY, -- ISO 4217: RUB, USD, EUR, BYN name VARCHAR(100) NOT NULL, symbol VARCHAR(10) NOT NULL, symbol_pos VARCHAR(10) NOT NULL DEFAULT 'after', decimals SMALLINT NOT NULL DEFAULT 2, is_active BOOLEAN NOT NULL DEFAULT true, is_default BOOLEAN NOT NULL DEFAULT false, rate_to_base NUMERIC(15,6) NOT NULL DEFAULT 1.0 ); CREATE TABLE product_prices ( id BIGSERIAL PRIMARY KEY, variant_id BIGINT NOT NULL REFERENCES product_variants(id), currency CHAR(3) NOT NULL REFERENCES currencies(code), price NUMERIC(12,2) NOT NULL, compare_at NUMERIC(12,2), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), UNIQUE (variant_id, currency) ); 

Коды валют стандартизированы по ISO 4217.

Как обновляются курсы?

Курсы обновляются по расписанию из публичных источников. ЦБ РФ публикует XML по адресу https://www.cbr.ru/scripts/XML_daily.asp, НБРБ — JSON API https://api.nbrb.by/exrates/rates?periodicity=0. Поддерживаются и другие провайдеры.

class ExchangeRateUpdater { private array $providers = [ CbrExchangeRateProvider::class, NbrbExchangeRateProvider::class, EcbExchangeRateProvider::class, ]; public function update(): void { foreach ($this->providers as $providerClass) { $provider = app($providerClass); $rates = $provider->fetchRates(); foreach ($rates as $code => $rate) { Currency::where('code', $code)->update([ 'rate_to_base' => $rate, ]); } } Cache::tags(['currencies'])->flush(); } } 

Автообновление курсов не означает автопересчёт цен в product_prices. Это отдельный шаг — либо ручной (менеджер нажимает «Пересчитать по курсу»), либо автоматический с порогом отклонения (пересчитывать только если курс изменился более чем на 2%).

Как пользователь выбирает валюту?

Выбор валюты реализован через переключатель в шапке сайта. Для гостей выбор сохраняется в cookie preferred_currency (срок 90 дней), для авторизованных — в users.preferred_currency. Middleware определяет текущую валюту при каждом запросе:

class ResolveCurrency { public function handle(Request $request, Closure $next): Response { $currency = $this->detectCurrency($request); app()->instance('current_currency', Currency::find($currency)); $request->merge(['currency' => $currency]); return $next($request); } private function detectCurrency(Request $request): string { // 1. Явный параметр в запросе if ($request->has('currency') && $this->isValid($request->currency)) { $this->persistChoice($request, $request->currency); return $request->currency; } // 2. Сохранённый выбор пользователя if ($request->user()?->preferred_currency) { return $request->user()->preferred_currency; } // 3. Cookie if ($cookie = $request->cookie('preferred_currency')) { return $cookie; } // 4. GeoIP (если включено) return $this->geoipCurrency->detect($request->ip()) ?? config('shop.default_currency', 'RUB'); } } 

Как форматируются цены?

Форматирование — нетривиальная задача: валюты имеют разные разделители и позицию символа. Мы используем гибкий класс PriceFormatter:

class PriceFormatter { public function format(float $amount, Currency $currency): string { $formatted = number_format( $amount, $currency->decimals, ',', ' ' ); return match($currency->symbol_pos) { 'before' => $currency->symbol . $formatted, 'after' => $formatted . ' ' . $currency->symbol, }; } } 

Как реализованы мультивалютные платежи?

Платёжный шлюз должен поддерживать мультивалютность. Stripe — оптимальный выбор: принимает платёж в любой валюте, конвертирует на стороне процессора. ЮKassa работает только в рублях, требуется конвертация на стороне магазина. CloudPayments поддерживает BYN, RUB, USD, EUR.

Шлюз Поддерживаемые валюты Конвертация на стороне Рекомендация
Stripe Любые Нет (автоматическая) Для международной торговли
ЮKassa RUB Требуется на стороне магазина Только Россия
CloudPayments BYN, RUB, USD, EUR Нет Для Беларуси и России

При оплате фиксируется валюта заказа и курс на момент оплаты:

ALTER TABLE orders ADD COLUMN currency CHAR(3) NOT NULL DEFAULT 'RUB'; ALTER TABLE orders ADD COLUMN exchange_rate NUMERIC(15,6) NOT NULL DEFAULT 1.0; ALTER TABLE orders ADD COLUMN base_currency_total NUMERIC(12,2); 

Это позволяет свести отчётность в единой валюте вне зависимости от того, в чём платил покупатель.

Округление и анти-паттерны

Никогда не храните деньги в FLOAT — потеря точности при математике. Всегда используйте NUMERIC(12,2) или DECIMAL.

Округление при конвертации: round($price * $rate, 2, PHP_ROUND_HALF_EVEN)банковское округление, ошибка не накапливается. При суммировании позиций заказа сначала суммируем, потом округляем.

Какие типичные ошибки допускают при реализации мультивалютности?

  1. Использование FLOAT для хранения денег — потеря точности.
  2. Округление каждой позиции отдельно, а не итоговой суммы.
  3. Отсутствие фиксации курса на момент заказа — отчёты в базовой валюте будут расходиться.
  4. Неучёт налогов для разных валют — НДС может различаться.
  5. Смешивание стратегий хранения цен в одном проекте.

Наши клиенты отмечают снижение ошибок в отчётности на 95% и экономию до 30% времени на сверке данных после внедрения правильной архитектуры.

Что входит в работу?

  • Аудит текущей архитектуры и выбор стратегии хранения цен.
  • Проектирование схемы базы данных и миграций.
  • Реализация модуля валют, автообновления курсов и форматирования.
  • Интеграция выбора валюты в интерфейс (cookie, профиль, GeoIP).
  • Настройка мультивалютных платежей (Stripe, CloudPayments и др.).
  • Разработка отчётов в базовой валюте.
  • Документация и обучение команды заказчика.
  • Поддержка после внедрения.

Мы реализовали мультивалютность в 30+ проектах, включая интернет-магазины с оборотом более $10M. Если вам нужна надёжная мультивалютность, свяжитесь с нами для оценки вашего проекта.

Сроки реализации

  • Базовая система (хранение + переключатель + форматирование): от 3 до 4 дней.
  • Автообновление курсов: от 1 дня.
  • Автопересчёт с порогом: от 1–2 дней.
  • Мультивалютные платежи (зависит от шлюза): от 2 до 4 дней.
  • Финансовая отчётность: от 1–2 дней.

Полная реализация для магазина с 3–5 валютами занимает от 1 до 2 недель.

Получите консультацию по вашему проекту — оценим объём работ и предложим оптимальное решение. Свяжитесь с нами, чтобы обсудить детали.