如何在 20 分钟内将 IP 地理定位添加到您的 SaaS
需要 IP 地理定位的四种 SaaS 功能:货币默认值、GDPR 横幅、欺诈检测和分析仪表板。 每个的工作代码,不需要谷歌地图。
您的 SaaS 向柏林的用户显示美元。 您的 cookie 横幅会向德克萨斯州的访客显示。 当尼日利亚 IP 使用德国帐单地址时,您的欺诈系统无法标记。 四大特点 需要 IP 地理定位,您可以使用一个 API 在 20 分钟内添加所有四个。
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"
}
}
一篇帖子将为您提供城市、地区、国家/地区代码、完整的国家/地区名称、坐标、时区、 ISP、组织和 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 个排名前 15 的 SaaS 市场。 为您的受众扩展它。 回退到 USD 可以优雅地处理 API 故障; 没有访客见过损坏的结账台 因为地理定位调用超时。
功能 2:仅适用于欧盟访客的 GDPR cookie 横幅
向每个访问者显示 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 对于会话中的同一 IP。 此缓存使用一个具有 1 小时 TTL 的简单 Map:
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 工作得很好。 如果您运行多个 负载均衡器后面的实例,将 Map 交换为 Redis。 相同的 TTL 逻辑适用; 将地理数据存储为 JSON 字符串,有效期为 3600 秒。
提取客户端IP
IP 地理定位最棘手的部分不是 API 调用,而是 API 调用。 它正在获取正确的IP
首先。 如果您的应用程序位于反向代理、负载均衡器或 CDN 后面,
req.connection.remoteAddress 返回代理的 IP,而不是访问者的 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 发送到 IP 地理定位 API(例如 POST /v1/ip/lookup),并使用返回的国家/地区、城市和时区数据来个性化他们的体验。 常见用例包括结帐时自动选择货币、向欧盟访客展示 GDPR 横幅、标记地理不匹配欺诈以及构建分析仪表板。 您可以使用单个 API 添加所有四个功能。
- SaaS 产品的最佳 IP 定位 API 是什么?
- 寻找一个能够在一次调用中返回国家、城市、地区、坐标、时区和 ISP 数据的 API。 Botoi 的 /v1/ip/lookup 返回所有这些字段,无需注册即可进行匿名访问(5 请求/分钟)。 对于生产用途,免费的 API 密钥每天可提供 1,000 个请求。 付费计划起价为 9 美元/月。
- 我可以在没有 Google 地图的情况下通过 IP 对用户进行地理定位吗?
- 是的。 IP 地理定位 API 可返回纬度、经度、城市和国家/地区数据,无需 Google 地图或任何地图服务。 如果您想在可视地图上显示位置,则只需要地图库。 对于货币默认、GDPR 合规性和欺诈检测等功能,您只需要来自 API 的原始地理位置数据。
- IP 地理定位检测用户位置的准确度如何?
- IP 地理定位在国家/地区级别的准确率约为 99%,在城市级别的准确率约为 80-90%。 移动运营商和 VPN 用户的准确度下降。 对于货币选择和 GDPR 合规性等 SaaS 功能,国家/地区级别的准确性就足够了。 对于欺诈检测,请将 IP 地理定位与账单地址数据相结合,而不是依赖城市级精度。
- 我应该在 SaaS 中缓存 IP 地理定位结果吗?
- 是的。 IP 到位置的映射很少更改,因此每个 IP 缓存结果 1 小时可显着减少 API 调用。 Use an in-memory Map for single-instance deployments or Redis for multi-instance setups. 大多数 SaaS 应用程序的缓存命中率高达 60-80%,因为回访者在会话中访问了相同的 IP。
开始使用 botoi 构建
150+ 个 API 端点,涵盖查询、文本处理、图片生成和开发者工具。免费套餐,无需信用卡。