Payment service code design (strategic mode, extensible, convenient access)

Recently, I have been studying some knowledge in depth, and I have not had time to write something out. I will slowly summarize and write on the blog in the follow-up. I still don’t want to write specific knowledge points for the blog. The summary and improvement are ultimately for myself. In order to let others see it well, it also improves one's own growth and record. This record is not only knowledge, knowledge is not only software and hardware, but also experience and thought. These are more precious.

It’s hard for us to really communicate with each other, but we can put our hearts here. Whoever sees it, it’s good to chat with it; this kind of communication is active like I don’t like programmers becoming more and more like robots. As rigid as the law, he should still be a flesh and blood thoughtful person, not an ape

 I originally shared some points of the payment service. Today, I have practiced the specific design. I recently read some other people’s designs. I think it’s good to share with you

The design of the service code should pay attention to the 6 principles of the design pattern

The logical relationship diagram is given below, which is equivalent to the abstract version of the strategy model. Each payment method is independently packaged. The service is called through PayCoreManager, the instance is obtained through payChannel internally, PayStrategy is called, and their respective packages are called.

The payment service interface called by the third party may not be coupled together, such as WeChat payment and Alipay payment interface separately;

    @GetMapping("/t2")
    public void t2(PayReq payReq){
        //调用支付接口,传入支付方式以及参数信息 对于业务方来说可以用支付服务的枚举类,或者多种支付的接口分开,分别由支付服务本身设置
        //如payReq.setPayType(PaymentEnums.ALI_PAY.getCode());
        payCoreManager.payChoose(payReq);
    }


    public String notifyUrl(HttpServletRequest request) {
        // 获取支付宝POST过来反馈信息
        Map<String, String> params = AliPayApi.toMap(request);
        CallBackParam callBackParam = new CallBackParam();
        callBackParam.setChannel(PaymentEnums.ALI_PAY.getCode());
        callBackParam.setParam(params);
        ServerResponse res = payCoreManager.payNotify(callBackParam);
        if ((boolean)res.getData() == true) {
            return "success";
        }else {
            return "failure";
        }
    }

Call payment interface, or callback interface, object-oriented

public interface PayCoreManager {
    /**
     * @Author: 老乡
     * @Date: 2020/6/13 16:44
     * @Describe: pay
     */
    ServerResponse payChoose(PayReq payReq);

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 22:22
     * @Describe: 查询
     */
    ServerResponse getOrderInfo(PayReq payReq);

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 22:48
     * @Describe: 回调
     */
    ServerResponse payNotify(CallBackParam callBackParam);
}

Implementation class, get the instance in the map to call, can control the transaction, business processing can also be placed in this layer

@Service
public class PayCoreManagerImpl implements PayCoreManager {

    @Override
    public ServerResponse payChoose(PayReq payReq) {
        //从静态map支付类别中取出对应的支付类别,对应的实例PayStrategy,若没有应检查是否对应
        //不同的类别获取到不同的支付实例
        PayStrategy p = PayStrategyContent.strategyMap.get(payReq.getPayType());
        if (p == null) {
            return ServerResponse.createByErrorMessage("payType is not valid");
        }
        //调用支付封装接口
        return p.toPayCore(payReq);
    }

    @Override
    public ServerResponse getOrderInfo(PayReq payReq) {
        PayStrategy p = PayStrategyContent.strategyMap.get(payReq.getPayType());
        if (p == null) {
            return ServerResponse.createByErrorMessage("payType is not valid");
        }
        return p.getPayInfo(payReq);
    }

    @Transactional
    @Override
    public ServerResponse payNotify(CallBackParam callBackParam) {
        PayStrategy p = PayStrategyContent.strategyMap.get(callBackParam.getChannel());
        if (p == null) {
            return ServerResponse.createByErrorMessage("payType is not valid");
        }

        return ServerResponse.createBySuccess(p.payNotify(callBackParam));
    }

}

Respective payment strategy

public interface PayStrategy {
    /**
     * @Author: 老乡
     * @Date: 2020/6/13 21:50
     * @Describe: 支付接口
     * 接入新的支付方式只需要增加枚举类,继承PayStrategyContent抽象类,重写支付等功能接口
     */
    ServerResponse toPayCore(PayReq payReq);

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 21:54
     * @Describe: 查询
     */
    ServerResponse getPayInfo(PayReq payReq);

    /**
     * @Author: 老乡
     * @Date: 2020/6/14 00:00
     * @Describe: notify
     */
    boolean payNotify(CallBackParam callBackParam);
}

The abstract class implements the strategy interface. Each payment strategy is preloaded as Map, and the value is the current strategy object PayStrategy. After each payment is abstracted, it is implemented by the subclass, and ConcurrentHashMap is used to ensure thread safety. This is the key point for other services to obtain instances based on payChannel

Processing business after successful verification

/**
 * @Author: 老乡
 * @Date: 2020/6/13 22:19
 * @Describe: 当增加一个功能的时候可以在抽象类中实现接口,通过抽象方法让子类去实现
 * 如果有的子类没有用到这个功能也可以不重写,将类改为抽象类即可;
 * 不过一般抽象出来的功能都是共用的行为,所以即使暂时不用也可以先重写后放在那里
 * <p>
 * 此抽象类完成了对无法选择的延迟到子类实现,实现了PayStrategy接口,转化找到
 */
public abstract class PayStrategyContent implements PayStrategy {

