跳到主要内容

自定义年龄门控

本指南将引导您使用 k-ID API 直接实现自定义年龄门控,无需使用预构建的小部件。这种方法让您完全控制用户界面,同时 k-ID 处理合规逻辑。

使用基本权限简化集成

本指南演示了一种简化的集成模式,其中所有权限在 Compliance Studio 中配置为基本权限。通过此配置,一旦创建会话(直接创建或在家长同意后创建),所有功能立即可用——无需会话升级流程。

Automatic age assurance

如果您的产品在目标司法管辖区启用了 Automatic age assurance,即使所有权限都配置为基本权限,对于声明年龄足以跳过父母同意的玩家,/age-gate/check 也可能返回 CHALLENGE_AGE_GATE_AGE_ASSURANCE 挑战。会话创建会推迟到玩家完成自助验证之后。该功能由仅 k-ID 可授予的组织级别设置控制;如果您的产品未启用此功能,可以跳过 步骤 4b

什么是自定义年龄门控?

自定义年龄门控是您构建和控制的年龄验证界面,而 k-ID 的 API 在后台处理合规逻辑。这种方法适用于以下情况:

  • 完全 UI 控制:设计符合您品牌和用户体验的年龄门控
  • 平台集成:为移动应用或游戏引擎构建原生体验
  • 自定义工作流程:将年龄验证作为更大入门流程的一部分实施

先决条件

开始之前,您需要:

  1. k-ID 产品:在 k-ID Compliance Studio创建和配置您的产品
  2. 基本权限配置:在产品的权限设置中将所有权限配置为"基本"
  3. API 密钥:从 Compliance Studio 中产品的开发者设置页面生成您的 API 密钥
  4. Webhook 端点(推荐):设置安全的 HTTPS 端点以接收挑战和会话事件。更多详情请参阅 Webhooks

步骤 1:获取年龄门控要求

在显示年龄门控之前,调用 /age-gate/get-requirements 以根据用户的司法管辖区确定需要显示的内容。

重要

在您的实现中,所有 API 调用都应该是服务器到服务器的,以保护您的 API 密钥不被暴露在客户端代码中。

请求示例

GET /api/v1/age-gate/get-requirements?jurisdiction=US-CA
Authorization: Bearer your-api-key

响应示例

{
"shouldDisplay": true,
"ageAssuranceRequired": false,
"digitalConsentAge": 13,
"civilAge": 18,
"minimumAge": 0,
"approvedAgeCollectionMethods": [
"date-of-birth",
"age-slider",
"platform-account"
]
}

响应字段

字段描述
shouldDisplay是否应显示年龄门控
ageAssuranceRequired此司法管辖区是否需要年龄验证
digitalConsentAge数字同意的最低年龄(低于此年龄的用户需要家长同意)
civilAge用户被视为法定成年人的年龄
minimumAge访问您产品的最低年龄(在 Compliance Studio 中配置)
approvedAgeCollectionMethods此司法管辖区允许的年龄收集方法
不需要年龄门控

如果 shouldDisplayfalse,则跳过年龄门控并调用 /age-gate/get-default-permissions 获取该司法管辖区的默认会话权限。

平台年龄信号

如果您的游戏拥有平台报告的年龄信号(Apple iOS、Google Play、Xbox、Meta Horizon 或 k-ID),请将其作为查询参数(platformNameplatformAgeLowplatformAgeHighplatformCategoryplatformDeclarationTypeplatformVerificationId)包含进来,这样已验证的成人信号可以将 shouldDisplay 翻转为 false 并完全跳过年龄门控。请参阅平台年龄信号

步骤 2:构建您的年龄门控 UI

根据响应,使用批准的收集方法构建您的年龄门控界面:

  • date-of-birth:完整出生日期输入(YYYY-MM-DD)
  • age-slider:年龄范围或滑块选择
  • platform-account:使用现有平台账户年龄数据
UX 指南

有关年龄滑块行为、日期选择器要求和无障碍访问考虑事项的详细设计建议,请参阅 UX 指南

年龄门控 UI 示例

<div id="age-gate">
<h2>请输入您的出生日期</h2>
<form id="age-gate-form">
<label for="dob">出生日期</label>
<input type="date" id="dob" name="dob" required />
<button type="submit">继续</button>
</form>
</div>

步骤 3:使用 API 检查年龄

当用户提交年龄时,使用收集的年龄信息和司法管辖区调用 /age-gate/check。根据您使用的年龄收集方法,可以传递 dateOfBirthage

使用出生日期的请求示例

