CDK

嵌入式流程

CDK 提供一个端到端小部件,在单个界面中处理完整的可验证父母同意 (VPC) 流程,在一个无缝体验中涵盖年龄门控、VPC、数据通知、权限和偏好设置。

在原生应用中,我们推荐原生 UX

在原生移动应用、主机平台以及其他不在浏览器中运行的环境中,我们推荐使用自定义工作流并遵循 CDK UX 指南,原生构建年龄门控和同意 UX。这能为玩家提供最无缝、最贴合品牌的体验。

端到端和年龄门控小部件在这些环境中同样受到完全支持:

  • 移动端:使用与验证 URL 相同的系统浏览器组件展示小部件 URL,并通过 redirectUrl 回调接收结果。有关显示方法,请参阅移动应用指南。
  • 主机平台:将小部件的短 URL 显示为二维码,让玩家在手机上完成流程,并通过 widgetId 在服务器上获取结果。请参阅在服务器上获取结果。

什么是端到端小部件?

端到端小部件是一个全面的解决方案,在单个界面中处理完整的合规流程,在一个无缝体验中涵盖年龄门控、VPC、数据通知、权限和偏好设置。此小部件可以由父母在孩子的设备上或他们自己的设备上使用,为同意过程提供最大的灵活性。

生成小部件 URL

调用 /widget/generate-e2e-url API 创建处理完整 VPC 流程的端到端小部件 URL。这将返回一个唯一 URL,供用户完成年龄收集和父母同意过程。

请求示例

POST /api/v1/widget/generate-e2e-url
Content-Type: application/json
Authorization: Bearer your-api-key

{
  "jurisdiction": "US-CA"
}

配置标志

可选的 flags 参数允许您自定义要跳过的流程部分:

  • skipDataNotices:跳过数据通知和同意收集
  • skipVerification:跳过验证步骤
  • skipPermissions:跳过权限管理
  • skipPreferences:跳过偏好设置

传递平台年龄信号(可选)

如果您的游戏已有来自平台的年龄数据(Apple iOS、Google Play、Xbox、Meta Horizon 或先前的 k-ID 验证),请将其作为 platformAgeSignal 包含在请求正文中。小部件会将该信号转发给底层的年龄门控检查,因此可以在已验证信号表明是成人时跳过年龄门控、无需额外验证步骤即可满足已验证年龄权限,并检测信号与玩家自报年龄之间的冲突。

POST /api/v1/widget/generate-e2e-url
Content-Type: application/json
Authorization: Bearer your-api-key

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

有关支持的平台和字段形式,请参阅平台年龄信号。

响应示例

{
  "url": "https://family.k-id.com/widget?token=eyJhbGciOiJFUzM4NCIs...",
  "shortUrl": "https://family.k-id.com/s/2f6c1d3e-8a4b-4c5d-9e6f-7a8b9c0d1e2f?pid=42&s=qr",
  "widgetId": "2f6c1d3e-8a4b-4c5d-9e6f-7a8b9c0d1e2f"
}
  • url:完整的小部件 URL,用于嵌入或打开。
  • shortUrl:重定向到 url 的短 URL,适合用于二维码。
  • widgetId:此小部件流程的 ID。如果结果需要在没有 DOM 事件或重定向的情况下到达你的服务器,请将其与玩家关联保存。请参阅在服务器上获取结果。

展示小部件 URL

小部件 URL 是一个托管的网页。请在适合您应用程序的环境中打开它:

  • 网站或 Web 应用:嵌入到 iframe 中(参见下方示例)、在弹出窗口中打开,或整页重定向。
  • 移动应用:为获得最贴合品牌的体验,我们推荐使用自定义工作流原生构建年龄门控和同意 UX。如需使用小部件,请在系统浏览器组件(Android 上的 Custom Tabs,iOS 上的 ASWebAuthenticationSession)中打开小部件 URL,并传递 options.redirectUrl,以深度链接的形式将结果返回到你的应用。有关显示方法,请参阅移动应用快速入门指南。
  • 主机平台(Switch、PlayStation、Xbox):我们推荐使用自定义工作流原生构建年龄门控和同意 UX,仅在家长或玩家需要在手机上操作时,将挑战 URL 显示为二维码。如需使用小部件,由于主机浏览器通常受限或不存在,请将 shortUrl 显示为二维码,让玩家在手机上完成流程。手机无法重定向回主机,因此请通过 widgetId 在服务器上获取结果。请参阅在服务器上获取结果。

小部件内可用的方法会自动适应司法管辖区要求,与托管环境无关。

Web 示例

<div id="vpc-container">
  <iframe 
    id="vpc-widget"
    src="WIDGET_URL" 
    width="100%" 
    height="600"
    frameborder="0"
    allow="camera;payment;publickey-credentials-get;publickey-credentials-create">
  </iframe>
</div>

