• 代码设计之多渠道支付


    支付网关中需要接入多个支付渠道,如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;
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> PayResultDTO <span class="hljs-title">pay</span><span class="hljs-params">(PayOrderDTO dto)</span> </span>{
        String payChannel = dto.getPayChannel();
        <span class="hljs-keyword">if</span> (payChannel.equalsIgnoreCase(<span class="hljs-string">"cybs"</span>))
            <span class="hljs-keyword">return</span> cybsPaymentService.pay(dto);
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (payChannel.equalsIgnoreCase(<span class="hljs-string">"fortumo"</span>))
            <span class="hljs-keyword">return</span> fortumoPaymentService.pay(dto);
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (payChannel.equalsIgnoreCase(<span class="hljs-string">"amx"</span>))
            <span class="hljs-keyword">return</span> amxPaymentService.pay(dto);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
    }
    

    }

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

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

    @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;
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> PayResultDTO <span class="hljs-title">pay</span>(<span class="hljs-params">PayOrderDTO dto</span>) </span>{
        String payChannel = dto.getPayChannel();
    
        <span class="hljs-keyword">return</span> channelServiceMap.<span class="hljs-keyword">get</span>(payChannel).pay(dto);
    }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setChannelServiceMap</span>(<span class="hljs-params">Map&lt;String, AbstractPaymentService&gt; serviceMap</span>) </span>{
        <span class="hljs-keyword">this</span>.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>
    
  • 相关阅读:
    LeetCode "Top K Frequent Elements"
    LeetCode "Integer Break"
    HackerRank "Angry Children 2"
    HackerRank "Kitty and Katty"
    HackerRank "Minimum Penalty Path"
    HackerRank "Larry's Array"
    HackerRank "TBS Problem" ~ NPC
    HackerRank "Morgan and a String"
    HackerRank "Favorite sequence"
    Windows不分区VHD装Linux多系统(三):VM虚拟机安装ubuntu18.04
  • 原文地址:https://www.cnblogs.com/jpfss/p/9290097.html
Copyright © 2020-2023  润新知