如何通过 credit card 和 payment gateway 验证用户年龄?
结论
通常无法通过 credit card 或普通 payment gateway 验证持卡人是否年满 18 岁。
支付网关主要确认卡片是否有效、交易是否获批,以及付款人是否完成 3-D Secure 等认证。它一般不会向商户提供持卡人的出生日期或年龄。即使付款成功,也不能据此认定用户已经成年。
如果业务需要限制未成年人使用,应把年龄验证和支付处理设为两个独立环节。先通过专门的 identity/age verification 服务完成验证,再允许用户进入支付和游戏流程。
为什么信用卡不能作为年龄证明
信用卡付款无法可靠证明年龄,原因包括:
- 未成年人可能持有附属卡,或被列为授权用户。
- 用户可能使用父母、监护人或其他人的卡片。
- debit card、prepaid card 和 virtual card 的申请条件不等同于成年要求。
- 卡片可能被借用或盗用,也可能由数字钱包代为提交。
- payment gateway 通常只能看到经过令牌化或脱敏的卡片信息。
CVV、AVS和3-D Secure验证的是支付凭据、账单信息或持卡人身份,不是年龄。
因此,交易通过最多只能说明发卡行批准了付款,不能证明当前用户就是持卡人,也不能证明其已满 18 岁。
推荐的验证流程
较稳妥的流程如下:
- 用户注册并填写出生日期。
- 服务端根据出生日期进行初步年龄判断。
- 对年龄限制严格的用户,调用独立的 age verification 服务。
- 服务端保存验证结果和必要的审计信息,不保存多余的身份证件数据。
- 年龄验证通过后,再创建 payment session。
- payment gateway 完成支付后,通过已验证签名的 webhook 更新订单状态。
- 用户同时满足年龄验证通过和支付成功两个条件后,才能开始游戏。
可以把用户状态拆分为两个字段:
age_verification_status:
pending | approved | rejected | expired
payment_status:
pending | paid | failed | refunded
授权条件应由服务端判断:
can_start_game =
age_verification_status == "approved"
AND payment_status == "paid"
不能仅凭前端传入的 isAdult: true 放行用户。
出生日期的服务端初步判断
下面的示例按自然日期判断用户是否已满 18 岁,避免直接用毫秒数除以 365 天,从而引发闰年或生日临界点错误。
function isAtLeastAge(dateOfBirth, minimumAge, now = new Date()) {
const birth = new Date(`${dateOfBirth}T00:00:00Z`);
if (Number.isNaN(birth.getTime())) {
throw new Error("Invalid date of birth");
}
const today = new Date(
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
);
const threshold = new Date(
Date.UTC(
today.getUTCFullYear() - minimumAge,
today.getUTCMonth(),
today.getUTCDate()
)
);
return birth <= threshold;
}
console.log(isAtLeastAge("2008-09-01", 18));
这个判断只能用于 self-declaration 初筛,无法证明用户填写的出生日期真实有效。
接入独立年龄验证服务
各服务商的 API、字段和验证方式差异很大。下面仅展示通用结构,并非任何真实服务商的接口。
import express from "express";
const app = express();
app.use(express.json());
app.post("/api/age-verification/session", async (req, res) => {
const user = await loadAuthenticatedUser(req);
if (!user) {
return res.status(401).json({ error: "Authentication required" });
}
if (!isAtLeastAge(user.dateOfBirth, 18)) {
return res.status(403).json({ error: "Minimum age requirement not met" });
}
const verificationSession =
await ageVerificationProvider.createSession({
referenceId: user.id,
minimumAge: 18,
callbackUrl: "https://example.com/age-verification/callback"
});
await saveAgeVerification({
userId: user.id,
providerSessionId: verificationSession.id,
status: "pending"
});
res.json({
redirectUrl: verificationSession.redirectUrl
});
});
验证服务可能使用身份证件、数据库匹配、数字身份或其他方式。应优先选择只返回用户是否达到年龄门槛的方案,例如返回 over_18: true,以免获取完整的出生日期和证件内容。
处理验证结果
不能因为用户的浏览器跳转到了成功页面,就认定验证已经通过。验证结果应通过服务端回调或 webhook 获取,并校验签名。
app.post(
"/api/webhooks/age-verification",
express.raw({ type: "application/json" }),
async (req, res) => {
const signature = req.headers["x-provider-signature"];
let event;
try {
event = verifyAgeWebhookSignature(
req.body,
signature,
process.env.AGE_WEBHOOK_SECRET
);
} catch {
return res.status(400).send("Invalid signature");
}
if (event.type === "verification.completed") {
await updateAgeVerification(event.data.sessionId, {
status: event.data.over18 ? "approved" : "rejected",
verifiedAt: new Date()
});
}
res.sendStatus(204);
}
);
x-provider-signature、事件名称和返回字段都是示意名称。实际接入时,必须替换为服务商文档规定的内容。
年龄通过后再创建支付
支付接口必须重新读取服务端保存的验证状态:
app.post("/api/payments/session", async (req, res) => {
const user = await loadAuthenticatedUser(req);
if (!user) {
return res.status(401).json({ error: "Authentication required" });
}
const verification = await getAgeVerification(user.id);
if (
!verification ||
verification.status !== "approved" ||
verification.expiresAt <= new Date()
) {
return res.status(403).json({
error: "Valid age verification required"
});
}
const paymentSession = await paymentGateway.createSession({
amount: req.body.amount,
currency: req.body.currency,
referenceId: user.id,
successUrl: "https://example.com/payment/success",
cancelUrl: "https://example.com/payment/cancel"
});
res.json({
redirectUrl: paymentSession.redirectUrl
});
});
价格、币种和商品信息也应由服务端根据商品或订单计算,不能直接信任客户端提交的 amount。
需要特别注意的事项
合规要求
如果这里的 gaming 涉及真钱赌博、投注、奖品兑换或其他受监管活动,只验证用户是否年满 18 岁可能还不够。实际要求可能包括:
- 身份验证和 KYC;
- 所在国家或地区验证;
- 更高的最低年龄;
- 制裁名单或反洗钱检查;
- 自我排除名单检查;
- 支付方式和资金来源限制;
- 特定牌照及记录保存要求。
最低年龄并非所有地区都是 18 岁,应以用户所在地、运营所在地和牌照条款为准。具体法律义务需要由熟悉目标司法辖区的律师或合规人员确认。
支付服务商限制
部分 payment gateway 或 acquiring bank 会限制赌博、博彩、抽奖及类似的高风险业务。技术上能够收款,不代表服务商合同允许经营这类业务。上线前应如实申报业务类型,并取得相应的商户类别和审批。
隐私与数据安全
身份证件和出生日期属于敏感个人信息。处理这些数据时应注意:
- 只收集完成验证所需的数据。
- 尽量让专业服务商直接处理证件。
- 本地只保存验证状态、验证时间和服务商引用 ID。
- 设置验证结果的有效期和重新验证规则。
- 对数据实施访问控制、加密和审计。
- 制定数据删除、申诉和人工复核流程。
- 不要把完整卡号、
CVV或证件图片写入应用日志。
支付卡数据应交给符合相应 PCI DSS 要求的支付服务处理。优先使用 hosted checkout 或 tokenization,减少应用直接接触卡片数据的范围。
最终建议
普通银行卡支付流程不能提供可靠的年龄证明,因此不应寻找一个能够从信用卡读取年龄的 payment gateway。合适的流程是:
账户认证
→ 出生日期初筛
→ 独立年龄或身份验证
→ 服务端记录验证结果
→ payment gateway 收款
→ 验证支付 webhook
→ 服务端授权开始游戏
信用卡可以用于支付,但不能作为年龄凭证。
备注:内容仅供参考。