对于移动环境,调用 /widget/generate-e2e-url 时传递 options.redirectUrl,并在应用中处理回调。端到端示例请参阅移动应用快速入门指南。

处理事件

DOM 事件可达的位置

下方的 JavaScript 事件(Widget.AgeGate.Result、Widget.AgeGate.Challenge、Widget.ExitReview)通过 postMessage 传递。要接收它们,宿主需要对小部件的 window 有一个活跃的 JavaScript 监听器。这包括 iframe 宿主、通过 window.open 打开的弹出窗口,以及带 JS 桥的移动端 WebView / WKWebView(参阅移动应用快速入门)。系统浏览器组件(ASWebAuthenticationSession、SFSafariViewController、Chrome Custom Tabs)和整页顶层重定向不暴露监听器,因此通过 redirectUrl 回调接收结果,并使用 /session/get 或 webhooks 在服务器端确认。

当年龄门控流程完成时,会创建会话以存储玩家的权限和年龄状态。每当流程需要挑战时都会创建挑战,用于可验证的家长同意,或在玩家申报的年龄足以跳过家长同意时用于 Automatic age assurance。

小部件发出您可以监听的 JavaScript 事件。请监听 Widget.AgeGate.Result 事件,其 data.status 表示流程如何结束:

  • PASS:玩家通过了年龄门控,并已创建会话。存在 data.sessionId。
  • FAIL:所需的挑战未通过(例如受信任的成人拒绝了家长同意,或玩家未完成 Automatic age assurance)。不会创建会话。
  • PROHIBITED:玩家未达到该产品在此管辖区的最低年龄,无法继续。不会创建会话;data.age 包含所输入的年龄。请阻止玩家继续。

如果流程中创建了挑战,事件还会包含 challengeId。有关挑战相关事件的详细信息,请参阅 Widget.AgeGate.Challenge。

关闭 UI

要确定何时关闭小部件 UI,请监听 Widget.ExitReview 事件。当用户单击"完成"按钮时会发出此事件,表示流程已完成,应关闭或隐藏 iframe。

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

  const message = event.data;

  if (message.eventType === 'Widget.AgeGate.Result') {
    if (message.data.status === 'PASS') {
      const sessionId = message.data.sessionId;

      // 如果存在 challengeId,则表示流程中解决了一个挑战
      // (例如家长同意或 Automatic age assurance)。
      if (message.data.challengeId) {
        console.log('Challenge resolved, session issued:', sessionId);
      } else {
        console.log('Session created (no challenge required):', sessionId);
      }

      grantAccess(sessionId);
    } else if (message.data.status === 'FAIL') {
      // 所需的挑战未通过(例如家长同意被拒绝,或
      // Automatic age assurance 未完成)。无会话。
      console.log('Age gate not passed');
      restrictAccess();
    } else if (message.data.status === 'PROHIBITED') {
      // 玩家未达到该产品的最低年龄。
      // 不会创建会话。阻止其继续。
      console.log('Player below minimum age:', message.data.age);
      restrictAccess();
    }
  }

  // 如有需要,处理挑战相关事件
  if (message.eventType === 'Widget.AgeGate.Challenge') {
    if (message.data.status === 'FAIL') {
      // 家长拒绝同意。限制访问
      console.log('Consent denied');
      restrictAccess();
    }
  }

  if (message.eventType === 'Widget.ExitReview') {
    // 当用户单击"完成"时关闭小部件 UI
    closeWidget();
  }
});

通过重定向 URL 接收结果

