Multi-application and multi-platform payment module design - the beginning of the basic module

Recently, I want to integrate the three-party payment platform into the author's framework to simplify and develop later business functions.

In order to meet the scalability of this function, it is necessary to define the rules of "order business interface" and "payment method interface".

When the user places an order, the specific business implementation is obtained according to the incoming "business type", and the specific order data is obtained according to the "payment method".

In this way, corresponding business functions can be implemented in specific business codes.

The beginning of development is design. This article is only a basic design article. In the later stage, the payment implementation class of each third-party platform will be gradually improved.

Long-winded so far, here is the official opening.

The idea design diagram is as follows

Here, the specific table structure is designed. Based on the current idea, the author designs two tables "Order Table" and "Order Process Table".

The database table structure is as follows

The table relationship diagram is as follows

That is, the table design is completed, and the business functions will be implemented under this.

At the beginning of the chapter, the author first defines the corresponding "order-related dictionary".

The "order business type" does not have a reserved dictionary type. When the specific business function is introduced into this library, it can be defined independently.

In addition, the current framework of the payment method only implements the "WeChat V2JSApi" method, which will be implemented in the later period, and related dictionaries will be defined, and all dictionaries will not be defined here for the time being.

The dictionary configuration class is as follows

package com.threeox.biz.order.config;

import com.threeox.drivenlibrary.engine.annotation.config.DictionaryConfig;

/**
 * 订单字典常量类
 *
 * @author 赵屈犇
 * @version 1.0
 * @date 创建时间: 2022/4/18 22:24
 */
public class OrderDictConstants {

    /**
     * 订单业务类型
     */
    @DictionaryConfig(dictionaryCode = OrderBizType.DICTIONARY_CODE, dictionaryName = "订单业务类型", remark = "订单业务类型", version = 2)
    public static class OrderBizType {

        public static final String DICTIONARY_CODE = "2008";

    }

    /**
     * 订单状态
     */
    @DictionaryConfig(dictionaryCode = OrderStatus.DICTIONARY_CODE, dictionaryName = "订单状态", remark = "订单状态", version = 2)
    public static class OrderStatus {

        public static final String DICTIONARY_CODE = "2009";
        /**
         * 已下单
         */
        @DictionaryConfig(dictionaryName = "已下单")
        public static final String HAVE_ORDER = "200901";
        /**
         * 支付成功
         */
        @DictionaryConfig(dictionaryName = "支付成功")
        public static final String PAYMENT_SUCCESS = "200902";
        /**
         * 支付失败
         */
        @DictionaryConfig(dictionaryName = "支付失败")
        public static final String PAYMENT_FAILED = "200903";
        /**
         * 支付金额有误
         */
        @DictionaryConfig(dictionaryName = "支付金额有误")
        public static final String PAYMENT_AMOUNT_WRONG = "200904";

    }

    /**
     * 支付方式
     */
    @DictionaryConfig(dictionaryCode = PayWay.DICTIONARY_CODE, dictionaryName = "支付方式", remark = "支付方式", version = 2)
    public static class PayWay {

        public static final String DICTIONARY_CODE = "2010";
        /**
         * 微信JSAPI支付
         */
        @DictionaryConfig(dictionaryName = "微信JSAPI_V2支付")
        public static final String WX_JS_API_V2 = "201001";
        /**
         * 微信APP支付
         */
        @DictionaryConfig(dictionaryName = "微信APP_V2支付")
        public static final String WX_APP_API_V2 = "201002";

    }

}

In order to realize the high scalability of the framework, the author intends to define two sets of interface specifications for the realization of specific business and payment methods.

interface specification class

Order business extension class

This interface currently declares two sets of interfaces, "Get business snapshot data" and "Process payment callback" functions, which are used for specific business implementation.

Later, if you need to implement it, you can add it.

package com.threeox.biz.order.factory.inter;

import com.threeox.biz.order.entity.OrderInfo;
import com.threeox.biz.order.entity.inter.IOrderBizInfo;
import com.threeox.biz.order.entity.inter.IPayNotifyInfo;
import com.threeox.dblibrary.executor.inter.ISqlExecutor;

