支付中心存在的目的

支付中心系统对内为各个业务线提供统一的支付、退款等服务,对外对接三方支付或银行服务实现资金的流转。如下图:

大部分公司基本都是这样的架构,主要有以下几方面的优点:

  1. 形成统一支付服务,降低业务线接入成本及重复研发成本。
  2. 更好更快的支持创新业务,为公司业务快速发展提供条件。
  3. 更利于构建安全,稳定,可扩展的支付系统。
  4. 利于核心支付数据的沉淀和统一利用。

支付流程

上图展示了用户支付的主要流程,分为三个步骤:

  1. 用户在业务订单确认页,唤起收银台页面。
  2. 用户在收银台页面选择支付方式,确认支付,显示第三方支付页面,输入密码,进行真实支付行为。
  3. 系统处理用户支付结果,并通知给用户及各个相关系统。

下面详细说下这三个步骤:

1. 唤起商户收银台

  1. 用户在订单确认页点击“去支付“按钮,调用收银台支付下单接口。
  2. 收银台将订单信息缓存并入库,然后将订单标识拼装到收银台URL上返回给订单系统。
  3. 订单系统接收到收银台地址跳转到收银台页面。

上图展示了两个业务线(景区业务线,酒店业务线)唤起的收银台页面,大概可以分为三个区域:

页面上部分显示的是支付剩余时间和应付金额;

中间部分是订单信息,根据收银台定义的数据格式,业务线动态传递过来的;

剩余部分展示的是支付渠道,支付渠道也是业务线根据自己的需求在支付后台管理系统配置的,想要哪些支付方式以及它们的顺序都可以自定义。

2. 用户确认支付

  1. 用户在收银台页面选择支付方式(支付宝支付,微信支付,银行卡支付等),点击立即支付按钮,
  2. 调用支付中心创单接口,支付中心调用三方支付创单接口,同步返回支付信息,支付中心对返回参数进行处理,返回给收银台,
  3. 收银台携带支付中心返回的参数,调用三方接口,唤起三方收银台,
  4. 用户输入密码,立即支付。

3. 支付结果处理

  1. 三方系统进行扣款处理,返回收银台结果(目前微信支付返回支付中,支付宝返回支付终态(支付成功或支付失败)),

以下几个步骤是异步执行的,不分先后。

  1. 收银台拿到三方返回的结果,确认用户已经支付,则分配定时任务轮询查询(注意超时时间)后台支付结果,拿到终态之后跳转到相应页面,
  2. 三方系统支付成功后会通知支付中心结果,支付中心做好自身逻辑处理,异步通知订单系统,然后返回三方系统通知结果,
  3. 如果长时间未收到三方支付结果的通知,为了防止掉单,支付中心会发起主动查询来获取支付最终结果,以保证支付结果的及时更新。
  4. 当然业务线订单系统为了防止支付系统出现异步通知问题,也可以定时轮询支付中心的支付状态,防止掉单。(图中未画)

支付中心系统一些问题及解决方案

1. 支付订单超时关闭问题

如果用户长时间没有支付,一般都会有一个超时时间(如上图商户收银台的支付剩余时间),到达这个超时时间支付单会自动关闭。实现此需求有很多方式,比如:

1. 轮询 DB

定时轮询DB,取出达到超时时间且在支付中的数据,然后执行关闭逻辑。

缺点:1. 存在延迟,取决于定时任务的频率。2. 影响数据库性能。

2. JDK 延时队列(DelayQueue)和时间轮算法

这两种的算法的实现方式自行搜索。

共同的缺点是 1. 数据易丢失,由于数据存储在内存中,服务重启后数据全部消失。2. 有内存限制,如果数据量过大,会出现OOM异常。

3. RocketMQ 延时队列

RocketMQ 支持消息延时发送,社区版不支持任意等级的延迟,目前默认支持18个延时等级:

1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
复制代码

比如支付单30分钟过期,在支付单创建成功后发送延迟消息(延时等级为 16),消费者在30分钟后会拉取到该消息然后执行关闭逻辑。

RocketMQ 延时队列,无论在数据安全性和及时性都有明显的优势,但是目前社区版没有支持任意级别的延迟。

目前我们使用的是 RocketMQ 延时队列实现的订单关闭。

2. 保证支付结果实时性

三方支付系统支付成功后99.9%的情况下都会回调通知我们,但也难免有意外,比如三方延迟回调或者三方系统宕机,为了保证支付结果的实时性,三方支付也要求我们不能完全依赖于回调接口,所以我们需要定时的调用主动查询接口来查询三方的支付结果。这里我们也是使用的 RocketMQ 延时队列实现的:

  1. 调用三方支付创单成功后,发送<支付主动查询>延时MQ消息。
  2. 消费消息,判断支付状态是否到达终态,如果到达终态,则返回处理成功,否则调用三方支付查询接口,如果支付成功则处理成功业务,返回处理成功。
  3. 如果客户未支付则判断是否达到最大的重试次数,如果达到最大重试次数则停止<支付主动查询>的重试,否则解析重试规则,发送下一轮的延时消息。

