Development of a multicurrency module for 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Developing a Multi-Currency Module for 1C-Bitrix

Bitrix has built-in currency support through the currency module: the b_currency table, rates in b_currency_rate, the bitrix:currency.rates component. At first glance — a ready-made solution. In practice, questions arise that the standard mechanism does not solve: automatic exchange rate updates from the Russian Central Bank or ECB, displaying prices in the visitor's currency with detection by IP, fixing the rate at the time of purchase, managing prices in different currencies independently (not through conversion from the base currency).

Module Architecture

The vendor.multicurrency module builds on top of the standard currency, not replacing it. The base currency is the ruble, rates are stored in the standard b_currency_rate. The module adds:

  1. Automatic rate updates from external sources
  2. User currency detection
  3. Rate fixing in the order
  4. Independent prices in currencies (not conversion)

Automatic Rate Updates

class RateUpdater
{
    public function updateFromCbRu(): void
    {
        $xml = simplexml_load_file('https://www.cbr.ru/scripts/XML_daily.asp');

        foreach ($xml->Valute as $valute) {
            $code  = (string)$valute->CharCode;
            $rate  = (float)str_replace(',', '.', (string)$valute->Value);
            $nominal = (int)$valute->Nominal;

            // Rate in Bitrix — how many rubles per 1 unit of foreign currency
            $ratePerUnit = $rate / $nominal;

            \CCurrencyRates::SetRatesList(SITE_ID, [[
                'CURRENCY'   => $code,
                'RATE'       => $ratePerUnit,
                'RATE_CNT'   => 1,
                'DATE_RATE'  => date('d.m.Y'),
            ]]);
        }
    }
}

The agent runs every 4 hours. The exchange rate source (Russian Central Bank, ECB, NBU, fixer.io) is a module settings parameter. A markup on the rate in percentage (bank spread) is supported.

User Currency Detection

Priority of detection:

  1. Explicit user choice (cookie user_currency)
  2. Detection by IP using MaxMind GeoIP2 or ip-api.com: RU → RUB, BY → BYB/RUB, DE/FR/... → EUR
  3. Browser Accept-Language header
  4. Default currency (from module settings)
$currency = CurrencyDetector::detect(
    $_COOKIE['user_currency'] ?? null,
    $_SERVER['REMOTE_ADDR'],
    $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''
);
// Save in session for current request
$_SESSION['CURRENT_CURRENCY'] = $currency;

Price Display

The helper component vendor:multicurrency.price takes a price in the base currency (rubles) and displays it in the user's current currency:

// Conversion through standard Bitrix method
$priceInUserCurrency = \CCurrencyRates::ConvertCurrency($priceRub, 'RUB', $userCurrency);
$formatted = \CCurrencyLang::CurrencyFormat($priceInUserCurrency, $userCurrency, true);
// → "€ 149,90"

The currency switcher is the vendor:multicurrency.switcher component. When selecting a currency — record to cookie and AJAX redraw price blocks without full page reload.

Independent Prices in Currencies

For B2B scenarios, sometimes prices manually set in each currency are needed (not conversion from rubles, since prices may not match the rate). In this case, prices are stored in additional Bitrix price types:

  • Price type BASE (RUB) — standard
  • Price type EUR_PRICE — price in euros, set manually
  • Price type USD_PRICE — price in dollars

The module adds logic: if a separate price type exists for the current currency — use it, otherwise — convert from the base.

Rate Fixing in the Order

The exchange rate at the time of the order is fixed in b_sale_order.USER_DESCRIPTION (or in a separate HL-block):

AddEventHandler('sale', 'OnBeforeSaleOrderAdd', ['\Vendor\Multicurrency\OrderHandler', 'fixRate']);

public static function fixRate(\Bitrix\Main\Event $event): void
{
    $order = $event->getParameter('ENTITY');
    $currency = $_SESSION['CURRENT_CURRENCY'] ?? 'RUB';
    $rate     = \CCurrencyRates::GetConvertFactor('RUB', $currency);

    $order->setField('CURRENCY', $currency);
    // Save rate in order's additional field
    OrderMetaTable::add(['ORDER_ID' => 0, 'CURRENCY' => $currency, 'RATE' => $rate]);
}

The fixed rate is needed for correct display of the order amount in the history and for accounting documents.

Development Timeline

Stage Duration
Automatic rate updates, agent 1 day
User currency detection (GeoIP) 1 day
Currency switcher, cookie 1 day
AJAX price redraw 1 day
Independent price types by currency 1 day
Rate fixing in the order 1 day
Formatting and localization 0.5 day
Testing 0.5 day

Total: 7 working days. Adding additional rate sources (NBU, National Bank of Belarus) — 0.5 day per source.