送达率回答的是错误的问题
送达回执描述的是网络对某条消息的报告。业务真正要问的问题不同:用户是否在仍处于流程中时完成了验证?这两个数字可能朝相反方向变化,只有后者才是一个真正的结果。
下面这套模型围绕结果及能解释结果的分布来构建,刻意不包含任何目标值。任何值得告警的阈值,都应来自项目自身按路由、按目的地的近期历史——一个市场、一个供 应商上的认证流程,不能和另一个直接比较。
结果指标
验证完成率。 在已发出验证码的认证会话中,验证成功的比例。这是核心指标。得出任何结论之前先做拆解。
首次尝试完成率与重发行为。 不经过重发就完成验证的比例,以及每次会话重发次数的分布。某条路由或目的地上重发率上升,往往先于完成率下降出现,因为用户会在放弃之前一直靠重发来弥补消息延迟。
重复发放。 同时存在多个仍然有效验证码的会话,以及用户输入了已被替代的验证码的会话。这两种都是可自行修复的失败,原因出在代码逻辑里。
过期与乱序验证码。 使用已过期验证码进行的验证尝试,或较早的验证码晚于较新的验证码到达。数量偏高指向延迟问题,而不是用户问题。
用户重试循环。 用户反复请求验证码却始终未完成验证的会话,以及最终放弃的会话。这正是支持工单的来源。
延迟分布
三个区间,用分布 而不是平均值来呈现,因为失败往往藏在尾部:
- 发放到接受 —— 从你的服务决定发放验证码,到供应商接受提交。这段是你自己的系统加上供应商的接收环节。
- 提交到最终状态 —— 从被接受到路由返回最终状态。这段涉及供应商、目标网络和运营商。
- 发放到验证 —— 从发放到成功完成验证。这里包含了人的因素,也是决定你设置的过期时间是否现实的关键区间。
将第三个分布与你设置的过期时间进行对比。过期时间应由这个分布和你的安全策略共同决定,而不是照抄一个默认值——同样的逻辑也定义了那个"再尝试也无济于事"的有效截止时间。
定位问题的拆解维度
聚合数据会掩盖失败,上面每个指标都需要能按以下维度拆解:
- 供应商与路由
- 国家,以及可获取时的运营商
- 发送方身份与模板
- 设备平台与应用版本
- 流量类别与场景(登录、注册、找回、支付确认)
- 一天中的时段
有两组对比值得持续关注:
- 送达率与验证率之间的差距,按路由拆分。一条报告送达率很高但验证率偏弱的路由,要么送达延迟,要么发送了用户不信任的内容,要么报告的状态没有反映真实情况。
- 验证接口的应用层错误,与用户操作错误区分开。聚合视图下,一个校验逻辑的 bug 看起来就像送达问题。
统一失败原因
供应商的错误码各不一致,因此只需做一次映射,把它们归入自己的分组,并按谁能处理来划分:
| 分组 | 示例 | 负责方 |
|---|---|---|
| 应用 | 目的地格式无效、缺少模板、接口错误 | 你的工程团队 |
| 策略 | 达到限流、目的 地被抑制、发送方未在该市场注册 | 你的运营团队 |
| 供应商 | 路由拒绝、被限流、提交出错、未返回最终状态 | 供应商管理 |
| 目标网络 | 被过滤、发送方被屏蔽、网络拒绝 | 供应商加运营商升级 |
| 收件人 | 不可达、用户暂时离线、终端状态 | 单条消息层面不可操作 |
| 未知 | 截止时间内未返回状态 | 作为数据质量问题排查 |
在统一状态之外保留供应商的原始状态和原始代码。没有这一层,更换供应商改变的是你的报表,而不是你的可靠性。
成本与滥用
- 单次成功验证的成本,而不是每条消息的成本。一条更便宜但产生更多重发、更少完成的路由,实际上更贵。
- 欺诈与滥用信号:按号码、账户和网络的请求速率;向异常或高成本目的地的集中请求;从未走向验证成功的请求。这些既影响支出,也影响其他所有指标的可信度。
在不记录验证码本身的前提下做关联
需要关联五类记录:认证会话、验证码发放、每次消息尝试、每次状态更新、以及验证结果。一个关联标识——在你的服务决定发放时生成,贯穿之后的每条记录,并同时存储在应用侧和消息侧——正是让这些数据能够关联起来的关键。
验证码本身绝不应出现在这条链路里。只存储验证提交所需的信息,如果需要把某次尝试与具体验证码关联,只保留不可逆的引用。普通的应用日志、分析工具和客服界面永远不应该包含验证码的值。
衡量数据结构
auth_session: session_id, user_ref, journey, started_at, outcome, outcome_at
otp_issuance: issuance_id, session_id, correlation_id, issued_at, expires_at,
attempt_index, superseded_by, channel_policy
message_attempt: attempt_id, correlation_id, issuance_id, provider, route,
sender_id, country, operator, submitted_at, accepted_at,
provider_message_ref, client_reference
message_status: attempt_id, canonical_status, canonical_reason, raw_status,
raw_code, status_at, is_final
verification: verification_id, session_id, issuance_id, attempted_at, result,
failure_kind, device_platform, app_version
本文的每一项指标都可以从这五张表中推导出来,其中没有一处包含验证码本身的值。
诊断表
| 观察现象 | 可能原因 | 首先要检查的 |
|---|---|---|
| 送达率报告良好,某条路由的验证率下降 | 该路由送达延迟或内容不受信任 | 该路由的提交到最终状态分布,以及发放到验证分布 |
| 重发率上升,完成率持平 | 消息延迟到达但仍在窗口内 | 按运营商拆分的提交到最终状态尾部分布 |
| 重发率上升,某 一国家完成率下降 | 该市场存在过滤或注册问题 | 目标网络分组下的统一原因;发送方注册状态 |
| 过期验证码尝试上升 | 过期时间短于真实用户行为,或延迟增长 | 发放到验证分布与配置的过期时间对比 |
| 被替代验证码尝试上升 | 同一会话存在多个有效验证码 | 按会话拆分的发放记录;会话层面去重 |
| 使用了有效且及时的验证码却仍验证失败 | 应用或校验逻辑缺陷 | 按应用版本拆分验证接口的失败类型 |
| 某条路由持续缺失最终状态 | 供应商状态报告存在缺口 | 按供应商统计无最终状态的比例,与供应商沟通 |
| 单次验证成本上升,量级不变 | 重发增多或存在滥用 | 每次完成验证对应的请求次数;按号码和网络的请求速率 |
告警
针对该路由和该目的地相对于项目自身基线的偏离设置告警,观察窗口要长到足以排除该流量级别下的正常波动。每条告警都要指定负责人和处理动作:升级给供应商、切换路由、暂停活动,或提出一个应用层缺陷。没有绑定处理动作的告警,两周内就会被忽略。
相关的送达机制见A2P SMS 送达实践指南。