mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-27 06:01:33 +00:00
feat 支付回调通知和签名相关方法抽取
This commit is contained in:
23
_doc/Task.md
23
_doc/Task.md
@@ -35,17 +35,22 @@
|
||||
- [x] 订单取消/修复/取消/同步等操作添加分布式锁, 防止出现重复操作
|
||||
- [x] 增加支付修复记录
|
||||
- 2024-01-07:
|
||||
- [x] 抽取签名工具类
|
||||
- [ ] 增加消息通知机制(通知客户端)
|
||||
- [ ] (前端) 更新代码为platform最新版本
|
||||
- [ ] (前端) 平台配置
|
||||
- [ ] (前端) 接口配置
|
||||
- **任务池**
|
||||
- [ ] 退款状态同步逻辑
|
||||
- [ ] 退款回调的处理
|
||||
- [ ] 支付状态同步处理考虑退款情况
|
||||
- **任务池**
|
||||
- 支付状态同步处理退款情况
|
||||
- 支付配置支持数据库配置和配置文件配置
|
||||
- 增加回调机制(通知客户端)
|
||||
- 增加消息通知机制(通知客户端)
|
||||
- 新增支付单预警功能, 处理支付单与网关状态不一致且无法自动修复的情况
|
||||
- 支付平台全局性配置
|
||||
- 微信消息通知相关配置
|
||||
- 钉钉消息通知配置
|
||||
- [ ] 支付配置支持数据库配置和配置文件配置
|
||||
- [ ] 增加回调机制(通知客户端)
|
||||
- [ ] 增加消息通知机制(通知客户端)
|
||||
- [ ] 新增支付单预警功能, 处理支付单与网关状态不一致且无法自动修复的情况
|
||||
- [ ] 支付平台全局性配置
|
||||
- [ ] 微信消息通知相关配置
|
||||
- [ ] 钉钉消息通知配置
|
||||
- 支付宝接口升级为V3接口
|
||||
- 微信支付接口更新为V3接口
|
||||
-
|
||||
|
@@ -0,0 +1,12 @@
|
||||
package cn.bootx.platform.daxpay.code;
|
||||
|
||||
/**
|
||||
* 支付系统相关常量
|
||||
* @author xxm
|
||||
* @since 2024/1/7
|
||||
*/
|
||||
public interface DaxPayCode {
|
||||
|
||||
/** 签名字段 */
|
||||
String FIELD_SIGN = "sign";
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
package cn.bootx.platform.daxpay.param;
|
||||
|
||||
/**
|
||||
* 通道支付参数标识
|
||||
* 通道支付参数标识接口
|
||||
* @author xxm
|
||||
* @since 2023/12/17
|
||||
*/
|
||||
public interface ChannelParam {
|
||||
public interface IChannelParam {
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.param.channel;
|
||||
|
||||
import cn.bootx.platform.daxpay.param.ChannelParam;
|
||||
import cn.bootx.platform.daxpay.param.IChannelParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -10,7 +10,7 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@Schema(title = "支付宝支付参数")
|
||||
public class AliPayParam implements ChannelParam {
|
||||
public class AliPayParam implements IChannelParam {
|
||||
|
||||
@Schema(description = "授权码(主动扫描用户的付款码)")
|
||||
private String authCode;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.param.channel;
|
||||
|
||||
import cn.bootx.platform.daxpay.param.ChannelParam;
|
||||
import cn.bootx.platform.daxpay.param.IChannelParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -12,7 +12,7 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@Schema(title = "储值卡支付参数")
|
||||
public class VoucherPayParam implements ChannelParam {
|
||||
public class VoucherPayParam implements IChannelParam {
|
||||
|
||||
@Schema(description = "储值卡号")
|
||||
private String cardNo;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.param.channel;
|
||||
|
||||
import cn.bootx.platform.daxpay.param.ChannelParam;
|
||||
import cn.bootx.platform.daxpay.param.IChannelParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -12,7 +12,7 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@Schema(title = "钱包支付参数")
|
||||
public class WalletPayParam implements ChannelParam {
|
||||
public class WalletPayParam implements IChannelParam {
|
||||
|
||||
@Schema(description = "钱包ID")
|
||||
private Long walletId;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.param.channel;
|
||||
|
||||
import cn.bootx.platform.daxpay.param.ChannelParam;
|
||||
import cn.bootx.platform.daxpay.param.IChannelParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -10,7 +10,7 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
@Schema(title = "微信支付参数")
|
||||
public class WeChatPayParam implements ChannelParam {
|
||||
public class WeChatPayParam implements IChannelParam {
|
||||
|
||||
@Schema(description = "微信openId")
|
||||
private String openId;
|
||||
|
@@ -19,8 +19,8 @@ import javax.validation.constraints.NotNull;
|
||||
* @since 2020/12/8
|
||||
*/
|
||||
@Data
|
||||
@Schema(title = "支付方式参数")
|
||||
public class PayWayParam {
|
||||
@Schema(title = "支付通道参数")
|
||||
public class PayChannelParam {
|
||||
|
||||
/**
|
||||
* @see PayChannelEnum#getCode()
|
@@ -1,7 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.param.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.serializer.TimestampToLocalDateTimeDeserializer;
|
||||
import cn.bootx.platform.daxpay.util.PayUtil;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
@@ -9,7 +8,6 @@ import lombok.Data;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支付公共参数
|
||||
@@ -68,8 +66,5 @@ public abstract class PayCommonParam {
|
||||
* 4. 嵌套对象转换成先转换成MAP再序列化为字符串
|
||||
* 5. 支持两层嵌套, 更多层级嵌套未测试, 可能会导致不可预知的问题
|
||||
*/
|
||||
public Map<String,String> toMap(){
|
||||
return PayUtil.toMap(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -41,8 +41,8 @@ public class PayParam extends PayCommonParam{
|
||||
@Schema(description = "用户付款中途退出返回商户网站的地址(部分支付场景中可用)")
|
||||
private String quitUrl;
|
||||
|
||||
@Schema(description = "支付方式信息参数")
|
||||
@NotNull(message = "支付方式信息参数不可为空")
|
||||
@Schema(description = "支付通道信息参数")
|
||||
@NotNull(message = "支付通道信息参数不可为空")
|
||||
@Valid
|
||||
private List<PayWayParam> payWays;
|
||||
private List<PayChannelParam> payChannels;
|
||||
}
|
||||
|
@@ -16,7 +16,4 @@ public class PayCommonResult {
|
||||
|
||||
@Schema(description = "请求ID")
|
||||
private String reqId = MDC.get(CommonCode.TRACE_ID);
|
||||
|
||||
@Schema(description = "商户扩展参数,回调时会原样返回")
|
||||
private String attach;
|
||||
}
|
||||
|
@@ -4,15 +4,17 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 统一下单响应参数
|
||||
* @author xxm
|
||||
* @since 2023/12/17
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Schema(title = "统一下单响应参数")
|
||||
public class PayResult {
|
||||
public class PayResult extends PayCommonResult{
|
||||
|
||||
@Schema(description = "支付ID")
|
||||
private Long paymentId;
|
||||
|
@@ -2,15 +2,17 @@ package cn.bootx.platform.daxpay.result.pay;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 退款响应参数
|
||||
* @author xxm
|
||||
* @since 2023/12/18
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Schema(title = "退款响应参数")
|
||||
public class RefundResult {
|
||||
public class RefundResult extends PayCommonResult{
|
||||
|
||||
@Schema(description = "退款ID")
|
||||
private Long refundId;
|
||||
|
@@ -0,0 +1,195 @@
|
||||
package cn.bootx.platform.daxpay.util;
|
||||
|
||||
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.bootx.platform.daxpay.code.DaxPayCode.FIELD_SIGN;
|
||||
|
||||
/**
|
||||
* 如果需要进行签名,
|
||||
* 1. 参数名ASCII码从小到大排序(字典序)
|
||||
* 2. 如果参数的值为空不参与签名
|
||||
* 3. 参数名不区分大小写
|
||||
* 4. 嵌套对象转换成先转换成MAP再序列化为字符串
|
||||
* 5. 支持两层嵌套, 更多层级嵌套未测试, 可能会导致不可预知的问题
|
||||
*/
|
||||
@UtilityClass
|
||||
public class PaySignUtil {
|
||||
|
||||
/**
|
||||
* 将参数转换为map对象. 使用ChatGPT生成
|
||||
* 1. 参数名ASCII码从小到大排序(字典序)
|
||||
* 2. 如果参数的值为空不参与签名;
|
||||
* 3. 参数名不区分大小写;
|
||||
*/
|
||||
public Map<String, String> toMap(Object object) {
|
||||
Map<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
toMap(object, map);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将参数转换为map对象. 使用ChatGPT生成, 仅局限于对请求支付相关参数进行签名
|
||||
*/
|
||||
@SneakyThrows
|
||||
private void toMap(Object object, Map<String, String> map) {
|
||||
Class<?> clazz = object.getClass();
|
||||
while (clazz != null) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
String fieldName = field.getName();
|
||||
Object fieldValue = field.get(object);
|
||||
if (fieldValue != null) {
|
||||
// 基础类型及包装类 和 字符串类型
|
||||
if (ClassUtil.isBasicType(field.getType())|| field.getType().equals(String.class)) {
|
||||
String fieldValueString = String.valueOf(fieldValue);
|
||||
map.put(fieldName, fieldValueString);
|
||||
|
||||
}
|
||||
// java8时间类型 转为时间戳
|
||||
else if (field.getType().equals(LocalDateTime.class)) {
|
||||
LocalDateTime localDateTime = (LocalDateTime) fieldValue;
|
||||
long timestamp = LocalDateTimeUtil.timestamp(localDateTime);
|
||||
map.put(fieldName, String.valueOf(timestamp));
|
||||
}
|
||||
// 集合类型
|
||||
else if (Collection.class.isAssignableFrom(field.getType())) {
|
||||
Collection<?> collection = (Collection<?>) fieldValue;
|
||||
if (!collection.isEmpty()) {
|
||||
List<Map<String, String>> maps = collection.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(item -> {
|
||||
Map<String, String> nestedMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
toMap(item, nestedMap);
|
||||
return nestedMap;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
map.put(fieldName, JSONUtil.toJsonStr(maps));
|
||||
}
|
||||
// 其他类型
|
||||
} else {
|
||||
Map<String, String> nestedMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
toMap(fieldValue, nestedMap);
|
||||
String nestedJson = JSONUtil.toJsonStr(map);
|
||||
map.put(fieldName, nestedJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 把所有元素排序, 并拼接成字符, 用于签名
|
||||
*/
|
||||
public static String createLinkString(Map<String, String> params) {
|
||||
String connStr = "&";
|
||||
List<String> keys = new ArrayList<>(params.keySet());
|
||||
Collections.sort(keys);
|
||||
StringBuilder content = new StringBuilder();
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
String key = keys.get(i);
|
||||
String value = params.get(key);
|
||||
// 拼接时,不包括最后一个&字符
|
||||
if (i == keys.size() - 1) {
|
||||
content.append(key)
|
||||
.append("=")
|
||||
.append(value);
|
||||
} else {
|
||||
content.append(key)
|
||||
.append("=")
|
||||
.append(value)
|
||||
.append(connStr);
|
||||
}
|
||||
}
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成16进制 MD5 字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @return MD5 字符串
|
||||
*/
|
||||
public String md5(String data) {
|
||||
return SecureUtil.md5(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成16进制的 sha256 字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @param signKey 密钥
|
||||
* @return sha256 字符串
|
||||
*/
|
||||
public String hmacSha256(String data, String signKey) {
|
||||
return SecureUtil.hmac(HmacAlgorithm.HmacSHA256, signKey).digestHex(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成待签名字符串
|
||||
* @param object 待签名对象
|
||||
* @param signKey 签名Key
|
||||
* @return 待签名字符串
|
||||
*/
|
||||
public String signString(Object object, String signKey){
|
||||
// 签名
|
||||
Map<String, String> map = toMap(object);
|
||||
// 生成签名前先去除sign参数
|
||||
map.remove(FIELD_SIGN);
|
||||
// 创建待签名字符串
|
||||
String data = createLinkString(map);
|
||||
// 将签名key追加到字符串最后
|
||||
return "&key=" + signKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* md5方式进行签名
|
||||
*
|
||||
* @return 签名值
|
||||
*/
|
||||
public String md5Sign(Object object, String signKey){
|
||||
String data = signString(object, signKey);
|
||||
return md5(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha256方式进行签名
|
||||
*
|
||||
* @return 签名值
|
||||
*/
|
||||
public String hmacSha256Sign(Object object, String signKey){
|
||||
String data = signString(object, signKey);
|
||||
return hmacSha256(data, signKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* MD5签名验证
|
||||
*/
|
||||
public boolean verifyMd5Sign(Object object, String signKey, String sign){
|
||||
String md5Sign = md5Sign(object, signKey);
|
||||
return md5Sign.equals(sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha256签名验证
|
||||
*/
|
||||
public boolean verifyHmacSha256Sign(Object object, String signKey, String sign){
|
||||
String hmacSha256Sign = hmacSha256Sign(object, signKey);
|
||||
return hmacSha256Sign.equals(sign);
|
||||
}
|
||||
|
||||
}
|
@@ -4,100 +4,31 @@ import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* 支付工具类
|
||||
* @author xxm
|
||||
* @since 2023/12/24
|
||||
*/
|
||||
@UtilityClass
|
||||
public class PayUtil {
|
||||
/**
|
||||
* 将参数转换为map对象. 使用ChatGPT生成
|
||||
* 1. 参数名ASCII码从小到大排序(字典序)
|
||||
* 2. 如果参数的值为空不参与签名;
|
||||
* 3. 参数名不区分大小写;
|
||||
*/
|
||||
public Map<String, String> toMap(Object object) {
|
||||
Map<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
toMap(object, map);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将参数转换为map对象. 使用ChatGPT生成, 仅局限于对请求支付相关参数进行签名
|
||||
*/
|
||||
@SneakyThrows
|
||||
private void toMap(Object object, Map<String, String> map) {
|
||||
Class<?> clazz = object.getClass();
|
||||
while (clazz != null) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
String fieldName = field.getName();
|
||||
Object fieldValue = field.get(object);
|
||||
if (fieldValue != null) {
|
||||
// 基础类型及包装类 和 字符串类型
|
||||
if (ClassUtil.isBasicType(field.getType())|| field.getType().equals(String.class)) {
|
||||
String fieldValueString = String.valueOf(fieldValue);
|
||||
map.put(fieldName, fieldValueString);
|
||||
|
||||
}
|
||||
// java8时间类型 转为时间戳
|
||||
else if (field.getType().equals(LocalDateTime.class)) {
|
||||
LocalDateTime localDateTime = (LocalDateTime) fieldValue;
|
||||
long timestamp = LocalDateTimeUtil.timestamp(localDateTime);
|
||||
map.put(fieldName, String.valueOf(timestamp));
|
||||
}
|
||||
// 集合类型
|
||||
else if (Collection.class.isAssignableFrom(field.getType())) {
|
||||
Collection<?> collection = (Collection<?>) fieldValue;
|
||||
if (!collection.isEmpty()) {
|
||||
List<Map<String, String>> maps = collection.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(item -> {
|
||||
Map<String, String> nestedMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
toMap(item, nestedMap);
|
||||
return nestedMap;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
map.put(fieldName, JSONUtil.toJsonStr(maps));
|
||||
}
|
||||
// 其他类型
|
||||
} else {
|
||||
Map<String, String> nestedMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
toMap(fieldValue, nestedMap);
|
||||
String nestedJson = JSONUtil.toJsonStr(map);
|
||||
map.put(fieldName, nestedJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查异步支付方式
|
||||
*/
|
||||
public void validationAsyncPay(PayParam payParam) {
|
||||
// 组合支付时只允许有一个异步支付方式
|
||||
List<PayWayParam> payModeList = payParam.getPayWays();
|
||||
List<PayChannelParam> payModeList = payParam.getPayChannels();
|
||||
|
||||
long asyncPayCount = payModeList.stream()
|
||||
.map(PayWayParam::getChannel)
|
||||
.map(PayChannelParam::getChannel)
|
||||
.map(PayChannelEnum::findByCode)
|
||||
.filter(PayChannelEnum.ASYNC_TYPE::contains)
|
||||
.count();
|
||||
@@ -110,10 +41,10 @@ public class PayUtil {
|
||||
/**
|
||||
* 检查支付金额
|
||||
*/
|
||||
public void validationAmount(List<PayWayParam> payModeList) {
|
||||
for (PayWayParam payWayParam : payModeList) {
|
||||
public void validationAmount(List<PayChannelParam> payModeList) {
|
||||
for (PayChannelParam payChannelParam : payModeList) {
|
||||
// 支付金额小于等于零
|
||||
if (payWayParam.getAmount() < 0) {
|
||||
if (payChannelParam.getAmount() < 0) {
|
||||
throw new PayAmountAbnormalException();
|
||||
}
|
||||
}
|
||||
@@ -150,9 +81,9 @@ public class PayUtil {
|
||||
/**
|
||||
* 判断是否有异步支付
|
||||
*/
|
||||
public boolean isNotSync(List<PayWayParam> payWayParams) {
|
||||
return payWayParams.stream()
|
||||
.map(PayWayParam::getChannel)
|
||||
public boolean isNotSync(List<PayChannelParam> payChannelParams) {
|
||||
return payChannelParams.stream()
|
||||
.map(PayChannelParam::getChannel)
|
||||
.noneMatch(PayChannelEnum.ASYNC_TYPE_CODE::contains);
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,13 @@
|
||||
package cn.bootx.platform.daxpay.gateway.controller;
|
||||
|
||||
import cn.bootx.platform.common.core.exception.BizException;
|
||||
import cn.bootx.platform.common.core.rest.Res;
|
||||
import cn.bootx.platform.common.core.rest.ResResult;
|
||||
import cn.bootx.platform.daxpay.service.core.timeout.task.PayExpiredTimeTask;
|
||||
import cn.bootx.platform.daxpay.service.core.timeout.task.PayWaitOrderSyncTask;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -11,6 +15,8 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 测试
|
||||
* @author xxm
|
||||
@@ -23,6 +29,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
public class TestController {
|
||||
private final PayExpiredTimeTask expiredTimeTask;;
|
||||
private final PayWaitOrderSyncTask waitOrderSyncTask;
|
||||
private final LockTemplate lockTemplate;
|
||||
|
||||
@Operation(summary = "同步")
|
||||
@GetMapping("/sync")
|
||||
@@ -37,4 +44,26 @@ public class TestController {
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@Operation(summary = "锁测试1")
|
||||
@GetMapping("/lock1")
|
||||
// @Lock4j(keys = "#name", acquireTimeout = 50)
|
||||
public ResResult<String> lock1(String name){
|
||||
LockInfo lock = lockTemplate.lock(name, 10000, 10);
|
||||
if (Objects.isNull(lock)){
|
||||
throw new BizException("未获取到锁");
|
||||
}
|
||||
System.out.println("进来了......");
|
||||
ThreadUtil.sleep(10000);
|
||||
lockTemplate.releaseLock(lock);
|
||||
return Res.ok(name);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "锁测试2")
|
||||
@GetMapping("/lock2")
|
||||
// @Lock4j(keys = "#name", acquireTimeout = 50)
|
||||
public ResResult<String> lock2(String name){
|
||||
return Res.ok(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -15,9 +15,12 @@ public class ApiInfoLocal {
|
||||
/** 当前支付接口编码 */
|
||||
private String apiCode;
|
||||
|
||||
/** 是否开启回调通知 */
|
||||
/** 是否开启通知 */
|
||||
private boolean notice;
|
||||
|
||||
/** 只有异步支付才进行通知 */
|
||||
private boolean onlyAsyncNotice;
|
||||
|
||||
/** 请求参数是否签名 */
|
||||
private boolean reqSign;
|
||||
|
||||
|
@@ -37,10 +37,24 @@ public class AliPayConfig extends MpBaseEntity implements EntityBaseFunction<Ali
|
||||
@DbColumn(comment = "支付宝商户appId")
|
||||
private String appId;
|
||||
|
||||
/** 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 */
|
||||
/**
|
||||
* 服务器异步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
|
||||
* 1. 需http://或者https://格式的完整路径,
|
||||
* 2. 不能加?id=123这类自定义参数,必须外网可以正常访问
|
||||
* 3. 调用顺序 支付宝网关 -> 本网关进行处理 -> 发送消息通知业务系统
|
||||
*/
|
||||
@DbColumn(comment = "异步通知页面路径")
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* 服务器同步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
|
||||
* 1. 需http://或者https://格式的完整路径,
|
||||
* 2. 不能加?id=123这类自定义参数,必须外网可以正常访问
|
||||
* 3. 消息顺序 支付宝网关 -> 本网关进行处理 -> 重定向到业务系统中
|
||||
*/
|
||||
@DbColumn(comment = "同步通知页面路径")
|
||||
private String returnUrl;
|
||||
|
||||
/** 请求网关地址 */
|
||||
@DbColumn(comment = "请求网关地址")
|
||||
private String serverUrl;
|
||||
|
@@ -8,7 +8,7 @@ import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliPayOrderManag
|
||||
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.service.PayOrderChannelService;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -39,22 +39,22 @@ public class AliPayOrderService {
|
||||
/**
|
||||
* 支付调起成功 更新payment中异步支付类型信息, 如果支付完成, 创建支付宝支付单
|
||||
*/
|
||||
public void updatePaySuccess(PayOrder payOrder, PayWayParam payWayParam) {
|
||||
public void updatePaySuccess(PayOrder payOrder, PayChannelParam payChannelParam) {
|
||||
// 更新支付宝异步支付类型信息
|
||||
payOrder.setAsyncPay(true).setAsyncChannel(PayChannelEnum.ALI.getCode());
|
||||
payOrderChannelService.updateChannel(payWayParam,payOrder);
|
||||
payOrderChannelService.updateChannel(payChannelParam,payOrder);
|
||||
|
||||
// 更新支付宝可退款类型信息
|
||||
List<OrderRefundableInfo> refundableInfos = payOrder.getRefundableInfos();
|
||||
refundableInfos.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
|
||||
refundableInfos.add(new OrderRefundableInfo()
|
||||
.setChannel(PayChannelEnum.ALI.getCode())
|
||||
.setAmount(payWayParam.getAmount())
|
||||
.setAmount(payChannelParam.getAmount())
|
||||
);
|
||||
payOrder.setRefundableInfos(refundableInfos);
|
||||
// 如果支付完成(付款码情况) 调用 updateSyncSuccess 创建支付宝支付记录
|
||||
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
|
||||
this.updateAsyncSuccess(payOrder, payWayParam.getAmount());
|
||||
this.updateAsyncSuccess(payOrder, payChannelParam.getAmount());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,7 @@ import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.util.PayUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -48,13 +48,13 @@ public class AliPayService {
|
||||
/**
|
||||
* 支付前检查支付方式是否可用
|
||||
*/
|
||||
public void validation(PayWayParam payWayParam, AliPayConfig alipayConfig) {
|
||||
public void validation(PayChannelParam payChannelParam, AliPayConfig alipayConfig) {
|
||||
|
||||
if (CollUtil.isEmpty(alipayConfig.getPayWays())){
|
||||
throw new PayFailureException("支付宝未配置可用的支付方式");
|
||||
}
|
||||
// 发起的支付类型是否在支持的范围内
|
||||
PayWayEnum payWayEnum = Optional.ofNullable(AliPayWay.findByCode(payWayParam.getWay()))
|
||||
PayWayEnum payWayEnum = Optional.ofNullable(AliPayWay.findByCode(payChannelParam.getWay()))
|
||||
.orElseThrow(() -> new PayFailureException("非法的支付宝支付类型"));
|
||||
if (!alipayConfig.getPayWays().contains(payWayEnum.getCode())) {
|
||||
throw new PayFailureException("该支付宝支付方式不可用");
|
||||
@@ -64,29 +64,29 @@ public class AliPayService {
|
||||
/**
|
||||
* 调起支付
|
||||
*/
|
||||
public void pay(PayOrder payOrder, PayWayParam payWayParam, AliPayParam aliPayParam, AliPayConfig alipayConfig) {
|
||||
Integer amount = payWayParam.getAmount();
|
||||
public void pay(PayOrder payOrder, PayChannelParam payChannelParam, AliPayParam aliPayParam, AliPayConfig alipayConfig) {
|
||||
Integer amount = payChannelParam.getAmount();
|
||||
String payBody = null;
|
||||
// 异步线程存储
|
||||
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
|
||||
// wap支付
|
||||
if (Objects.equals(payWayParam.getWay(), PayWayEnum.WAP.getCode())) {
|
||||
if (Objects.equals(payChannelParam.getWay(), PayWayEnum.WAP.getCode())) {
|
||||
payBody = this.wapPay(amount, payOrder, alipayConfig);
|
||||
}
|
||||
// 程序支付
|
||||
else if (Objects.equals(payWayParam.getWay(), PayWayEnum.APP.getCode())) {
|
||||
else if (Objects.equals(payChannelParam.getWay(), PayWayEnum.APP.getCode())) {
|
||||
payBody = this.appPay(amount, payOrder, alipayConfig);
|
||||
}
|
||||
// pc支付
|
||||
else if (Objects.equals(payWayParam.getWay(), PayWayEnum.WEB.getCode())) {
|
||||
else if (Objects.equals(payChannelParam.getWay(), PayWayEnum.WEB.getCode())) {
|
||||
payBody = this.webPay(amount, payOrder, alipayConfig);
|
||||
}
|
||||
// 二维码支付
|
||||
else if (Objects.equals(payWayParam.getWay(), PayWayEnum.QRCODE.getCode())) {
|
||||
else if (Objects.equals(payChannelParam.getWay(), PayWayEnum.QRCODE.getCode())) {
|
||||
payBody = this.qrCodePay(amount, payOrder, alipayConfig);
|
||||
}
|
||||
// 付款码支付
|
||||
else if (Objects.equals(payWayParam.getWay(), PayWayEnum.BARCODE.getCode())) {
|
||||
else if (Objects.equals(payChannelParam.getWay(), PayWayEnum.BARCODE.getCode())) {
|
||||
String tradeNo = this.barCode(amount, payOrder, aliPayParam, alipayConfig);
|
||||
asyncPayInfo.setTradeNo(tradeNo);
|
||||
}
|
||||
@@ -112,10 +112,10 @@ public class AliPayService {
|
||||
|
||||
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
|
||||
request.setBizModel(model);
|
||||
// 异步回调必须到当前系统中
|
||||
// 异步回调必须到当前支付网关系统中, 然后系统负责转发
|
||||
request.setNotifyUrl(alipayConfig.getNotifyUrl());
|
||||
// 同步回调地址
|
||||
request.setReturnUrl(noticeInfo.getReturnUrl());
|
||||
// 同步回调地址必须到当前支付网关系统中, 然后系统负责跳转
|
||||
request.setReturnUrl(alipayConfig.getReturnUrl());
|
||||
|
||||
try {
|
||||
// 通过GET方式的请求, 返回URL的响应, 默认是POST方式的请求, 返回的是表单响应
|
||||
@@ -172,7 +172,7 @@ public class AliPayService {
|
||||
// 异步回调必须到当前系统中
|
||||
request.setNotifyUrl(alipayConfig.getNotifyUrl());
|
||||
// 同步回调
|
||||
request.setReturnUrl(noticeInfo.getReturnUrl());
|
||||
request.setReturnUrl(alipayConfig.getReturnUrl());
|
||||
try {
|
||||
// 通过GET方式的请求, 返回URL的响应, 默认是POST方式的请求, 返回的是表单响应
|
||||
AlipayTradePagePayResponse response = AliPayApi.pageExecute(request, Method.GET.name());
|
||||
|
@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.service.core.channel.cash.dao.CashPayOrderManage
|
||||
import cn.bootx.platform.daxpay.service.core.channel.cash.entity.CashPayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -28,7 +28,7 @@ public class CashService {
|
||||
/**
|
||||
* 支付
|
||||
*/
|
||||
public void pay(PayWayParam payMode, PayOrder payment, PayParam payParam) {
|
||||
public void pay(PayChannelParam payMode, PayOrder payment, PayParam payParam) {
|
||||
CashPayOrder walletPayment = new CashPayOrder();
|
||||
walletPayment.setPaymentId(payment.getId())
|
||||
.setBusinessNo(payParam.getBusinessNo())
|
||||
|
@@ -7,7 +7,7 @@ import cn.bootx.platform.daxpay.service.core.channel.voucher.entity.VoucherPayOr
|
||||
import cn.bootx.platform.daxpay.service.core.channel.voucher.entity.VoucherRecord;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -31,7 +31,7 @@ public class VoucherPayOrderService {
|
||||
/**
|
||||
* 添加支付记录
|
||||
*/
|
||||
public void savePayment(PayOrder payOrder, PayParam payParam, PayWayParam payMode, VoucherRecord voucherRecord) {
|
||||
public void savePayment(PayOrder payOrder, PayParam payParam, PayChannelParam payMode, VoucherRecord voucherRecord) {
|
||||
VoucherPayOrder voucherPayOrder = new VoucherPayOrder().setVoucherRecord(voucherRecord);
|
||||
voucherPayOrder.setPaymentId(payOrder.getId())
|
||||
.setBusinessNo(payParam.getBusinessNo())
|
||||
|
@@ -11,7 +11,7 @@ import cn.bootx.platform.daxpay.service.core.channel.voucher.entity.VoucherRecor
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.channel.VoucherPayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONException;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@@ -42,11 +42,11 @@ public class VoucherPayService {
|
||||
/**
|
||||
* 获取并检查储值卡
|
||||
*/
|
||||
public Voucher getAndCheckVoucher(PayWayParam payWayParam) {
|
||||
public Voucher getAndCheckVoucher(PayChannelParam payChannelParam) {
|
||||
VoucherPayParam voucherPayParam;
|
||||
try {
|
||||
// 储值卡参数验证
|
||||
String extraParamsJson = payWayParam.getChannelExtra();
|
||||
String extraParamsJson = payChannelParam.getChannelExtra();
|
||||
if (StrUtil.isNotBlank(extraParamsJson)) {
|
||||
voucherPayParam = JSONUtil.toBean(extraParamsJson, VoucherPayParam.class);
|
||||
} else {
|
||||
|
@@ -7,7 +7,7 @@ import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.Wallet;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.WalletPayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -31,7 +31,7 @@ public class WalletPayOrderService {
|
||||
/**
|
||||
* 保存钱包支付记录
|
||||
*/
|
||||
public void savePayment(PayOrder payOrder, PayParam payParam, PayWayParam payMode, Wallet wallet) {
|
||||
public void savePayment(PayOrder payOrder, PayParam payParam, PayChannelParam payMode, Wallet wallet) {
|
||||
WalletPayOrder walletPayOrder = new WalletPayOrder().setWalletId(wallet.getId());
|
||||
walletPayOrder.setPaymentId(payOrder.getId())
|
||||
.setBusinessNo(payParam.getBusinessNo())
|
||||
|
@@ -41,6 +41,24 @@ public class WeChatPayConfig extends MpBaseEntity implements EntityBaseFunction<
|
||||
@DbColumn(comment = "微信应用appId")
|
||||
private String wxAppId;
|
||||
|
||||
/**
|
||||
* 服务器异步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
|
||||
* 1. 需http://或者https://格式的完整路径,
|
||||
* 2. 不能加?id=123这类自定义参数,必须外网可以正常访问
|
||||
* 3. 消息顺序 微信网关 -> 本网关进行处理 -> 发送消息通知业务系统
|
||||
*/
|
||||
@DbColumn(comment = "异步通知路径")
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* 服务器同步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
|
||||
* 1. 需http://或者https://格式的完整路径,
|
||||
* 2. 不能加?id=123这类自定义参数,必须外网可以正常访问
|
||||
* 3. 消息顺序 微信网关 -> 本网关进行处理 -> 重定向到业务系统中
|
||||
*/
|
||||
@DbColumn(comment = "同步通知路径")
|
||||
private String returnUrl;
|
||||
|
||||
/** 商户平台「API安全」中的 APIv2 密钥 */
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
@BigField
|
||||
@@ -69,10 +87,6 @@ public class WeChatPayConfig extends MpBaseEntity implements EntityBaseFunction<
|
||||
@DbColumn(comment = "API证书中p12证书Base64")
|
||||
private String p12;
|
||||
|
||||
/** 这个是跳转到当前网关服务上的, 不是通知到客户系统的 */
|
||||
@DbColumn(comment = "异步通知路径")
|
||||
private String notifyUrl;
|
||||
|
||||
/** 是否沙箱环境 */
|
||||
@DbColumn(comment = "是否沙箱环境")
|
||||
private boolean sandbox;
|
||||
|
@@ -10,7 +10,7 @@ import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayOrde
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.service.PayOrderChannelService;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.service.PayOrderService;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -40,23 +40,23 @@ public class WeChatPayOrderService {
|
||||
/**
|
||||
* 支付调起成功 更新payment中异步支付类型信息, 如果支付完成, 创建微信支付单
|
||||
*/
|
||||
public void updatePaySuccess(PayOrder payOrder, PayWayParam payWayParam) {
|
||||
public void updatePaySuccess(PayOrder payOrder, PayChannelParam payChannelParam) {
|
||||
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();;
|
||||
payOrder.setAsyncPay(true).setAsyncChannel(PayChannelEnum.WECHAT.getCode());
|
||||
|
||||
payOrderChannelService.updateChannel(payWayParam,payOrder);
|
||||
payOrderChannelService.updateChannel(payChannelParam,payOrder);
|
||||
|
||||
// 更新微信可退款类型信息
|
||||
List<OrderRefundableInfo> refundableInfos = payOrder.getRefundableInfos();
|
||||
refundableInfos.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
|
||||
refundableInfos.add(new OrderRefundableInfo()
|
||||
.setChannel(PayChannelEnum.WECHAT.getCode())
|
||||
.setAmount(payWayParam.getAmount())
|
||||
.setAmount(payChannelParam.getAmount())
|
||||
);
|
||||
payOrder.setRefundableInfos(refundableInfos);
|
||||
// 如果支付完成(付款码情况) 调用 updateSyncSuccess 创建微信支付记录
|
||||
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
|
||||
this.createWeChatOrder(payOrder, payWayParam.getAmount());
|
||||
this.createWeChatOrder(payOrder, payChannelParam.getAmount());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,7 +15,7 @@ import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.service.param.channel.wechat.WeChatPayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.util.PayUtil;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
@@ -62,13 +62,13 @@ public class WeChatPayService {
|
||||
/**
|
||||
* 校验
|
||||
*/
|
||||
public void validation(PayWayParam payWayParam, WeChatPayConfig weChatPayConfig) {
|
||||
public void validation(PayChannelParam payChannelParam, WeChatPayConfig weChatPayConfig) {
|
||||
List<String> payWays = weChatPayConfig.getPayWays();
|
||||
if (CollUtil.isEmpty(payWays)){
|
||||
throw new PayFailureException("未配置微信支付方式");
|
||||
}
|
||||
|
||||
PayWayEnum payWayEnum = Optional.ofNullable(WeChatPayWay.findByCode(payWayParam.getWay()))
|
||||
PayWayEnum payWayEnum = Optional.ofNullable(WeChatPayWay.findByCode(payChannelParam.getWay()))
|
||||
.orElseThrow(() -> new PayFailureException("非法的微信支付类型"));
|
||||
if (!payWays.contains(payWayEnum.getCode())) {
|
||||
throw new PayFailureException("该微信支付方式不可用");
|
||||
@@ -78,13 +78,13 @@ public class WeChatPayService {
|
||||
/**
|
||||
* 支付
|
||||
*/
|
||||
public void pay(PayOrder payOrder, WeChatPayParam weChatPayParam, PayWayParam payWayParam, WeChatPayConfig weChatPayConfig) {
|
||||
public void pay(PayOrder payOrder, WeChatPayParam weChatPayParam, PayChannelParam payChannelParam, WeChatPayConfig weChatPayConfig) {
|
||||
|
||||
Integer amount = payWayParam.getAmount();
|
||||
Integer amount = payChannelParam.getAmount();
|
||||
String totalFee = String.valueOf(amount);
|
||||
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();;
|
||||
String payBody = null;
|
||||
PayWayEnum payWayEnum = PayWayEnum.findByCode(payWayParam.getWay());
|
||||
PayWayEnum payWayEnum = PayWayEnum.findByCode(payChannelParam.getWay());
|
||||
|
||||
// wap支付
|
||||
if (payWayEnum == PayWayEnum.WAP) {
|
||||
|
@@ -0,0 +1,33 @@
|
||||
package cn.bootx.platform.daxpay.service.core.notice.result;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayWayEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 各支付通道参数
|
||||
* @author xxm
|
||||
* @since 2024/1/7
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "各支付通道参数")
|
||||
public class PayChannelResult {
|
||||
/**
|
||||
* @see PayChannelEnum#getCode()
|
||||
*/
|
||||
@Schema(description = "支付通道编码")
|
||||
private String channel;
|
||||
|
||||
/**
|
||||
* @see PayWayEnum#getCode()
|
||||
*/
|
||||
@Schema(description = "支付方式编码")
|
||||
private String way;
|
||||
|
||||
@Schema(description = "支付金额")
|
||||
private Integer amount;
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package cn.bootx.platform.daxpay.service.core.notice.result;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付异步通知类
|
||||
* @author xxm
|
||||
* @since 2024/1/7
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "支付异步通知类")
|
||||
public class PayNoticeResult {
|
||||
|
||||
@Schema(description = "支付ID")
|
||||
private Long paymentId;
|
||||
|
||||
@Schema(description = "业务号")
|
||||
private String businessNo;
|
||||
|
||||
@Schema(description = "是否是异步支付")
|
||||
private boolean asyncPay;
|
||||
|
||||
/**
|
||||
* @see PayChannelEnum#ASYNC_TYPE_CODE
|
||||
*/
|
||||
@Schema(description = "异步支付通道")
|
||||
private String asyncPayChannel;
|
||||
|
||||
@Schema(description = "支付金额")
|
||||
private Integer amount;
|
||||
|
||||
@Schema(description = "支付通道信息")
|
||||
private List<PayChannelResult> payChannels;
|
||||
|
||||
/**
|
||||
* @see PayStatusEnum
|
||||
*/
|
||||
@Schema(description = "支付状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "支付成功时间")
|
||||
private LocalDateTime payTime;
|
||||
|
||||
@Schema(description = "支付创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "商户扩展参数,回调时会原样返回")
|
||||
private String attach;
|
||||
|
||||
@Schema(description = "签名")
|
||||
private String sign;
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package cn.bootx.platform.daxpay.service.core.notice.result;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 支付同步通知类
|
||||
* @author xxm
|
||||
* @since 2024/1/7
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "支付同步通知类")
|
||||
public class PayReturnResult {
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
package cn.bootx.platform.daxpay.service.core.notice.service;
|
||||
|
||||
import cn.bootx.platform.common.core.exception.DataNotExistException;
|
||||
import cn.bootx.platform.daxpay.code.PaySignTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.context.ApiInfoLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.context.PlatformLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.notice.result.PayChannelResult;
|
||||
import cn.bootx.platform.daxpay.service.core.notice.result.PayNoticeResult;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.dao.PayOrderChannelManager;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.dao.PayOrderExtraManager;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.dao.PayOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrderExtra;
|
||||
import cn.bootx.platform.daxpay.util.PaySignUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 支付相关消息通知
|
||||
* @author xxm
|
||||
* @since 2024/1/7
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayNoticeService {
|
||||
private final PayOrderManager payOrderManager;
|
||||
private final PayOrderExtraManager payOrderExtraManager;
|
||||
private final PayOrderChannelManager payOrderChannelManager;
|
||||
|
||||
/**
|
||||
* 发送支付完成回调通知
|
||||
*/
|
||||
public void sendPayCallbackNotice(Long paymentId){
|
||||
try {
|
||||
// 获取通知地址和内容相关的信息
|
||||
ApiInfoLocal apiInfo = PaymentContextLocal.get().getApiInfo();
|
||||
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
|
||||
// 首先判断接口是开启了通知回调功能
|
||||
if (apiInfo.isNotice()){
|
||||
PayOrder payOrder = payOrderManager.findById(paymentId).orElseThrow(DataNotExistException::new);
|
||||
// 判断是否是同步支付, 并且配置不进行消息通知
|
||||
if (!payOrder.isAsyncPay() && apiInfo.isOnlyAsyncNotice()){
|
||||
return;
|
||||
}
|
||||
// 判断客户端发送的请求是否需要发送回调通知
|
||||
PayOrderExtra payOrderExtra = payOrderExtraManager.findById(paymentId).orElseThrow(DataNotExistException::new);
|
||||
if (payOrderExtra.isNotNotify()){
|
||||
return;
|
||||
}
|
||||
List<PayChannelResult> orderChannels = payOrderChannelManager.findAllByPaymentId(paymentId).stream()
|
||||
.map(o->new PayChannelResult().setChannel(o.getChannel()).setWay(o.getPayWay()).setAmount(o.getAmount()))
|
||||
.collect(Collectors.toList());
|
||||
// 组装内容
|
||||
PayNoticeResult payNoticeResult = new PayNoticeResult()
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setAsyncPay(payOrder.isAsyncPay())
|
||||
.setBusinessNo(payOrder.getBusinessNo())
|
||||
.setAmount(payOrder.getAmount())
|
||||
.setPayTime(payOrder.getPayTime())
|
||||
.setCreateTime(payOrder.getCreateTime())
|
||||
.setStatus(payOrder.getStatus())
|
||||
.setAttach(payOrderExtra.getAttach())
|
||||
.setPayChannels(orderChannels);
|
||||
// 是否需要签名
|
||||
if (apiInfo.isNoticeSign()){
|
||||
// 签名
|
||||
if (Objects.equals(platform.getSignType(), PaySignTypeEnum.MD5.getCode())){
|
||||
payNoticeResult.setSign(PaySignUtil.md5Sign(payNoticeResult,platform.getSignSecret()));
|
||||
} else {
|
||||
payNoticeResult.setSign(PaySignUtil.hmacSha256Sign(payNoticeResult,platform.getSignSecret()));
|
||||
}
|
||||
}
|
||||
// 地址
|
||||
String notifyUrl = payOrderExtra.getNotifyUrl();
|
||||
if (StrUtil.isBlank(notifyUrl)){
|
||||
throw new DataNotExistException("通知地址为空");
|
||||
}
|
||||
// 发送请求数据
|
||||
HttpResponse execute = HttpUtil.createPost(notifyUrl)
|
||||
.body(JSONUtil.toJsonStr(payNoticeResult), ContentType.JSON.getValue())
|
||||
.timeout(10000)
|
||||
.execute();
|
||||
log.info(execute.body());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("发送失败",e);
|
||||
// 记录错误信息, 同时配置下次通知的时间
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,18 +1,16 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.common.service;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PaySignTypeEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayCommonParam;
|
||||
import cn.bootx.platform.daxpay.service.common.context.ApiInfoLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.context.PlatformLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayCommonParam;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ijpay.core.kit.PayKit;
|
||||
import cn.bootx.platform.daxpay.util.PaySignUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -25,7 +23,6 @@ import java.util.Objects;
|
||||
@RequiredArgsConstructor
|
||||
public class PaymentSignService {
|
||||
|
||||
private static final String FIELD_SIGN = "sign";
|
||||
|
||||
private final PaymentAssistService paymentAssistService;;
|
||||
|
||||
@@ -43,24 +40,15 @@ public class PaymentSignService {
|
||||
}
|
||||
// 参数转换为Map对象
|
||||
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
|
||||
Map<String, String> params = param.toMap();
|
||||
String signType = platform.getSignType();
|
||||
// 生成签名前先去除sign参数
|
||||
params.remove(FIELD_SIGN);
|
||||
String data = PayKit.createLinkString(params);
|
||||
if (Objects.equals(PaySignTypeEnum.HMAC_SHA256.getCode(), signType)){
|
||||
// 签名验证
|
||||
data += "&key=" + platform.getSignSecret();
|
||||
String sha256 = PayKit.hmacSha256(data, platform.getSignSecret());
|
||||
if (!Objects.equals(sha256, params.get(FIELD_SIGN))){
|
||||
boolean verified = PaySignUtil.verifyHmacSha256Sign(param, platform.getSignSecret(), param.getSign());
|
||||
if (!verified){
|
||||
throw new PayFailureException("签名验证未通过");
|
||||
}
|
||||
} else if (Objects.equals(PaySignTypeEnum.MD5.getCode(), signType)){
|
||||
data += "&key=" + platform.getSignSecret();
|
||||
String md5 = PayKit.md5(data.toUpperCase());
|
||||
String sign = StrUtil.toString(params.get(FIELD_SIGN));
|
||||
// 签名验证
|
||||
if (!md5.equalsIgnoreCase(sign)){
|
||||
boolean verified = PaySignUtil.verifyMd5Sign(param, platform.getSignSecret(), param.getSign());
|
||||
if (!verified){
|
||||
throw new PayFailureException("签名验证未通过");
|
||||
}
|
||||
} else {
|
||||
|
@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.service.core.payment.pay.factory;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.pay.strategy.*;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
@@ -29,12 +29,12 @@ public class PayStrategyFactory {
|
||||
|
||||
/**
|
||||
* 根据传入的支付通道创建策略
|
||||
* @param payWayParam 支付类型
|
||||
* @param payChannelParam 支付类型
|
||||
* @return 支付策略
|
||||
*/
|
||||
public AbsPayStrategy createAsyncFront(PayWayParam payWayParam) {
|
||||
public AbsPayStrategy createAsyncFront(PayChannelParam payChannelParam) {
|
||||
AbsPayStrategy strategy;
|
||||
PayChannelEnum channelEnum = PayChannelEnum.findByCode(payWayParam.getChannel());
|
||||
PayChannelEnum channelEnum = PayChannelEnum.findByCode(payChannelParam.getChannel());
|
||||
switch (channelEnum) {
|
||||
case ALI:
|
||||
strategy = SpringUtil.getBean(AliPayStrategy.class);
|
||||
@@ -57,7 +57,7 @@ public class PayStrategyFactory {
|
||||
default:
|
||||
throw new PayUnsupportedMethodException();
|
||||
}
|
||||
strategy.setPayWayParam(payWayParam);
|
||||
strategy.setPayChannelParam(payChannelParam);
|
||||
return strategy;
|
||||
}
|
||||
|
||||
@@ -65,49 +65,49 @@ public class PayStrategyFactory {
|
||||
* 根据传入的支付类型批量创建策略, 异步支付在后面
|
||||
* 同步支付逻辑完后才执行异步支付的逻辑, 预防异步执行成功, 然同步执行失败. 导致异步支付无法回滚的问题
|
||||
*/
|
||||
public static List<AbsPayStrategy> createAsyncLast(List<PayWayParam> payWayParamList) {
|
||||
return createAsyncFront(payWayParamList, false);
|
||||
public static List<AbsPayStrategy> createAsyncLast(List<PayChannelParam> payChannelParamList) {
|
||||
return createAsyncFront(payChannelParamList, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的支付类型批量创建策略, 异步支付在前面 font
|
||||
*/
|
||||
public List<AbsPayStrategy> createAsyncFront(List<PayWayParam> payWayParamList) {
|
||||
return createAsyncFront(payWayParamList, true);
|
||||
public List<AbsPayStrategy> createAsyncFront(List<PayChannelParam> payChannelParamList) {
|
||||
return createAsyncFront(payChannelParamList, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的支付类型批量创建策略
|
||||
* @param payWayParamList 支付类型
|
||||
* @param payChannelParamList 支付类型
|
||||
* @return 支付策略
|
||||
*/
|
||||
private List<AbsPayStrategy> createAsyncFront(List<PayWayParam> payWayParamList, boolean asyncFirst) {
|
||||
if (CollectionUtil.isEmpty(payWayParamList)) {
|
||||
private List<AbsPayStrategy> createAsyncFront(List<PayChannelParam> payChannelParamList, boolean asyncFirst) {
|
||||
if (CollectionUtil.isEmpty(payChannelParamList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<AbsPayStrategy> list = new ArrayList<>(payWayParamList.size());
|
||||
List<AbsPayStrategy> list = new ArrayList<>(payChannelParamList.size());
|
||||
|
||||
// 同步支付
|
||||
List<PayWayParam> syncPayWayParamList = payWayParamList.stream()
|
||||
List<PayChannelParam> syncPayChannelParamList = payChannelParamList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(payModeParam -> !ASYNC_TYPE_CODE.contains(payModeParam.getChannel()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 异步支付
|
||||
List<PayWayParam> asyncPayWayParamList = payWayParamList.stream()
|
||||
List<PayChannelParam> asyncPayChannelParamList = payChannelParamList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(payModeParam -> ASYNC_TYPE_CODE.contains(payModeParam.getChannel()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<PayWayParam> sortList = new ArrayList<>(payWayParamList.size());
|
||||
List<PayChannelParam> sortList = new ArrayList<>(payChannelParamList.size());
|
||||
|
||||
// 异步在后面
|
||||
if (asyncFirst) {
|
||||
sortList.addAll(asyncPayWayParamList);
|
||||
sortList.addAll(syncPayWayParamList);
|
||||
sortList.addAll(asyncPayChannelParamList);
|
||||
sortList.addAll(syncPayChannelParamList);
|
||||
} else {
|
||||
sortList.addAll(syncPayWayParamList);
|
||||
sortList.addAll(asyncPayWayParamList);
|
||||
sortList.addAll(syncPayChannelParamList);
|
||||
sortList.addAll(asyncPayChannelParamList);
|
||||
}
|
||||
|
||||
// 此处有一个根据Type的反转排序,
|
||||
|
@@ -6,7 +6,7 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.context.NoticeLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.context.PlatformLocal;
|
||||
@@ -65,7 +65,7 @@ public class PayAssistService {
|
||||
*/
|
||||
public void initExpiredTime(PayOrder order, PayParam payParam){
|
||||
// 不是异步支付,没有超时时间
|
||||
if (PayUtil.isNotSync(payParam.getPayWays())){
|
||||
if (PayUtil.isNotSync(payParam.getPayChannels())){
|
||||
return;
|
||||
}
|
||||
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
|
||||
@@ -109,23 +109,23 @@ public class PayAssistService {
|
||||
/**
|
||||
* 获取异步支付参数
|
||||
*/
|
||||
public PayWayParam getAsyncPayParam(PayParam payParam, PayOrder payOrder) {
|
||||
public PayChannelParam getAsyncPayParam(PayParam payParam, PayOrder payOrder) {
|
||||
// 查询之前的支付方式
|
||||
String asyncPayChannel = payOrder.getAsyncChannel();
|
||||
PayOrderChannel payOrderChannel = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), asyncPayChannel)
|
||||
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
|
||||
|
||||
// 新的异步支付方式
|
||||
PayWayParam payWayParam = payParam.getPayWays()
|
||||
PayChannelParam payChannelParam = payParam.getPayChannels()
|
||||
.stream()
|
||||
.filter(payMode -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payMode.getChannel()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
|
||||
// 新传入的金额是否一致
|
||||
if (!Objects.equals(payOrderChannel.getAmount(), payWayParam.getAmount())){
|
||||
if (!Objects.equals(payOrderChannel.getAmount(), payChannelParam.getAmount())){
|
||||
throw new PayFailureException("传入的支付金额非法!与订单金额不一致");
|
||||
}
|
||||
return payWayParam;
|
||||
return payChannelParam;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +139,7 @@ public class PayAssistService {
|
||||
PayOrderExtra payOrderExtra = PaymentBuilder.buildPayOrderExtra(payParam, payOrder.getId());
|
||||
payOrderExtraManager.save(payOrderExtra);
|
||||
// 构建支付通道表并保存
|
||||
List<PayOrderChannel> payOrderChannels = PaymentBuilder.buildPayChannel(payParam.getPayWays())
|
||||
List<PayOrderChannel> payOrderChannels = PaymentBuilder.buildPayChannel(payParam.getPayChannels())
|
||||
.stream()
|
||||
.peek(o -> o.setPaymentId(payOrder.getId()).setAsync(payOrder.isAsyncPay()))
|
||||
.collect(Collectors.toList());
|
||||
@@ -155,10 +155,12 @@ public class PayAssistService {
|
||||
public void updatePayOrderExtra(PayParam payParam,Long paymentId){
|
||||
PayOrderExtra payOrderExtra = payOrderExtraManager.findById(paymentId)
|
||||
.orElseThrow(() -> new DataNotExistException("支付订单不存在"));
|
||||
String notifyUrl = PaymentContextLocal.get().getNoticeInfo().getNotifyUrl();
|
||||
payOrderExtra.setReqTime(payParam.getReqTime())
|
||||
.setSign(payParam.getSign())
|
||||
.setNotNotify(payParam.isNotNotify())
|
||||
.setNotifyUrl(payParam.getNotifyUrl())
|
||||
.setNotifyUrl(notifyUrl)
|
||||
.setAttach(payParam.getAttach())
|
||||
.setClientIp(payParam.getClientIp());
|
||||
payOrderExtraManager.updateById(payOrderExtra);
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.SimplePayParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PayResult;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.pay.factory.PayStrategyFactory;
|
||||
@@ -92,13 +92,13 @@ public class PayService {
|
||||
public PayResult simplePay(SimplePayParam simplePayParam) {
|
||||
// 组装支付参数
|
||||
PayParam payParam = new PayParam();
|
||||
PayWayParam payWayParam = new PayWayParam();
|
||||
payWayParam.setChannel(simplePayParam.getPayChannel());
|
||||
payWayParam.setWay(simplePayParam.getPayWay());
|
||||
payWayParam.setAmount(simplePayParam.getAmount());
|
||||
payWayParam.setChannelExtra(simplePayParam.getChannelExtra());
|
||||
PayChannelParam payChannelParam = new PayChannelParam();
|
||||
payChannelParam.setChannel(simplePayParam.getPayChannel());
|
||||
payChannelParam.setWay(simplePayParam.getPayWay());
|
||||
payChannelParam.setAmount(simplePayParam.getAmount());
|
||||
payChannelParam.setChannelExtra(simplePayParam.getChannelExtra());
|
||||
BeanUtil.copyProperties(simplePayParam,payParam, CopyOptions.create().ignoreNullValue());
|
||||
payParam.setPayWays(Collections.singletonList(payWayParam));
|
||||
payParam.setPayChannels(Collections.singletonList(payChannelParam));
|
||||
// 复用支付下单接口
|
||||
return this.pay(payParam);
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public class PayService {
|
||||
}
|
||||
|
||||
// 2. 价格检测
|
||||
PayUtil.validationAmount(payParam.getPayWays());
|
||||
PayUtil.validationAmount(payParam.getPayChannels());
|
||||
|
||||
// 3. 创建支付相关的记录并返回支付订单对象
|
||||
payOrder = payAssistService.createPayOrder(payParam);
|
||||
@@ -131,7 +131,7 @@ public class PayService {
|
||||
private void firstPayHandler(PayParam payParam, PayOrder payOrder) {
|
||||
|
||||
// 1.获取支付方式,通过工厂生成对应的策略组
|
||||
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.createAsyncLast(payParam.getPayWays());
|
||||
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.createAsyncLast(payParam.getPayChannels());
|
||||
if (CollectionUtil.isEmpty(paymentStrategyList)) {
|
||||
throw new PayUnsupportedMethodException();
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class PayService {
|
||||
// 发起支付成功进行的执行方法
|
||||
strategies.forEach(AbsPayStrategy::doSuccessHandler);
|
||||
// 所有支付方式都是同步时, 对支付订单进行处理
|
||||
if (PayUtil.isNotSync(payParam.getPayWays())) {
|
||||
if (PayUtil.isNotSync(payParam.getPayChannels())) {
|
||||
// 修改支付订单状态为成功
|
||||
payOrderObj.setStatus(PayStatusEnum.SUCCESS.getCode());
|
||||
payOrderObj.setPayTime(LocalDateTime.now());
|
||||
@@ -171,8 +171,8 @@ public class PayService {
|
||||
}
|
||||
|
||||
// 2.获取 异步支付通道,通过工厂生成对应的策略组(只包含异步支付的策略, 同步支付相关逻辑不再进行执行)
|
||||
PayWayParam payWayParam = payAssistService.getAsyncPayParam(payParam, payOrder);
|
||||
List<AbsPayStrategy> asyncStrategyList = PayStrategyFactory.createAsyncLast(Collections.singletonList(payWayParam));
|
||||
PayChannelParam payChannelParam = payAssistService.getAsyncPayParam(payParam, payOrder);
|
||||
List<AbsPayStrategy> asyncStrategyList = PayStrategyFactory.createAsyncLast(Collections.singletonList(payChannelParam));
|
||||
|
||||
// 3.初始化支付的参数
|
||||
for (AbsPayStrategy paymentStrategy : asyncStrategyList) {
|
||||
|
@@ -11,7 +11,7 @@ import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
|
||||
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONException;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
@@ -56,7 +56,7 @@ public class AliPayStrategy extends AbsPayStrategy {
|
||||
public void doBeforePayHandler() {
|
||||
try {
|
||||
// 支付宝参数验证
|
||||
String extraParamsJson = this.getPayWayParam().getChannelExtra();
|
||||
String extraParamsJson = this.getPayChannelParam().getChannelExtra();
|
||||
if (StrUtil.isNotBlank(extraParamsJson)) {
|
||||
this.aliPayParam = JSONUtil.toBean(extraParamsJson, AliPayParam.class);
|
||||
}
|
||||
@@ -68,13 +68,13 @@ public class AliPayStrategy extends AbsPayStrategy {
|
||||
throw new PayFailureException("支付参数错误");
|
||||
}
|
||||
// 检查金额
|
||||
PayWayParam payMode = this.getPayWayParam();
|
||||
PayChannelParam payMode = this.getPayChannelParam();
|
||||
if (payMode.getAmount() <= 0) {
|
||||
throw new PayAmountAbnormalException();
|
||||
}
|
||||
// 检查并获取支付宝支付配置
|
||||
this.initAlipayConfig();
|
||||
aliPayService.validation(this.getPayWayParam(), alipayConfig);
|
||||
aliPayService.validation(this.getPayChannelParam(), alipayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ public class AliPayStrategy extends AbsPayStrategy {
|
||||
*/
|
||||
@Override
|
||||
public void doPayHandler() {
|
||||
aliPayService.pay( this.getOrder(), this.getPayWayParam(), this.aliPayParam, this.alipayConfig);
|
||||
aliPayService.pay( this.getOrder(), this.getPayChannelParam(), this.aliPayParam, this.alipayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +90,7 @@ public class AliPayStrategy extends AbsPayStrategy {
|
||||
*/
|
||||
@Override
|
||||
public void doSuccessHandler() {
|
||||
aliPaymentService.updatePaySuccess(this.getOrder(), this.getPayWayParam());
|
||||
aliPaymentService.updatePaySuccess(this.getOrder(), this.getPayChannelParam());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.service.core.channel.cash.service.CashService;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.service.PayOrderService;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@@ -43,8 +43,8 @@ public class CashPayStrategy extends AbsPayStrategy {
|
||||
@Override
|
||||
public void doBeforePayHandler() {
|
||||
// 检查金额
|
||||
PayWayParam payWayParam = this.getPayWayParam();
|
||||
if (payWayParam.getAmount() <= 0) {
|
||||
PayChannelParam payChannelParam = this.getPayChannelParam();
|
||||
if (payChannelParam.getAmount() <= 0) {
|
||||
throw new PayAmountAbnormalException();
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class CashPayStrategy extends AbsPayStrategy {
|
||||
*/
|
||||
@Override
|
||||
public void doPayHandler() {
|
||||
cashService.pay(this.getPayWayParam(), this.getOrder(), this.getPayParam());
|
||||
cashService.pay(this.getPayChannelParam(), this.getOrder(), this.getPayParam());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -42,7 +42,7 @@ public class VoucherPayStrategy extends AbsPayStrategy {
|
||||
@Override
|
||||
public void doBeforePayHandler() {
|
||||
// 获取并校验储值卡
|
||||
this.voucher = voucherPayService.getAndCheckVoucher(this.getPayWayParam());
|
||||
this.voucher = voucherPayService.getAndCheckVoucher(this.getPayChannelParam());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,11 +54,11 @@ public class VoucherPayStrategy extends AbsPayStrategy {
|
||||
public void doPayHandler() {
|
||||
VoucherRecord voucherRecord;
|
||||
if (this.getOrder().isAsyncPay()){
|
||||
voucherRecord = voucherPayService.freezeBalance(this.getPayWayParam().getAmount(), this.getOrder(), this.voucher);
|
||||
voucherRecord = voucherPayService.freezeBalance(this.getPayChannelParam().getAmount(), this.getOrder(), this.voucher);
|
||||
} else {
|
||||
voucherRecord = voucherPayService.pay(this.getPayWayParam().getAmount(), this.getOrder(), this.voucher);
|
||||
voucherRecord = voucherPayService.pay(this.getPayChannelParam().getAmount(), this.getOrder(), this.voucher);
|
||||
}
|
||||
voucherPayOrderService.savePayment(this.getOrder(), getPayParam(), getPayWayParam(), voucherRecord);
|
||||
voucherPayOrderService.savePayment(this.getOrder(), getPayParam(), getPayChannelParam(), voucherRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -54,7 +54,7 @@ public class WalletPayStrategy extends AbsPayStrategy {
|
||||
WalletPayParam walletPayParam = new WalletPayParam();
|
||||
try {
|
||||
// 钱包参数验证
|
||||
String extraParamsJson = this.getPayWayParam().getChannelExtra();
|
||||
String extraParamsJson = this.getPayChannelParam().getChannelExtra();
|
||||
if (StrUtil.isNotBlank(extraParamsJson)) {
|
||||
walletPayParam = JSONUtil.toBean(extraParamsJson, WalletPayParam.class);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class WalletPayStrategy extends AbsPayStrategy {
|
||||
throw new WalletBannedException();
|
||||
}
|
||||
// 判断余额
|
||||
if (this.wallet.getBalance() < getPayWayParam().getAmount()) {
|
||||
if (this.wallet.getBalance() < getPayChannelParam().getAmount()) {
|
||||
throw new WalletLackOfBalanceException();
|
||||
}
|
||||
}
|
||||
@@ -83,11 +83,11 @@ public class WalletPayStrategy extends AbsPayStrategy {
|
||||
public void doPayHandler() {
|
||||
// 异步支付方式时使用冻结方式
|
||||
if (this.getOrder().isAsyncPay()){
|
||||
walletPayService.freezeBalance(getPayWayParam().getAmount(), this.getOrder(), this.wallet);
|
||||
walletPayService.freezeBalance(getPayChannelParam().getAmount(), this.getOrder(), this.wallet);
|
||||
} else {
|
||||
walletPayService.pay(getPayWayParam().getAmount(), this.getOrder(), this.wallet);
|
||||
walletPayService.pay(getPayChannelParam().getAmount(), this.getOrder(), this.wallet);
|
||||
}
|
||||
walletPayOrderService.savePayment(this.getOrder(), this.getPayParam(), this.getPayWayParam(), this.wallet);
|
||||
walletPayOrderService.savePayment(this.getOrder(), this.getPayParam(), this.getPayChannelParam(), this.wallet);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.service.core.payment.pay.strategy;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.service.common.exception.ExceptionInfo;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayCloseService;
|
||||
@@ -60,7 +60,7 @@ public class WeChatPayStrategy extends AbsPayStrategy {
|
||||
this.initWeChatPayConfig();
|
||||
try {
|
||||
// 微信参数验证
|
||||
String extraParamsJson = this.getPayWayParam().getChannelExtra();
|
||||
String extraParamsJson = this.getPayChannelParam().getChannelExtra();
|
||||
if (StrUtil.isNotBlank(extraParamsJson)) {
|
||||
this.weChatPayParam = JSONUtil.toBean(extraParamsJson, WeChatPayParam.class);
|
||||
}
|
||||
@@ -73,14 +73,14 @@ public class WeChatPayStrategy extends AbsPayStrategy {
|
||||
}
|
||||
|
||||
// 检查金额
|
||||
PayWayParam payMode = this.getPayWayParam();
|
||||
PayChannelParam payMode = this.getPayChannelParam();
|
||||
if (payMode.getAmount() <= 0) {
|
||||
throw new PayAmountAbnormalException();
|
||||
}
|
||||
|
||||
// 检查并获取微信支付配置
|
||||
this.initWeChatPayConfig();
|
||||
weChatPayService.validation(this.getPayWayParam(), weChatPayConfig);
|
||||
weChatPayService.validation(this.getPayChannelParam(), weChatPayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,7 +88,7 @@ public class WeChatPayStrategy extends AbsPayStrategy {
|
||||
*/
|
||||
@Override
|
||||
public void doPayHandler() {
|
||||
weChatPayService.pay(this.getOrder(), this.weChatPayParam, this.getPayWayParam(), this.weChatPayConfig);
|
||||
weChatPayService.pay(this.getOrder(), this.weChatPayParam, this.getPayChannelParam(), this.weChatPayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +96,7 @@ public class WeChatPayStrategy extends AbsPayStrategy {
|
||||
*/
|
||||
@Override
|
||||
public void doSuccessHandler() {
|
||||
weChatPayOrderService.updatePaySuccess(this.getOrder(), this.getPayWayParam());
|
||||
weChatPayOrderService.updatePaySuccess(this.getOrder(), this.getPayChannelParam());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -11,7 +11,7 @@ import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrderChannel;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrderExtra;
|
||||
import cn.bootx.platform.daxpay.service.common.entity.OrderRefundableInfo;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PayResult;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -42,16 +42,16 @@ public class PaymentBuilder {
|
||||
.getAsyncPayInfo()
|
||||
.getExpiredTime();
|
||||
// 可退款信息
|
||||
List<OrderRefundableInfo> refundableInfos = buildRefundableInfo(payParam.getPayWays());
|
||||
List<OrderRefundableInfo> refundableInfos = buildRefundableInfo(payParam.getPayChannels());
|
||||
// 计算总价
|
||||
int sumAmount = payParam.getPayWays().stream()
|
||||
.map(PayWayParam::getAmount)
|
||||
int sumAmount = payParam.getPayChannels().stream()
|
||||
.map(PayChannelParam::getAmount)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(Integer::sum)
|
||||
.orElse(0);
|
||||
// 是否有异步支付方式
|
||||
Optional<String> asyncPay = payParam.getPayWays().stream()
|
||||
.map(PayWayParam::getChannel)
|
||||
Optional<String> asyncPay = payParam.getPayChannels().stream()
|
||||
.map(PayChannelParam::getChannel)
|
||||
.filter(PayChannelEnum.ASYNC_TYPE_CODE::contains)
|
||||
.findFirst();
|
||||
// 构建支付订单对象
|
||||
@@ -62,7 +62,7 @@ public class PaymentBuilder {
|
||||
.setStatus(PayStatusEnum.PROGRESS.getCode())
|
||||
.setAmount(sumAmount)
|
||||
.setExpiredTime(expiredTime)
|
||||
.setCombinationPay(payParam.getPayWays().size() > 1)
|
||||
.setCombinationPay(payParam.getPayChannels().size() > 1)
|
||||
.setAsyncPay(asyncPay.isPresent())
|
||||
.setAsyncChannel(asyncPay.orElse(null))
|
||||
.setRefundableBalance(sumAmount);
|
||||
@@ -83,6 +83,7 @@ public class PaymentBuilder {
|
||||
.setNotifyUrl(noticeInfo.getNotifyUrl())
|
||||
.setSign(payParam.getSign())
|
||||
.setSignType(platform.getSignType())
|
||||
.setAttach(payParam.getAttach())
|
||||
.setApiVersion(payParam.getVersion())
|
||||
.setReqTime(payParam.getReqTime());
|
||||
payOrderExtra.setId(paymentId);
|
||||
@@ -92,11 +93,11 @@ public class PaymentBuilder {
|
||||
/**
|
||||
* 构建订单关联通道信息
|
||||
*/
|
||||
public List<PayOrderChannel> buildPayChannel(List<PayWayParam> payWayParams) {
|
||||
if (CollectionUtil.isEmpty(payWayParams)) {
|
||||
public List<PayOrderChannel> buildPayChannel(List<PayChannelParam> payChannelParams) {
|
||||
if (CollectionUtil.isEmpty(payChannelParams)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return payWayParams.stream()
|
||||
return payChannelParams.stream()
|
||||
.map(o-> new PayOrderChannel()
|
||||
.setChannel(o.getChannel())
|
||||
.setPayWay(o.getWay())
|
||||
@@ -108,11 +109,11 @@ public class PaymentBuilder {
|
||||
/**
|
||||
* 构建支付订单的可退款信息列表
|
||||
*/
|
||||
private List<OrderRefundableInfo> buildRefundableInfo(List<PayWayParam> payWayParamList) {
|
||||
if (CollectionUtil.isEmpty(payWayParamList)) {
|
||||
private List<OrderRefundableInfo> buildRefundableInfo(List<PayChannelParam> payChannelParamList) {
|
||||
if (CollectionUtil.isEmpty(payChannelParamList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return payWayParamList.stream()
|
||||
return payChannelParamList.stream()
|
||||
.map(o-> new OrderRefundableInfo()
|
||||
.setChannel(o.getChannel())
|
||||
.setAmount(o.getAmount()))
|
||||
|
@@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -17,6 +18,14 @@ import java.util.Optional;
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayOrderChannelManager extends BaseManager<PayOrderChannelMapper, PayOrderChannel> {
|
||||
|
||||
/**
|
||||
* 根据订单查找
|
||||
*/
|
||||
public List<PayOrderChannel> findAllByPaymentId(Long paymentId){
|
||||
return findAllByField(PayOrderChannel::getPaymentId,paymentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单id和支付通道查询
|
||||
*/
|
||||
|
@@ -7,6 +7,7 @@ import cn.bootx.table.modify.annotation.DbTable;
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
@@ -42,6 +43,8 @@ public class PayOrderExtra extends MpBaseEntity {
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String notifyUrl;
|
||||
|
||||
/** 同步通知 */
|
||||
|
||||
/** 签名类型 */
|
||||
@DbColumn(comment = "签名类型")
|
||||
private String signType;
|
||||
@@ -50,6 +53,9 @@ public class PayOrderExtra extends MpBaseEntity {
|
||||
@DbColumn(comment = "签名,以最后一次为准")
|
||||
private String sign;
|
||||
|
||||
@Schema(description = "商户扩展参数,回调时会原样返回")
|
||||
private String attach;
|
||||
|
||||
/** API版本号 */
|
||||
@DbColumn(comment = "API版本号")
|
||||
private String apiVersion;
|
||||
|
@@ -4,7 +4,7 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.dao.PayOrderChannelManager;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrderChannel;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -27,22 +27,22 @@ public class PayOrderChannelService {
|
||||
* 更新支付订单的通道信息
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateChannel(PayWayParam payWayParam, PayOrder payOrder){
|
||||
public void updateChannel(PayChannelParam payChannelParam, PayOrder payOrder){
|
||||
Optional<PayOrderChannel> payOrderChannelOpt = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), PayChannelEnum.WECHAT.getCode());
|
||||
if (!payOrderChannelOpt.isPresent()){
|
||||
payOrderChannelManager.deleteByPaymentIdAndAsync(payOrder.getId());
|
||||
payOrderChannelManager.save(new PayOrderChannel()
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setChannel(PayChannelEnum.ALI.getCode())
|
||||
.setAmount(payWayParam.getAmount())
|
||||
.setPayWay(payWayParam.getWay())
|
||||
.setChannelExtra(payWayParam.getChannelExtra())
|
||||
.setAmount(payChannelParam.getAmount())
|
||||
.setPayWay(payChannelParam.getWay())
|
||||
.setChannelExtra(payChannelParam.getChannelExtra())
|
||||
.setAsync(true)
|
||||
);
|
||||
} else {
|
||||
payOrderChannelOpt.get()
|
||||
.setChannelExtra(payWayParam.getChannelExtra())
|
||||
.setPayWay(payWayParam.getWay());
|
||||
.setChannelExtra(payChannelParam.getChannelExtra())
|
||||
.setPayWay(payChannelParam.getWay());
|
||||
payOrderChannelManager.updateById(payOrderChannelOpt.get());
|
||||
}
|
||||
}
|
||||
|
@@ -43,6 +43,9 @@ public class PayApiConfig extends MpBaseEntity implements EntityBaseFunction<Pay
|
||||
@DbColumn(comment = "是否开启回调通知")
|
||||
private boolean notice;
|
||||
|
||||
@DbColumn(comment = "只有异步支付才进行通知")
|
||||
private boolean onlyAsyncNotice;
|
||||
|
||||
@DbColumn(comment = "默认回调地址")
|
||||
private String noticeUrl;
|
||||
|
||||
|
@@ -8,7 +8,6 @@ import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -30,9 +29,9 @@ public class PayExpiredTimeTask {
|
||||
|
||||
private final LockTemplate lockTemplate;
|
||||
|
||||
@Scheduled(cron = "*/5 * * * * ?")
|
||||
// @Scheduled(cron = "*/5 * * * * ?")
|
||||
public void task(){
|
||||
log.info("执行超时取消任务....");
|
||||
log.debug("执行超时取消任务....");
|
||||
Set<String> expiredKeys = repository.getExpiredKeys(LocalDateTime.now());
|
||||
for (String expiredKey : expiredKeys) {
|
||||
LockInfo lock = lockTemplate.lock("payment:expired:" + expiredKey,10000,0);
|
||||
|
@@ -29,7 +29,7 @@ public class PayWaitOrderSyncTask {
|
||||
private final LockTemplate lockTemplate;
|
||||
|
||||
public void task(){
|
||||
log.info("开始同步支付订单");
|
||||
log.debug("开始同步支付订单状态");
|
||||
// 从超时订单列表中获取到未超时的订单号
|
||||
Set<String> keys = repository.getNormalKeysBy30Day();
|
||||
for (String key : keys) {
|
||||
|
@@ -4,7 +4,7 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.exception.ExceptionInfo;
|
||||
import cn.bootx.platform.daxpay.service.core.record.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -26,7 +26,7 @@ public abstract class AbsPayStrategy implements PayStrategy{
|
||||
private PayParam payParam = null;
|
||||
|
||||
/** 支付方式参数 支付参数中的与这个不一致, 以这个为准 */
|
||||
private PayWayParam payWayParam = null;
|
||||
private PayChannelParam payChannelParam = null;
|
||||
|
||||
/**
|
||||
* 策略标识
|
||||
|
@@ -2,17 +2,16 @@ package cn.bootx.platform.daxpay.core.payment.common.service;
|
||||
|
||||
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
|
||||
import cn.bootx.platform.daxpay.param.channel.WeChatPayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.util.PaySignUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.ijpay.core.kit.PayKit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支付签名服务
|
||||
@@ -35,7 +34,7 @@ class PaymentSignServiceTest {
|
||||
// 传入的话需要传输时间戳
|
||||
payParam.setReqTime(LocalDateTime.now());
|
||||
|
||||
PayWayParam p1 = new PayWayParam();
|
||||
PayChannelParam p1 = new PayChannelParam();
|
||||
p1.setAmount(100);
|
||||
p1.setChannel("wechat");
|
||||
p1.setWay("wx_app");
|
||||
@@ -43,7 +42,7 @@ class PaymentSignServiceTest {
|
||||
aliPayParam.setAuthCode("6688");
|
||||
p1.setChannelExtra(JSONUtil.toJsonStr(aliPayParam));
|
||||
|
||||
PayWayParam p2 = new PayWayParam();
|
||||
PayChannelParam p2 = new PayChannelParam();
|
||||
p2.setAmount(100);
|
||||
p2.setChannel("wechat");
|
||||
p2.setWay("wx_app");
|
||||
@@ -51,19 +50,15 @@ class PaymentSignServiceTest {
|
||||
weChatPayParam.setOpenId("w2qsz2xawe3gbhyyff28fs01fd");
|
||||
weChatPayParam.setAuthCode("8866");
|
||||
p2.setChannelExtra(JSONUtil.toJsonStr(weChatPayParam));
|
||||
List<PayWayParam> payWays = Arrays.asList(p1, p2);
|
||||
payParam.setPayWays(payWays);
|
||||
Map<String, String> map = payParam.toMap();
|
||||
log.info("map: {}",map);
|
||||
String data = PayKit.createLinkString(map);
|
||||
List<PayChannelParam> payWays = Arrays.asList(p1, p2);
|
||||
payParam.setPayChannels(payWays);
|
||||
|
||||
log.info("拼接字符串: {}",data);
|
||||
// 签名
|
||||
String sign = "123456";
|
||||
data += "&sign="+sign;
|
||||
data = data.toUpperCase();
|
||||
log.info("添加秘钥并转换为大写的字符串: {}",data);
|
||||
log.info("MD5: {}",PayKit.md5(data));
|
||||
log.info("HmacSHA256: {}",PayKit.hmacSha256(data,sign));
|
||||
String md5Sign = PaySignUtil.md5Sign(payParam, sign);
|
||||
String hmacSha256Sign = PaySignUtil.hmacSha256Sign(payParam, sign);
|
||||
log.info("MD5: {}",md5Sign);
|
||||
log.info("HmacSHA256: {}", hmacSha256Sign);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -146,3 +146,17 @@ table-modify:
|
||||
database-type: mysql
|
||||
fail-fast: true
|
||||
scan-package: cn.bootx.platform.daxpay
|
||||
dromara:
|
||||
#文件存储配置,
|
||||
x-file-storage:
|
||||
#默认使用的存储平台
|
||||
default-platform: local
|
||||
#缩略图后缀,例如【.min.jpg】【.png】
|
||||
thumbnail-suffix: ".min.jpg"
|
||||
local-plus:
|
||||
# 不启用自带的访问映射, 使用自定义的访问实现
|
||||
# 存储平台标识
|
||||
- platform: local
|
||||
enableStorage: true
|
||||
# 文件存储路径
|
||||
storage-path: D:/data/files/
|
||||
|
Reference in New Issue
Block a user