/**
 * 订单接口扩展类
 *
 * @author 赵屈犇
 * @version 1.0
 * @date 创建时间: 2022/4/13 23:43
 */
public interface IOrderExtend<T extends IOrderBizInfo> {


    /**
     * 获取业务快照数据
     *
     * @param executor
     * @param orderInfo
     * @return a
     * @throws Exception
     * @author 赵屈犇
     * @date 创建时间: 2022/4/18 21:30
     * @version 1.0
     */
    T getBizSnapshot(ISqlExecutor executor, OrderInfo orderInfo) throws Exception;


    /**
     * 处理支付回调
     *
     * @param executor
     * @param orderInfo
     * @param notifyInfo
     * @param currentStatus
     * @return a
     * @throws Exception
     * @author 赵屈犇
     * @date 创建时间: 2022/5/28 23:52
     * @version 1.0
     */
    void handlePayNotify(ISqlExecutor executor, OrderInfo orderInfo, IPayNotifyInfo notifyInfo, String currentStatus) throws Exception;

}

Payment method interface specification

This interface, currently only needs to obtain the unified order data function of each payment platform.

package com.threeox.biz.order.factory.inter;

import com.alibaba.fastjson.JSONObject;
import com.threeox.biz.order.entity.OrderInfo;

/**
 * 支付接口定义
 *
 * @author 赵屈犇
 * @version 1.0
 * @date 创建时间: 2022/4/12 21:59
 */
public interface IPaymentFactory {

    /**
     * 统一下单
     *
     * @param orderInfo
     *                  订单对象
     * @author 赵屈犇
     * @return
     * @date 2022/4/23 17:45
     */
    JSONObject placeOrder(OrderInfo orderInfo) throws Exception;

}

So far, the interface specification has been defined.

Here, the author wants to implement the basic implementation class of payment methods for inheritance and extension of specific business logic.

Payment method basic implementation class

This basic class currently only needs to obtain channel information according to the specific application channel, and call the unified order implementation method of the corresponding subclass.

package com.threeox.biz.order.factory.impl.base;

import com.alibaba.fastjson.JSONObject;
import com.threeox.biz.order.entity.OrderInfo;
import com.threeox.biz.order.factory.inter.IPaymentFactory;
import com.threeox.drivenlibrary.engine.config.DrivenEngineConfig;
import com.threeox.drivenlibrary.engine.entity.driven.config.OpenPlatformConfigMessage;
import com.threeox.drivenlibrary.enums.ResponseResult;
import com.threeox.drivenlibrary.exception.ResponseException;

/**
 * 基础支付工厂
 *
 * @author 赵屈犇
 * @version 1.0
 * @date 创建时间: 2022/4/23 17:37
 */
public abstract class BasePaymentFactory implements IPaymentFactory {

    @Override
    public JSONObject placeOrder(OrderInfo orderInfo) throws Exception {
        OpenPlatformConfigMessage openPlatform = DrivenEngineConfig.getInstance().getOpenPlatform(orderInfo.getAppChannelCode());
        if (openPlatform == null) {
            throw ResponseException.newInstance(ResponseResult.UN_CONFIG_OPEN_INFO);
        }
        return placeOrder(orderInfo, openPlatform);
    }

    /**
     * 统一下单实现方法
     *
     * @param orderInfo
     * @param openPlatform
     * @author 赵屈犇
     * @return
     * @date 2022/4/23 17:50
     */
    protected abstract JSONObject placeOrder(OrderInfo orderInfo, OpenPlatformConfigMessage openPlatform) throws Exception ;
}

So far, the specific specification definition has been implemented. In this case, the definition of "order interface" will be implemented first.

Unified order interface implementation

Before the specific implementation of this interface, the author obtains the specified business implementation class according to the business type, and stores the "business snapshot data" in the order table, which is convenient for later operation and maintenance.

