Перейти к содержимому
Integration

Как добавить геолокацию по IP в ваш SaaS за 20 минут

| 7 min read

Четыре функции SaaS, для которых требуется геолокация IP: настройки валюты по умолчанию, баннеры GDPR, обнаружение мошенничества и аналитические панели. Рабочий код для каждого, Google Maps не требуется.

World map with location pins showing IP geolocation data
Photo by NASA on Unsplash

Ваш SaaS показывает доллары США пользователю в Берлине. Ваш баннер cookie отображается для посетителей из Техаса. Ваша система мошенничества не может обнаружить, что нигерийский IP-адрес использует немецкий платежный адрес. Четыре особенности нужна геолокация по IP, и вы можете добавить все четыре за 20 минут с помощью одного API.

API-вызов

Каждая функция в этом посте начинается с одной и той же конечной точки. Вот необработанный завиток:

curl -X POST https://api.botoi.com/v1/ip/lookup \\
  -H "Content-Type: application/json" \\
  -d '{"ip": "8.8.8.8"}'

Ответ:

{
  "success": true,
  "data": {
    "ip": "8.8.8.8",
    "city": "Mountain View",
    "region": "California",
    "country": "US",
    "countryName": "United States",
    "latitude": 37.386,
    "longitude": -122.0838,
    "timezone": "America/Los_Angeles",
    "isp": "Google LLC",
    "org": "Google Public DNS",
    "as": "AS15169 Google LLC"
  }
}

Один POST дает вам город, регион, код страны, полное название страны, координаты, часовой пояс, Интернет-провайдер, организация и номер AS. Данных достаточно для работы всех четырех функций, представленных ниже.

Функция 1. Автоматический выбор валюты при оформлении заказа.

Показание неправильной валюты при оформлении заказа снижает коэффициент конверсии. Гость из Германии видит «49,99 долларов» и должен мысленно перевести деньги в евро, прежде чем принять решение. Хуже того, они могут Предположим, вы не обслуживаете их регион.

Исправьте это с помощью промежуточного программного обеспечения, которое сопоставляет страну IP посетителя с валютой по умолчанию:

const COUNTRY_CURRENCY = {
  US: "USD", GB: "GBP", DE: "EUR", FR: "EUR", JP: "JPY",
  IN: "INR", BR: "BRL", AU: "AUD", CA: "CAD", CN: "CNY",
  KR: "KRW", MX: "MXN", SE: "SEK", CH: "CHF", SG: "SGD",
};

async function currencyMiddleware(req, res, next) {
  const ip = req.headers["x-forwarded-for"]?.split(",")[0] || req.ip;

  try {
    const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Api-Key": process.env.BOTOI_API_KEY,
      },
      body: JSON.stringify({ ip }),
    });

    const { data } = await response.json();
    req.defaultCurrency = COUNTRY_CURRENCY[data.country] || "USD";
  } catch {
    req.defaultCurrency = "USD";
  }

  next();
}

// Usage in Express
app.get("/checkout", currencyMiddleware, (req, res) => {
  res.render("checkout", { currency: req.defaultCurrency });
});

Карта стран и валют охватывает 15 крупнейших рынков SaaS. Расширьте его для своей аудитории. Переход к доллару США корректно обрабатывает сбои API; ни один посетитель никогда не увидит сломанную кассу потому что истекло время ожидания вызова геолокации.

Функция 2: баннер cookie GDPR только для посетителей ЕС

Показ баннера согласия на использование файлов cookie каждому посетителю ненужен и раздражает. GDPR распространяется на посетителей в Европейском Союзе. Все остальные могут пропустить это.

Это промежуточное программное обеспечение проверяет IP-адрес страны посетителя по списку государств-членов ЕС. и устанавливает файл cookie, который читает интерфейс:

const EU_COUNTRIES = new Set([
  "AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR",
  "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL",
  "PL", "PT", "RO", "SK", "SI", "ES", "SE",
]);

async function gdprMiddleware(req, res, next) {
  const ip = req.headers["x-forwarded-for"]?.split(",")[0] || req.ip;

  try {
    const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Api-Key": process.env.BOTOI_API_KEY,
      },
      body: JSON.stringify({ ip }),
    });

    const { data } = await response.json();
    req.isEU = EU_COUNTRIES.has(data.country);
  } catch {
    // Default to showing the banner when the lookup fails
    req.isEU = true;
  }

  next();
}

// Set a cookie so the frontend knows whether to show the banner
app.use(gdprMiddleware, (req, res, next) => {
  res.cookie("gdpr_applies", req.isEU ? "1" : "0", {
    httpOnly: false,
    maxAge: 86400 * 1000,
  });
  next();
});

