号码检查、OTP、身份核验:各自回答什么问题?
注册流程里有一个手机号字段,团队中有人要求对它进行“验证”。这个词其实包含三个不同的问题。号码检查描述的是号码本身:格式是否正确、是否有效,以及可能属于哪种线路。OTP 检验的是正在输入的人此刻能否通过某个渠道收到验证码。身份核验评估的是关于某个人的身份声明。三者都不能替代彼此的答案:移动线路不能说明号码归谁所有,验证码送达不能确立身份,Approved 的身份结果本身也不授予任何权限。本文帮助你判断注册任务需要哪一种证明,并展示 GridZen 可下载的合成示例如何把三者分开处理。
curl https://gridzen.ai/console/api/quickstart/trial \
-H 'Content-Type: application/json' \
-d '{"task": "signup_phone", "scenario": "success"}'决策:先明确问题,再选择证明
首先写下注册这一步必须确认什么。如果只是想在创建账户前拦截输入错误和格式不正确的号码,你需要的是号码属性。如果需要确认这个人能在该号码上收到消息,你需要的是渠道访问证明,也就是通过你自己运营的服务商发送 OTP。如果后续操作(例如允许卖家上架商品)取决于这个人是谁,你需要通过托管引导流程核验身份声明,然后再应用你自己的业务规则。常见的错误是把较弱的信号当作较强的信号:把有效的手机号当作归属证明,或者把 Approved 当作权限。每种证明都有各自的失败模式、重试方式和负责方,因此应把它们放在独立的字段和独立的代码路径中。
可下载示例实际实现了什么
指南中的 verification_delivery 示例是一个无依赖的 Python 3.10+ 脚本,不需要凭据,也不发起任何网络调用。它的 phone 命令有四个分支:normal、unknown、provider_failure 和 otp。identity 命令使用本地 SQLite 文件保存夹具会话和事件。每个输出都带有 mode: synthetic_fixture。phone 各分支始终返回 otp_sent: false 和 live_line_type_available: false。identity 路径只接受来源为 fixture_callback 的合成事件,没有生产环境的签名验证器。它记录的任何权限都只是本地夹具标记,每次读取身份状态都会返回 live_permission_changed: false。这个四分支下载示例与匿名的三场景 HTTP 试用是两回事,不要把其中一个的结果当成另一个的结果。本文中超出上述输出的其他内容,都是为你自己的代码提出的应用设计建议。
假设示例:卖家注册
设想一个假设的 B2B 交易平台。新卖家输入手机号。normal 分支返回 state: simulated、valid: true、line_type: mobile 和 next: continue_customer_policy。对你的设计来说,这只说明号码看起来可用,并不涉及归属或可达性。如果团队需要渠道访问证明,otp 分支会明确回答:state: unsupported、valid: null、line_type: null、next: customer_otp_provider。验证码必须由你自己的 OTP 服务商发送。之后,在卖家可以上架商品之前,identity 路径会创建一个会话。创建步骤返回 state: simulated、session: fixture-session、一个位于 example.invalid 的 hosted_link、link_executable: false 以及 next: send_real_provider_link_only_after_authorization。一个 policy 为 true 的合成 Approved 事件返回 status: approved、deadline: 60、permission_granted: true 和 grant_count: 1。在这个流程中,号码检查结果、OTP 结果和身份状态各自保存在独立字段中。
由调用方负责的失败处理
号码结果为未知并不等于拒绝。unknown 分支返回 state: unknown,next 为 review_or_existing_otp。provider_failure 分支返回同样的 unknown 状态,next 为 bounded_retry_then_review。你的设计应限制重试次数,然后把注册转入人工审核,而不是静默拦截。在身份这一侧,重复事件不会改变任何内容:输出仍为 approved,grant_count 为 1。随后到达的 Declined 事件被视为更正,会把 status 设为 declined、permission_granted 设为 false,而 grant_count 仍为 1。如果在截止时间前没有收到决定,recover 会把处于 pending、review 或 unknown 的会话标记为 expired,并返回 permission_granted: false、grant_count: 0、next: review_or_authorized_provider_refresh 和 provider_polled: false。过期在本地完成,recover 从不联系服务商。会话或 tenant/subject 绑定不匹配的事件会以 BINDING_MISMATCH 被拒绝。读取不存在的会话会返回 SESSION_NOT_FOUND。
真实路由的限制
截至 2026-10-17,符合条件的美国和英国企业可以自助注册免费的基础号码路由。它只检查格式和有效性,每个已验证的企业域名最多 10 次请求,不请求线路类型、号码归属、可达性或 OTP 发送。另有托管身份核验试用,需要单独接受 Didit 附加条款,限额为每个已验证的企业域名 2 次请求、共享终身总量 10 次请求。新获批的用户以及客户随后的业务操作,尚未经过端到端验证。合成夹具中的权限不是真实的业务权限,匿名试用也不代表生产环境批准。
验收清单
运行全部四个 phone 分支,确认每个分支输出的 state、valid、line_type 和 next 与上文完全一致,并且每次都是 otp_sent: false。确认你的注册流程把号码属性、OTP 结果和身份状态分别存放在独立字段中。确认 unknown 和 provider_failure 会进入有上限的重试或人工审核,绝不会自动拒绝。运行 python3 -m unittest -v test_delivery,确认重复事件、更正、过期和绑定错误等用例全部通过。确认没有任何代码路径会因夹具值或仅凭 Approved 授予真实权限。在处理任何真实用户之前,另行选择经过授权的真实 OTP 或身份核验路由。如果这些检查符合你的任务需求,下一步可以保存一个沙盒请求。
运行已检查的模拟示例
下载并解压 https://gridzen.ai/downloads/verification-delivery.zip,进入 verification_delivery,用 Python 3.10+ 运行。此本地示例不联网;独立身份实验请使用新的数据库。
python3 delivery.py phone --branch unknown
python3 delivery.py phone --branch provider_failure来源与范围
https://gridzen.ai/guides/phone-otp-identity/ https://gridzen.ai/guides/current-capabilities/ 依据已检查的来源快照与本地模拟;建议的业务规则属于设计建议,不代表已证明的生产行为。内容使用 AI 辅助写作,并经过独立审稿。
下载不等于运行成功;本地示例与在线沙盒分别测试。