And after the order information is successfully stored in the warehouse, the specific payment implementation class is obtained according to the payment method, the order data is obtained, and the "caller" is provided to make a payment request.

package com.threeox.biz.order.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.threeox.biz.order.config.OrderDictConstants;
import com.threeox.biz.order.config.OrderRequestConfig;
import com.threeox.biz.order.entity.OrderInfo;
import com.threeox.biz.order.entity.inter.IOrderBizInfo;
import com.threeox.biz.order.factory.inter.IOrderExtend;
import com.threeox.biz.order.factory.inter.IPaymentFactory;
import com.threeox.drivenlibrary.engine.ExtendFactory;
import com.threeox.drivenlibrary.engine.annotation.api.Api;
import com.threeox.drivenlibrary.engine.annotation.api.ApiVerifyConfig;
import com.threeox.drivenlibrary.engine.config.entity.request.base.RequestMessage;
import com.threeox.drivenlibrary.engine.entity.params.RequestParam;
import com.threeox.drivenlibrary.engine.function.impl.AbstractApiExtend;
import com.threeox.drivenlibrary.engine.request.build.RequestBuilder;
import com.threeox.drivenlibrary.engine.util.RequestUtils;
import com.threeox.utillibrary.date.TimeUtils;
import com.threeox.utillibrary.java.IDGenerate;

import java.util.Date;

/**
 * 下单api扩展
 *
 * @author 赵屈犇
 * @version 1.0
 * @date 创建时间: 2022/4/13 22:21
 */
@Api(apiUrl = "placeOrder", apiName = "统一下单", verifyConfigs = {
        @ApiVerifyConfig(paramCode = "orderName", emptyHint = "请传入订单名称"),
        @ApiVerifyConfig(paramCode = "payWay", emptyHint = "请传入支付方式"),
        @ApiVerifyConfig(paramCode = "bizType", emptyHint = "请传入业务类型"),
        @ApiVerifyConfig(paramCode = "bizKey", emptyHint = "请传入业务主键")
}, moduleUrl = "pay", requestCodes = {
        OrderRequestConfig.SAVE_ORDER_INFO,
        OrderRequestConfig.SAVE_ORDER_PROCESS_INFO
})
public class PlaceOrderExtend extends AbstractApiExtend<OrderInfo> {

    @Override
    public boolean onBeforeApiExecute() throws Exception {
        OrderInfo requestParams = getRequestParams();
        // 获取业务类型
        String bizType = requestParams.getBizType();
        // 根据业务类型
        IOrderExtend orderExtend = ExtendFactory.getInstance().getOrderExtend(bizType);
        if (orderExtend != null) {
            IOrderBizInfo bizSnapshot = orderExtend.getBizSnapshot(getSqlExecutor(), requestParams);
            if (bizSnapshot != null) {
                putRequestParam("bizSnapshot", JSON.toJSONString(bizSnapshot));
                requestParams.setBizSnapshot(getParamValue("bizSnapshot"));
            }
            putRequestParam("payMoney", bizSnapshot.getPayMoney());
            requestParams.setPayMoney(bizSnapshot.getPayMoney());
        } else {
            errorResult("未找到对应支付业务!");
            return false;
        }
        String orderNum = IDGenerate.getOrderNum();
        putRequestParam("orderNum", orderNum);
        requestParams.setOrderNum(orderNum);
        Date nowTimeDate = TimeUtils.getNowTimeDate();
        putRequestParam("operateTime", nowTimeDate);
        requestParams.setOperateTime(nowTimeDate);
        String ipAddress = RequestUtils.getIPAddress(servletRequest());
        putRequestParam("ipAddress", ipAddress);
        requestParams.setIpAddress(ipAddress);
        putRequestParam("accountId", getParamValue("accountId"));
        requestParams.setAccountId(getParamValue("accountId"));
        putRequestParam("currentStatus", OrderDictConstants.OrderStatus.HAVE_ORDER);
        requestParams.setCurrentStatus(OrderDictConstants.OrderStatus.HAVE_ORDER);
        return super.onBeforeApiExecute();
    }

