目前主流的第三方支付平台主要有支付宝支付、微信支付和银联支付等,多通道聚合支付融合了支付宝支付、微信支付和银联支付三种支付方式于一体,也称第四方支付。如下图1所示为聚合支付原理示意图。
图1 聚合支付原理示意图
在传统的支付对接方式中,如果商户端想要同时对接支付宝支付、微信支付和银联支付就要分别对接三个不同的支付接口,随着第三方支付工具的不断增多使得对接业务十分复杂冗余,开发人员需要熟悉不同的第三方支付工具接口的对接流程,开发起来十分复杂,可扩展性差,后期维护成本高。
聚合支付服务会提前将支付宝支付、微信支付和银联支付等第三方支付接口对接好,聚合支付服务再暴露支付接口给商户端进行调用,对接业务内聚化,可以灵活的对接新的第三方支付工具,适配性强。
简单的说,由于各第三方支付工具对接过程比较相似,聚合支付将第三方支付对接过程抽取出来,形成易用的通用型支付接入方案,从而提高开发效率和业务可扩展性。如下图2所示为传统第三方支付与聚合支付对比示意图。
图2 传统第三方支付与聚合支付对比
首先是数据库设计,抽取第三方支付接入渠道、商户等信息以构建数据库表结构。在传统的第三方支付接入过程中,第三方支付工具的关键配置信息以及商户信息等一般是写在项目业务代码中,不利于项目上线后相关信息的修改。聚合支付服务将第三方支付渠道信息以及商户信息等存储在数据库中,便于集中管理且不会影响项目的正常运行,是实现聚合支付的重要部分。
本文聚合支付服务数据库表主要由支付渠道表payment_channel、支付商户信息表payment_mch_info和支付信息表payment_info组成。支付渠道表用以存储支付渠道信息,包括渠道id、渠道名称、商户id和渠道状态等。支付商户信息表用以存储商户信息,包括商户id、商户名称、商户类型、请求私钥和响应私钥等。支付信息表用以存储交易号、订单id、商户id和渠道id等。
聚合支付服务对接多个不同的支付接口,但是支付接口实现思路都是相同的,唯一不同的是实现的接口不同。首先创建策略接口,然后创建第三方支付的支付策略实现类,策略工厂就根据支付请求参数中的支付策略实现类(AlipayStrategy、WxPayStrategy、UnionPayStrategy)的地址使用反射机制创建具体的支付策略,最终实现具体的支付策略。
如下图3为利用策略设计模式、工厂模式以及反射机制实现第三方支付接入业务代码的融合设计示意图。
图3 利用设计模式实现多通道支付融合
用户在提交订单后订单服务就调用支付服务生成支付令牌,执行预提交后跳转至支付页面,用户在支付页面选择支付方式支付即可。如下图4所示为多通道融合支付算法执行流程。
图4 多通道融合支付算法执行流程
支付服务根据不同渠道返回不同的form表单提交参数,方法调用需要传入的参数有支付渠道channelId和支付令牌payToken。首先使用渠道id到数据库中获取渠道信息,然后使用payToken获取支付参数,再执行具体的支付渠道的算法获取html表单数据。如下所示支付页面生成部分代码实现。
@Override
public BaseResponse<JSONObject>toPayHtml(String channelId, String payToken){
PaymentChannelEntity pymentChannel =
paymentChannelMapper.selectBychannelId(channeIId);
if (pymentChannel == null){
return setResultError("没有查询到该渠道信息");
}
BaseResponse<PayMentInfoDTO>tokenByPaymentInfo =
payMentInfoService
.tokenByPayMent(payToken);
if(!isSuccess(tokenByPayMentInfo)){
return setResultError(tokenByPayMentInfo.getMsg());
}
PayMentInfoDTO payMentInfoDTO = tokenByPayMentInfo.getData();
String classAddres = pymentChannel.getClassAddres();
PayStrategy payStrategy = StrategyFactory.getPayStrategy(classAddres);
String payHtml = payStrategy.toPayHtml(pymentChannel,payMentInfoDTO);
JSONObject data = new JSONObject();
data.put("payHtml",payHtml);
return setResultSuccess(data);
}