Реализация автоопределения валюты по GeoIP для сайта

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация автоопределения валюты по GeoIP для сайта
Средняя
~1 рабочий день
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1243
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1170
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    873
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1088
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    830
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    849

Реализация автоопределения валюты по GeoIP для сайта

GeoIP-определение валюты — дополнение к системе мультивалютности, которое снимает с пользователя необходимость самостоятельно выбирать валюту при первом визите. Покупатель из Беларуси видит цены в BYN, из Германии — в EUR, из США — в USD. Реализация проще, чем кажется, но есть нюансы с точностью баз, кешированием и уважением к выбору пользователя.

Как работает GeoIP

IP-адрес → страна → валюта страны. Цепочка из двух шагов.

Шаг 1: IP → страна. Используются базы данных геолокации. Основные варианты:

База Тип Точность по стране Стоимость
MaxMind GeoLite2 Локальная MMDB ~95–99% Бесплатно (регистрация)
MaxMind GeoIP2 Локальная + API ~99%+ Платно
ip-api.com HTTP API ~98% Бесплатно (1000/мин)
ipinfo.io HTTP API ~99% Freemium
DB-IP Локальная ~95% Freemium

Для большинства проектов MaxMind GeoLite2 — оптимальный выбор: локальная база не зависит от внешних сервисов и не тормозит запросы.

Шаг 2: страна → валюта. Статическая таблица соответствий ISO 3166-1 → ISO 4217.

Установка MaxMind GeoLite2

composer require geoip2/geoip2

База обновляется MaxMind каждые вторник и пятницу. Для автоматического обновления используется утилита geoipupdate:

# /etc/GeoIP.conf
AccountID 123456
LicenseKey your_license_key
EditionIDs GeoLite2-Country
DatabaseDirectory /var/lib/GeoIP

# cron каждую среду и субботу
0 3 * * 3,6 /usr/local/bin/geoipupdate

Сервис определения валюты

class GeoIpCurrencyDetector
{
    private Reader $geoIpReader;

    public function __construct()
    {
        $this->geoIpReader = new Reader(
            storage_path('app/geoip/GeoLite2-Country.mmdb')
        );
    }

    public function detect(string $ip): ?string
    {
        // Приватные и зарезервированные диапазоны — пропускаем
        if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
            return null;
        }

        try {
            $record = $this->geoIpReader->country($ip);
            $countryCode = $record->country->isoCode; // 'BY', 'RU', 'DE' и т.д.

            return $this->mapCountryToCurrency($countryCode);
        } catch (AddressNotFoundException) {
            return null;
        }
    }

    private function mapCountryToCurrency(string $countryCode): ?string
    {
        $map = config('geoip.country_currency_map');
        return $map[$countryCode] ?? null;
    }
}

Конфиг country_currency_map — это массив из ~250 стран. Ключевые записи:

// config/geoip.php
return [
    'country_currency_map' => [
        'BY' => 'BYN',
        'RU' => 'RUB',
        'UA' => 'UAH',
        'KZ' => 'KZT',
        'US' => 'USD',
        'CA' => 'CAD',
        'GB' => 'GBP',
        'DE' => 'EUR', 'FR' => 'EUR', 'IT' => 'EUR', 'ES' => 'EUR',
        'PL' => 'PLN',
        'CZ' => 'CZK',
        'CN' => 'CNY',
        'JP' => 'JPY',
        // ...и ещё ~200 стран
    ],
    'fallback_currency' => 'USD',
];

Кеширование результата

Определение по IP — быстрая операция (локальная база, ~0.5 мс), но кешировать всё равно стоит: защита от повторных операций чтения файла при каждом запросе.

public function detectCached(string $ip): ?string
{
    $cacheKey = 'geoip:' . md5($ip);

    return Cache::remember($cacheKey, now()->addDay(), function () use ($ip) {
        return $this->detect($ip);
    });
}

TTL 24 часа — баланс между актуальностью (пользователь не меняет страну каждый час) и точностью (VPN-переключение подхватится на следующий день).

Интеграция в middleware

class ResolveCurrencyFromGeoIp
{
    public function handle(Request $request, Closure $next): Response
    {
        // Если пользователь уже сделал явный выбор — не перебиваем
        if ($this->hasExplicitChoice($request)) {
            return $next($request);
        }

        $ip = $request->ip();

        // Учитываем прокси и балансировщики
        if ($request->header('CF-Connecting-IP')) {
            $ip = $request->header('CF-Connecting-IP'); // Cloudflare
        } elseif ($request->header('X-Real-IP')) {
            $ip = $request->header('X-Real-IP'); // nginx proxy_pass
        }

        $currency = $this->detector->detectCached($ip);

        if ($currency && $this->isSupportedCurrency($currency)) {
            session(['auto_currency' => $currency]);
            Cookie::queue('preferred_currency', $currency, 60 * 24 * 90);
        }

        return $next($request);
    }

    private function hasExplicitChoice(Request $request): bool
    {
        // Пользователь явно переключал валюту
        return session()->has('explicit_currency_choice')
            || $request->user()?->preferred_currency;
    }
}

Обработка IP за прокси

Проблемы с IP возникают при:

  • Cloudflare — реальный IP в CF-Connecting-IP
  • nginx reverse proxy — реальный IP в X-Real-IP или X-Forwarded-For
  • AWS ELB — X-Forwarded-For (первый в списке)

Для Laravel правильная конфигурация через TrustProxies middleware:

// app/Http/Middleware/TrustProxies.php
protected $proxies = '*'; // или конкретные IP балансировщиков
protected $headers = Request::HEADER_X_FORWARDED_FOR
    | Request::HEADER_X_FORWARDED_HOST
    | Request::HEADER_X_FORWARDED_PORT
    | Request::HEADER_X_FORWARDED_PROTO;

После этого $request->ip() вернёт корректный клиентский IP.

UX: уведомление об автоопределении

Хорошая практика — показать пользователю тост/баннер при первом автоопределении:

Мы определили, что вы из Беларуси. Цены показаны в BYN. Изменить валюту →

Баннер показывается один раз (флаг в sessionStorage) и содержит быструю ссылку для смены валюты. Это уважение к пользователю: автоматика помогает, а не навязывает.

Когда GeoIP не работает

  • VPN/прокси: пользователь из RU выглядит как US → показывается USD. Решение: явная ссылка «Изменить валюту» всегда доступна.
  • Корпоративные сети: IP зарегистрирован в другой стране. Аналогично.
  • Tor: exit-нода в случайной стране. Fallback на валюту по умолчанию.
  • IPv6: база GeoLite2-Country поддерживает IPv6 начиная с версии 2020+. Убедитесь, что используется актуальная версия.

Тестирование

Для локального тестирования с фиктивными IP:

// В тестах или локальном окружении
if (app()->environment('local')) {
    $ip = config('geoip.test_ip', '178.124.0.1'); // белорусский IP
}

Для автотестов — mock сервиса:

$this->mock(GeoIpCurrencyDetector::class, function ($mock) {
    $mock->shouldReceive('detectCached')->andReturn('BYN');
});

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

  • Установка GeoLite2 + базовое определение + маппинг стран: 1 день
  • Middleware + кеширование + интеграция с системой мультивалютности: 1 день
  • UX-уведомление + обработка явного выбора: 0,5 дня
  • Автообновление базы (cron + geoipupdate): 0,5 дня

Итого: 2–3 дня при условии, что система мультивалютности уже реализована.