    @Override
    public void handlerRequestBuilder(RequestMessage requestInfo, RequestBuilder requestBuilder, RequestParam requestParam) {
        super.handlerRequestBuilder(requestInfo, requestBuilder, requestParam);
        if (OrderRequestConfig.SAVE_ORDER_PROCESS_INFO.equals(requestInfo.getRequestCode())) {
            requestParam.putRequestParam("ipAddress", getParamValue("ipAddress"));
            requestParam.putRequestParam("orderTime", getParamValue("operateTime"));
            requestParam.putRequestParam("orderStatus", getParamValue("currentStatus"));
            requestParam.putRequestParam("orderId", getResultValue(OrderRequestConfig.SAVE_ORDER_INFO));
        }
    }

    @Override
    public boolean onAfterApiExecute(JSONObject resultSet) throws Exception {
        // 根据支付方式获取订单信息
        IPaymentFactory paymentExtend = ExtendFactory.getInstance().getPaymentExtend(getRequestParams().getPayWay());
        if (paymentExtend != null) {
            successResult(paymentExtend.placeOrder(getRequestParams()));
        }
        return super.onAfterApiExecute(resultSet);
    }

}

So far, the order interface has been implemented. When the user pays successfully, the corresponding payment callback interface will be activated actively.

因此,笔者欲定义一套基础支付回调通知处理类,在具体支付回调接口,继承并实现即可。

支付回调接口对象类

实现基础回调实现类之前,笔者考虑片刻,顿觉定义支付回调接口对象,用此作为具体回调接口的接口参数定义类。

此接口,目前定义两个需实现的函数,为“获取订单号”、“支付金额”用于回调接口时使用。

package com.threeox.biz.order.entity.inter;

import java.math.BigDecimal;

/**
 * 支付通知对象
 *
 * @author 赵屈犇
 * @version 1.0
 * @date 创建时间: 2022/5/27 22:44
 */
public interface IPayNotifyInfo {

    /**
     * 获取订单号
     *
     * @return
     */
    String getOrderNum();

    /**
     * 获取支付金额
     *
     * @return
     */
    BigDecimal getPayMoney();

}

基础支付回调实现类

此类,先根据订单编号查询具体的订单信息。

当查询到具体订单信息时,调用子类实现的“是否支付成功”函数。

若,支付成功后,在此之下,校验支付金额是否一致。

若,支付金额一致时,更新订单信息,并存储订单过程记录。

在此之后,即是通知具体业务实现类回调。

package com.threeox.biz.order.api.notify.base;

import com.alibaba.fastjson.JSONObject;
import com.threeox.biz.order.config.OrderDictConstants;
import com.threeox.biz.order.config.OrderRequestConfig;
import com.threeox.biz.order.entity.OrderInfo;
import com.threeox.biz.order.entity.inter.IPayNotifyInfo;
import com.threeox.biz.order.factory.inter.IOrderExtend;
import com.threeox.dblibrary.constants.GeneratedKeyInfo;
import com.threeox.drivenlibrary.engine.ExtendFactory;
import com.threeox.drivenlibrary.engine.config.DrivenEngineConfig;
import com.threeox.drivenlibrary.engine.constants.config.DrivenModelDBConstants;
import com.threeox.drivenlibrary.engine.entity.driven.config.OpenPlatformConfigMessage;
import com.threeox.drivenlibrary.engine.function.impl.AbstractApiExtend;
import com.threeox.drivenlibrary.engine.request.build.SqlBuilder;
import com.threeox.drivenlibrary.engine.request.execute.ExecuteRequestFactory;
import com.threeox.drivenlibrary.engine.request.inter.OnExecuteRequest;
import com.threeox.drivenlibrary.engine.util.RequestUtils;
import com.threeox.drivenlibrary.enums.dictionary.QueryType;
import com.threeox.utillibrary.date.TimeUtils;

/**
 * 基础支付回调扩展类
 *
 * @author 赵屈犇
 * @version 1.0
 * @date 创建时间: 2022/5/24 22:18
 */
