一篇讲“AI代理重试付费接口时怎么不重复扣款”的文章,拿到309次浏览、73条评论,评论来自11个人。作者说,大部分评论都戳中了真实漏洞。这篇是后续:每个漏洞是什么、改了什么、还有什么没做完。除了特别说明,下面这些改动都已经在网关上跑起来了。
![]()
先说最扎眼的一条。有读者指出,很多代理运行时把任何409都当成终局。原来的payment_in_flight响应只在文字里写了句“稍后重试”,程序读不出任何可执行的信息,运行时只能放弃,然后在下一步重新生成一个支付id。
现在这个进行中的409带上了程序能直接用的字段:
- retry_after:5秒
- poll:shop.order.get,用来查订单状态的调用
- order_id:订单已存在时给出
代理指南把话说死了:payment_in_flight不是终局。等retry_after秒,用同一个id再发一次,或者直接去读订单。
第二个漏洞更糟。有读者问,如果第一个请求直接挂掉会怎样。作者去查了,结果比预想的坏:进程在请求中途死掉,支付id会一直挂在“进行中”,直到24小时耗尽。对账任务能从链上把订单结算掉,但id那一行永远不知道这件事。重试可能对着一个已经付过款的订单,反复收到409。
现在的规则是:进行中的记录超过180秒,就从订单本身去解析状态。180秒比x402的120秒支付超时更长,所以还活着的首次尝试不会被抢先处理。订单已付款,重试拿回存好的订单,并带上replayed: true;订单还在付款中,重试拿到上面那个409。这一行在下次请求时就自愈,不用等一天。
第三个问题关于信号区分。有读者指出,冲突和进行中对客户端意味着完全不同的东西:一个说意图变了,一个说等着。两者本来就有各自的错误码,但冲突没说明原因。现在冲突会带上reason: payer或reason: request,而且只对冲突告警。同一个请求的普通重复是正常现象,保持安静。
第四个是客户端侧的假设。有读者点破了四层服务端逻辑底下共同的前提:客户端在崩溃后还能拿出同一个id。这只有在代理发任何东西之前就把id存下来时才成立。
文档现在把这条写成规则:先存意图,包括支付id、报价、收款方,以及签名授权本身,然后再发送。重启之后,用同一个id再发,绝不为同一份报价签新的授权。存下来的两样东西保护的是不同的事——签名授权管住钱,因为一次性nonce在链上重放会失败;id负责找回工作,因为它是拿回原始结果的凭据。指南还要求工具适配器拒绝发送没有存储意图的支付。
第五个漏洞来自一场长讨论:重试的查询被按全价收费。大多数工具是查询类。走x402时,重复的查询即使答案来自缓存也照全价收,因为缓存折扣只存在于预付余额那条路上。
现在走x402,同一个钱包在120秒内重复同一个可缓存的查询,拿到答案但不二次收费。响应里带X-APIbase-Charged: false,以及原始付费调用的收据。范围刻意收得很窄:只覆盖读,绝不覆盖写,因为相隔一分钟的两笔相同订单,可能正是用户想要的。商店类工具和MPP通道不在范围内。钱包即会话,所以这假设一个代理一个钱包,买家文档本来就这么建议。
还有读者提醒了易变字段的问题:参数里带时间戳或游标,每次重试看起来都像新请求。工具现在可以标记这类字段,让它们不参与匹配键。默认不排除任何字段,所以漏标的最坏结果是匹配不上,而不是给出错误答案。
最长的一串讨论落在一个不对称上。在Tempo上,钱在工作执行前就动了,所以早就为失败或重复的调用建了退款欠款记录。在Base上,只有结果就绪后才结算,所以一直没人需要这个。打破平衡的边界情况是:一笔重复转账在结算失败之后才落地。这种问题只有有人去查才会被发现。
现在两条通道共用一本退款欠款账本,记录两种要紧的情况:订单已终局之后才到账的付款,以及重复付款。退款状态会出现在公开收据和订单查询里,付款方自己能看到。
还有几项已经设计好、排进队列,但还没上线:
- Base上结算失败后的定向检查。既然知道该看哪一笔授权,就不等周期性对账,而是在接下来几分钟里读几次那个nonce。读不出结论就保持unknown,回退到对账,绝不写成failed。
- 从账本自动退款,用一个专门留出的钱包分批支付,付款方不用关心自己走的是哪条通道。
- 凭收据在24小时内取回付费结果,由付款钱包签名,服务那些运行时丢了、回来晚了的代理。
也有明确决定不做的事。工作流级别的id会把同一次运行里两笔诚实的相同查询合并掉,所以id仍然按工具调用走。写操作绝不根据请求长什么样来合并,只认代理显式生成的键。按会话限流很容易被绕开,因为服务端看到的是钱包而不是会话,所以硬边界仍然落在代理付款所用的那个钱包上,并对它设上限。
作者在结尾谢了所有评论的人:上面每一项改动都始于那条讨论串。这一版要是还有漏洞,他愿意接着听。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
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.