有三个重要参数,这些参数可以放到配置中心或者配置库中,

// 初始延迟级别,对应RocketMQ延时等级,比如3对应的延时时间就是10s
private Integer queryInitLevel = 3;// 重试次数
private Integer queryCount = 6;// 重试级别,对应RocketMQ延时等级,5s,10s,30s,1m,10m,20m
private String queryDelayLevels = 2,3,4,5,14,15;
复制代码

支付创单成功后发送延时消息:

public void payQueryTask(String orderNo) {PayQueryMessage payQueryMessage = new PayQueryMessage();payQueryMessage.setOrderNo(orderNo);RetryMessage<PayQueryMessage> retryMessage = new RetryMessage<>();retryMessage.setTotalCount(queryCount);retryMessage.setDelayLevels(queryDelayLevels);retryMessage.setTopic(TopicConst.PAY_QUERY_TOPIC);retryMessage.setEventType(RetryEventTypeEnum.PAY_QUERY);retryMessage.setEventDesc(RetryEventTypeEnum.PAY_QUERY.getDesc());retryMessage.setData(payQueryMessage);log.info("{} - 发送消息, retryMessage: {}", LOG_DESC, retryMessage);rocketMqProducer.asyncSend(retryMessage.getTopic(), JsonUtil.toJson(retryMessage),CodeEnum.codeOf(RocketMQDelayLevelEnum.class, queryInitLevel).orElse(RocketMQDelayLevelEnum.FiveSeconds), LOG_DESC);
}
复制代码

判断的是否继续执行任务:

