跳到主要内容

自定义年龄门控

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

使用基本权限简化集成

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

什么是自定义年龄门控?

自定义年龄门控是您构建和控制的年龄验证界面,而 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 获取该司法管辖区的默认会话权限。

步骤 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
}

可能的响应

API 返回三种状态之一:

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"
}

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

CHALLENGE: 需要家长同意

用户的年龄需要可验证的家长同意(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)。

步骤 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);
}

步骤 5:处理 Webhook 事件

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

Challenge.StateChange 事件

当受信任的成人批准或拒绝同意请求时,会触发此事件。

{
"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"
}
}
状态描述操作
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 处理复杂的司法管辖区逻辑和家长同意流程。