На веб-интерфейсе прочитайте файл cookie и переключите баннер:

// Frontend: read the cookie and conditionally show the banner
function shouldShowCookieBanner() {
  const match = document.cookie.match(/gdpr_applies=(\d)/);
  return match ? match[1] === "1" : true; // default to showing
}

if (shouldShowCookieBanner()) {
  document.getElementById("cookie-banner").style.display = "block";
}

Поведение по умолчанию в случае сбоя — показ баннера. Это ошибка со стороны соответствия; даже если географический поиск не удался, вы все равно соответствуете требованиям GDPR. Файл cookie сохраняется 24 часа, поэтому вы вызываете API только один раз для каждого посетителя в день.

Функция 3: Обнаружение мошенничества с несовпадением географического местоположения

Когда кто-то расплачивается с платежным адресом в Германии, но его IP-адрес привязан к Нигерия, это сигнал, который стоит изучить. Это не означает, что транзакция мошеннический; люди путешествуют, используют VPN и покупают подарки друзьям за границей. Но это данные укажите потребности вашей группы по рассмотрению случаев мошенничества.

async function checkGeoMismatch(ip, billingCountry) {
  const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": process.env.BOTOI_API_KEY,
    },
    body: JSON.stringify({ ip }),
  });

  const { data } = await response.json();

  const mismatch = data.country !== billingCountry;

  return {
    mismatch,
    ipCountry: data.country,
    ipCity: data.city,
    billingCountry,
    riskNote: mismatch
      ? \`IP located in \${data.countryName} but billing address is \${billingCountry}\`
      : null,
  };
}

// Usage during checkout
app.post("/checkout", async (req, res) => {
  const ip = req.headers["x-forwarded-for"]?.split(",")[0] || req.ip;
  const { billingCountry } = req.body;

  const geo = await checkGeoMismatch(ip, billingCountry);

  if (geo.mismatch) {
    // Flag for manual review instead of blocking
    await flagOrder(req.body.orderId, geo.riskNote);
  }

  // Continue processing the order
  await processOrder(req.body);
  res.json({ success: true });
});

Функция возвращает структурированный объект с флагом несоответствия и удобочитаемым текстом. Примечание о рисках, которое может просмотреть ваша служба поддержки. Пометить заказ для проверки вручную вместо полностью заблокировав его. Объедините это с другими сигналами (возраст домена электронной почты, оплата скорость, отпечаток устройства) для более полной картины.

Функция 4. Панель аналитики с распределением пользователей.

Знание того, где находятся ваши пользователи, поможет вам решить, какие языки поддерживать и в каких регионах. нацелиться на маркетинг и где разместить пограничные серверы. Этот скрипт обрабатывает пакет IP-адресов посетителей и производит отсортированное распределение по странам:

async function buildCountryDistribution(ips) {
  const counts = {};

  // Process in batches to respect rate limits
  for (const ip of ips) {
    try {
      const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Api-Key": process.env.BOTOI_API_KEY,
        },
        body: JSON.stringify({ ip }),
      });

      const { data } = await response.json();
      const country = data.countryName || "Unknown";
      counts[country] = (counts[country] || 0) + 1;
    } catch {
      counts["Unknown"] = (counts["Unknown"] || 0) + 1;
    }
  }

  // Sort by count descending
  return Object.entries(counts)
    .sort(([, a], [, b]) => b - a)
    .map(([country, count]) => ({
      country,
      count,
      percentage: ((count / ips.length) * 100).toFixed(1) + "%",
    }));
}

// Example output:
// [
//   { country: "United States", count: 4521, percentage: "34.2%" },
//   { country: "Germany", count: 1893, percentage: "14.3%" },
//   { country: "United Kingdom", count: 1247, percentage: "9.4%" },
//   ...
// ]

Запустите это как ночное задание для журналов доступа. Вывод говорит вам, какой именно страны привлекают больше всего трафика. Если 14% ваших пользователей находятся в Германии, но только ваше приложение поддерживает английский язык, это возможность локализации, которую вы можете оценить количественно.

Стратегия кэширования

Сопоставления IP-адресов и местоположений меняются не часто. Нет причин снова вызывать API for the same IP within a session. Этот кеш использует простую карту с TTL в 1 час:

class GeoCache {
  constructor(ttlMs = 60 * 60 * 1000) {
    this.cache = new Map();
    this.ttlMs = ttlMs;
  }

  get(ip) {
    const entry = this.cache.get(ip);
    if (!entry) return null;
    if (Date.now() - entry.timestamp > this.ttlMs) {
      this.cache.delete(ip);
      return null;
    }
    return entry.data;
  }

  set(ip, data) {
    this.cache.set(ip, { data, timestamp: Date.now() });
  }
}

const geoCache = new GeoCache();

async function lookupWithCache(ip) {
  const cached = geoCache.get(ip);
  if (cached) return cached;

  const response = await fetch("https://api.botoi.com/v1/ip/lookup", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": process.env.BOTOI_API_KEY,
    },
    body: JSON.stringify({ ip }),
  });

  const { data } = await response.json();
  geoCache.set(ip, data);
  return data;
}

Для серверов Node.js с одним экземпляром карта в памяти работает нормально. Если вы запускаете несколько экземпляров за балансировщиком нагрузки, замените Map на Redis. Применяется та же логика TTL; сохраните геоданные в виде строки JSON со сроком действия 3600 секунд.

Извлечение IP-адреса клиента

Самая сложная часть IP-геолокации — это не вызов API; он получает правильный IP в первую очередь. Если ваше приложение находится за обратным прокси-сервером, балансировщиком нагрузки или CDN, req.connection.remoteAddress возвращает IP-адрес прокси-сервера, а не посетителя.

Вот как получить реальный IP-адрес клиента в каждой среде:

// Express
const ip = req.headers["x-forwarded-for"]?.split(",")[0]?.trim() || req.ip;

// Next.js (App Router)
import { headers } from "next/headers";
const headerList = await headers();
const ip = headerList.get("x-forwarded-for")?.split(",")[0]?.trim();

// Cloudflare Workers
const ip = request.headers.get("cf-connecting-ip");

// Vercel (edge or serverless)
const ip = request.headers.get("x-real-ip")
  || request.headers.get("x-forwarded-for")?.split(",")[0]?.trim();

Всегда разделяйте по первой запятой x-forwarded-for. Этот заголовок может содержать цепочка IP-адресов, когда трафик проходит через несколько прокси. Первая запись — это исходный IP-адрес клиента.

Если вы используете Cloudflare, используйте cf-connecting-ip. Cloudflare устанавливает этот заголовок по каждому запросу, и его сложнее подделать, чем x-forwarded-for.

FAQ

Как добавить геолокацию IP в мое SaaS-приложение?
Отправьте IP-адрес вашего посетителя в API-интерфейс геолокации IP (например, POST /v1/ip/lookup) и используйте возвращенные данные о стране, городе и часовом поясе, чтобы персонализировать их опыт. Общие случаи использования включают автоматический выбор валюты при оформлении заказа, показ баннеров GDPR посетителям ЕС, обнаружение мошенничества из-за несоответствия географического положения и создание аналитических панелей. Вы можете добавить все четыре функции с помощью одного API.
Какой API определения IP-адреса лучше всего подходит для продуктов SaaS?
Найдите API, который возвращает данные о стране, городе, регионе, координатах, часовом поясе и интернет-провайдере за один вызов. /v1/ip/lookup Botoi возвращает все эти поля без необходимости регистрации для анонимного доступа (5 запросов в минуту). Для производственного использования бесплатный ключ API обеспечивает 1000 запросов в день. Платные планы начинаются от 9 долларов в месяц.
Могу ли я определять местоположение пользователей по IP-адресу без Google Maps?
Да. API-интерфейсы IP-геолокации возвращают данные о широте, долготе, городе и стране без необходимости использования Google Maps или какой-либо картографической службы. Библиотека картографирования вам понадобится только в том случае, если вы хотите отобразить местоположение на визуальной карте. Для таких функций, как настройки валюты по умолчанию, соблюдение GDPR и обнаружение мошенничества, все, что вам нужно, — это необработанные данные геолокации из API.
Насколько точна геолокация IP для определения местоположения пользователя?
Геолокация IP точна на уровне страны примерно в 99% случаев и на уровне города примерно в 80-90% случаев. Точность снижается для операторов мобильной связи и пользователей VPN. Для функций SaaS, таких как выбор валюты и соблюдение GDPR, точности на уровне страны достаточно. Для обнаружения мошенничества объедините геолокацию IP с данными платежного адреса, а не полагайтесь на точность на уровне города.
Должен ли я кэшировать результаты геолокации IP в своем SaaS?
Да. Сопоставления IP-адресов меняются нечасто, поэтому кэширование результатов в течение 1 часа для каждого IP-адреса значительно сокращает количество вызовов API. Используйте карту в памяти для развертываний с одним экземпляром или Redis для установок с несколькими экземплярами. Большинство SaaS-приложений имеют показатель попадания в кэш 60–80 %, поскольку возвращающиеся посетители заходят на один и тот же IP-адрес в течение сеанса.

Начните разработку с botoi

150+ API-эндпоинтов для поиска, обработки текста, генерации изображений и утилит для разработчиков. Бесплатный тариф, без банковской карты.