SMPP 送达回执的结构
提交短信时若将 registered_delivery 设为 1,SMSC 之后会通过接收端或收发端连接回传一个 deliver_sm PDU。其 esm_class 中的 0x04 位表示这是送达回执而非入站消息,short_message 中携带文本回执。SMPP v3.4 规范的附录 B 将这一格式作为示例给出,几乎所有 SMSC 都遵循它:id:IIIIIIIIII sub:SSS dlvrd:DDD submit date:YYMMDDhhmm done date:YYMMDDhhmm stat:DDDDDDD err:E text:…。
由于附录 B 只是示例而非严格规则,真实回执会有差异:键名小写、多余空格、submit_date 带下划线、带秒的 12 位日期,或缺少 text。本解码器兼容上述所有情况,并告诉您缺少哪些字段。
- id:SMSC 在 submit_sm_resp 中返回的消息 ID,用于把回执与您的消息对应。规范示例为 10 个字符,但许多 SMSC 使用更长的 ID,有时是十六进制。
- sub 和 dlvrd:最初提交的消息数和已送达的消息数,均为 3 位数字。主要用于分发列表;单条短信通常为 001 和 001(或 000)。
- submit date 和 done date:消息提交的时间和到达最终状态的时间,采用 SMSC 的时钟。
- stat:最终或中间状态,是形如 DELIVRD 的 7 个字母代码。
- err:3 个字符的错误码,由网络或 SMSC 自定义;000 表示无错误。
- text:原始消息的前 20 个字符,通常被截断或为空。
- 可选 TLV 会以更清晰的形式重复这些数据:receipted_message_id(0x001E)、message_state(0x0427)和 network_error_code(0x0423)。
DLR stat 状态码及含义
SMPP v3.4 定义了八种消息状态。ENROUTE 和 ACCEPTD 为中间状态,其余为最终状态。我们的 API 将所有渠道统一为六种 webhook 状态(SUBMITTED、SENT、DELIVERED、READ、FAILED、REJECTED),解码器会为每个 stat 值显示最接近的对应状态。该映射仅供参考:适用于您账户的确切规则会在开通阶段确认。
- DELIVRD(message_state 2):已送达手机。最接近的 webhook 状态:DELIVERED。
- ACCEPTD(6):正式含义是「客服代用户人工读取」,但多数 SMSC 把它当作「网络已接受」的中间回执发送。最接近:SENT。
- ENROUTE(1):仍在传输中,例如排队等待重试。最接近:SENT。
- UNDELIV(5):无法送达,例如号码无效或未激活。最接近:FAILED。
- EXPIRED(3):在有效期结束前未能联系到手机,常因手机关机或不在服务区。最接近:FAILED。
- DELETED(4):送达前被删除,例如通过 cancel_sm。最接近:FAILED。
- REJECTD(8):被网络或防火墙拒绝,例如 Sender ID 未报备或内容被拦截。最接近:REJECTED。
- UNKNOWN(7):SMSC 没有最终状态。请视为未确认,而不是已证实的失败。
err 错误码:为什么没有通用列表
err 字段宽度为三个字符,SMPP 规范并未定义其取值。000 始终表示无错误,但非零值来自 SMSC 厂商、运营商或中间的聚合商。有些 SMSC 会透传 GSM MAP 错误原因(例如 MAP 错误 1 表示未知用户,27 表示用户不可达),其他 SMSC 则全部映射到自己的错误表。
如果回执带有 network_error_code TLV(0x0423),则更可靠:第一个八位组表示网络类型(3 代表 GSM),后两个八位组为错误码。若 DELIVRD 回执的 err 非零,则前后矛盾,值得向服务商反馈;解码器会对此标记。
command_status 错误与送达回执的区别
SMPP 在两个时点报告问题。submit_sm_resp 中的 command_status 在毫秒内到达,说明 SMSC 是否接受了您的请求;0x00000000(ESME_ROK)表示已接受,而不是已送达。送达回执在数秒到数天后到达,携带最终结果。
command_status 的取值定义于 SMPP v3.4 的 5.1.3 节。生产环境中最常见的有:0x00000058 ESME_RTHROTTLED(超出约定的每秒消息数:请降速,并以指数退避重试)、0x00000014 ESME_RMSGQFUL(队列已满:请退避)、0x0000000A 和 0x0000000B(源或目的地址无效),以及 0x0000000D 或 0x0000000E(绑定失败或密码错误)。0x00000400 到 0x000004FF 保留给 SMSC 厂商自定义错误,遇到这些请向您的服务商咨询。
日期、时区与送达延迟
回执日期采用 YYMMDDhhmm,许多 SMSC 会追加秒数(YYMMDDhhmmss)。回执不包含时区:时间取决于 SMSC 时钟,通常为 UTC。解码器按原样显示日期,并将延迟计算为 done date 减去 submit date;只要两者来自同一时钟,就不受时区影响。
延迟是有用的质量信号。发往可达手机的健康验证码流量,通常在数秒内完成。较长的延迟随后出现 EXPIRED,说明手机可能关机或处于漫游;done 日期早于 submit 日期则说明两个时钟不一致。精度为分钟的日期无法衡量不足一分钟的延迟,因此建议选择会返回秒数的 SMSC。
在生产环境中可靠处理回执
对每个 deliver_sm 都要回复 deliver_sm_resp,即使您的处理失败,否则 SMSC 会重发并可能对该连接限速。请保存原始回执,再按消息 ID 异步且幂等地处理,因为回执可能重复或乱序到达:迟到的 ENROUTE 绝不能覆盖 DELIVRD。
如果您不想解析回执,我们的 REST 短信 API 会以统一的 webhook 状态提供同样的信息,遗漏的回执还可通过 POST /sms/resendDlr 重新推送。大流量发送方和渠道合作伙伴可使用基于 TLS 的 SMPP 连接。
