大家好,我是隐墨星辰,专注境内/跨境支付架构设计十余年。
在前面介绍过支付系统的整体设计,有兴趣的读者可点击链接查看:图解支付系统整体设计,今天聊聊支付系统的一些关键设计细节。
内容主要包括一些支付系统常用的设计,比如领域建模,状态机,幂等,日志规范,业务ID生成规范,监控,资损防控,支付安全等。这些技术在互联网其它领域比如电商也是通用的。
这里只摘录了部分精华内容出来,但已经能表达最核心的设计理念。
1. 领域建模
领域驱动设计(DDD)思想在支付系统的设计中应用非常广泛,但需要对行业有非常深刻的理解,否则构建出来的模型稳定性极差,动不动就需要修改核心模型。
收单、结算、支付引擎、会员、商户服务、渠道网关等各关键域都有自己独特的业务,模型也是不一样的。给两个小示例:
比如会员的三户模型。

说明:
- 客户是一种社会属性,代表真实社会的一个实体。比如张三分别使用1388888888和1399999999两个手机号在微信支付进行注册,然后使用同一个身份证做了实名认证,那么这两个用户仍然被归属于同一个客户。
- 用户是一种业务属性,就是使用产品的身份。比如张三分别使用1388888888和1399999999两个手机号在微信支付进行注册,那么就会存在2个用户,这2个用户的业务是独立的。
- 账户是一种金融属性,代表使用资金的身份。比如张三使用1388888888开通了微信支付,经过实名认证后,可以开通余额,也可以开通基金账户。
如果没有金融属性,一般就没有账户,比如注册博客园后,就只有用户没有账户。
下图是会员的模型:

说明:
- 用户、客户、账户的关系,详见上面的三户模型说明。会员注册后,就会有一个用户(UserId),完成实名认证,就会有一个客户(CustomeId),开通余额,就会有账户(AccountNo)。
- 其它的关系,图中已经很清楚。
支付渠道的模型:

说明:
渠道:对外部渠道做一个抽象,比如国内微信、支付宝、银联等,国外的WPG、MGPS等。
业务能力:支付、退款、撤销、请款等能力详细描述。比如退款有效期、最小限额等。
接口能力:描述接口本身的能力,比如渠道的请求号生成规则。特殊情况下可能有短号问题。
2. 状态机
状态机,也称为有限状态机(FSM, Finite State Machine),是一种行为模型,由一组定义良好的状态、状态之间的转换规则和一个初始状态组成。它根据当前的状态和输入的事件,从一个状态转移到另一个状态。
下图就是收单子域设计中交易单的状态机设计。

从图中可以看到,一共4个状态,每个状态之间的转换由指定的事件触发。因为有组合支付的情况,所以全额支付成功才会推进到成功。
常见代码实现误区
- 经常看到工作多年的研发工程师实现状态机时,仍然使用if else或switch case来写。这是不对的,会让实现变得复杂,且容易出现问题。
- 直接在订单的领域模型里面使用String来定义,而不是把状态模式封装单独的类。
- 直接调用领域模型更新状态,而不是通过事件来驱动。
- 业务不做拆分,一个状态机包含了所有的业务状态,比如支付单把退款和撤销状态也耦合进去,导致状态机巨复杂。
下面是一个比较差的状态机设计:

限于篇幅,这里无法给出完整示例代码。有兴趣的可以去网上看看,良好的示例有很多。
/**
* 支付状态机
*/
public enum PaymentStatus implements BaseStatus {
INIT("INIT", "初始化"),
PAYING("PAYING", "支付中"),
PAID("PAID", "支付成功"),
FAILED("FAILED", "支付失败"),
;
// 支付状态机内容
private static final StateMachine<PaymentStatus, PaymentEvent> STATE_MACHINE = new StateMachine<>();
static {
// 初始状态
STATE_MACHINE.accept(null, PaymentEvent.PAY_CREATE, INIT);
// 支付中
STATE_MACHINE.accept(INIT, PaymentEvent.PAY_PROCESS, PAYING);
// 支付成功
STATE_MACHINE.accept(PAYING, PaymentEvent.PAY_SUCCESS, PAID);
// 支付失败
STATE_MACHINE.accept(PAYING, PaymentEvent.PAY_FAIL, FAILED);
}
// 状态
private final String status;
// 描述
private final String description;
PaymentStatus(String status, String description) {
this.status = status;
this.description = description;
}
/**
* 通过源状态和事件类型获取目标状态
*/
public static PaymentStatus getTargetStatus(PaymentStatus sourceStatus, PaymentEvent event) {
return STATE_MACHINE.getTargetStatus(sourceStatus, event);
}
}
3. 幂等
幂等是针对重复请求的,支付系统一般会面临以下几个重复请求的场景:
- 用户多次点击支付按钮:在网络较差或系统过载情况下,用户由于不确定交易是否完成而重复点击。
- 自动重试机制:系统在超时或失败时重试请求,可能导致同一支付多次尝试。
- 网络数据包重复:数据包在网络传输过程中,复制出了多份,导致支付平台收到多次一模一样的请求。
- 异常恢复:在系统升级或崩溃后,未决事务需要根据已有记录恢复和完成。内部系统重发操作。
幂等解决方案

所谓业务幂等,就是由各域自己把唯一性的交易ID作为数据库唯一索引,这样可以保证不会重复处理。

最低0.47元/天 解锁文章
64

被折叠的 条评论
为什么被折叠?



