近来,欲将三方支付平台对接入笔者框架内,简化,后期业务功能的开发。
为满足此功能的可扩展性,欲定义“订单业务接口”、“支付方式接口”规则。
当,用户下单时,根据传入的“业务类型”获取具体的业务实现,根据“支付方式”获取具体的下单数据。
如此,即可在具体的业务代码中,实现对应的业务功能。
开发之始,即为设计,此篇仅为基础设计篇,后期将会逐渐完善各三方平台支付实现类。
啰嗦至此,此下,正式开文。
思路设计图如下
此下,设计具体的表结构,笔者基于目前想法,设计两张表“订单表”、“订单过程表”。
数据库表结构如下
表关系图如下
既,表设计完成,此下将具体实现业务功能。
开篇之始,笔者先定义对应的“订单相关字典”。
其中“订单业务类型”未预留字典类型,具体业务功能引入此库实现时,自主定义即可。
另,支付方式目前框架仅实现“微信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支付逻辑。