Skip to content
全部文章

将验证状态映射到您的产品状态

查尔斯·阿奇邦

, 联合创始人

从状态和结果出发驱动你的产品,并在旁边存储 checkStatus 作为解释。将十个状态值分别映射到你系统中的一个状态,并从 verification.status_updated 或轮询中获取最新状态。

标题“状态已入,产品状态已出”,旁边配有三个上升层级的插图,背景为深靛蓝色渐变。

要点

  • 根据状态(即结果)采取行动;将 checkStatus 保留为自动检查发现的内容的记录。
  • 已批准但 checkStatus 失败是一个已记录的异常,而不是矛盾,值得记录。
  • 使用工作流决策规则时,verification.completed 不是最终结果;最终结果以 verification.status_updated 的形式给出。
  • 如果结果为失败或找不到,请向申请人说明原因;​​如果错误信息是发给您的,则不要向申请人说明原因。

您的产品状态应仅根据一个字段改变:status这是验证的结果,取十个值之一,从not_started到approved。 店铺checkStatus旁边会记录自动检查的结果,因为这是两者结果不一致时你唯一能找到的解释。它们出现分歧的频率比工程师预期的要高,而这正是设计使然。

实际上,这意味着三件事:绘制十个地图中的每一个status在你的系统中,值恰好对应一个状态,取最新值status从verification.status_updated使用 webhook 或轮询方式,而不是从收到的第一个事件开始,并使用reasonCode(绝不能只看原始状态)来决定如何告知申请人。本文的其余部分会将此信息转化为您可以直接使用的表格和您可以修改的代码。

为什么有两个字段而不是一个?

因为验证结果由两部分决定。自动化检查会读取文件、查询政府记录、进行人脸比对并读取芯片信息。然后,您的工作流程决策规则或团队成员会决定如何处理此人。验证生命周期文档 (在新标签页中打开)为每个问题都设置了单独的字段。

场地

它回答的问题

价值观

status

结果如何?

not_started,in_progress,processing,in_review,awaiting_resubmission,approved,declined,abandoned,expired,error

checkStatus

检查结果如何?

pending,verified,failed,not_found,error

status规则的制定遵循固定的顺序,首先适用的规则优先:首先是审核员的决定,然后是工作流程的决定,最后是仅凭检查结果做出的决定。checkStatus不会因为规则或审核员而改变。因此,即使申请人的面部特征与照片不符,但如果审核员在视频通话后批准了该申请人,最终结果仍然是:status: "approved"和checkStatus: "failed"两者都正确,如果只存储第一个,以后就无法解释为什么允许这种例外情况。

每个状态应该如何与您的产品对应?

以下是一个示例贷款应用程序的映射图。账户状态和消息均为示例;右侧列是需要复制的部分。

status

解决了吗?

您的账户状态(示例)

申请人所看到的(示例)

你的后端做什么

not_started

不

kyc_invited

“完成身份验证”

目前还没有。如果您使用链接,请发送提醒。

in_progress

不

kyc_in_progress

“继续你未完成的工作”

目前还没有消息。

processing

不

kyc_checking

我们正在核实您的信息。

稍等。阅读waitingOn如果需要很长时间。

in_review

不

kyc_manual_review

“我们团队的一名成员正在审核此事。”

等等。牵涉到个人了。

awaiting_resubmission

不

kyc_action_needed

审稿人的要求,以及重新进入状态的方法

请等待新的尝试。

approved

是的

kyc_passed

提供产品访问权限

保存结果,继续新用户引导流程。

declined

是的

kyc_failed

消息选自reasonCode

决定是否允许再次尝试。

abandoned

是的

kyc_dropped

“重新开始”

提供新的验证方式。

expired

是的

kyc_link_expired

您的链接已过期

发送新链接。

error

是的

kyc_retry

一句笼统的“出错了”

请重试或联系客服。您无需支付任何费用。

即使你把表格中的每个州都重新命名,表格中的两个设计选择也值得借鉴。

首先,每个值对应一个状态,即使两个值看起来相同。很容易会想把两个值合并起来。in_review进入processing因为两者都表示“等待”。请将它们分开:in_review这意味着你团队中的某人需要做出决定,这是一个内部队列指标,而processing平台运行正常吗?

第二,declined是一种状态,并附带原因。政府数据库中没有的ID是……declined和reasonCode: "identity_not_found",而不是一种单独的状态。分支应基于代码,而不是基于状态的激增。

处理过程实际上会等待什么?

通常情况下,只需要进行检查。但如果你的工作流程将决策推迟到较慢的环节,processing持续时间更长,状态端点包括waitingOn说什么。价值观包括"screening"(制裁,) PEP 或不良媒体筛查)和"key_people"(企业申请中的董事和所有者完成他们自己的核实过程);null表示检查仍在运行。状态端点参考 (在新标签页中打开)列出了这些检查。

这个字段可以让申请人筛选页面显示比加载指示器更有用的信息,也能让运营团队了解为什么某个业务申请会被拒绝。processing两天。

为什么 verification.completed 并不总是最终结果?

因为它描述的是检查过程,而不是最终决定。这些事件verification.completed,.failed,.not_found和.error检查完成后立即发送。如果您的工作流包含决策规则,则在工作流做出决策之前发送,并且这些规则会立即发送。status仅反映支票金额。