如果您使用日期选择器收集完整的出生日期:

POST /api/v1/age-gate/check
Content-Type: application/json
Authorization: Bearer your-api-key

{
"jurisdiction": "US-CA",
"dateOfBirth": "2015-04-15"
}

使用年龄的请求示例

如果您使用年龄滑块收集用户的年龄:

POST /api/v1/age-gate/check
Content-Type: application/json
Authorization: Bearer your-api-key

{
"jurisdiction": "US-CA",
"age": 9
}

使用平台年龄信号的请求示例

您也可以包含 platformAgeSignal,可单独使用或与 dateOfBirth/age 一起使用。已验证信号无需额外验证步骤即可满足已验证年龄权限;未验证信号仍会用于年龄冲突检测。请参阅平台年龄信号

POST /api/v1/age-gate/check
Content-Type: application/json
Authorization: Bearer your-api-key

{
"jurisdiction": "US-CA",
"dateOfBirth": "2005-04-15",
"platformAgeSignal": {
"name": "apple-ios",
"ageLow": 18,
"ageHigh": 25,
"declarationType": "governmentIDChecked"
}
}

可能的响应

API 返回三种状态之一:PASSPROHIBITEDCHALLENGEPASS 时立即创建会话。CHALLENGE 响应必须先解决才能存在会话,您必须根据 challenge.type 决定如何呈现:

  • CHALLENGE_PARENTAL_CONSENT:声明的年龄太小,无法在没有父母同意的情况下继续;可信成人必须批准(步骤 4)。
  • CHALLENGE_AGE_GATE_AGE_ASSURANCE:声明的年龄足以跳过父母同意,但您的产品在此司法管辖区启用了 Automatic age assurance,玩家必须证明该声明(步骤 4b)。

PASS: 用户可以继续

用户的年龄允许立即访问。创建具有所有权限的会话。

{
"status": "PASS",
"session": {
"sessionId": "608616da-4fd2-4742-82bf-ec1d4ffd8187",
"ageStatus": "LEGAL_ADULT",
"dateOfBirth": "2005-04-15",
"jurisdiction": "US-CA",
"permissions": [...],
"status": "ACTIVE"
}
}

操作:存储 sessionId 并允许用户继续。

PROHIBITED: 用户被阻止

用户的年龄低于为您的产品配置的最低年龄。

{
"status": "PROHIBITED"
}

操作:显示适合年龄的消息并阻止访问。

CHALLENGECHALLENGE_PARENTAL_CONSENT:需要家长同意

用户的年龄需要可验证的家长同意(VPC)。创建挑战供受信任的成人批准。

{
"status": "CHALLENGE",
"challenge": {
"challengeId": "683409f1-2930-4132-89ad-827462eed9af",
"oneTimePassword": "PP5BUS",
"type": "CHALLENGE_PARENTAL_CONSENT",
"url": "https://family.k-id.com/authorize?otp=PP5BUS"
}
}

操作:存储 challengeId 并显示受信任成人挑战屏幕(参见步骤 4)。

CHALLENGECHALLENGE_AGE_GATE_AGE_ASSURANCE:Automatic age assurance

当玩家声明的年龄足以跳过父母同意,并且您的产品在该司法管辖区启用了 Automatic age assurance 时返回。在创建会话之前,玩家必须证明该声明(面部年龄估计或 ID 文件)。不涉及可信成人,也不签发 OTP。

{
"status": "CHALLENGE",
"challenge": {
"challengeId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "CHALLENGE_AGE_GATE_AGE_ASSURANCE",
"url": "https://family.k-id.com/age-gate/verify?token=..."
}
}

操作:存储 challengeId 并将 challenge.url 嵌入到 iframe 中(参见步骤 4b)。

步骤 4:构建受信任成人挑战屏幕

当您收到 CHALLENGE 响应时,显示一个屏幕,允许用户联系受信任的成人以获取同意。挑战响应提供了您所需的一切。

UX 指南

有关监护人同意流程的详细设计建议,包括布局示例和实现技巧,请参阅 UX 指南中的监护人同意

挑战屏幕选项

您的挑战屏幕应为受信任的成人提供三个选项来完成同意:

选项 1:电子邮件通知

允许用户输入受信任成人的电子邮件地址。提交后,调用 /challenge/send-email 发送同意请求电子邮件。

POST /api/v1/challenge/send-email
Content-Type: application/json
Authorization: Bearer your-api-key

{
"challengeId": "683409f1-2930-4132-89ad-827462eed9af",
"email": "parent@example.com"
}