public void sendDelayRetry(RetryMessage<?> retryMessage) {int currentCount;retryMessage.setCurrentCount(currentCount = retryMessage.getCurrentCount() + 1);// 重试达到最大次数if (currentCount > retryMessage.getTotalCount()) {log.warn("{} - 达到最大次数-{}, 停止重试! retryMessage: {}", retryMessage.getEventDesc(), retryMessage.getTotalCount(), JsonUtil.toJson(retryMessage));return;}log.info("{} - 发送重试消息-{}/{}, retryMessage: {}", retryMessage.getEventDesc(), retryMessage.getCurrentCount(), retryMessage.getTotalCount(), JsonUtil.toJson(retryMessage));int delayLevel = Integer.parseInt(retryMessage.getDelayLevels().split(",")[retryMessage.getCurrentCount() - 1]);rocketMqProducer.asyncSend(retryMessage.getTopic(), retryMessage,CodeEnum.codeOf(RocketMQDelayLevelEnum.class, delayLevel).orElse(RocketMQDelayLevelEnum.FiveSeconds), retryMessage.getEventDesc()+", 发送重试消息");}
复制代码

3. 支付结果通知上游容错

在回调通知上游系统支付结果时,可能会回调失败,比如网络异常或上游系统发生短时故障,如果发生这种情况我们单靠简单的重试是无法完全解决问题的。为了尽可能的通知成功,我们需要针对没有通知成功的数据,每隔一段时间通知一次,那这个需求和我们上一个问题差不多,所以可以复用我们的延时重试框架。

流程和保证支付结果实时的差不多,不再赘述。

支付中心系统中设计模式的应用

模板方法

模板方法模式思想:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

简单的理解就是定义一个模版方法,然后子类实现模版方法中的抽象方法实现个性化的需求。

就支付而言,无论何种支付产品,都是走的同一个支付流程,那我们就可以定义一个支付流程的模板,然后每种支付产品实现这个模板中特定步骤来实现自己的特定需求。

策略

策略模式主要思想:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。

在支付系统中,支付结果主动查询需要查询不同的渠道,比如支付宝,微信,银联等,每个渠道查询的方式和参数不尽相同,可以将每种渠道查询封装成不同的策略类,然后根据查询条件来调用不同的策略类。

查询策略有两个策略接口,callChannel功能是组装查询参数和查询三方,execute 是处理三方返回的结果统一为支付中心状态。(因callChannel有其他地方共用所以分开了两个方法)。

Spring 下使用策略模式,在项目启动时,将所有的策略类加载到Map中,然后使用时直接在Map中获取。

@Component
public class PayQueryStrategyContext {private final Map<String, PayQueryStrategy> payQueryStrategyMap = Maps.newConcurrentMap();public PayQueryStrategyContext(Map<String, PayQueryStrategy> payQueryStrategyMap) {this.payQueryStrategyMap.clear();payQueryStrategyMap.forEach(this.payQueryStrategyMap::put);}public PayQueryStrategy getPayQuery(@NotNull String channelCode) {return this.payQueryStrategyMap.get(OperationTypeConst.Pay_Query + channelCode);}
}

聚合支付系统的设计与实现相关推荐

  1. 记一次支付系统的设计体验

    0.写在前面的话 支付系统是一个老生常谈的话题,我也相信每个公司开发的支付系统不尽相同,因为业务形态并不太一样. 在此,我并不想讲一个大而全的支付系统,个人也没有能力去阐述. 在我看来,一个支付系统应 ...

  2. 从零打造聚合支付系统:一、浅谈聚合支付的核心价值

    支付被誉为一切交易活动的咽喉,是商业活动的本质环节. 近两年,市场如雨后春笋般地涌现出一批"聚合支付"商家,如收钱吧.Ping++.钱方好近等等. 从零打造聚合支付系统系列文章将带 ...

  3. 聚合支付系统开发究竟难不难?

    随着聚合支付的盛行,现在很多做聚合支付的服务商都推出了属于自己的聚合支付系统,虽然平台很多,但是市场上的聚合支付系统的功能还是参差不齐,多种收款方式的聚合是支付系统的最基本的功能,而由聚合支付衍生出更 ...

  4. 盒子科技研发部刘恒:聚合支付系统演进

    大家好,我是来自盒子科技研发部支付线刘恒,目前主要是负责公司的一个聚合支付系统的研发工作.今天主要是讲一下我们这个支付系统从2016年年初到现在,一个整个的技术演变. 首先我会从那三个方向,第一个就是 ...

  5. 支付系统整体设计:整体架构设计以及注意要点(三)

    一般来说,银行会提供两种支付途径:无跳转的快捷支付接口和带跳转的网银接口.前者在绑卡,支付的时候,不需要跳到银行页面上去处理,后者则需要在银行的网银页面上完成.显然前者对用户来说体验要好多了,用户流程 ...

  6. 聚合项目访问后台接口失败_聚合支付系统和免签支付系统对未来支付市场有哪些影响...

    时势所趋,在如今支付通道不稳定的情况下,四方聚合支付的出现弥补了通道不稳的情况,四方聚合支付可以接入多个三方,实现在三方不稳的情况直接后台切换三方,实现一秒切换,还可以接入个人免签支付系统,实现商户实 ...

  7. ThihkPHP开发聚合支付系统源码 兼容所有易支付程序

    简介: TP聚合支付系统源码兼容世面上所有易支付系统 基于PHP+MySQL+ThihkPHP框架开发,支持对接银联通道.京东钱包.免签系统.PDD.淘宝代付.话费等,全响应式前端界面,简约易操作,安 ...

  8. 新版UI聚合支付系统四方系统源码+修复漏洞完美版

    正文: 全新UI聚合支付系统四方系统源码+升级修复漏洞完美版,这个源码是上个月的四方系统源码,现在发给大家吧,源码内附安装教程,20多项功能及安全方面的更新文档,源码说明等,小白也能轻松搭建. 没有马 ...

  9. 怎么去搭建聚合支付系统比较划算

    怎么去搭建聚合支付系统比较划算 全新聚合支付系统开发 包含: 1.扫码收款 2.二维码主扫收款 3.快捷收款 4.NFC收款 打造全网最低费率,支持美团,白条,花呗 可分销,可裂变!打造更全面的聚合支 ...

  10. 2022最新独立版智狐聚合支付v1.0.5.21_聚合支付系统源码

    源码下载:2022最新独立版智狐聚合支付v1.0.5.21_聚合支付系统源码-小程序文档类资源-CSDN下载 PHP聚合支付源码 独立版智狐聚合支付v1.0.5.21 1.在宝塔新建个站点,php版本 ...

最新文章

  1. swiper 滑动出现闪白
  2. 解密ControlRotation与ActorRotation
  3. MyBatis : Example 条件查询 in 用法
  4. Linux下grub.cnf详解
  5. Spring是如何解决循环依赖的
  6. 安卓学习笔记26:菜单
  7. mvc html.dropdownlist,ASP.NET MVC4中使用Html.DropDownListFor的方法示例
  8. kafka sasl java_kafka 添加SASL鉴权
  9. 螺旋传动设计系统lisp_石狮螺旋输送机质量可靠
  10. memcached—认识Memcache
  11. python开发酷q插件gui_酷Q机器人插件开发笔记
  12. 罗斯蒙特8712ESR1A1N0M4流量变送器
  13. 随笔之---java版本哲学家就餐问题【信号量的实现】
  14. 2011年戴尔服务器型号,PowerEdge 11G R310机架式服务器
  15. ora12514问题怎么解决linux,ORA-12514问题解决
  16. Window串口编程
  17. 电子元器件B2B电商平台建设方案:优化企业商流,拓宽B2B交易渠道
  18. postman的操作步骤
  19. 四级恋练有词课程 (10课时-朱伟)1.Unit1A单词识记(上)
  20. blinker 第三方库_blinker 库开发注意事项

热门文章

  1. PS——证件照换底色的极简方法
  2. Webstorm返回上一步操作快捷键 格式化代码快捷键
  3. python 读取并显示图片的两种方法
  4. C语言中自定义的标识符
  5. 中国电信物联网平台入门学习笔记4:连接时间过长,数据传输消失,电信平台显示延迟...
  6. 第十课◆ Newland Edu Li nearI ayout和Re lati vel ayout
  7. 数据分析 告诉你《飞驰人生》为什么这么燃?
  8. Ci24R1_与Si24R1的通讯兼容,更具性价比的2.4GHz收发芯片
  9. 数据结构——散列表--线性探测法
  10. Collective Opinion Spam Detection: Bridging Review Networks and Metadata(2015KDD)