![]()
上周跑对账脚本时,3笔交易全部撞墙——2笔明明扣款成功、回调确认、收据入库,STK查询接口却一口咬定"失败"。这不是代码bug,是非洲最大支付平台沙盒环境的系统性撒谎。
数据不会说谎,但接口会
开发者Chris在Daraja沙盒跑 reconciliation(对账)时,抓到了一组荒诞的返回:checkoutRequestId 尾号 29173 和 29173 的两笔交易,数据库存着 SUCCESS 状态和收据编号 UCQ5UAQ403、UCQ5UAPYRY,测试账户也确实扣了款。STK 回调当时给的 ResultCode 是 0——M-Pesa 的官方成功信号。
同一套系统,同一个 transaction ID,STK Query API 的回应却是 FAILED。
他翻遍 Stack Overflow、Safaricom 的 GitHub 仓库、所有社区集成方案。零记录。零讨论。零 issue。这个坑似乎从未被人公开报告过。
沙盒的回调和查询接口,根本不在读同一个数据源。
沙盒的"模拟"有多敷衍
Safaricom 文档承认过一件事:沙盒无法完整模拟 USSD 网络层。这就是第三方工具 Pesa Playground 存在的原因——开发者需要更真实的测试环境。但文档没说的是,沙盒的 STK Query 端点连"确认成功"都做不到。
![]()
它的默认策略是:只要无法明确解析交易状态,一律返回 FAILED。不管回调已经说了什么,不管钱有没有真的扣。
换句话说,沙盒查询接口的设计逻辑是"宁可错杀"——但错杀的是已经完成的支付。
Chris 写的 mpesa-stk@0.1.1 库处理得很克制:matched:0。发现查询响应和库存的 SUCCESS 冲突,选择不覆盖。那笔 orphaned(孤儿)的 PENDING 记录保持原样,而不是被强行改成 FAILED。
这个选择背后是个残酷事实:一个会覆盖 SUCCESS 的对账系统,比什么都不做的系统更危险。
生产环境会不会也这样
Chris 没跑过生产环境测试。Safaricom 文档暗示生产端的 STK Query 是准的——问题出在沙盒。但这个"暗示"本身就是问题:开发者被默许用一套撒谎的环境测试核心支付流程,然后被告知"生产应该不一样"。
非洲移动支付的基础设施里,M-Pesa 是事实标准。2.96 亿用户、年交易流水占肯尼亚 GDP 的 87%。它的开发者生态却长期被沙盒的粗糙对待——回调延迟、状态不一致、文档滞后。
这次暴露的是更底层的断裂:同一个平台内部,两个官方接口对同一笔交易给出矛盾结论。回调说成功,查询说失败。开发者该信谁?
![]()
Chris 的代码选择了信回调——因为那是交易发生时的第一手信号。查询是事后补录,只能用来处理那些从未收到回调的 PENDING 记录。这是防御性编程,也是对平台不信任的投票。
给你的对账系统两条铁律
第一,永远不要仅凭查询响应覆盖终态记录。 SUCCESS 或确认过的 FAILED 都是终态,查询只能动 PENDING。回调的权威性高于查询,这是架构设计问题,不是偏好问题。
第二,别在沙盒里测对账逻辑。 它的 STK Query 不是可靠的测试面。要么上生产环境测,要么接受沙盒这条路径的结果是噪音。
有开发者在这条 issue 下留言:"我们去年就被这个坑过,当时以为是自己的代码问题,重写三遍。"另一条回复更扎心:"生产环境确实没这问题——但发现这一点之前,我们已经浪费了两个月。"
平台方的沉默正在制造重复劳动。一个未被文档化的沙盒行为,让无数开发者陷入自我怀疑。
Chris 最后抛了个问题:有人在生产环境跑过 reconciliation 吗?STK Query 在那边真的可靠吗?
评论区至今没人给出确凿答案。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.