1. 前言
对支付系统的开发而言,最害怕的就是产生了资金损失问题,资金损失可以分为长款
和短款
。站在公司的角度上看,长款就是多收用户的钱,比如订单的重复支付,短款指的是少收用户的钱,或者多给了用户的钱,比如说重复结算。
一般来说,长款问题相对比较好处理,多收了用户的钱退还给用户即可,最多损失用户体验。短款问题需要通过向用户要回多发的钱来解决,而这一般就比较难实现了。
2. 资金损失的产生和解决
一般而言,可以从两个指标对支付系统进行考量:
- 系统能够提供服务的稳定性好不好,一年有多长时间不可用;
- 资金损失问题是否严重,能不能做到不算错一分账。
大体而言资金损失产生的原因有两个:人为操作失误
和系统逻辑发生了错误(即系统有bug)
。这里我们忽略人为操作失误,来看下在技术实现中有哪些场景可能会产生资损问题。
2.1. 网络异常问题
如果支付系统和银行系统出现了网络异常,支付系统的代码中往往会try catch住,进入失败的分支,告诉上游系统失败,而这笔交易在银行侧可能是成功、失败或处理中,此时上游系统发起重试,就可能会引起重复支付。
当发生网络异常问题时,在支付系统中应该当成处理中进行,然后通过查询或者银行侧通知来更新最终状态。
2.2. 查询和通知问题
支付系统中的查询接口用来查询订单的最终状态,但是对于查询接口需要注意的是,查询操作本身也有可能失败,开发人员需要将查询操作的自身的结果和订单的状态区分开,有可能是查询交易操作失败了,而并不代表交易失败。
查询频率过快,假如在交易过后立即去反查交易状态,查询接口返回“无此订单”,这有可能是由于银行交易链路较长,查询请求先到。如果根据查询的结果对交易失败处理,就可能会引起上游系统重试。这个需要在对接的时候咨询一下接口提供方查询的频率,看需要多久之后才能查询。
2.3. 通知问题
某些支付公司由于接口的特殊性,可能会返回两遍通知,而比较恶心的点是这两遍通知有能通知的结果不一致。这种情况,我们可以以第一遍的通知为准,如果出现前后通知不一致的话,就告警出来人工干涉。
异步通知有可能会比同比结果先到,假如异步返回成功,你将你系统的订单状态修改成了成功,同步结果返回处理中,你再将订单状态修改成了处理中,此时很明显订单状态就不对了。针对订单的更新一定是要带状态更新,不可直接更新,比如不能从终态更新成处理中状态,不能从一个终态更新成另外一个终态
。
消息队列的消费者一定要实现幂等。
2.4. 接口幂等问题
交易接口都需要支持幂等,好让上游系统能够进行重试。一般可以使用产品号+上游请求流水+交易时间
作为唯一键在DB层面进行约束,最好再在请求的入口通过redis挡一层。