    /**
     * 各种支付策略实例预加载
     */
    public static ConcurrentHashMap<Integer, PayStrategy> strategyMap = new ConcurrentHashMap<>(20);

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 22:11
     * @Describe: 只在启动时加载一次,加载类别在子类中控制
     * this为当前PayStrategy,因为调用的时候取到具体的实例如支付宝实例,返回的是接口PayStrategy
     */
    @PostConstruct
    public void init() {
        strategyMap.put(channelInit(), this);
        System.out.println("map:" + JSON.toJSONString(strategyMap));
    }

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 21:51
     * @Describe: 支付实现类,具体支付类别由子类实现,消除if else分支,接入其他支付时不违反开闭原则
     */
    @Override
    public ServerResponse toPayCore(PayReq payReq) {
        return toPay(payReq);
    }

    /**
     * @Author: 老乡
     * @Date: 2020/6/13 21:56
     * @Describe: 查询
     */
    @Override
    public ServerResponse getPayInfo(PayReq payReq) {
        return getInfo();
    }

    @Override
    public boolean payNotify(CallBackParam callBackParam){
        String orderSn = pNotifyCheckSign(callBackParam);
        if (StrUtil.isEmpty(orderSn)) {
            //验签失败
            return false;
        }

        //处理业务 添加记录,通知业务方
        return true;
    }

    //支付
    public abstract ServerResponse toPay(PayReq payReq);

    //查询
    public abstract ServerResponse getInfo();

    //验签 此处验签
    public abstract String pNotifyCheckSign(CallBackParam callBackParam);

    /**
     * @Author: 老乡
     * @Date: 2020/6/12 21:40
     * @Describe: 获取支付渠道
     */
    public abstract Integer channelInit();

}

Alipay subclasses implement abstract classes to override abstract methods to ensure independent changes

Why is orderSn returned? Because multiple payment verification returns are different, orderSn can be used as a public field for follow-up business

public class AliPayStrategy extends PayStrategyContent {

    private static final String aliPublicKey = "x";

    @Override
    public ServerResponse toPay(PayReq payReq) {
        System.out.println("ali pay");
        return null;
    }

    @Override
    public ServerResponse getInfo() {
        System.out.println("ali info");
        return null;
    }


    @Override
    public String pNotifyCheckSign(CallBackParam callBackParam) {
        //验签
        try {
            boolean verifyResult = AlipaySignature.rsaCheckV1(callBackParam.getParam(), aliPublicKey, "UTF-8", "RSA2");
            if (verifyResult) {
                System.out.println("notify_url 验证成功succcess");
                return callBackParam.getParam().get("orderSn");
            } else {
                System.out.println("notify_url 验证失败");
                return null;
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Integer channelInit() {
        return PaymentEnums.ALI_PAY.getCode();
    }


}

WeChat independent change category

public class WxPayStrategy extends PayStrategyContent {

    @Override
    public ServerResponse toPay(PayReq payReq) {
        System.out.println("微信支付===");
        return null;
    }

    @Override
    public ServerResponse getInfo() {
        System.out.println("wx info");
        return null;
    }

    @Override
    public String pNotifyCheckSign(CallBackParam callBackParam) {
        //验签,返回orderSn
        return null;
    }

    @Override
    public Integer channelInit() {
        return PaymentEnums.WX_PAY.getCode();
    }

}

Enumeration class

public enum PaymentEnums {

    /**
     * 支付方式
     */
    ALI_PAY(1, AliPayStrategy.class.getSimpleName()),
    WX_PAY(2, WxPayStrategy.class.getSimpleName());

    /**
     * 枚举定义+描述
     */
    private Integer code;
    private String beanName;

    PaymentEnums(Integer code, String beanName) {
        this.code = code;
        this.beanName = StringUtils.isNotEmpty(beanName) ? beanName.toLowerCase() : null;
    }
    
    /**
     * 根据code获取对应的枚举对象
     */
    public static PaymentEnums getEnum(Integer code) {
        PaymentEnums[] values = PaymentEnums.values();
        if (null != code && values.length > 0) {
            for (PaymentEnums value : values) {
                if (value.code.equals(code)) {
                    return value;
                }
            }
        }
        return null;
    }

    /**
     * 该code在枚举列表code属性是否存在
     */
    public static boolean containsCode(Integer code) {
        PaymentEnums anEnum = getEnum(code);
        return anEnum != null;
    }

    /**
     * 判断code与枚举中的code是否相同
     */
    public static boolean equals(Integer code, PaymentEnums calendarSourceEnum) {
        return calendarSourceEnum.code.equals(code);
    }


    public Integer getCode() {
        return code;
    }

    public String getBeanName() {
        return beanName;
    }
}

Callback class

public class CallBackParam {
    /** 支付渠道*/
    private Integer channel;

    /**
     * 支付回调参数
     */
    private Map<String,String> param;

    public Integer getChannel() {
        return channel;
    }

    public CallBackParam setChannel(Integer channel) {
        this.channel = channel;
        return this;
    }

    public Map<String, String> getParam() {
        return param;
    }

    public CallBackParam setParam(Map<String, String> param) {
        this.param = param;
        return this;
    }
}

ServerResponse is a return package class, which can be customized, so I won’t post it here

In this way, flexible changes can be achieved. For example, to add a payment method, you only need to define a package class to inherit PayStrategyContent, enumerate and add categories, and add a new interface to the controller.

Use the strategy model, do not violate the principle of opening and closing, rely on the principle of reversal, and the principle of substitution

It’s just that if you add a function, you need to change it a bit, but if you don’t involve various payment methods, you don’t need to delay to subclasses. If you delay to subclasses, you can implement this function separately. Even if you don’t implement it, you can change it to an abstract class (abstract class Features, if inheriting an abstract class is not fully realized, the subclass should also be changed to an abstract class)

Many other explanations are already in the code, you can check it yourself. It is recommended to think about it from your own perspective, and then practice why you want to design this way, such as why not directly implement it

PayCoreManager call, why subclass inheritance, etc.

If you need the source code, you can private me

Guess you like

Origin blog.csdn.net/Goligory/article/details/106740171