Реалізація автовизначення валюти за GeoIP для сайту

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація автовизначення валюти за GeoIP для сайту
Середня
~1 робочий день
Часті питання

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

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація автоопределення валюти по 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 дні при умові, що система мультивалютності вже реалізована.