判决随后出炉。verification.status_updated, 和source: "workflow"当审核员在仪表板中做出决定时,也会触发相同的事件(source: "dashboard"或者由后端通过审核来决定 API (source: "api"它携带previousStatus以及新的status而且,与大多数活动不同,它是以扁平形式交付的,而不是用包装纸包裹的。data正如webhooks 文档中 (在新标签页中打开)所述,对象。

一个具体的例子能使陷阱更加明确。工作流程会拒绝任何与监控名单匹配的申请人。申请人通过了所有审核:verification.completed到达时status: "approved"几秒钟后,筛查等待结束,工作流程也随之减少。verification.status_updated到达时status: "declined"和source: "workflow"如果你的客服人员在第一次活动中就为客户办理了入职手续,那么你就为一位违反你公司政策的客户办理了入职手续。

规则:如果你的工作流程有决策规则,请谨慎处理。verification.status_updated(或新的民意调查)作为信息来源status如果没有决策规则,检查本身就是决策。verification.completed除非审阅者之后修改,否则此版本为最终版本。

你应该告诉申请人什么?

使用reasonCode选择消息并使用reason小心。

在failed或者not_found查看,reason这份文件是为申请人编写的:使用通俗易懂的语言,说明哪些地方不匹配以及下一步该怎么做,不涉及评分、门槛,也不引用任何政府记录。您可以直接展示这份文件。

在error结果,reason是为您写的。它可能写着“充值余额”或“联系我们”。 Toden 这对申请人来说毫无意义。向他们显示一条通用消息并进行路由。reason致你的团队。

reasonCode(举例)

建议申请人采取的下一步行动

document_blurry,document_unreadable

再拍一张照片

document_expired

使用现有文档

selfie_mismatch

重新拍一张自拍

identity_not_found,gov_data_mismatch

请核对输入的号码和详细信息。

provider_error,system_error

通用重试消息

随着时间的推移,可能会添加新的代码,因此请将无法识别的代码映射到通用故障消息,而不是映射到代码中的错误。

这用代码怎么写?

一个用 TypeScript 编写的处理程序,用于应用上述规则。accounts通话只是你坚持不懈的占位符。

type Status =
  | 'not_started' | 'in_progress' | 'processing' | 'in_review'
  | 'awaiting_resubmission' | 'approved' | 'declined'
  | 'abandoned' | 'expired' | 'error';

const ACCOUNT_STATE: Record<Status, string> = {
  not_started: 'kyc_invited',
  in_progress: 'kyc_in_progress',
  processing: 'kyc_checking',
  in_review: 'kyc_manual_review',
  awaiting_resubmission: 'kyc_action_needed',
  approved: 'kyc_passed',
  declined: 'kyc_failed',
  abandoned: 'kyc_dropped',
  expired: 'kyc_link_expired',
  error: 'kyc_retry',
};

export async function applyVerificationStatus(event: {
  verificationId: string;
  status: Status;
  checkStatus?: string;
  reasonCode?: string | null;
  reason?: string | null;
}) {
  const state = ACCOUNT_STATE[event.status] ?? 'kyc_checking'; // unknown value: keep waiting
  await accounts.updateByVerificationId(event.verificationId, {
    kycState: state,
    kycStatus: event.status,
    kycCheckStatus: event.checkStatus ?? null, // the explanation, kept for audit
    kycReasonCode: event.reasonCode ?? null,
    kycReason: event.reason ?? null,
  });
}

从两者的角度来看verification.status_updated处理程序和轮询路径,因此两者不会发生偏移。对重复的 webhook 发送进行去重。X-Myaza-Delivery调用前请先添加头部信息。

当验证信息被发送回来时会发生什么?

它会保留自己的ID。当审核员发送验证信息后,如果申请人重新提交申请,则同一份验证信息会启动新的验证尝试:attempt增加1,checkStatus返回pending然后检查再次运行。您的映射处理这种情况时没有特殊处理:状态从awaiting_resubmission到processing然后再次稳定下来。将您的记录录入到verificationId关键在于,它不是基于“该用户的最新验证”查询,而是基于其他因素。

要了解全貌,包括之前的每一次尝试和每一个决定,请阅读以下内容。GET /api/kyc/verifications/:id从您的后端使用密钥返回。review和statusHistory按验证结果参考 (在新标签页中打开)中的描述,最新结果优先。

检查清单

  • 每个产品状态status价值,与in_review和processing分开存放。

  • checkStatus,reason和reasonCode存放于旁边status, 总是。

  • 最新的status摘自verification.status_updated或者在工作流程包含决策规则时进行轮询。

  • 申请人副本由……选择reasonCode;reason仅在failed和not_found。

  • 未知status或者reasonCode处理结果为“继续等待”或“一般故障”,绝不会作为崩溃处理。

  • 记录以……为键verificationId因此,重新提交会更新同一行。

开发者中心链接了此处使用的 webhook 和状态引用。

参考来源

查尔斯·阿奇邦

关于作者

Charles Archibong

联合创始人

Charles Archibong是Toden的联合创始人。他撰写有关身份验证、金融科技以及构建可信数字服务的实际工作的文章。

  • 开发者

    围绕异步验证结果设计您的产品

    将提交视为状态机的开始,而非请求的结束。显示一个基于验证 ID 的待处理状态,通过 Webhook 更新该状态,并允许状态多次更改。

  • 开发者

    正确验证 webhook 签名并处理重试

    对原始请求字节验证 V2 签名,拒绝重放窗口之外的时间戳,在执行任何工作之前,在唯一约束下记录事件 ID,并且只有在事件被持久存储后才返回 2xx。

  • 开发者

    幂等验证请求:避免重复检查和重复收费

    为每个逻辑验证分配一个请求 ID,在发送请求之前保存该 ID,并在每次重试时重复使用该 ID。重复的请求 ID 会返回已存在的验证,而不是创建并收取第二个验证的费用。

打造您的产品。其余交给我们。

端到端身份验证与合规服务,遵循全球标准,为创业者提供合理定价。