图解支付系统的关键设计

大家好,我是隐墨星辰,专注境内/跨境支付架构设计十余年。

在前面介绍过支付系统的整体设计,有兴趣的读者可点击链接查看:图解支付系统整体设计,今天聊聊支付系统的一些关键设计细节。

内容主要包括一些支付系统常用的设计,比如领域建模,状态机,幂等,日志规范,业务ID生成规范,监控,资损防控,支付安全等。这些技术在互联网其它领域比如电商也是通用的。

这里只摘录了部分精华内容出来,但已经能表达最核心的设计理念。

1. 领域建模

领域驱动设计(DDD)思想在支付系统的设计中应用非常广泛,但需要对行业有非常深刻的理解,否则构建出来的模型稳定性极差,动不动就需要修改核心模型。

收单、结算、支付引擎、会员、商户服务、渠道网关等各关键域都有自己独特的业务,模型也是不一样的。给两个小示例:

比如会员的三户模型。

说明:

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

如果没有金融属性,一般就没有账户,比如注册博客园后,就只有用户没有账户。

下图是会员的模型:

说明:

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

支付渠道的模型:

说明:

渠道:对外部渠道做一个抽象,比如国内微信、支付宝、银联等,国外的WPG、MGPS等。

业务能力:支付、退款、撤销、请款等能力详细描述。比如退款有效期、最小限额等。

接口能力:描述接口本身的能力,比如渠道的请求号生成规则。特殊情况下可能有短号问题。

2. 状态机

状态机,也称为有限状态机(FSM, Finite State Machine),是一种行为模型,由一组定义良好的状态、状态之间的转换规则和一个初始状态组成。它根据当前的状态和输入的事件,从一个状态转移到另一个状态。

下图就是收单子域设计中交易单的状态机设计。

从图中可以看到,一共4个状态,每个状态之间的转换由指定的事件触发。因为有组合支付的情况,所以全额支付成功才会推进到成功。

常见代码实现误区

  1. 经常看到工作多年的研发工程师实现状态机时,仍然使用if else或switch case来写。这是不对的,会让实现变得复杂,且容易出现问题。
  2. 直接在订单的领域模型里面使用String来定义,而不是把状态模式封装单独的类。
  3. 直接调用领域模型更新状态,而不是通过事件来驱动。
  4. 业务不做拆分,一个状态机包含了所有的业务状态,比如支付单把退款和撤销状态也耦合进去,导致状态机巨复杂。

下面是一个比较差的状态机设计:

限于篇幅,这里无法给出完整示例代码。有兴趣的可以去网上看看,良好的示例有很多。

/**
 * 支付状态机
 */
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. 幂等

幂等是针对重复请求的,支付系统一般会面临以下几个重复请求的场景:

  1. 用户多次点击支付按钮:在网络较差或系统过载情况下,用户由于不确定交易是否完成而重复点击。
  2. 自动重试机制:系统在超时或失败时重试请求,可能导致同一支付多次尝试。
  3. 网络数据包重复:数据包在网络传输过程中,复制出了多份,导致支付平台收到多次一模一样的请求。
  4. 异常恢复:在系统升级或崩溃后,未决事务需要根据已有记录恢复和完成。内部系统重发操作。

幂等解决方案

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

本书是我的专栏《百图解支付系统设计与实现》系列文章汇总整理并重新修订。汇集了我十多年支付系统架构设计经验总结,以实战为导向,深入浅出讲解支付系统的架构设计与实现,涵盖基础概念、核心流程、核心子系统设计、核心技术专题等。这些知识点以实用为目标,可直接应用到日常研发设计中。 全书共18章,分为入门篇、核心子域设计篇、技术专题篇。入门篇(第1~10章)详细讲述支付系统涉及的基本概念,概要设计,核心流程。核心子域设计篇(第4~10章)深入地剖析支付系统各核心子域的架构设计精华。技术专题篇(第11~18章)介绍了一些支付系统领域内非常实用的最佳实践,这些技术还可以应用到非支付系统设计中。 书中通过采用大量手绘风格的图示来讲解专业领域的概念和设计思路,让大家更容易阅读和理解,达到“一图胜千言”的效果。虽然绘图对我而言是一大非常大的挑战,需要极强的抽象和表达能力,但我认为这种方式对于阐述支付系统设计和实现至关重要。有时,我可能用一个小时就能撰写几千字的文章,但为了一幅能准确表达意思的图,却要花费好几个小时。 需要说明的是,所撰写的内容主要基于我个人的经验总结,并以实战为导向,但内容不会与任何特定公司的支付系统直接相关。同时,我努力使文章内容浅显易懂,但毕竟支付是一个高度专业的领域,难以涵盖所有细节。因此,建议有兴趣的读者也多参考其他优秀的书籍或资料。 本书适合初学者入门,同时书中很多设计思路对于想进一步提升架构设计能力的资深研发人员也很有参考价值,并可作为培训机构相关专业的教学参考书。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值