多应用多平台支付模块设计-基础模块开篇

近来,欲将三方支付平台对接入笔者框架内,简化,后期业务功能的开发。

为满足此功能的可扩展性,欲定义“订单业务接口”、“支付方式接口”规则。

当,用户下单时,根据传入的“业务类型”获取具体的业务实现,根据“支付方式”获取具体的下单数据。

如此,即可在具体的业务代码中,实现对应的业务功能。

开发之始,即为设计,此篇仅为基础设计篇,后期将会逐渐完善各三方平台支付实现类。

啰嗦至此,此下,正式开文。

思路设计图如下

此下,设计具体的表结构,笔者基于目前想法,设计两张表“订单表”、“订单过程表”。

数据库表结构如下

表关系图如下

既,表设计完成,此下将具体实现业务功能。

开篇之始,笔者先定义对应的“订单相关字典”。

其中“订单业务类型”未预留字典类型,具体业务功能引入此库实现时,自主定义即可。

另,支付方式目前框架仅实现“微信V2JSApi”方式,后期陆续实现,并定义相关字典,此处暂不定义全部字典。

字典配置类如下

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";

    }

}

为实现框架高扩展性,笔者欲定义两套接口规范,供具体业务、支付方式实现。

接口规范类

订单业务扩展类

此接口目前暂声明两套接口,“获取业务快照数据”、“处理支付回调”函数,用于具体业务实现。

后期,如需具体实现再添加。

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;

}

支付方式接口规范

此接口,目前仅需获取各支付平台获取统一下单数据函数。

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;

}

至此,接口规范已定义。

此下,笔者欲实现支付方式基础实现类,供具体业务逻辑继承扩展。

支付方式基础实现类

此基础类,目前仅需根据具体的应用渠道获取渠道信息,调用对应子类的统一下单实现方法。

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 ;
}

以上至此,已实现具体规范定义。此下将首先实现“下单接口”定义。

统一下单接口实现

此接口,笔者在具体实现之前,根据业务类型,获取了指定的业务实现类,并给订单表中存储了“业务快照数据”,方便后期运维查看。

并在订单信息入库成功后,根据支付方式获取具体的支付实现类,获取下单数据,提供给“调用方”进行支付请求。

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);
    }

}

至此,下单接口已实现,当用户支付成功后,会主动调起对应的支付回调接口。

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

支付回调接口对象类

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

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

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支付逻辑。

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

猜你喜欢

转载自juejin.im/post/7118376095790940197
今日推荐