系统浏览器组件(ASWebAuthenticationSession、SFSafariViewController、Chrome Custom Tabs)和整页顶层重定向无法暴露 DOM 监听器,因此改为通过 redirectUrl 回调接收年龄门控结果。在调用 /widget/generate-e2e-url 或 /widget/generate-age-gate-url 时传入 options.redirectUrl。该 URL 支持 http(s) 和带自定义协议方案的移动深度链接(例如 myapp://age-gate/return)。

当年龄门控流程完成时,k-ID 会将 Widget.AgeGate.Result 事件所携带的相同结果字段作为查询参数追加,并将玩家导航到您的 URL。重定向 URL 上的现有查询参数将被保留。

参数说明
statusPASS、FAIL 或 PROHIBITED(见下文)
sessionId新创建的会话。仅在 status 为 PASS 时存在。
challengeId流程中涉及的挑战(如果创建了)。
age玩家输入的年龄。当 status 为 PROHIBITED 时存在。

status 的值与 DOM 事件一致:

  • PASS:玩家通过了年龄门控,并已创建会话。使用 sessionId 授予访问权限。
  • FAIL:所需的挑战未通过(例如家长同意被拒绝,或 Automatic age assurance 未完成)。不会创建会话。
  • PROHIBITED:玩家未达到该产品在此管辖区的最低年龄。不会创建会话。请阻止玩家继续。

例如,未达到最低年龄的玩家将被导航到:

myapp://age-gate/return?status=PROHIBITED&age=8

请将重定向查询参数视为 UX 提示,而非可信结果。在授予访问权限之前,请通过 Challenge.StateChange webhook 或 /session/get 在服务器端确认结果。

在服务器上获取结果

当玩家在另一台设备上完成小部件时,例如用手机扫描主机上显示的二维码,DOM 事件和重定向都无法到达你的应用。此时由你的服务器从 k-ID 获取结果:轮询小部件状态、使用 webhook,或两者兼用,并以 /widget/generate-e2e-url 和 /widget/generate-age-gate-url 返回的 widgetId 作为键。其他小部件端点不返回 widgetId。

在主机平台上,我们推荐原生 UX

我们推荐使用自定义工作流原生构建年龄门控和同意 UX,仅在需要挑战时,将挑战 URL 显示为二维码转交到手机。下面的小部件流程在主机平台上同样可用,但玩家需要在手机上完成整个流程。

  1. 从服务器生成小部件 URL,并在显示二维码之前将 widgetId 与玩家关联保存。将 shortUrl 视为不透明值,按返回的原样编码。
  2. 轮询 GET /widget/get-status(一次最多 100 个小部件时使用 POST /widget/get-status),或通过 webhook 获取结果。请参阅通过 webhook 接收结果。
  3. 如果是 PASS,使用返回的 sessionId 调用 /session/get 读取会话,并在你这边保存结果。k-ID 只在一段时间内保留小部件状态,之后会返回 NOT_FOUND。
状态含义处理方式
PENDING玩家尚未完成年龄门控。expiresAt 表示 URL 失效的时间。继续显示二维码,每隔几秒轮询一次。
PASS某次尝试已通过,并设置了 sessionId。小部件一旦报告 PASS,就会一直报告 PASS。读取会话并允许玩家进入。
IN_CHALLENGE家长同意等挑战尚未完成。请查看 challengeType 和 challengeStatus。告诉玩家需要由谁来操作。家长可能需要数天,因此请放慢轮询频率或依赖 webhook。
FAILED挑战失败,例如家长拒绝了请求。不允许玩家进入。
PROHIBITED玩家无法继续。prohibitedReason 为 BELOW_MINIMUM_AGE 或 JURISDICTION_UNAVAILABLE。不允许玩家进入。
EXPIREDURL 在任何尝试之前已过期,或挑战已无法再处理。停止轮询。

流程结束后(通过、禁止、失败或过期),无论在哪台设备上,都无法用同一个二维码重新开始。如需让玩家重试,例如年龄输入错误时,请生成一个新的小部件。widgetId 是 shortUrl 的一部分,因此不是机密,但只有生成它的产品才能读取其状态;其他产品会收到 NOT_FOUND。

通过 webhook 接收结果

如需以推送代替轮询来获取挑战结果,请订阅 Challenge.StateChange,从小部件发起的挑战会携带 widgetId。最常见的无挑战通过和禁止结果不会发送 webhook,因此请继续通过轮询获取这两种结果。

/widget/get-status 是小部件结果的权威来源。Webhook 报告的是相同的结果,但它采用至少一次投递,可能会丢失,因此在未收到 webhook 时,请将轮询作为后备手段。

通过 Safe Start 之后,即使家长后来拒绝同意,小部件也会继续报告 PASS。你会收到同意挑战的 FAIL Challenge.StateChange,但 Safe Start 会话仍保持有效,同意状态可通过 challengeStatus 查看。

平台年龄信号

只有经过验证的 Apple iOS 或 Google Play 信号才能让玩家跳过年龄输入界面。Xbox 和 Meta Horizon 信号会被正常接受,但玩家仍需在手机上输入年龄。

小部件处理的内容

小部件自动处理:

  • 年龄收集:符合司法管辖区要求的年龄收集方法
  • 数据通知:根据 Compliance Studio 中的产品配置接受的数据通知
  • 权限:根据 Compliance Studio 中的产品配置管理的权限
  • 父母同意挑战:如果确定用户是未成年人,则会创建挑战以供可信成人批准
  • Automatic age assurance:如果产品在该司法管辖区启用了 Automatic age assurance,那些声明年龄足以跳过父母同意的玩家将被要求在小部件内证明所声明的年龄(面部年龄估计或 ID 文件),然后才能签发会话。

具体流程取决于司法管辖区和您在 Compliance Studio 中的产品配置。

启用 Automatic age assurance 后的会话创建时机

当 Automatic age assurance 在端到端小部件内触发时,会话是在玩家通过验证之后创建,而不是在年龄门控之后立即创建。流程完成后仍会触发 Widget.AgeGate.Result 事件,并在 PASS 时包含 sessionId。在该事件到达之前,请将流程视为正在进行中。

有关实施 VPC 的更多信息,请参阅 快速入门指南。

On this page