public abstract class BasePayNotifyExtend<T extends IPayNotifyInfo> extends AbstractApiExtend<T> {

    @Override
    public boolean onBeforeApiExecute() throws Exception {
        // 当前状态
        String currentStatus = null;
        IOrderExtend orderExtend = null;
        ExecuteRequestFactory requestFactory = ExecuteRequestFactory.builder().setSqlExecutor(getSqlExecutor());
        // 查询对应的订单信息
        OrderInfo orderInfo = requestFactory.configCode(DrivenModelDBConstants.DRIVEN_MANAGE_MODEL_DB_CODE).requestCode(OrderRequestConfig.QUERY_ORDER_INFO)
                .execute((OnExecuteRequest<SqlBuilder>) builder -> builder.and("order_num", QueryType.EQUAL, getRequestParams().getOrderNum()));
        if (orderInfo != null) {
            JSONObject updateData = new JSONObject();
            OpenPlatformConfigMessage openPlatform = DrivenEngineConfig.getInstance().getOpenPlatform(orderInfo.getAppChannelCode());
            if (openPlatform != null) {
                boolean isPaySuccess = isPaySuccess(orderInfo, openPlatform);
                if (isPaySuccess) {
                    // 校验支付金额是否一致
                    if (orderInfo.getPayMoney().compareTo(getRequestParams().getPayMoney()) == 0) {
                        currentStatus = OrderDictConstants.OrderStatus.PAYMENT_SUCCESS;
                    } else {
                        currentStatus = OrderDictConstants.OrderStatus.PAYMENT_AMOUNT_WRONG;
                        errorResult("支付金额不一致,请检查!");
                    }
                } else {
                    currentStatus = OrderDictConstants.OrderStatus.PAYMENT_FAILED;
                    errorResult("验证失败,支付回调不成功!");
                }
                updateData.put("currentStatus", currentStatus);
                updateData.put("operateTime", TimeUtils.getNowTimeDate());
                updateData.put("ipAddress", RequestUtils.getIPAddress(servletRequest()));
                // 更新订单信息
                GeneratedKeyInfo keyInfo = requestFactory.requestCode(OrderRequestConfig.UPDATE_ORDER_INFO).requestParam(updateData).execute((OnExecuteRequest<SqlBuilder>) builder ->
                        builder.and("order_id", QueryType.EQUAL, orderInfo.getOrderId()));
                if (keyInfo.toInteger() > 0) {
                    JSONObject processData = new JSONObject();
                    processData.put("processSnapshot", getParams().toJSONString());
                    processData.put("orderId", orderInfo.getOrderId());
                    processData.put("orderTime", updateData.get("operateTime"));
                    processData.put("orderStatus", updateData.get("currentStatus"));
                    processData.put("ipAddress", updateData.getString("ipAddress"));
                    // 保存订单过程
                    requestFactory.requestCode(OrderRequestConfig.SAVE_ORDER_PROCESS_INFO).requestParam(processData).execute();
                }
            } else {
                errorResult("未找到配置的开放信息!");
            }
            // 更新订单信息
            orderExtend = ExtendFactory.getInstance().getOrderExtend(orderInfo.getBizType());
        } else {
            errorResult("未查询到对应的订单信息!");
        }
        if (orderExtend != null) {
            orderExtend.handlePayNotify(getSqlExecutor(), orderInfo, getRequestParams(), currentStatus);
        }
        return super.onBeforeApiExecute();
    }

    /**
     * 是否支付成功
     *
     * @param orderInfo
     * @param openPlatform
     * @return a
     * @author 赵屈犇
     * date 创建时间: 2022/5/27 21:03
     * @version 1.0
     */
    protected abstract boolean isPaySuccess(OrderInfo orderInfo, OpenPlatformConfigMessage openPlatform) throws Exception;

}

至此,业已实现支付基础模块设计,下篇具体实现微信V2支付逻辑。

文至于此,诸君加油、共勉。

Guess you like

Origin juejin.im/post/7118376095790940197