《明日方舟:终末地》支付事故技术复盘清单

🍎 原文地址

https://www.pixiv.net/artworks/140268431

🍎 原文内容(从上面的链接粘过来的)

菲比的终末地之旅《酷狗回来玩鸣潮了》
有关小麦地,大佬的事故报告通俗版来了:
 
开门见山,这次事故算是鹰角和PayPal91分锅,确实是26年IT领域开年第一狠活。
原理其实各路分析也都说的差不多了,哥们也就再用非专业术语概述一下。
 
首先需要科普的是,鹰角用的微服务、多线程之类的技术,严格来说都是不安全的。安全性必须由开发者充足完善的思考来保证,但很显然,鹰角并没有保证这一点。于是,就发生了以下事项。
 
海外玩家小A,开开心心打开了终末地氪金,理想状况下的流程是这样的:游戏客户端识别到了他的游戏内uid,然后开始走支付流程->小A使用了PayPal进行支付->付款成功->PayPal标记了小A本次授权的凭证为已核销->PayPal通知鹰角->充值到账。
 
但是笨笨鹰角开始发力,业内大伙都会把客户端UID和支付凭证关联,绑定在一起避免出错,但是鹰角偏不。整个流程里,这一环是完全缺失的。这就导致了不管是哪个号发起的支付请求,只要池子里有支付凭证,鹰角拿起来就用。并且其的分析报告也提到了,凭证可能被放到了全局作用域中,这就类似以下场景:有十个人排着队拿钱买瓜,排第一的小A把钱包递给摊主鹰角,然后十个人开口说自己要几个瓜,鹰角一边说好,一边把10个人的账单都用小A的钱包付款,鹰角无视了小A不断提醒的“我已经付过钱啦”然后狠狠的收费。
 
❗️为什么说PayPal有1分锅呢,因为这个管钱包的,他是等着店家拿票给他看,他看到票付钱,但是他反应确实会慢半拍,他把钱付出去之后,会稍晚那么一小会儿,才能留下“这个钱我付过了,之后再拿这个票要钱的不能给通过”的记录,那就这么一小会儿,就有天选倒霉蛋霉霉遭扣了5000美刀。
 
总而言之,移动支付这个国内已经做了太多太久,非常成熟的技术。微服务也火了这么多年,所以我只能认为这并非是设计失误,而是执行坏了)目前有资历能做架构设计的,他就是神志不清了也整不出这种烂活。所以更大的可能是:因为要考虑成本,所以能省则省。又因为过度乐观的预估了热度,觉得充值订单系统压力会很大,所以把线程池留够了但是没成本考虑线程安全了。测试混日子,最终促成了这一惊天大狠活。
 
另外还有个点,我没看到人说。众所周知,这么重要的发时刻还是公测首日,一般来说肯定是要全程盯着的。特别是支付系统的日志,鹰角在普遍概念里已经算是大厂了,我不信他们会没人盯着,那为什么开服三四个小时才有第一波急救措施,熔断PayPal支付呢?我有个无责任脑洞:这部分降本增效过度,导致日志根本没考虑到要核验并打印出付款uid和支付凭证是否匹配的提示,只有个数字流水在滚动了…
 
本来想的是公测首日超抖登顶登基T0级霸权手游
结果成了联合斩杀顷刻炼化老外的P0级事故案例

🍎 PayPal方的问题

原文内容:

❗️为什么说PayPal有1分锅呢,因为这个管钱包的,他是等着店家拿票给他看,他看到票付钱,但是他反应确实会慢半拍,他把钱付出去之后,会稍晚那么一小会儿,才能留下“这个钱我付过了,之后再拿这个票要钱的不能给通过”的记录,那就这么一小会儿,就有天选倒霉蛋霉霉遭扣了5000美刀。

在这次《明日方舟:终末地》支付事故中,业内提到 “PayPal 也有 1 分锅”,主要是从技术机制和风险设计的角度,而不是说它要为核心漏洞负责。具体原因可以拆解为这几点:


1. 支付凭证核销的延迟与 “窗口期” 风险

PayPal 在收到商户的扣款请求后,会先执行扣款,再延迟一小段时间才标记该凭证为 “已核销”。这个短暂的延迟就形成了一个 “窗口期”。

在这个窗口内,同一凭证如果被商户系统再次提交,PayPal 会默认认为是新的合法请求并再次扣款。这就为鹰角的凭证复用漏洞提供了可乘之机,导致了用户被连续扣费的极端情况(比如案例中被多扣 5000 美元的玩家)。


2. 免密授权的安全设计缺陷

鹰角使用了 PayPal 的Billing Agreement ID(BAID)免密授权机制,该机制允许商户在用户首次授权后,后续无需用户再次确认即可直接发起扣款。

PayPal 的这一设计是为了提升支付流畅度,但前提是商户必须严格校验 “订单与用户的唯一绑定关系”。当鹰角的系统缺失了这个校验时,PayPal 的免密机制就放大了风险,让恶意或错误的扣款请求能够直接执行。