选项 2:二维码

显示从 challenge.url 字段生成的二维码。受信任的成人可以用手机扫描此二维码直接访问同意门户。

// 从挑战 URL 生成二维码
const qrCodeUrl = challenge.url;
// 使用二维码库渲染: "https://family.k-id.com/authorize?otp=PP5BUS"

选项 3:手动输入代码

显示 challenge.oneTimePassword 并指示受信任的成人访问 asktoplay.com 并输入代码。

挑战屏幕实现示例

<div id="challenge-screen">
<h2>请让受信任的成人帮助您</h2>
<p>请让受信任的成人使用以下方法之一完成设置。</p>

<div class="options-container">
<!-- 选项 1:电子邮件 -->
<div class="option">
<h3>选项 1</h3>
<p>输入受信任成人的电子邮件</p>
<form id="email-form">
<label for="email">电子邮件</label>
<input type="email" id="email" placeholder="电子邮件地址" required />
<button type="submit">提交</button>
</form>
</div>

<!-- 选项 2:二维码 -->
<div class="option">
<h3>选项 2</h3>
<p>请让受信任的成人扫描或点击二维码。</p>
<div id="qr-code">
<!-- 从 challenge.url 渲染二维码 -->
</div>
</div>

<!-- 选项 3:手动代码 -->
<div class="option">
<h3>选项 3</h3>
<p>
前往 <a href="https://asktoplay.com">asktoplay.com</a> 并输入以下代码。
</p>
<div class="code-display">
<span id="otp-code">PP5BUS</span>
<button onclick="copyCode()">复制</button>
</div>
</div>
</div>

<button id="do-later">稍后再做</button>
</div>

存储挑战

在等待同意期间,存储 challengeId。您可以将其存储在本地存储、与用户账户关联的数据库或适合您平台的任何其他持久存储中。如果用户在同意授予之前返回您的应用,请使用 /challenge/get 检索挑战以恢复挑战屏幕。

// 创建挑战时存储(使用 localStorage 的示例)
localStorage.setItem("pendingChallenge", challengeId);

// 应用重启时,检查待处理的挑战
const pendingChallenge = localStorage.getItem("pendingChallenge");
if (pendingChallenge) {
// 获取挑战详情并显示挑战屏幕
const challenge = await fetchChallenge(pendingChallenge);
showChallengeScreen(challenge);
}

步骤 4b:处理自动年龄保证挑战

如果您的产品未启用 Automatic age assurance,请跳过此步骤。当 challenge.typeCHALLENGE_AGE_GATE_AGE_ASSURANCE 时,玩家自行验证;不要显示步骤 4 中的可信成人屏幕。

嵌入验证 iframe

直接将 challenge.url 渲染到 iframe 中。该 iframe 处理面部年龄估计或 ID 文件验证,具体取决于司法管辖区和产品配置。

<iframe
id="age-assurance-widget"
src="CHALLENGE_URL"
width="100%"
height="600"
frameborder="0"
allow="camera;payment;publickey-credentials-get;publickey-credentials-create">
</iframe>

allow 属性是必需的:

  • camera:面部年龄估计
  • payment:基于信用卡的验证
  • publickey-credentials-get / publickey-credentials-create:WebAuthn / AgeKey

监听 Verification.Result DOM 事件

当玩家完成时,iframe 会发送 Verification.Result 消息。此消息仅用于响应式 UI 更新;为了数据完整性,请等待步骤 5 中的 webhook 或轮询 /challenge/get-status

window.addEventListener("message", (event) => {
if (!event.origin.endsWith(".k-id.com")) {
return;
}

const message = event.data;
if (message?.eventType === "Verification.Result") {
if (message.data.status === "PASS") {
// 玩家验证成功。
// 会话正在创建 - 等待 Challenge.StateChange webhook
// 或轮询 /challenge/get-status 以检索 sessionId。
closeAgeAssuranceIframe();
} else if (message.data.status === "FAIL") {
// 玩家不符合年龄标准。未创建会话。
closeAgeAssuranceIframe();
showVerificationFailedMessage();
}
}
});

有关详细的事件结构,请参阅 Verification.Result

方面CHALLENGE_PARENTAL_CONSENT(步骤 4)CHALLENGE_AGE_GATE_AGE_ASSURANCE(步骤 4b)
谁来验证可信成人玩家本人
oneTimePassword存在不存在
challenge.urlhttps://family.k-id.com/authorize?otp=...https://family.k-id.com/age-gate/verify?token=...
UI邮件 / 二维码 / OTP 输入带摄像头权限的 iframe
/challenge/send-email适用不适用
会话创建时间成人批准后创建玩家通过验证后创建

