大家好,又见面了,我是你们的朋友全栈君。
最近公司商城系统要重做,我接手了支付相关的需求,发现里面弯弯绕绕的地方还是有不少的,所以把碰到的问题记录一下。
支付问题
- 在第一次对接微信支付时,生成预支付单的接口会让使用微信商家平台的API密钥进行加签,但是就算你使用的API密钥确定没有问题,也可能会返回验签失败,一点办法也没有。 解决方法:使用UUID重新生成了32位纯小写的密钥(我怀疑就是密钥格式问题引起的,从来没有见过密钥让用户手填的),然后等待15分钟新的API密钥生效,重新调用接口即可。
- 支付宝统一下单接口中可以设置超时时间timeout_express,这个字段的含义是以用户点击了 “支付宝支付” 这一刻算起的TTL,有可能与业务上要求的订单超时时间并不一致。 解决方法:使用time_expire字段,该字段含义为在time_expire后的支付都为超时支付。
订单金额问题(划重点)
这个可以说是一个我碰到过的严重线上问题了,之前我一直认为,创建订单的所有参数都要经过加签,所以参数都是不可修改的。 万万没有想到,对于订单的支付金额,支付宝那里居然没有进行加签验证,这样会导致一个什么样的问题呢? 如果你的App被人恶意攻破了,拿到从服务端返回的用于唤起支付的链接后,客户端或者H5就可以去修改链接中的订单金额参数,比方说,我服务端生成的订单金额为100元,客户端就能改成0.1元。。 由于支付宝没有对订单金额进行校验,就会导致用户能唤起支付,能支付成功,能触发服务端的回调,然后你人就离职了 #_#
所以服务端在创建订单的时候,一定要在订单表记录一下用户需要支付的金额,并在回调的时候进行金额校验(对比支付宝返回的实际支付金额和预存的需要支付金额)
支付回调问题
支付回调的问题是最严重的,以支付宝举例(不管是微信还是支付宝,支付完成都有回调通知的)。支付宝的统一下单接口中可以传两个参数,return_url(页面跳转地址)和notify_url(结果通知地址)。 一般做法是在notify_url对应的接口中根据支付接口触发订单的后续逻辑的(更改订单状态什么的),因为这样做会比较安全,可以对参数设置加密返回或者对返回结果验签。这样做就会碰到以下几个问题。
- 因为notify_url是异步通知的,所以就会必然存在一个问题,用户收到了支付宝同步返回的支付结果,提示支付成功了,但是这时候,服务端还没有收到异步回调,相应的订单状态还没有进行修改,用户查看订单时显示的可能还是未支付状态。
- 可能因为网络问题、域名问题、或者支付宝本身问题(是系统就会出问题的= =),导致服务端根本就没有接收到回调,订单状态一直无法修改,直到超时取消。
- 支付宝发送异步通知时,如果服务端没有返回success,则支付宝有自身的重试机制,会进行重推,导致订单后续逻辑会重复执行。
针对问题三,这个是无法避免的,所以在异步通知的接口中订单处理逻辑一定要做幂等。 针对问题二,起定时任务,对待支付订单主动查询支付状态进行补偿。 针对问题一,成本最低的做法,可以让用户在收到支付成功时在页面上强制多停留几秒钟(测试的时候,用户收到支付成功和服务端收到回调的时间差也就一两秒钟以内,有时候收到回调可能还会更快。。),但是该种做法只能解决90%的场景,毕竟是网络环境,也可能几分钟才回调过来,或者网络直接崩了。
完美的办法(开发成本也是最高的),在用户收到支付成功后,由客户端调用接口执行订单后续逻辑的触发(加密啊、加签啊什么的都要做,为了安全),并且服务端收到调用后,也要主动去支付宝查询该笔订单的支付结果,进行再次确认。并且,为了防止因服务器处理异常产生的订单没有支付成功的现象,同时启动定时任务,定时轮询待支付的订单,查看支付到底有没有成功,进行补偿(会发生与客户端回调并发处理的问题,所以要加锁控制)。虽然这样基本上能够杜绝99%的问题发生了,但是性能上一定会有损耗,编码的难度也上升了。
所以,为了权衡,使用异步回调 定时任务的方式下,发生问题的概率就已经比较小了(会牺牲一下用户体验,就是用户支付成功了,可能要过个十分钟,订单状态才会变为已支付)。 毕竟系统越简单,bug才会越少。
SDK相关
支付宝的java sdk支持的很好,微信就一言难尽了(不过我在2021年又看到微信也提供了官方支付SDK https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1) 微信SDK也可以用一个开源的SDK接入,地址如下 https://github.com/Wechat-Group/WxJava,功能维护的很全,我之前一直在使用。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/134946.html原文链接:https://javaforall.cn