3. 重复交易的防御机制未完全生效

PayPal 本身提供了Invoice ID 去重的功能,当商户提交重复的 Invoice ID 时,PayPal 会拒绝该请求。但在这次事故中,鹰角的系统并没有为每个用户的订单生成唯一的 Invoice ID,而是全局复用凭证,导致 PayPal 的去重机制完全没有发挥作用。

这一点更偏向于商户侧的误用,但也暴露了 PayPal 在高并发场景下,对重复交易的实时拦截能力存在不足。


4. 行业视角的 “91 分锅” 本质

所谓 “鹰角 9 分锅,PayPal1 分锅” 的说法,本质是行业对事故责任的定性:

  • 鹰角(9 分):核心责任方,因为它的支付系统完全缺失了 “用户 ID 与支付凭证强绑定” 的基础校验,是事故的根本原因。
  • PayPal(1 分):次要责任,在于它的支付机制存在可被误用的风险点,且在极端并发场景下的防御不够彻底。

简单来说,PayPal 的问题不是主动犯错,而是它的设计假设 “商户会做好校验” 在这次事故中失效了,间接导致了损失的扩大。

🍎 事故分析

一、事故核心问题概述

本次支付事故是典型的支付凭证池线程安全缺陷,核心原因是系统未对支付凭证做用户关联校验,且凭证池设计为全局共享无锁资源,导致多用户并发支付时出现凭证被重复核销的严重问题。


二、关键技术缺陷与风险点

1. 支付凭证关联校验缺失

  • 问题描述:未将游戏用户 ID(uid)与 PayPal 支付凭证进行绑定,仅以凭证本身作为核销依据。
  • 技术风险:任何新的充值请求都可以复用池中的凭证,导致原始支付用户被重复扣费。
  • 影响范围:多用户并发支付场景下,早期支付用户会被高频重复扣费(如案例中的 5000 美元损失)。

2. 全局共享凭证池无线程安全控制

  • 问题描述:所有支付凭证被放入全局共享池,且未实现加锁、唯一标识校验等线程安全机制。
  • 技术风险:并发请求会从池中 “抢用” 凭证,同一凭证被多个请求重复消费。
  • 类比场景:10 个人排队付钱,但收银员只看钱不看人,把第一个人的钱反复给后面所有人结账。

3. 支付流程状态校验不足

  • 问题描述:仅依赖支付凭证状态(已核销 / 未核销),未结合用户 ID 做复合校验。
  • 技术风险:凭证被核销后,若未及时从池中移除或标记归属,会被后续请求重复获取。

4. 监控与日志设计缺陷

  • 问题描述:日志仅记录流水,未包含用户 ID 与支付凭证的关联信息。
  • 技术风险:事故发生后无法快速定位异常请求,导致响应滞后 3-4 小时。

5. 线程池容量与安全设计失衡

  • 问题描述:为应对高并发仅扩容线程池容量,未同步保障线程安全校验。
  • 技术风险:高并发场景下放大了凭证复用问题,导致损失快速扩大。

三、技术防范措施与修复方案

1. 支付凭证与用户强绑定

// 优化后的支付凭证结构
type PaymentVoucher struct {
    VoucherID string // PayPal返回的凭证ID
    UserID    string // 对应游戏用户ID
    Status    string // 状态:未核销/已核销/已失效
}
  • 操作要点:

    1. 支付请求发起时,生成VoucherID + UserID的唯一关联键。
    2. 核销凭证时,必须校验VoucherID与当前请求的UserID是否匹配。

2. 凭证池线程安全改造

  • 方案一:分布式锁控制

    使用 Redis 分布式锁或数据库乐观锁,确保同一凭证仅被一个请求获取。

  • 方案二:凭证池分片

    按用户 ID 哈希值将凭证池分片,每个请求仅访问对应分片,减少并发冲突。

3. 支付流程状态机完善

  • 设计完整的支付状态流转机制:

    发起支付 → 生成凭证 → 支付完成 → 标记凭证为「待核销」 → 校验用户关联 → 核销完成 → 标记凭证为「已核销」
    
  • 关键节点增加幂等性校验,避免重复处理同一请求。

4. 监控与日志增强

  • 日志字段补充:强制记录UserIDVoucherID关联校验结果
  • 告警规则配置:设置重复核销频次阈值,触发实时告警。

5. 压测与灰度验证

  • 增加并发支付场景的压测,模拟 1000 + 用户同时支付。
  • 灰度发布时,对支付功能做全链路验证,覆盖异常场景。

四、事故反思与规范建议

  1. 安全优先原则:涉及资金的系统必须优先保障安全,而非成本或性能。
  2. 支付系统审计:定期对支付核心逻辑做安全审计,重点校验关联、锁机制、幂等性。
  3. 应急响应机制:建立支付故障快速响应流程,包含紧急切断、回滚、用户补偿预案。
posted on 2026-01-23 14:10  江湖乄夜雨  阅读(440)  评论(0)    收藏  举报