步骤 5:处理 Webhook 事件

配置您的 webhook 端点以在挑战状态更改或会话被删除时接收事件。

Challenge.StateChange 事件

无论是父母同意挑战还是自动年龄保证挑战,解决时都会触发此事件。只要创建了挑战(即 /age-gate/check 响应为 status: "CHALLENGE"),就会发送此事件。

{
"eventType": "Challenge.StateChange",
"data": {
"id": "683409f1-2930-4132-89ad-827462eed9af",
"productId": 42,
"status": "PASS",
"dob": "2015-04-15",
"sessionId": "0ad1641f-c154-4c2-8bb2-74dbd0de7723",
"approverEmail": "parent@example.com",
"kuid": "7a1f2c3d-4e5f-6789-abcd-ef0123456789"
}
}

approverEmaildobkuid 等字段是可选的;您收到的字段集取决于产生该挑战的流程。使用 challengeId 将事件关联回您发出的挑战。有关完整的字段约定,请参阅 Challenge.StateChange

状态描述操作
PASS同意已授予存储 sessionId 并允许访问
FAIL同意被拒绝显示适当消息,用户无法继续
IN_PROGRESS挑战仍在等待中继续等待

处理成功的同意

当您收到 PASS 状态时(通过 webhook 或轮询),响应中包含 sessionId。您应该:

  1. 获取会话权限:使用 sessionId 调用 /session/get 以获取包含权限的完整会话详情。
GET /api/v1/session/get?id=0ad1641f-c154-4c2-8bb2-74dbd0de7723
Authorization: Bearer your-api-key

响应示例:

{
"session": {
"ageStatus": "DIGITAL_MINOR",
"dateOfBirth": "2015-04-15",
"etag": "6d9d24fccd428f845b355122799948dd0a52fc5d",
"jurisdiction": "US-CA",
"kuid": "7a1f2c3d-4e5f-6789-abcd-ef0123456789",
"permissions": [
{
"enabled": true,
"managedBy": "GUARDIAN",
"name": "text-chat-private"
},
{
"enabled": false,
"managedBy": "GUARDIAN",
"name": "voice-chat"
}
],
"sessionId": "0ad1641f-c154-4c2-8bb2-74dbd0de7723",
"status": "ACTIVE"
},
"status": "PASS"
}
  1. 将存储的挑战替换为会话:清除 challengeId,改为存储 sessionId。这表示用户现在拥有一个已授予权限的活跃会话。
// 清除待处理的挑战并存储活跃会话
user.challengeId = null;
user.sessionId = data.sessionId;
  1. 应用权限:使用会话响应中的权限在您的应用程序中启用或禁用功能。

Session.Delete 事件

当会话被删除时(例如,家长通过 Family Connect 撤销访问权限时),会触发此事件。

{
"eventType": "Session.Delete",
"data": {
"id": "0ad1641f-c154-4c2-8bb2-74dbd0de7723",
"productId": 42
}
}

操作:删除存储的会话并要求用户再次完成年龄门控流程。

Webhook 处理程序示例

app.post("/webhook/k-id", (req, res) => {
const { eventType, data } = req.body;

switch (eventType) {
case "Challenge.StateChange":
if (data.status === "PASS") {
// 授予访问权限 - 为用户存储 sessionId
grantAccess(data.sessionId, data.kuid);
} else if (data.status === "FAIL") {
// 拒绝访问
denyAccess(data.id);
}
break;

case "Session.Delete":
// 撤销访问权限 - 用户必须再次完成年龄门控
revokeSession(data.id);
break;
}

res.status(200).send("OK");
});

轮询作为备用方案

如果 webhook 不可用,您可以轮询 /challenge/get-status 来检查挑战状态:

GET /api/v1/challenge/get-status?id=683409f1-2930-4132-89ad-827462eed9af
Authorization: Bearer your-api-key
轮询限制

轮询时,请求之间至少等待 5 秒。如果轮询过于频繁,API 可能返回 HTTP 429。

Webhook 配置

对于自定义年龄门控流程,请确保您的 webhook 端点配置为接收:

有关验证 webhook 签名的信息,请参阅 Webhooks

下一步

现在您已经实现了自定义年龄门控,请探索这些资源以增强您的集成:

通过 k-ID 的自定义年龄门控集成,您可以构建完全品牌化的合规体验,同时 k-ID 处理复杂的司法管辖区逻辑和家长同意流程。