代码设计之多渠道支付

Posted 星朝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码设计之多渠道支付相关的知识,希望对你有一定的参考价值。

支付网关中需要接入多个支付渠道,如cybs、fortumo、amx,其中cybs为信用卡支付,其他两个为运营商支付。

不管使用那种支付渠道,支付流程都是类似的。显而易见可以使用模板方法。首先定义一个模板类,支付流程中各支付渠道通用的方法放在模板类中,特异于各个支付渠道的方法作为抽象方法,由子类实现。

如下图所示:

技术分享图片

其中AbstractPaymentService为模板类,其他三个为各支付渠道实现子类。

模板类--AbstractPaymentService伪代码如下所示:

protected final Logger logger = LoggerFactory.getLogger(AbstractPaymentService.class);

public PayResultDTO pay(PayOrderDTO dto){
    logger.info("Entering pay, and the dto is {}", dto);
    //将DTO转为Entity(数据库实体)
    PayOrder payOrder = convertDTOToEntity(dto);
    //落地支付单
    insertPayOrder(payOrder);
    //去支付 抽象方法 由各个支付渠道自己去实现
    return doPay(dto);
}
protected abstract PayResultDTO doPay(PayOrderDTO dto) ;
private void insertPayOrder(PayOrder payOrder) {
    // do something here...
}
private PayOrder convertDTOToEntity(PayOrderDTO dto) {
    //do something here...
    return null;
}

Cybs支付渠道实现子类–CybsPaymentService伪代码如下:

@Override
 protected PayResultDTO doPay(PayOrderDTO dto) {
     //使用cybs 第一次支付需要特殊处理
     if(dto.isFirstPay())
         return doFirstPay();
     else
         return doNormalPay();
 }
 private PayResultDTO doNormalPay() {
     //do smething here...
     return result;
 }
 private PayResultDTO doFirstPay() {
     //do smething here...
     return result;
 }

其他两个支付渠道实现类类似,只要实现doPay方法即可。比如Amx以HttpClient方式调用Amx Server的Http接口发起支付并同步等待返回。

若这时需要接入一个新的支付渠道,如Paypal,只需定义一个PaypalPaymentService继承模板类–AbstractPaymentService,同时实现doPay方法即可。

接着再定义一个支付门面类,方便客户端(交易系统)调用支付方法,即客户端无需关心是哪种支付渠道,只须调用pay方法即可。支付门面代码如下所示:

@Service
public class PaymentFacadeService {
    @Autowired
    private CybsPaymentService cybsPaymentService;
    @Autowired
    private FortumoPaymentService fortumoPaymentService;
    @Autowired
    private AmxPaymentService amxPaymentService;

    public PayResultDTO pay(PayOrderDTO dto) {
        String payChannel = dto.getPayChannel();
        if (payChannel.equalsIgnoreCase("cybs"))
            return cybsPaymentService.pay(dto);
        else if (payChannel.equalsIgnoreCase("fortumo"))
            return fortumoPaymentService.pay(dto);
        else if (payChannel.equalsIgnoreCase("amx"))
            return amxPaymentService.pay(dto);
        return null;
    }
}

门面类应该有更优雅的实现方式,即不用在门面类的成员变量中全部罗列出所有的支付渠道的实现类。

这时客户端调用支付方法伪代码示例为:

@Autowired
private PaymentFacadeService paymentServcie;

public MyResultDTO pay(...){
    //构造payOrderDTO
    PayOrderDTO dto = new PayOrderDTO();
    //填充属性
    //...
    PayResultDTO resultDTO = paymentServcie.pay(dto);
    //do something with resultDTO
    return myResult;
}

支付门面设计方案二:

public class PaymentFacadeService2 {
    private Map<String,AbstractPaymentService> channelServiceMap;

    public PayResultDTO pay(PayOrderDTO dto) {
        String payChannel = dto.getPayChannel();

        return channelServiceMap.get(payChannel).pay(dto);
    }
    public void setChannelServiceMap(Map<String, AbstractPaymentService> serviceMap) {
        this.channelServiceMap = serviceMap;
    }

}

spring 配置:

<bean id="paymentFacadeService2"
        class="com.tcl.gateway.service.refactoring.PaymentFacadeService2">
        <property name="channelServiceMap">
            <map>
                <entry key="cybs">
                    <bean class="com.tcl.gateway.service.refactoring.CybsPaymentService"></bean>
                </entry>
                <entry key="fortumo">
                    <bean class="com.tcl.gateway.service.refactoring.FortumoPaymentService"></bean>
                </entry>
                <entry key="amx">
                    <bean class="com.tcl.gateway.service.refactoring.AmxPaymentService"></bean>
                </entry>
            </map>
        </property>
    </bean>

以上是关于代码设计之多渠道支付的主要内容,如果未能解决你的问题,请参考以下文章

设计模式六 策略模式

经典设计模式之策略模式如何重构聚合支付平台,对接支付宝,微信,银联支付

支付系统整体设计:整体架构设计以及注意要点

支付系统整体设计:整体架构设计以及注意要点

PHP对接第三方支付渠道之微信支付v3版本

支付渠道路由系统进化史