做了这么多年支付风控和渗透测试,我最深的体会是:真正让企业一夜之间损失惨重的,往往不是SQL注入、不是RCE,而是那些看起来人畜无害,打起来刀刀见血的支付逻辑漏洞。它不依赖你用了什么框架、什么中间件,只依赖你写业务代码时那一两个“想当然”的判断。从微信支付回调没验签,到优惠券可以重复领取,再到订单号换个数字就能查别人订单,每一条都是真实发生过的线上事故。
这篇文章就以支付场景为靶场,拆解8类最常见的逻辑漏洞,每类都会给攻击思路、复现方法、修复方案,也会聊一聊大家踩坑最多的第三方支付集成问题,比如微信支付signature错误、uniapp打包App和微信小程序支付参数不一致、支付宝沙箱测试、苹果IAP退款等。适合正在做支付模块的开发、搞安全测试的同学,也适合想系统排查一遍自己业务里有没有同类问题的架构师。
1. 支付逻辑漏洞的认知框架与分类
1.1 为什么支付场景是逻辑漏洞的重灾区
支付业务天然就是逻辑漏洞的沃土,因为它不是单一功能,而是由商品、订单、用户、金额、优惠券、支付渠道、异步回调、退款等多个子系统拼起来的。每个子系统之间通过接口传递参数、通过状态字段流转,一旦某个环节的设计者默认“客户端传上来的值都是合法的”,或者默认“这一步只有我们自己系统会调用”,漏洞就出现了。
传统漏洞比如SQL注入、XSS,本质是代码层面的输入输出处理不当,扫描器可以靠特征识别。但逻辑漏洞没有通用特征,它是对业务规则本身的违背。比如后端没校验金额就入库,这在扫描器看来就是一个普通的整数参数,响应还是200,只有人肉去改一下1分钱才能发现问题。这也是为什么很多企业安全扫描报告一片绿,却被羊毛党薅到倒闭。
支付逻辑漏洞的攻防,本质是攻击者与业务设计者之间的规则博弈。攻击者不需要精通二进制汇编,需要的只是对支付流程足够熟悉,然后用Burp Suite改几个参数、重放几个请求、并发抢一下,就能找到绕过路径。反过来,防御方也必须比攻击者更懂自己的业务流程,把所有不变量找出来,逐一加上校验。
1.2 攻击者眼中的“钱”与8类漏洞速览
我把支付场景里高频出现的逻辑漏洞归纳成8类,下面这张表先给大家一个整体视角。需要注意的是,真实攻击中往往不是单点利用,而是两三类漏洞组合使用,比如用并发漏洞和优惠券漏洞搞出0元单,或者用越权和退款漏洞盗刷他人资金。
| 编号 | 漏洞类别 | 核心问题 | 典型后果 |
|---|---|---|---|
| 1 | 价格与参数篡改 | 后端信任客户端提交的金额、数量、折扣 | 1分钱下单、负数金额、0元购 |
| 2 | 用户与订单越权 | 用订单号或用户ID访问不属于自己的资源 | 查看/操作他人订单、盗刷他人账户 |
| 3 | 订单状态逻辑绕过 | 状态机校验缺失,可跳过或倒退 | 未支付直接发货、重复确认收货 |
| 4 | 并发与重放 | 缺少幂等控制,同一请求可多次生效 | 优惠券重复使用、回调重复入账 |
| 5 | 优惠与积分规则漏洞 | 规则设计粗糙,可组合套利 | 无限领券、积分篡改、负折扣 |
| 6 | 退款/逆向流程漏洞 | 退款不与原支付单正确绑定 | 超额退款、重复退款、退款不削权 |
| 7 | 回调通知验签缺失 | 异步通知未验签、未验金额、未防重 | 伪造支付成功通知,直接改订单状态 |
| 8 | 第三方支付集成与配置漏洞 | 沙箱/正式环境混淆、各端参数不一致、签名错误 | 支付卡单、无法调起、被渠道拒付 |
这8类并不是完全平行的,第7类回调通知和接口集成是重灾区,我遇到过的安全事件里,至少有一半最后能追溯到“回调没验签”或者“回调没做幂等”。下面逐类展开,重点讲攻击手法和修复要点。
2. 高频漏洞第一梯队:金额、越权、状态机与并发
2.1 金额与参数篡改:别相信客户端传上来的价格
这是最直观、也最容易修的一种漏洞,但仍然在大量系统中反复出现。典型场景是下单接口长这样:前端把商品ID、数量、单价、优惠金额、总金额全部POST到后端,后端没有回表查商品价格,直接用接收到的金额生成订单。攻击者只要把总金额改成0.01,甚至改成负数,订单就成立了。
测试手法也很简单,用Burp Suite代理开启拦截,正常下单走一遍流程,把创建订单的请求拿出来,逐个修改这几种字段:
- amount / total_fee / totalPrice 改小,比如改成0.01
- quantity / count 改成负数或小数
- discount / couponAmount 改成负数
- currency 或币种字段,比如把CNY改成JPY,利用汇率差
特别注意几点:有些系统做了金额校验,但只校验了客户端传过来的“单价×数量=总价”,没校验证后端商品表里的价格,攻击者可以把单价改成“后端不存在的历史低价”,只要乘出来相等就能绕过。还有的系统把“运费”“税费”也作为可传参数,这里同样可以做手脚。
正确的修复方式很明确:所有与钱相关的数值,一律以服务端数据库为准。下单接口只接收商品ID和数量,金额由服务端按下单时的商品表价格、优惠规则重新计算。如果要保留前端展示友好性,也可以接收金额,但必须与后端计算值比对,不一致就拒绝。任何客户端可改的字段都不能作为记账依据。
2.2 越权操作:用户ID、订单号、支付单号的裸奔
越权在支付场景里的表现形式非常丰富,而且往往危害更大。最常见的是水平越权:两个用户A和B,A登录后调用“查询订单详情”接口,请求里带的是orderId=1001,攻击者把orderId改成1002,响应返回了B的订单。这看起来只是信息泄露,但如果这个接口同时支持“取消订单”“申请退款”“重新支付”,那我就能取消别人的订单,或者用自己的支付方式去支付别人的订单,甚至把别人的订单退款退到自己账户里。
更隐蔽的越权是通过支付单号。很多系统为了对接第三方支付,会生成一个out_trade_no(商户订单号)或prepay_id,这个号有时候是纯数字、可遍历的。如果查单接口、支付结果查询接口、退款接口直接信任这个号码而不校验归属用户,攻击者就能操作任意一笔交易。
我常用的测试方法是准备两个测试账号,A和B各自下一个单,然后互通有无:用A的登录态去访问B的订单详情、取消B的订单、给B的订单发起退款,看有没有被拒绝。还可以抓包对比正常请求和越权请求,看后端是否从Session里取用户ID,还是从请求参数里取。
修复思路分两层。第一层,用户身份必须来自服务端会话,不能由客户端传参。第二层,任何订单、支付单、退款单查询或操作,都要追加归属校验,SQL里强制带上WHERE user_id = 当前用户。不要觉得多写一层判断麻烦,这层判断是支付系统的安全底线。
2.3 订单状态逻辑绕过:先发货后付款,或重复确认收货
订单状态逻辑漏洞,本质上是对状态机的破坏。正常情况下一个订单的生命周期是:待支付 -> 已支付 -> 已发货 -> 已完成。如果系统在“待支付”状态下就允许调用“发货”接口,或者在“已支付”状态下还能继续发起支付、然后又走一遍退款,就会产生资金和货物都对不上的局面。
一个经典案例是这样的:商品详情页下单后,支付收银台在客户端弹出来,但有些系统为了用户体验做了“后端自动创建支付单并轮询状态”,同时把“确认支付”接口暴露了出来。攻击者在Burp里把这个接口的请求看一遍,发现它只校验了订单编号,没有校验该订单是否真的完成了第三方支付,于是直接调用这个接口,订单状态就变成了“已支付”。配合上发货自动化,等于0元下单。
还有一种是逆向逻辑绕过。订单已经完成了退款,但用户还能对订单发起“确认收货”,把虚拟权益也领一遍。这属于状态机没有限制单向流转,允许倒退回更早的状态,或者允许在一个终态上继续执行动作。
这一类漏洞的修复不是简单加几个if判断,而是要建一个清晰的订单状态机。建议用一个枚举或状态表,定义每个状态允许的动作和前置状态。比如:
- 只有状态为“待支付”才允许调起支付
- 只有状态为“已支付”才允许发货
- 只有状态为“已发货”才允许确认收货
- “退款中”“已退款”是终态,任何其他操作都不能改变它
此外,所有状态变更要在同一个事务里更新,并使用乐观锁(版本号)防止并发下两个请求同时把订单从“已支付”改成“已发货”和“已退款”,导致一笔订单出现两个终态。
2.4 并发请求与重放:同一个订单和优惠券被薅穿
并发漏洞是支付场景里最容易打、也最容易被忽视的一类。核心问题是系统缺少幂等控制,导致同一个请求被同时发多次,或者同一份资源被多线程重复消费。
举个例子,用户下单时使用了“新用户立减50元”优惠券,这个优惠券在数据库里是coupon_id + status=NORMAL。正常逻辑是:下单时把优惠券状态改为USED。但如果没有对优惠券ID加唯一索引或加锁,两个线程同时读到NORMAL,都判断“可以使用”,然后先后改为USED,从逻辑上两张订单都占用了同一张优惠券。这个优惠券可能只能使用一次,也可能允许重复使用,重点在于攻击者可以利用并发把N次核销变成N+1次。
支付回调的重放问题更致命。第三方支付成功后会异步通知我们服务器的notify_url,如果回调处理逻辑是:“收到通知 -> 验签成功 -> 更新订单为已支付 -> 给用户加余额”,那么只要攻击者截获一次合法通知,然后用任何能模拟HTTP请求的工具(Burp的Repeater、curl脚本都行)反复发送同一份通知,每发一次,用户余额就加一次。这种情况在真实世界里频繁出现,因为很多开发者只做了“所以看状态再处理”,比如先查订单是不是已支付,不是才更新。但如果第一遍通知处理完后忘了把订单状态改掉,重放就能穿透。
修复并发重放,我一贯推荐三板斧:
- 为回调通知、优惠券使用、退款申请这类写操作加唯一索引,比如回调流水表里的event_id + order_id 唯一,重复插入直接报错;
- 在处理关键业务之前用分布式锁或数据库悲观锁锁住订单/优惠券行记录,防止两个事务同时读取相同状态;
- 对客户端重复请求做幂等键,同一个业务唯一标识(比如订单号)在短时间内只允许处理一次。
这几种手段不是选一个,而是最好都上。唯一索引防重复事件,锁防并发状态错乱,幂等键防用户误触和重放。
3. 第二梯队:优惠、退款、回调和第三方集成
3.1 优惠券与积分:负数金额和套利组合拳
优惠和积分是羊毛党最常盯的两块。原因很简单,它们的计算规则复杂,涉及比例、阈值、互斥、叠加、返还,任何一条规则没想清楚都能变成套利工具。
经常翻车的情况有这么几种:
- 优惠券领取接口没有防重,直接用脚本无限领券;
- 下单时优惠金额由前端传,改成负数后退款时倒赚;
- 积分抵现金没有下限,积分余额不足也能抵扣,抵扣后积分变成负数;
- 组合使用多张优惠券,叠加上限只校验了单张,没校验总和;
- 跨店满减,一个订单拆成多个子订单,用同一张券在多个子订单里同时核销。
实操中我会先抓下单请求,看哪些字段和优惠相关,然后构造一个“总金额>0但优惠金额>总金额”的请求,看系统是拒绝还是生成负数的应付金额。有些系统处理负金额时会“返还”到用户余额里,这就是直接被薅钱了。还需要测试并发核销,用Burp Intruder同时发送多个携带同一个优惠券ID的请求,看返回结果。
修复维度也分三层。第一,优惠券和积分的使用必须走服务端规则引擎,所有优惠金额由后端统一计算,不接收客户端的折扣值。第二,使用记录要加唯一约束,一张券只能在一个订单里核销一次,积分扣减记录也要有唯一流水号。第三,数值范围校验必须有下限,优惠金额最大只能到应付金额,不出现负应收。第四,对组合优惠做总量校验,所有优惠累加不能超过商品总价。
3.2 退款流程中的漏洞:金额、次数、权限都容易翻车
退款是逆向流程,但攻击难度比正向支付低很多。多数系统在支付环节做了层层校验,到了退款接口反而放飞了。我遇到过几种典型的退款漏洞:
- 退款金额由前端传入,攻击者把退款数额改成大于实际支付金额,系统直接通过并发起退款;
- 退款时只校验订单已经支付,不校验退款总额是否已超过实付金额,一笔订单可以退款多次,每次都退全额;
- 对虚拟商品,用户购买后立即申请退款,但系统没有在退款成功后收回虚拟权益,相当于免费嫖了一次演唱会直播、一份会员或一套课程;
- 调用第三方退款接口时,把out_refund_no(退款单号)改成另一个订单的号,做成“同一笔退款单应用到两个订单”的交叉退款。
测试退款接口有个小技巧:先支付一笔小额订单,然后发起退款并抓包,修改refund_amount为订单金额的倍数,观察是否被拒绝;再正常退一次款,同一订单再次发起退款,看第二笔是否成功;最后检查退款被接受时,用户虚拟权益有没有被同时冻结。
修复上,退款金额必须与原支付单的实付金额、已退款金额做累计校验,简单写就是:本次退款金额 <= 实付金额 - 已退款金额。这笔逻辑要在数据库事务里通过行锁或SQL条件更新实现,比如UPDATE 退款流水 SET ... WHERE 累计退款 <= 实付金额,如果影响行数为0就拒绝。同时,退款成功后必须联动更新权益状态,虚拟商品要收回相应的权限或会员时长。
3.3 回调通知验签缺失与重放攻击
这是支付逻辑漏洞里最“容易出事故”的一个环节,我单独拿出来细讲。支付回调的本质是异步通知,支付宝、微信都会在用户支付成功后,向商户配置的notify_url发送一条HTTP请求,里面带有订单号、交易金额、签名等信息。这条通知是外部输入的,但很多系统在处理它时表现得像处理内部RPC一样随意。
最常见的问题有三个:
- 完全不验签,只判断字段里有“success”或订单号就更新订单;
- 验签逻辑错误,比如用错了证书/公钥,或者验签时没把需要参与签名的参数取全;
- 验签通过后没有校验通知里的金额与订单金额是否一致,攻击者伪造一个签名正确的通知,但把金额改成任意值。
还有一类问题是验签正确但没校验商户号。在多商户平台上,攻击者可以用甲商户的支付成功通知,带着自己的商户号和订单号打到乙商户的notify_url,如果系统只验签不验商户身份,乙商户的用户也能收到虚假到账。
重放攻击在这里尤其致命。第三方支付为了确保通知送达,本身就会重试多次,每重试一次,后端回调函数就会被调用一次。如果回调处理函数没有把“已处理”和“未处理”区分开,那每次重试都会重复执行加余额、发奖励、更新订单等操作。正常情况下的重试已经是安全事故,更不用说攻击者手动重放了。
正确做法可以总结成四步:
- 验签必须使用对应支付渠道的官方SDK或公钥,验签参数按渠道规则拼接;
- 验签通过后,校验商户号、订单号、金额必须与本地订单完全一致;
- 处理前查询回调流水表,同一通知ID/同一订单已处理过就直接丢弃;
- 业务更新(加钱、加权益、改状态)与“标记回调已处理”要在同一个事务里完成,确保要么一起成功要么一起失败。
这四步缺一不可。哪怕你第三步幂等做得很好,没有第一二步,攻击者照样能伪造出“验证通过”的通知——所以顺序也很重要,先验签、再验金额、最后查重处理。
3.4 用户态签名错误与第三方支付集成的那些坑
顺着热搜词里出现的“微信支付提示用户态签名signature错误”,我讲讲这类集成问题。严格说这不算攻击漏洞,但它经常导致支付功能不可用,用户一路折腾最后流失,而且很多团队的查错方式也很痛苦。
“用户态签名”错误,通常发生在我们从小程序或APP调起微信支付的时候。微信支付要求客户端调起支付前,后端先生成预支付订单,再用预支付交易会话标识prepay_id和其他参数拼一个签名,这个签名要参与调起。报signature错误,多数是这几种原因:
- 后端生成签名时使用的AppID和当前登录小程序/APP的AppID不一致,尤其是同时做了小程序和App,共用一个商户号,但AppID传混了;
- 参与签名的参数名写错,比如把package写成package_val,或者大小写不一致,微信官方文档对参数名的要求非常严格;
- 签名key用错,商户API密钥、API v3密钥、证书序列号搞混,不同接口用的密钥不一样;
- 忽略了时间戳和随机字符串的生成规则,同一个参数在不同的调用里没有更新。
排查这类问题,我会先把微信支付官方示例代码的参数拼接顺序找出来,逐项比对后端输出的签名原串,再检查当前请求里的appId是否与后端配置一致。最快的方法是在后端临时打印签名原串,将它与微信官方诊断工具生成的签名比对,Diff一下就能定位是参数还是密钥问题。
还有一个高频问题:uniapp打包App支付和微信小程序支付时支付流程和参数是否相同。答案是“后端统一下单是一样,但前端调起方式完全不同,参数也不同”。小程序支付用wx.requestPayment,需要传入timeStamp、nonceStr、package、signType、paySign;App支付用微信开放平台SDK的WXPayTask或者当前客户端环境的拉起方法,需要传入appId、partnerId、prepayId、nonceStr、timeStamp、sign。这两套参数的来源字段名都有区别,如果直接把小程序的返回数据拿去给App端用,大概率调不起支付。
更隐蔽的是安卓系统唤醒微信支付以后,用户可能又回到APP,但APP在前台没有自动刷新订单状态。很多开发者以为支付回调一定能到达后端,然后由后端推送消息给APP,实际上回调是有延迟的,甚至个别情况会丢失。所以正确做法是在APP从后台回到前台时,主动调一次后端“查询订单支付结果”接口,不要只依赖回调。这也是我在集成排障时反复强调的一点。
关于支付宝沙箱,测试时要注意沙箱环境和正式环境的“密钥对、网关、AppID”完全不同,经常有人把沙箱的公私钥配到正式环境,导致下单报错或无法调起。另外,微信小程序是否可以加入支付宝支付渠道?按照平台规则,微信小程序内不允许直接唤起支付宝,只能通过“跳转浏览器打开”或引导用户复制链接到支付宝完成支付,但很多业务又想要支付宝渠道,这就得做H5跳转方案或者引导下载App。在设计支付网关时,最好在PRD阶段就把各端渠道写清楚,避免开发时在不同平台之间跳来跳去。
谷歌支付失败OR-PFGVEM这个错误码,通常表示用户账号或付款方式异常,比如账号未登录、未绑定有效的付款方式、被风控拦截。它提示了支付失败,但没有给出具体原因。我们在做Google Play内购时,要结合订单查询接口和用户账号状态进一步判断,不能只把这个错误当作业务拒绝,否则会把一部分正常用户挡在付费门外。
4. 攻防实战:从攻击链到防御链,我的通杀排查清单
4.1 测试前的准备与接口清单梳理
支付逻辑测试不是拿起Burp就乱打,而是要有章法。建议按下面的步骤来:
- 先搞清楚业务流:用户从加购到下单、支付、回调、发货、退款,哪些环节有接口,每个接口的请求参数、上下文状态是什么,最好画一张接口调用时序图;
- 准备两个以上测试账号,分别拥有不同的身份、优惠券、积分和订单,方便做越权测试;
- 在测试环境开启支付宝沙箱、微信支付测试号,或者使用可用回调模拟器,不要直接用生产环境刷真实支付;
- 用Fiddler或Burp抓包一次完整的正常支付流程,把所有请求保留下来,作为后续篡改的模板。
有一点必须提醒,逻辑漏洞测试要严格限定在你拥有权限的系统内进行。找别人系统的漏洞并在未授权情况下利用,是违法行为。这篇文章的所有思路都服务于安全评估和自建系统加固,不是让你去薅别人羊毛。
4.2 八个排查动作与对应测试手法
下面把8类漏洞对应的排查动作整理成一张速查表。每次做支付专项测试时,我基本就是照着这个表一个一个过的,效率很高,而且不容易漏。
| 漏洞类型 | 测试动作 | 关注响应与状态 |
|---|---|---|
| 金额篡改 | 修改下单/支付的金额、数量、币种、优惠字段 | 是否接受非正常值并生成订单 |
| 越权 | 用A账号访问B的订单详情、退款、取消接口 | 是否返回完整的B用户数据或操作成功 |
| 状态绕过 | 直接调用发货、确认收货、关闭订单接口 | 是否在未支付/已退款状态下执行成功 |
| 并发重放 | 同一下单/核销/回调请求多线程发送 | 是否产生多张订单或多个入账 |
| 优惠积分 | 重复使用同一券、积分抵现负数、超限叠加 | 是否库存异常或应收为负 |
| 退款 | 超额退款、重复退款、退款后不削权 | 是否累计退款超过实付、权益未收回 |
| 回调验签 | 改动回调参数、重放回调、替换商户号 | 是否仍被业务逻辑接受 |
| 第三方集成 | 切换沙箱/生产配置,不同端调起参数比对 | 签名错误、支付卡单、跨端流程不匹配 |
测试时有个小习惯:每改一个参数,对比一下响应与正常响应的差异,然后重新跑一遍正常流程,防止误测。实际漏洞往往不是一次改一个字段就能触发的,而是要同时改两个字段。比如既要改订单号实现越权,又要改退款金额实现超额退款,组合拳比单点测试更能碰到真实问题。
4.3 修复与加固:从代码到架构的几道必须做的“保险”
如果让我给一个支付系统做安全设计,下面这几道保险是标准配置,缺一不可。
第一道是“服务端不信任客户端数据”。所有影响资金计算和资格判断的参数,能不用客户端传的就不用。商品ID、数量、用户ID可以从会话或服务端缓存取,金额由后端统一计算。
第二道是“订单状态机严格单向流转”。用状态枚举和动作权限表管理订单流转,任何状态变更都必须通过统一的service方法,里面先锁行再判断前置状态。不要在控制器里到处写if (order.Status == 1)这种散装的判断,很容易漏。
第三道是“所有资金操作幂等”。订单表、优惠券使用表、退款流水表、回调流水表都要有唯一的业务键,用数据库唯一索引作为兜底。同时在高频操作上加分布式锁或redis锁。
第四道是“回调与查询对账双保险”。支付结果不要只依赖回调,还要定期调用支付宝、微信的订单查询接口做主动对账。这样可以发现回调丢失、回调重放、金额不一致等异常。
第五道是“敏感操作日志与风控”。每笔下单、支付、退款都要记录用户ID、设备指纹、IP、金额、订单号,并设置阈值告警。比如同一用户秒内多次发起退款,或者同一IP批量使用不同用户ID下单,都应当被风控规则捕获。
4.4 支付集成常见错误速查与解决思路
结合前面提到的一些热词,把真实开发过程中最容易踩的坑整理一下:
| 现象 | 可能原因 | 解决思路 |
|---|---|---|
| 微信支付调起时报“用户态签名signature错误” | AppID/商户号不匹配、参与签名字段拼错、API密钥用错 | 打印签名原串逐项核对,检查appId和密钥类型 |
| uniapp打包App支付和微信小程序支付流程参数看起来一样 | 后端统一下单可复用,但前端调起参数和SDK不同 | 根据平台判断组装不同的调起数据,App端使用openSDK的拉起方式 |
| 安卓系统从微信返回APP后,订单仍显示未支付 | 回调延迟或丢失,APP未主动刷新 | 在onShow生命周期里调查询接口,主动向服务端获取结果 |
| 微信小程序想加入支付宝支付渠道 | 平台不允许小程序内直接唤起支付宝 | 做H5跳转或引导复制链接到支付宝,考虑在小程序外完成支付 |
| gin-vue-admin配置支付宝支付后一直调不起 | 私钥/公钥配置混淆、沙箱与生产网关混用 | 按支付宝官方规范区分商户私钥和支付宝公钥,检查网关地址是否为生产环境 |
| 谷歌支付失败OR-PFGVEM | 账号状态异常或付款方式无效 | 引导用户检查Google Play账号,结合后台订单接口二次确认 |
| 苹果IAP退款后用户仍能用付费功能 | 退款回调只通知了支付平台,没关停应用内权益 | 监听App Store Server通知,在退款事件发生后调整用户权限 |
这些坑大多不是算法难度问题,而是环境配置和流程设计问题。我建议在做支付模块时,先写一份“支付网关设计文档”,明确各端的支持渠道、调起方式、回调地址、错误码映射、对账机制,再开始编码。否则等联调阶段再回头改,成本会高出好几倍。
做了这么多年支付安全测试,我最大的感受是:逻辑漏洞的攻防,拼的不是谁漏洞库多,而是谁更了解自己的业务流程。在你写代码的时候,永远要问一句“这个接口如果被不认识的人直接调用,会发生什么?”把反问变成习惯,支付系统的安全性就会上一个台阶。最后再分享一个小技巧,每次发布支付相关代码前,用上面那个排查表过一遍,成本很低,但能避免大多数线上事故。