ref 支付上下文优化, 时间戳与java8时间转换, 支付退款移植, 支付流程优化

This commit is contained in:
nws
2023-12-26 00:09:33 +08:00
parent 6aebdc3fd9
commit e276d3597c
72 changed files with 1376 additions and 282 deletions

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay;
package cn.bootx.platform.daxpay.admin;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;
@@ -13,5 +13,5 @@ import org.springframework.context.annotation.ComponentScan;
@ConfigurationPropertiesScan
@MapperScan(annotationClass = Mapper.class)
@ComponentScan
public class DaxPaySingleGatewayApp {
public class DaxPaySingleAdminApp {
}

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.openapi.controller.channel;
package cn.bootx.platform.daxpay.admin.controller.channel;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
@@ -22,7 +22,7 @@ import java.util.List;
*/
@Tag(name = "支付宝配置")
@RestController
@RequestMapping("/alipay")
@RequestMapping("/alipay/config")
@AllArgsConstructor
public class AlipayConfigController {
@@ -35,16 +35,16 @@ public class AlipayConfigController {
return Res.ok();
}
@Operation(summary = "根据Id查询")
@GetMapping("/findById")
public ResResult<AlipayConfigDto> findById() {
@Operation(summary = "获取配置")
@GetMapping("/getConfig")
public ResResult<AlipayConfigDto> getConfig() {
return Res.ok(alipayConfigService.getConfig().toDto());
}
@Operation(summary = "支付宝支持支付方式")
@GetMapping("/findPayWayList")
public ResResult<List<LabelValue>> findPayWayList() {
return Res.ok(alipayConfigService.findPayWayList());
@GetMapping("/findPayWays")
public ResResult<List<LabelValue>> findPayWays() {
return Res.ok(alipayConfigService.findPayWays());
}
@SneakyThrows

View File

@@ -0,0 +1,60 @@
package cn.bootx.platform.daxpay.admin.controller.channel;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.dto.LabelValue;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.dto.channel.alipay.AlipayConfigDto;
import cn.bootx.platform.daxpay.dto.channel.wechat.WeChatPayConfigDto;
import cn.bootx.platform.daxpay.param.channel.wechat.WeChatPayConfigParam;
import cn.hutool.core.codec.Base64;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* @author xxm
* @since 2021/3/19
*/
@Tag(name = "微信支付配置")
@RestController
@RequestMapping("/wechat/pay/config")
@AllArgsConstructor
public class WeChatPayConfigController {
private final WeChatPayConfigService weChatPayConfigService;
@Operation(summary = "更新")
@PostMapping("/update")
public ResResult<Void> update(@RequestBody WeChatPayConfigParam param) {
weChatPayConfigService.update(param);
return Res.ok();
}
@Operation(summary = "获取配置")
@GetMapping("/getConfig")
public ResResult<WeChatPayConfigDto> getConfig() {
return Res.ok(weChatPayConfigService.getConfig().toDto());
}
@Operation(summary = "微信支持支付方式")
@GetMapping("/findPayWays")
public ResResult<List<LabelValue>> findPayWays() {
return Res.ok(weChatPayConfigService.findPayWays());
}
@SneakyThrows
@Operation(summary = "将文件转换成base64")
@PostMapping("/toBase64")
public ResResult<String> toBase64(MultipartFile file){
return Res.ok(Base64.encode(file.getBytes()));
}
}

View File

@@ -1 +1 @@
cn.bootx.platform.daxpay.DaxPaySingleGatewayApp
cn.bootx.platform.daxpay.DaxPaySingleAdminApp

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay;
package cn.bootx.platform.daxpay.gateway;
import org.apache.ibatis.annotations.Mapper;
import org.mybatis.spring.annotation.MapperScan;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.openapi.controller;
package cn.bootx.platform.daxpay.gateway.controller;
import cn.bootx.platform.common.core.annotation.IgnoreAuth;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayCallbackService;

View File

@@ -1,8 +1,9 @@
package cn.bootx.platform.daxpay.openapi.controller;
package cn.bootx.platform.daxpay.gateway.controller;
import cn.bootx.platform.common.core.annotation.IgnoreAuth;
import cn.bootx.platform.daxpay.annotation.PaymentApi;
import cn.bootx.platform.daxpay.core.payment.pay.service.PayService;
import cn.bootx.platform.daxpay.core.payment.refund.service.PayRefundService;
import cn.bootx.platform.daxpay.param.pay.*;
import cn.bootx.platform.daxpay.result.DaxResult;
import cn.bootx.platform.daxpay.result.pay.PayResult;
@@ -28,20 +29,20 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
public class UniPayController {
private final PayService payService;
private final PayRefundService payRefundService;
@PaymentApi("pay")
@Operation(summary = "统一下单")
@PostMapping("/pay")
public DaxResult<PayResult> pay(@RequestBody PayParam payParam){
payService.pay(payParam);
return DaxRes.ok();
return DaxRes.ok(payService.pay(payParam));
}
@PaymentApi("simplePay")
@Operation(summary = "简单下单")
@PostMapping("/simplePay")
public DaxResult<PayResult> simplePay(){
return DaxRes.ok();
public DaxResult<PayResult> simplePay(@RequestBody SimplePayParam payParam){
return DaxRes.ok(payService.simplePay(payParam));
}
@PaymentApi("cancel")
@@ -62,7 +63,7 @@ public class UniPayController {
@Operation(summary = "统一退款")
@PostMapping("/refund")
public DaxResult<RefundResult> refund(@RequestBody RefundParam param){
return DaxRes.ok();
return DaxRes.ok(payRefundService.refund(param));
}
@PaymentApi("simpleRefund")

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.common.context;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;
/**
@@ -8,7 +8,7 @@ import lombok.experimental.Accessors;
* @author xxm
* @since 2023/12/22
*/
@Data
@Getter
@Accessors(chain = true)
public class PaymentContext {
@@ -27,4 +27,7 @@ public class PaymentContext {
/** 消息通知相关信息 */
private final NoticeLocal noticeInfo = new NoticeLocal();
/** 支付请求相关信息 */
private final RequestLocal request = new RequestLocal();
}

View File

@@ -0,0 +1,34 @@
package cn.bootx.platform.daxpay.common.context;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 支付请求相关信息
* @author xxm
* @since 2023/12/25
*/
@Data
@Accessors(chain = true)
public class RequestLocal {
/** 客户端ip */
private String clientIp;
/** 商户扩展参数,回调时会原样返回 */
private String extraParam;
/** 签名 */
private String sign;
/** API版本号 */
private String version;
/** 请求时间,时间戳转时间 */
private LocalDateTime reqTime;
/** 请求链路id */
private String reqId;
}

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.order.pay.entity;
package cn.bootx.platform.daxpay.common.entity;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import lombok.Data;
@@ -11,7 +11,7 @@ import lombok.experimental.Accessors;
*/
@Data
@Accessors(chain = true)
public class PayOrderRefundableInfo {
public class OrderRefundableInfo {
/**
* @see PayChannelEnum#getCode()
*/

View File

@@ -38,10 +38,17 @@ public class CallbackNotify extends MpCreateEntity {
private String notifyInfo;
/**
* 处理状态
* 支付状态
* @see PayStatusEnum
*/
@DbComment("处理状态")
@DbComment("支付状态")
private String payStatus;
/**
* 回调处理状态
* @see
*/
@DbComment("回调处理状态")
private String status;
/** 提示信息 */

View File

@@ -6,7 +6,6 @@ import cn.bootx.platform.daxpay.code.AliPayCode;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.callback.dao.CallbackNotifyManager;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AlipayConfigManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AlipayConfig;
import cn.bootx.platform.daxpay.core.payment.callback.service.PayCallbackService;
import cn.bootx.platform.daxpay.func.AbsPayCallbackStrategy;
@@ -33,12 +32,12 @@ import java.util.Objects;
@Service
public class AliPayCallbackService extends AbsPayCallbackStrategy {
private final AlipayConfigManager alipayConfigManager;
private final AlipayConfigService aliasConfigService;
public AliPayCallbackService(RedisClient redisClient, CallbackNotifyManager callbackNotifyManager,
PayCallbackService payCallbackService, AlipayConfigManager alipayConfigManager) {
PayCallbackService payCallbackService, AlipayConfigService aliasConfigService) {
super(redisClient, callbackNotifyManager, payCallbackService);
this.alipayConfigManager = alipayConfigManager;
this.aliasConfigService = aliasConfigService;
}
@Override
@@ -69,7 +68,7 @@ public class AliPayCallbackService extends AbsPayCallbackStrategy {
log.error("支付宝回调报文 appId 为空 {}", callReq);
return false;
}
AlipayConfig alipayConfig = null;
AlipayConfig alipayConfig = aliasConfigService.getConfig();
if (Objects.isNull(alipayConfig)) {
log.error("支付宝支付配置不存在: {}", callReq);
return false;

View File

@@ -6,10 +6,11 @@ import cn.bootx.platform.daxpay.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AliPayOrderManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayOrder;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderRefundableInfo;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.RequiredArgsConstructor;
@@ -17,7 +18,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -40,30 +40,43 @@ public class AliPayOrderService {
private final PayOrderManager payOrderManager;
private final PayOrderChannelManager payOrderChannelManager;
/**
* 支付调起成功 更新payment中异步支付类型信息, 如果支付完成, 创建支付宝支付单
*/
public void updatePaySuccess(PayOrder payOrder, PayWayParam payWayParam) {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
payOrder.setAsyncPayMode(true).setAsyncPayChannel(PayChannelEnum.ALI.getCode());
payOrder.setAsyncPayMode(true)
.setAsyncPayChannel(PayChannelEnum.ALI.getCode());
// 更新支付宝异步支付类型信息
Optional<PayOrderChannel> payOrderChannelOpt = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), PayChannelEnum.ALI.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())
.setAsync(true)
);
} else {
payOrderChannelOpt.get()
.setChannelExtra(payWayParam.getChannelExtra())
.setPayWay(payWayParam.getWay());
payOrderChannelManager.updateById(payOrderChannelOpt.get());
}
// TODO 支付
List<PayOrderChannel> payChannelInfo = new ArrayList<>();
List<PayOrderRefundableInfo> refundableInfos = new ArrayList<>();
// 清除已有的异步支付类型信息
payChannelInfo.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
refundableInfos.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
// 更新支付宝支付类型信息
payChannelInfo.add(new PayOrderChannel().setChannel(PayChannelEnum.ALI.getCode())
.setPayWay(payWayParam.getWay())
.setAmount(payWayParam.getAmount())
.setChannelExtra(payWayParam.getChannelExtra()));
// TODO 更新支付关联信息
// payOrder.setPayChannelInfo(payChannelInfo);
// 更新支付宝可退款类型信息
refundableInfos
.add(new PayOrderRefundableInfo().setChannel(PayChannelEnum.ALI.getCode())
.setAmount(payWayParam.getAmount()));
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())
);
payOrder.setRefundableInfos(refundableInfos);
// 如果支付完成(付款码情况) 调用 updateSyncSuccess 创建支付宝支付记录
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
@@ -91,7 +104,7 @@ public class AliPayOrderService {
.setAmount(payWayParam.getAmount())
.setRefundableBalance(payWayParam.getAmount())
.setBusinessNo(payOrder.getBusinessNo())
.setStatus(PayStatusEnum.PROGRESS.getCode())
.setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(LocalDateTime.now());
aliPayOrderManager.save(aliPayOrder);
}
@@ -102,7 +115,7 @@ public class AliPayOrderService {
public void updateClose(Long paymentId) {
Optional<AliPayOrder> aliPaymentOptional = aliPayOrderManager.findByPaymentId(paymentId);
aliPaymentOptional.ifPresent(aliPayOrder -> {
aliPayOrder.setStatus(PayStatusEnum.CANCEL.getCode());
aliPayOrder.setStatus(PayStatusEnum.CLOSE.getCode());
aliPayOrderManager.updateById(aliPayOrder);
});
}

View File

@@ -33,7 +33,7 @@ import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class AlipayConfigService {
/** 默认支付配置的主键ID */
/** 默认支付配置的主键ID */
private final static Long ID = 0L;
private final AlipayConfigManager alipayConfigManager;
@@ -50,7 +50,7 @@ public class AlipayConfigService {
/**
* 支付宝支持支付方式
*/
public List<LabelValue> findPayWayList() {
public List<LabelValue> findPayWays() {
return AliPayWay.getPayWays()
.stream()
.map(e -> new LabelValue(e.getName(),e.getCode()))

View File

@@ -20,7 +20,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
@@ -192,7 +191,7 @@ public class VoucherPayService {
* 部分退款, 会退到指定的一张卡上, 如果不指定, 则自动退到有效期最久的卡上
*/
@Transactional(rollbackFor = Exception.class)
public void refund(Long paymentId, BigDecimal amount) {
public void refund(Long paymentId, Integer amount) {
VoucherPayment voucherPayment = voucherPaymentManager.findByPaymentId(paymentId)
.orElseThrow(() -> new PayFailureException("储值卡支付记录不存在"));
// 全部退款还是部分退款

View File

@@ -5,7 +5,6 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.WeChatPayCode;
import cn.bootx.platform.daxpay.core.callback.dao.CallbackNotifyManager;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.payment.callback.service.PayCallbackService;
import cn.bootx.platform.daxpay.func.AbsPayCallbackStrategy;
@@ -18,6 +17,7 @@ import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import static cn.bootx.platform.daxpay.code.WeChatPayCode.APPID;
@@ -31,13 +31,12 @@ import static cn.bootx.platform.daxpay.code.WeChatPayCode.APPID;
@Slf4j
@Service
public class WeChatPayCallbackService extends AbsPayCallbackStrategy {
private final WeChatPayConfigManager weChatPayConfigManager;
private final WeChatPayConfigService weChatPayConfigService;
public WeChatPayCallbackService(RedisClient redisClient, CallbackNotifyManager callbackNotifyManager,
PayCallbackService payCallbackService, WeChatPayConfigManager weChatPayConfigManager) {
PayCallbackService payCallbackService, WeChatPayConfigService weChatPayConfigService) {
super(redisClient, callbackNotifyManager, payCallbackService);
this.weChatPayConfigManager = weChatPayConfigManager;
this.weChatPayConfigService = weChatPayConfigService;
}
@Override
@@ -84,8 +83,8 @@ public class WeChatPayCallbackService extends AbsPayCallbackStrategy {
return false;
}
WeChatPayConfig weChatPayConfig = null;
if (weChatPayConfig == null) {
WeChatPayConfig weChatPayConfig = weChatPayConfigService.getConfig();
if (Objects.isNull(weChatPayConfig)) {
log.warn("微信支付配置不存在: {}", callReq);
return false;
}

View File

@@ -1,32 +1,23 @@
package cn.bootx.platform.daxpay.core.channel.wechat.service;
import cn.bootx.platform.common.spring.exception.RetryableException;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.code.WeChatPayCode;
import cn.bootx.platform.daxpay.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderRefundableInfo;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.CloseOrderModel;
import com.ijpay.wxpay.model.RefundModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
@@ -59,45 +50,6 @@ public class WeChatPayCloseService {
this.verifyErrorMsg(result);
}
/**
* 退款
*/
public void refund(PayOrder payOrder, BigDecimal amount,
WeChatPayConfig weChatPayConfig) {
PayOrderRefundableInfo refundableInfo = payOrder.getRefundableInfos().stream()
.filter(o -> Objects.equals(o.getChannel(), PayChannelEnum.WECHAT.getCode()))
.findFirst()
.orElseThrow(() -> new PayFailureException("未找到微信支付的详细信息"));
String refundFee = amount.multiply(BigDecimal.valueOf(100)).toBigInteger().toString();
String totalFee = refundableInfo.getAmount().toString();
// 设置退款信息
AsyncRefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setRefundNo(IdUtil.getSnowflakeNextIdStr());
Map<String, String> params = RefundModel.builder()
.appid(weChatPayConfig.getWxAppId())
.mch_id(weChatPayConfig.getWxMchId())
.out_trade_no(String.valueOf(payOrder.getId()))
.out_refund_no(refundInfo.getRefundNo())
.total_fee(totalFee)
.refund_fee(refundFee)
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256);
// 获取证书文件
if (StrUtil.isBlank(weChatPayConfig.getP12())){
String errorMsg = "微信p.12证书未配置,无法进行退款";
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(PayRefundStatusEnum.SUCCESS.getCode());
throw new PayFailureException(errorMsg);
}
byte[] fileBytes = Base64.decode(weChatPayConfig.getP12());
ByteArrayInputStream inputStream = new ByteArrayInputStream(fileBytes);
// 证书密码为 微信商户号
String xmlResult = WxPayApi.orderRefund(false, params, inputStream, weChatPayConfig.getWxMchId());
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
}
/**
* 验证错误信息
*/
@@ -109,7 +61,7 @@ public class WeChatPayCloseService {
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(WeChatPayCode.RETURN_MSG);
}
log.error("订单退款/关闭失败 {}", errorMsg);
log.error("订单关闭失败 {}", errorMsg);
AsyncRefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(Optional.ofNullable(resultCode).orElse(returnCode));

View File

@@ -1,14 +1,10 @@
package cn.bootx.platform.daxpay.core.channel.wechat.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.dto.LabelValue;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.code.WeChatPayWay;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.dto.channel.wechat.WeChatPayConfigDto;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.wechat.WeChatPayConfigParam;
import cn.hutool.core.bean.BeanUtil;
@@ -31,49 +27,35 @@ import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class WeChatPayConfigService {
/** 默认微信支付配置的主键ID */
private final static Long ID = 0L;
private final WeChatPayConfigManager weChatPayConfigManager;
/**
* 添加微信支付配置
*/
@Transactional(rollbackFor = Exception.class)
public void add(WeChatPayConfigParam param) {
WeChatPayConfig weChatPayConfig = WeChatPayConfig.init(param);
weChatPayConfigManager.save(weChatPayConfig);
}
/**
* 修改
*/
@Transactional(rollbackFor = Exception.class)
public void update(WeChatPayConfigParam param) {
WeChatPayConfig weChatPayConfig = weChatPayConfigManager.findById(param.getId())
WeChatPayConfig weChatPayConfig = weChatPayConfigManager.findById(ID)
.orElseThrow(() -> new PayFailureException("微信支付配置不存在"));
param.setActivity(null);
BeanUtil.copyProperties(param, weChatPayConfig, CopyOptions.create().ignoreNullValue());
weChatPayConfigManager.updateById(weChatPayConfig);
}
/**
* 分页
*/
public PageResult<WeChatPayConfigDto> page(PageParam pageParam, WeChatPayConfigParam param) {
return MpUtil.convert2DtoPageResult(weChatPayConfigManager.page(pageParam, param));
}
/**
* 获取
* 获取支付配置
*/
public WeChatPayConfigDto findById(Long id) {
return weChatPayConfigManager.findById(id).map(WeChatPayConfig::toDto).orElseThrow(DataNotExistException::new);
public WeChatPayConfig getConfig(){
return weChatPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("微信支付配置不存在"));
}
/**
* 微信支持支付方式
*/
public List<LabelValue> findPayWayList() {
public List<LabelValue> findPayWays() {
return WeChatPayWay.getPayWays()
.stream()
.map(e -> new LabelValue(e.getName(),e.getCode()))

View File

@@ -10,7 +10,7 @@ import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayment;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderRefundableInfo;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -45,7 +45,7 @@ public class WeChatPayOrderService {
payOrder.setAsyncPayMode(true).setAsyncPayChannel(PayChannelEnum.WECHAT.getCode());
List<PayOrderChannel> payTypeInfos = new ArrayList<>();
List<PayOrderRefundableInfo> refundableInfos = new ArrayList<>();
List<OrderRefundableInfo> refundableInfos = new ArrayList<>();
// 清除已有的异步支付类型信息
payTypeInfos.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
refundableInfos.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
@@ -57,7 +57,7 @@ public class WeChatPayOrderService {
// TODO 更新支付方式列表
// 更新微信可退款类型信息
refundableInfos.add(
new PayOrderRefundableInfo().setChannel(PayChannelEnum.WECHAT.getCode())
new OrderRefundableInfo().setChannel(PayChannelEnum.WECHAT.getCode())
.setAmount(payWayParam.getAmount()));
payOrder.setRefundableInfos(refundableInfos);
// 如果支付完成(付款码情况) 调用 updateSyncSuccess 创建微信支付记录

View File

@@ -78,9 +78,9 @@ public class WeChatPayService {
/**
* 支付
*/
public void pay(int amount, PayOrder payOrder, WeChatPayParam weChatPayParam, PayWayParam payWayParam,
WeChatPayConfig weChatPayConfig) {
// 微信传入的是分, 将元转换为分
public void pay(PayOrder payOrder, WeChatPayParam weChatPayParam, PayWayParam payWayParam, WeChatPayConfig weChatPayConfig) {
Integer amount = payWayParam.getAmount();
String totalFee = String.valueOf(amount);
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();;
String payBody = null;
@@ -105,7 +105,7 @@ public class WeChatPayService {
// 付款码支付
else if (payWayEnum == PayWayEnum.BARCODE) {
String tradeNo = this.barCode(totalFee, payOrder, weChatPayParam.getAuthCode(), weChatPayConfig);
asyncPayInfo.setTradeNo(tradeNo).setExpiredTime(false);
asyncPayInfo.setTradeNo(tradeNo);
}
asyncPayInfo.setPayBody(payBody);
}
@@ -232,6 +232,7 @@ public class WeChatPayService {
* 构建参数
*/
private UnifiedOrderModelBuilder buildParams(String amount, PayOrder payment, WeChatPayConfig weChatPayConfig, String tradeType) {
LocalDateTime expiredTime = PaymentContextLocal.get()
.getAsyncPayInfo()
.getExpiredTime();

View File

@@ -0,0 +1,96 @@
package cn.bootx.platform.daxpay.core.channel.wechat.service;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.code.WeChatPayCode;
import cn.bootx.platform.daxpay.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.RefundModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
/**
* 微信退款服务
* @author xxm
* @since 2023/12/25
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatRefundService {
/**
* 退款
*/
public void refund(PayOrder payOrder, int amount,
WeChatPayConfig weChatPayConfig) {
OrderRefundableInfo refundableInfo = payOrder.getRefundableInfos().stream()
.filter(o -> Objects.equals(o.getChannel(), PayChannelEnum.WECHAT.getCode()))
.findFirst()
.orElseThrow(() -> new PayFailureException("未找到微信支付的详细信息"));
String refundFee = String.valueOf(amount);
String totalFee = refundableInfo.getAmount().toString();
// 设置退款信息
AsyncRefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setRefundNo(IdUtil.getSnowflakeNextIdStr());
Map<String, String> params = RefundModel.builder()
.appid(weChatPayConfig.getWxAppId())
.mch_id(weChatPayConfig.getWxMchId())
.out_trade_no(String.valueOf(payOrder.getId()))
.out_refund_no(refundInfo.getRefundNo())
.total_fee(totalFee)
.refund_fee(refundFee)
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256);
// 获取证书文件
if (StrUtil.isBlank(weChatPayConfig.getP12())){
String errorMsg = "微信p.12证书未配置,无法进行退款";
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(PayRefundStatusEnum.SUCCESS.getCode());
throw new PayFailureException(errorMsg);
}
byte[] fileBytes = Base64.decode(weChatPayConfig.getP12());
ByteArrayInputStream inputStream = new ByteArrayInputStream(fileBytes);
// 证书密码为 微信商户号
String xmlResult = WxPayApi.orderRefund(false, params, inputStream, weChatPayConfig.getWxMchId());
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String returnCode = result.get(WeChatPayCode.RETURN_CODE);
String resultCode = result.get(WeChatPayCode.RESULT_CODE);
if (!WxPayKit.codeIsOk(returnCode) || !WxPayKit.codeIsOk(resultCode)) {
String errorMsg = result.get(WeChatPayCode.ERR_CODE_DES);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(WeChatPayCode.RETURN_MSG);
}
log.error("订单退款失败 {}", errorMsg);
AsyncRefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(Optional.ofNullable(resultCode).orElse(returnCode));
throw new PayFailureException(errorMsg);
}
}
}

View File

@@ -3,11 +3,13 @@ package cn.bootx.platform.daxpay.core.order.pay.builder;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderRefundableInfo;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.result.pay.PayResult;
@@ -35,7 +37,7 @@ public class PaymentBuilder {
*/
public PayOrder buildPayOrder(PayParam payParam) {
// 可退款信息
List<PayOrderRefundableInfo> refundableInfos = buildRefundableInfo(payParam.getPayWays());
List<OrderRefundableInfo> refundableInfos = buildRefundableInfo(payParam.getPayWays());
// 计算总价
int sumAmount = payParam.getPayWays().stream()
.map(PayWayParam::getAmount)
@@ -66,16 +68,15 @@ public class PaymentBuilder {
* @param paymentId 支付订单id
*/
public PayOrderExtra buildPayOrderExtra(PayParam payParam, Long paymentId) {
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
PayOrderExtra payOrderExtra = new PayOrderExtra()
.setClientIp(payParam.getClientIp())
.setDescription(payParam.getDescription())
.setNotReturn(payParam.isNotReturn())
.setReturnUrl(payParam.getReturnUrl())
.setNotNotify(payParam.isNotNotify())
.setNotifyUrl(payParam.getNotifyUrl())
.setNotifyUrl(noticeInfo.getNotifyUrl())
.setSign(payParam.getSign())
.setSignType(payParam.getSignType())
.setSignType(payParam.getSign())
.setSignType(platform.getSignType())
.setApiVersion(payParam.getVersion())
.setReqTime(payParam.getReqTime());
payOrderExtra.setId(paymentId);
@@ -101,12 +102,12 @@ public class PaymentBuilder {
/**
* 构建支付订单的可退款信息列表
*/
private List<PayOrderRefundableInfo> buildRefundableInfo(List<PayWayParam> payWayParamList) {
private List<OrderRefundableInfo> buildRefundableInfo(List<PayWayParam> payWayParamList) {
if (CollectionUtil.isEmpty(payWayParamList)) {
return Collections.emptyList();
}
return payWayParamList.stream()
.map(o-> new PayOrderRefundableInfo()
.map(o-> new OrderRefundableInfo()
.setChannel(o.getChannel())
.setAmount(o.getAmount()))
.collect(Collectors.toList());
@@ -120,11 +121,11 @@ public class PaymentBuilder {
*/
public PayResult buildPayResultByPayOrder(PayOrder payOrder) {
PayResult paymentResult;
paymentResult = new PayResult()
.setPaymentId(payOrder.getId())
.setAsyncPayMode(payOrder.isAsyncPayMode())
.setAsyncPayChannel(payOrder.getAsyncPayChannel())
.setStatus(payOrder.getStatus());
paymentResult = new PayResult();
paymentResult.setPaymentId(payOrder.getId());
paymentResult.setAsyncPayMode(payOrder.isAsyncPayMode());
paymentResult.setAsyncPayChannel(payOrder.getAsyncPayChannel());
paymentResult.setStatus(payOrder.getStatus());
// 设置异步支付参数
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();;

View File

@@ -20,10 +20,21 @@ public class PayOrderChannelManager extends BaseManager<PayOrderChannelMapper,
/**
* 根据订单id和支付渠道查询
*/
public Optional<PayOrderChannel> findByOderIdAndChannel(Long paymentId, String channel) {
public Optional<PayOrderChannel> findByPaymentIdAndChannel(Long paymentId, String channel) {
return lambdaQuery()
.eq(PayOrderChannel::getPaymentId,paymentId)
.eq(PayOrderChannel::getChannel,channel)
.oneOpt();
}
/**
* 根据订单id删除异步支付记录
*/
public void deleteByPaymentIdAndAsync(Long paymentId){
lambdaUpdate()
.eq(PayOrderChannel::getPaymentId,paymentId)
.eq(PayOrderChannel::isAsync,true)
.remove();
}
}

View File

@@ -5,6 +5,7 @@ import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.common.mybatisplus.handler.JacksonRawTypeHandler;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType;
import cn.bootx.table.modify.mysql.annotation.DbMySqlIndex;
@@ -64,13 +65,13 @@ public class PayOrder extends MpBaseEntity {
/**
* 退款信息列表
* @see PayOrderRefundableInfo
* @see OrderRefundableInfo
*/
@TableField(typeHandler = JacksonRawTypeHandler.class)
@BigField
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
@DbColumn(comment = "退款信息列表")
private List<PayOrderRefundableInfo> refundableInfos;
private List<OrderRefundableInfo> refundableInfos;
/**
* 支付状态

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.core.order.pay.entity;
import cn.bootx.platform.common.mybatisplus.base.MpDelEntity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
import cn.bootx.platform.daxpay.param.channel.VoucherPayParam;
import cn.bootx.platform.daxpay.param.channel.WalletPayParam;
@@ -22,7 +22,7 @@ import lombok.experimental.Accessors;
@Accessors(chain = true)
@DbTable(comment = "支付订单关联支付时通道信息")
@TableName("pay_order_channel")
public class PayOrderChannel extends MpDelEntity {
public class PayOrderChannel extends MpCreateEntity {
@DbColumn(comment = "支付id")
private Long paymentId;
@@ -33,6 +33,9 @@ public class PayOrderChannel extends MpDelEntity {
@DbColumn(comment = "支付方式")
private String payWay;
@DbColumn(comment = "异步支付方式")
private boolean async;
@DbColumn(comment = "金额")
private Integer amount;

View File

@@ -3,6 +3,8 @@ package cn.bootx.platform.daxpay.core.order.pay.entity;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -21,27 +23,21 @@ import java.time.LocalDateTime;
@TableName("pay_order_extra")
public class PayOrderExtra extends MpBaseEntity {
/** 支付终端ip */
@DbColumn(comment = "支付终端ip")
private String clientIp;
/** 描述 */
@DbColumn(comment = "描述")
private String description;
@DbColumn(comment = "是否不进行同步通知的跳转")
private boolean notReturn;
/** 同步通知URL */
@DbColumn(comment = "同步通知URL")
private String returnUrl;
/** 支付终端ip */
@DbColumn(comment = "支付终端ip")
private String clientIp;
/** 是否不启用异步通知 */
@DbColumn(comment = "是否不启用异步通知")
@DbColumn(comment = "是否不启用异步通知,以最后一次为准")
private boolean notNotify;
/** 异步通知地址 */
@DbColumn(comment = "异步通知地址")
@DbColumn(comment = "异步通知地址,以最后一次为准")
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String notifyUrl;
/** 签名类型 */
@@ -49,15 +45,15 @@ public class PayOrderExtra extends MpBaseEntity {
private String signType;
/** 签名 */
@DbColumn(comment = "签名")
@DbColumn(comment = "签名,以最后一次为准")
private String sign;
/** API版本号 */
@DbColumn(comment = "API版本号")
private String apiVersion;
/** 请求时间,时间戳转时间 */
@DbColumn(comment = "请求时间,传输时间戳")
/** 请求时间,时间戳转时间, 以最后一次为准 */
@DbColumn(comment = "请求时间,传输时间戳,以最后一次为准")
private LocalDateTime reqTime;
/** 错误码 */

View File

@@ -10,7 +10,7 @@ import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderRefundableInfo;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -86,8 +86,8 @@ public class PayOrderService {
*/
public void updateRefundSuccess(PayOrder payment, int amount, PayChannelEnum payChannelEnum) {
// 删除旧有的退款记录, 替换退款完的新的
List<PayOrderRefundableInfo> refundableInfos = payment.getRefundableInfos();
PayOrderRefundableInfo refundableInfo = refundableInfos.stream()
List<OrderRefundableInfo> refundableInfos = payment.getRefundableInfos();
OrderRefundableInfo refundableInfo = refundableInfos.stream()
.filter(o -> Objects.equals(o.getChannel(), payChannelEnum.getCode()))
.findFirst()
.orElseThrow(() -> new PayFailureException("退款数据不存在"));

View File

@@ -0,0 +1,19 @@
package cn.bootx.platform.daxpay.core.order.refund.convert;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.dto.order.refund.PayRefundOrderDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author xxm
* @since 2022/3/2
*/
@Mapper
public interface RefundConvert {
RefundConvert CONVERT = Mappers.getMapper(RefundConvert.class);
PayRefundOrderDto convert(PayRefundOrder in);
}

View File

@@ -0,0 +1,34 @@
package cn.bootx.platform.daxpay.core.order.refund.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.dto.order.refund.PayRefundOrderDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.util.Objects;
/**
* @author xxm
* @since 2022/3/2
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class PayRefundOrderManager extends BaseManager<PayRefundOrderMapper, PayRefundOrder> {
public Page<PayRefundOrder> page(PageParam pageParam, PayRefundOrderDto param) {
Page<PayRefundOrder> mpPage = MpUtil.getMpPage(pageParam, PayRefundOrder.class);
return lambdaQuery().orderByDesc(MpIdEntity::getId)
.like(Objects.nonNull(param.getPaymentId()), PayRefundOrder::getPaymentId, param.getPaymentId())
.like(Objects.nonNull(param.getBusinessNo()), PayRefundOrder::getBusinessNo, param.getBusinessNo())
.like(Objects.nonNull(param.getTitle()), PayRefundOrder::getTitle, param.getTitle())
.page(mpPage);
}
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.core.order.refund.dao;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author xxm
* @since 2022/3/2
*/
@Mapper
public interface PayRefundOrderMapper extends BaseMapper<PayRefundOrder> {
}

View File

@@ -0,0 +1,77 @@
package cn.bootx.platform.daxpay.core.order.refund.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.common.mybatisplus.handler.JacksonRawTypeHandler;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.core.order.refund.convert.RefundConvert;
import cn.bootx.platform.daxpay.dto.order.refund.PayRefundOrderDto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
/**
* 退款记录
*
* @author xxm
* @since 2022/3/2
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName(value = "pay_refund_order", autoResultMap = true)
public class PayRefundOrder extends MpBaseEntity implements EntityBaseFunction<PayRefundOrderDto> {
/** 支付单号 */
private Long paymentId;
/** 关联的业务号 */
private String businessNo;
/** 异步方式关联退款请求号(部分退款情况) */
private String refundRequestNo;
/** 标题 */
private String title;
/** 退款金额 */
private Integer amount;
/** 剩余可退 */
private Integer refundableBalance;
/** 退款终端ip */
private String clientIp;
/** 退款时间 */
private LocalDateTime refundTime;
/**
* 退款信息列表
*/
@TableField(typeHandler = JacksonRawTypeHandler.class)
private List<OrderRefundableInfo> refundableInfo;
/**
* 退款状态
* @see PayStatus#REFUND_PROCESS_FAIL
*/
private String refundStatus;
/** 错误码 */
private String errorCode;
/** 错误信息 */
private String errorMsg;
@Override
public PayRefundOrderDto toDto() {
return RefundConvert.CONVERT.convert(this);
}
}

View File

@@ -0,0 +1,43 @@
package cn.bootx.platform.daxpay.core.order.refund.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.order.refund.dao.PayRefundOrderManager;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.dto.order.refund.PayRefundOrderDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 退款
*
* @author xxm
* @since 2022/3/2
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayRefundRecordService {
private final PayRefundOrderManager refundRecordManager;
/**
* 分页查询
*/
public PageResult<PayRefundOrderDto> page(PageParam pageParam, PayRefundOrderDto param) {
Page<PayRefundOrder> page = refundRecordManager.page(pageParam, param);
return MpUtil.convert2DtoPageResult(page);
}
/**
* 根据id查询
*/
public PayRefundOrderDto findById(Long id) {
return refundRecordManager.findById(id).map(PayRefundOrder::toDto).orElseThrow(DataNotExistException::new);
}
}

View File

@@ -18,7 +18,7 @@ public class PayCallbackResult {
* 处理状态
* @see PayStatusEnum
*/
private String code;
private String status;
/** 提示信息 */
private String msg;

View File

@@ -42,22 +42,22 @@ public class PayCallbackService {
*/
public PayCallbackResult callback(Long paymentId, String tradeStatus, Map<String, String> map) {
// 获取payment和paymentParam数据
// 获取支付单
PayOrder payOrder = payOrderService.findById(paymentId).orElse(null);
// 支付单不存在,记录回调记录
if (Objects.isNull(payOrder)) {
return new PayCallbackResult().setCode(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单不存在,记录回调记录");
return new PayCallbackResult().setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单不存在,记录回调记录");
}
// 回调时间超出了支付单超时时间, 记录一下, 不做处理
// 回调时间超出了支付单超时时间, 记录一下, 不做处理 TODO 这块应该吧订单给正常处理了,
if (Objects.nonNull(payOrder.getExpiredTime())
&& LocalDateTimeUtil.ge(LocalDateTime.now(), payOrder.getExpiredTime())) {
return new PayCallbackResult().setCode(PayNotifyStatusEnum.FAIL.getCode()).setMsg("回调时间超出了支付单支付有效时间");
return new PayCallbackResult().setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("回调时间超出了支付单支付有效时间");
}
// 成功状态
if (Objects.equals(PayNotifyStatusEnum.SUCCESS, tradeStatus)) {
if (Objects.equals(PayNotifyStatusEnum.SUCCESS.getCode(), tradeStatus)) {
return this.success(payOrder, map);
}
else {
@@ -70,16 +70,16 @@ public class PayCallbackService {
* 成功处理
*/
private PayCallbackResult success(PayOrder payOrder, Map<String, String> map) {
PayCallbackResult result = new PayCallbackResult().setCode(PayNotifyStatusEnum.SUCCESS.getCode());
PayCallbackResult result = new PayCallbackResult().setStatus(PayNotifyStatusEnum.SUCCESS.getCode());
// payment已经被支付,不需要重复处理
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
return result.setCode(PayNotifyStatusEnum.IGNORE.getCode()).setMsg("支付单已经是支付成功状态,不进行处理");
return result.setStatus(PayNotifyStatusEnum.IGNORE.getCode()).setMsg("支付单已经是支付成功状态,不进行处理");
}
// payment已被取消,记录回调记录
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())) {
return result.setCode(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单不是待支付状态,记录回调记录");
return result.setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单不是待支付状态,记录回调记录");
}
// 2.通过工厂生成对应的策略组
@@ -87,7 +87,7 @@ public class PayCallbackService {
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.create(payParam.getPayWays());
if (CollectionUtil.isEmpty(paymentStrategyList)) {
return result.setCode(PayStatusEnum.FAIL.getCode()).setMsg("支付单数据非法,未找到对应的支付方式");
return result.setStatus(PayStatusEnum.FAIL.getCode()).setMsg("支付单数据非法,未找到对应的支付方式");
}
// 3.初始化支付的参数
@@ -110,7 +110,7 @@ public class PayCallbackService {
// eventSender.sendPayComplete(PayEventBuilder.buildPayComplete(payOrder));
}
else {
return result.setCode(PayStatusEnum.FAIL.getCode()).setMsg("回调处理过程报错");
return result.setStatus(PayStatusEnum.FAIL.getCode()).setMsg("回调处理过程报错");
}
return result;
}
@@ -119,23 +119,23 @@ public class PayCallbackService {
* 失败处理, 关闭并退款 按说这块不会发生
*/
private PayCallbackResult fail(PayOrder payOrder, Map<String, String> map) {
PayCallbackResult result = new PayCallbackResult().setCode(PayStatusEnum.SUCCESS.getCode());
PayCallbackResult result = new PayCallbackResult().setStatus(PayStatusEnum.SUCCESS.getCode());
// payment已被取消,记录回调记录,后期处理
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())) {
return result.setCode(PayNotifyStatusEnum.IGNORE.getCode()).setMsg("支付单已经取消,记录回调记录");
return result.setStatus(PayNotifyStatusEnum.IGNORE.getCode()).setMsg("支付单已经取消,记录回调记录");
}
// payment支付成功, 状态非法
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
return result.setCode(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单状态非法,支付网关状态为失败,但支付单状态为已完成");
return result.setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单状态非法,支付网关状态为失败,但支付单状态为已完成");
}
// 2.通过工厂生成对应的策略组
PayParam payParam = null;
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.create(payParam.getPayWays());
if (CollectionUtil.isEmpty(paymentStrategyList)) {
return result.setCode(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单数据非法,未找到对应的支付方式");
return result.setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单数据非法,未找到对应的支付方式");
}
// 3.初始化支付关闭的参数
for (AbsPayStrategy paymentStrategy : paymentStrategyList) {
@@ -156,7 +156,7 @@ public class PayCallbackService {
// eventSender.sendPayRefund(PayEventBuilder.buildPayRefund(payOrder));
}
else {
return result.setCode(PayNotifyStatusEnum.FAIL.getCode()).setMsg("回调处理过程报错");
return result.setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("回调处理过程报错");
}
return result;

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.core.payment.common.aop;
import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.annotation.PaymentApi;
import cn.bootx.platform.daxpay.core.payment.common.service.PaymentSignService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
@@ -12,7 +13,7 @@ import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* 支付签名切面, 用于对支付参数进行签名
* 支付签名切面, 用于对支付参数进行校验和签名
* 执行顺序: 过滤器 -> 拦截器 -> 切面 -> 方法
* @author xxm
* @since 2023/12/24
@@ -21,7 +22,7 @@ import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class PaymentSignAop {
public class PaymentVerifySignAop {
private final PaymentSignService paymentSignService;
@Before("@annotation(paymentApi)")
@@ -32,6 +33,9 @@ public class PaymentSignAop {
}
Object param = args[0];
if (param instanceof PayCommonParam){
// 参数校验
ValidationUtil.validateParam(param);
// 验签
paymentSignService.verifySign((PayCommonParam) param);
} else {
throw new PayFailureException("支付参数需要继承PayCommonParam");

View File

@@ -2,9 +2,9 @@ package cn.bootx.platform.daxpay.core.payment.common.service;
import cn.bootx.platform.daxpay.code.PaySignTypeEnum;
import cn.bootx.platform.daxpay.common.context.ApiInfoLocal;
import cn.bootx.platform.daxpay.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.system.entity.PlatformConfig;
import cn.bootx.platform.daxpay.core.system.service.PlatformConfigService;
import cn.bootx.platform.daxpay.core.payment.pay.service.PayAssistService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayCommonParam;
import cn.hutool.core.util.StrUtil;
@@ -28,13 +28,16 @@ public class PaymentSignService {
private static final String FIELD_SIGN = "sign";
private final PlatformConfigService platformConfigService;
private final PayAssistService payAssistService;;
/**
* 签名
*/
public void verifySign(PayCommonParam param) {
// 先触发一下平台配置上下文的初始化
payAssistService.initPlatform();
ApiInfoLocal apiInfo = PaymentContextLocal.get().getApiInfo();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
// 判断当前接口是否不需要签名
if (!apiInfo.isReqSign()){
@@ -42,20 +45,19 @@ public class PaymentSignService {
}
// 参数转换为Map对象
Map<String, String> params = param.toMap();
PlatformConfig config = platformConfigService.getConfig();
String signType = config.getSignType();
String signType = platform.getSignType();
// 生成签名前先去除sign
params.remove(FIELD_SIGN);
String data = PayKit.createLinkString(params);
if (Objects.equals(PaySignTypeEnum.HMAC_SHA256.getCode(), signType)){
// 签名验证
data += "&key=" + config.getSignSecret();
String sha256 = PayKit.hmacSha256(data, config.getSignSecret());
data += "&key=" + platform.getSignSecret();
String sha256 = PayKit.hmacSha256(data, platform.getSignSecret());
if (!Objects.equals(sha256, params.get(FIELD_SIGN))){
throw new PayFailureException("签名验证未通过");
}
} else if (Objects.equals(PaySignTypeEnum.MD5.getCode(), signType)){
data += "&key=" + config.getSignSecret();
data += "&key=" + platform.getSignSecret();
String md5 = PayKit.md5(data.toUpperCase());
String sign = StrUtil.toString(params.get(FIELD_SIGN));
// 签名验证

View File

@@ -1,16 +1,23 @@
package cn.bootx.platform.daxpay.core.payment.pay.service;
import cn.bootx.platform.common.core.code.CommonCode;
import cn.bootx.platform.daxpay.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.common.context.RequestLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.system.entity.PlatformConfig;
import cn.bootx.platform.daxpay.core.system.service.PlatformConfigService;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.util.PayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Objects;
/**
@@ -23,6 +30,32 @@ import java.util.Objects;
@RequiredArgsConstructor
public class PayAssistService {
private final PlatformConfigService platformConfigService;
/**
* 初始化平台配置上下文
*/
public void initPlatform(){
PlatformConfig config = platformConfigService.getConfig();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
platform.setSignType(config.getSignType());
platform.setSignSecret(config.getSignSecret());
platform.setNotifyUrl(config.getNotifyUrl());
platform.setOrderTimeout(config.getOrderTimeout());
platform.setWebsiteUrl(config.getWebsiteUrl());
}
/**
* 初始化支付相关上下文
*/
public void initPayContext(PayOrder order, PayParam payParam){
// 初始化支付订单超时时间
this.initExpiredTime(order,payParam);
// 初始化通知相关上下文
this.initNotice(payParam);
// 初始化请求相关上下文
this.initRequest(payParam);
}
/**
* 初始化支付订单超时时间
* 1. 如果支付记录为空, 超时时间读取顺序 PayParam -> 平台设置
@@ -34,6 +67,7 @@ public class PayAssistService {
return;
}
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
// 支付订单是非为空
if (Objects.nonNull(order)){
asyncPayInfo.setExpiredTime(order.getExpiredTime());
@@ -44,7 +78,41 @@ public class PayAssistService {
asyncPayInfo.setExpiredTime(payParam.getExpiredTime());
return;
}
PlatformConfig config = platformConfigService.getConfig();
PayUtil.getPaymentExpiredTime(config.getOrderTimeout());
LocalDateTime paymentExpiredTime = PayUtil.getPaymentExpiredTime(platform.getOrderTimeout());
asyncPayInfo.setExpiredTime(paymentExpiredTime);
}
/**
* 初始化通知相关上下文
*/
private void initNotice(PayParam payParam){
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
PlatformLocal platform = PaymentContextLocal.get().getPlatform();
// 异步回调
if (!payParam.isNotNotify()){
noticeInfo.setNotifyUrl(payParam.getReturnUrl());
if (StrUtil.isNotBlank(payParam.getNotifyUrl())){
noticeInfo.setNotifyUrl(platform.getNotifyUrl());
}
}
// 同步回调
if (!payParam.isNotReturn()){
noticeInfo.setReturnUrl(payParam.getReturnUrl());
}
// 退出回调地址
noticeInfo.setQuitUrl(payParam.getQuitUrl());
}
/**
* 初始化支付请求相关信息
*/
public void initRequest(PayParam payParam){
RequestLocal request = PaymentContextLocal.get().getRequest();
request.setClientIp(payParam.getClientIp())
.setExtraParam(payParam.getExtraParam())
.setSign(payParam.getSign())
.setVersion(payParam.getVersion())
.setReqTime(payParam.getReqTime())
.setReqId(MDC.get(CommonCode.TRACE_ID));
}
}

View File

@@ -1,16 +1,16 @@
package cn.bootx.platform.daxpay.core.payment.pay.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.order.pay.builder.PaymentBuilder;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.payment.pay.factory.PayStrategyFactory;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
@@ -18,8 +18,11 @@ import cn.bootx.platform.daxpay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.func.PayStrategyConsumer;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.param.pay.SimplePayParam;
import cn.bootx.platform.daxpay.result.pay.PayResult;
import cn.bootx.platform.daxpay.util.PayUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -43,7 +46,7 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class PayService {
private final PayOrderService payOrderService;
private final PayOrderManager payOrderManager;
private final PayAssistService payAssistService;;
@@ -51,41 +54,57 @@ public class PayService {
private final PayOrderChannelManager payOrderChannelManager;
/**
* 支付方法(同步/异步/组合支付) 同步支付:都只会在第一次执行中就完成支付,例如钱包、积分都是调用完就进行了扣减,完成了支付记录
* 异步支付:例如支付宝、微信,发起支付后还需要跳转第三方平台进行支付,支付后通常需要进行回调,之后才完成支付记录
* 组合支付:主要是混合了同步支付和异步支付,同时异步支付只能有一个,在支付时先对同步支付进行扣减,然后异步支付回调结束后完成整个支付单
* 组合支付在非第一次支付的时候只对新传入的异步支付PayMode进行处理PayMode的价格使用第一次发起的价格旧的同步支付如果传入后也不做处理
* Payment中PayModeList将会为 旧有的同步支付+新传入的异步支付方式(在具体支付实现中处理)
* 支付下单接口(同步/异步/组合支付)
* 1. 同步支付:都只会在第一次执行中就完成支付,例如钱包、储值卡都是调用完就进行了扣减,完成支付记录
* 2. 异步支付:例如支付宝、微信,发起支付后还需要跳转第三方平台进行支付,支付后通常需要进行回调,之后才完成支付记录
* 3. 组合支付:主要是混合了同步支付和异步支付,同时异步支付只能有一个,在支付时先对同步支付进行扣减,然后异步支付回调结束后完成整个支付单
* 注意:
* 组合支付在非第一次支付的时候,只对新传入的异步支付通道进行处理,该通道的价格使用第一次发起的价格,旧的同步支付如果传入后也不做处理,
* 支付单中支付通道列表将会为 旧有的同步支付+新传入的异步支付方式(在具体支付实现中处理)
*/
@Transactional(rollbackFor = Exception.class)
public PayResult pay(PayParam payParam) {
// 检验参数
ValidationUtil.validateParam(payParam);
// 异步支付方式检查
PayUtil.validationAsyncPayMode(payParam);
// 获取并校验支付状态
PayOrder payOrder = this.getAndCheckByBusinessId(payParam.getBusinessNo());
// 获取并校验支付订单状态
PayOrder payOrder = this.getAndCheckByBusinessNo(payParam.getBusinessNo());
// 初始化上下文
payAssistService.initExpiredTime(payOrder, payParam);
payAssistService.initPayContext(payOrder, payParam);
// 异步支付且非第一次支付
if (Objects.nonNull(payOrder) && payOrder.isAsyncPayMode()) {
return this.paySyncNotFirst(payParam, payOrder);
} else {
// 第一次发起支付或同步支付
return this.payFirst(payParam, payOrder);
return this.firstPay(payParam, payOrder);
}
}
/**
* 简单下单, 可以视为不支持组合支付的下单接口
*/
@Transactional(rollbackFor = Exception.class)
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());
BeanUtil.copyProperties(simplePayParam,payWayParam, CopyOptions.create().ignoreNullValue());
payParam.setPayWays(Collections.singletonList(payWayParam));
// 复用支付下单接口
return this.pay(payParam);
}
/**
* 发起的第一次支付请求(同步/异步)
*/
private PayResult payFirst(PayParam payParam, PayOrder payOrder) {
private PayResult firstPay(PayParam payParam, PayOrder payOrder) {
// 1. 已经发起过支付情况直接返回支付结果
if (Objects.nonNull(payOrder)) {
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
@@ -98,18 +117,16 @@ public class PayService {
payOrder = this.createPayOrder(payParam);
// 4. 调用支付方法进行发起支付
this.payFirstMethod(payParam, payOrder);
this.firstPayHandler(payParam, payOrder);
// 5. 返回支付结果
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
}
/**
* 执行支付方法 (第一次支付)
* @param payParam 支付参数
* @param payOrder 支付订单
* 执行第一次支付的方法
*/
private void payFirstMethod(PayParam payParam, PayOrder payOrder) {
private void firstPayHandler(PayParam payParam, PayOrder payOrder) {
// 1.获取支付方式,通过工厂生成对应的策略组
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.create(payParam.getPayWays());
@@ -135,7 +152,7 @@ public class PayService {
payOrderObj.setStatus(PayStatusEnum.SUCCESS.getCode());
payOrderObj.setPayTime(LocalDateTime.now());
}
payOrderService.updateById(payOrderObj);
payOrderManager.updateById(payOrderObj);
});
}
@@ -144,32 +161,35 @@ public class PayService {
*/
private PayResult paySyncNotFirst(PayParam payParam, PayOrder payOrder) {
// 0. 处理支付完成情况(完成/退款)
List<String> trades = Arrays.asList(PayStatusEnum.SUCCESS.getCode(), PayStatusEnum.CANCEL.getCode(),
// 1. 处理支付完成情况(完成/退款)
List<String> trades = Arrays.asList(PayStatusEnum.SUCCESS.getCode(), PayStatusEnum.CANCEL.getCode(),PayStatusEnum.CLOSE.getCode(),
PayStatusEnum.PARTIAL_REFUND.getCode(), PayStatusEnum.REFUNDED.getCode());
if (trades.contains(payOrder.getStatus())) {
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
}
// 1.获取 异步支付 通道道,通过工厂生成对应的策略组(只包含异步支付的策略, 同步支付不再进行执行)
// 2.获取 异步支付道,通过工厂生成对应的策略组(只包含异步支付的策略, 同步支付相关逻辑不再进行执行)
PayWayParam payWayParam = this.getAsyncPayParam(payParam, payOrder);
List<AbsPayStrategy> asyncStrategyList = PayStrategyFactory.create(Collections.singletonList(payWayParam));
// 2.初始化支付的参数
// 3.初始化支付的参数
for (AbsPayStrategy paymentStrategy : asyncStrategyList) {
paymentStrategy.initPayParam(payOrder, payParam);
}
// 3.支付前准备
// 4.支付前准备
this.doHandler(payOrder, asyncStrategyList, AbsPayStrategy::doBeforePayHandler, null);
// 4. 发起支付
// 5. 发起支付
this.doHandler(payOrder, asyncStrategyList, AbsPayStrategy::doPayHandler, (strategyList, paymentObj) -> {
// 发起支付成功进行的执行方法
strategyList.forEach(AbsPayStrategy::doSuccessHandler);
payOrderService.updateById(paymentObj);
payOrderManager.updateById(paymentObj);
});
// 5. 组装返回参数
// 6. 更新支付订单扩展参数
updatePayOrderExtra(payParam,payOrder.getId());
// 7. 组装返回参数
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
}
@@ -196,7 +216,7 @@ public class PayService {
private PayWayParam getAsyncPayParam(PayParam payParam, PayOrder payOrder) {
// 查询之前的支付方式
String asyncPayChannel = payOrder.getAsyncPayChannel();
PayOrderChannel payOrderChannel = payOrderChannelManager.findByOderIdAndChannel(payOrder.getId(), asyncPayChannel)
PayOrderChannel payOrderChannel = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), asyncPayChannel)
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
// 新的异步支付方式
@@ -218,7 +238,7 @@ public class PayService {
private PayOrder createPayOrder(PayParam payParam) {
// 构建支付订单并保存
PayOrder payOrder = PaymentBuilder.buildPayOrder(payParam);
payOrderService.saveOder(payOrder);
payOrderManager.save(payOrder);
// 构建支付订单扩展表并保存
PayOrderExtra payOrderExtra = PaymentBuilder.buildPayOrderExtra(payParam, payOrder.getId());
payOrderExtraManager.save(payOrderExtra);
@@ -231,12 +251,28 @@ public class PayService {
return payOrder;
}
/**
* 更新支付订单扩展参数
* @param payParam 支付参数
* @param paymentId 支付订单id
*/
private void updatePayOrderExtra(PayParam payParam,Long paymentId){
PayOrderExtra payOrderExtra = payOrderExtraManager.findById(paymentId)
.orElseThrow(() -> new DataNotExistException("支付订单不存在"));
payOrderExtra.setReqTime(payParam.getReqTime())
.setSign(payParam.getSign())
.setNotNotify(payParam.isNotNotify())
.setNotifyUrl(payParam.getNotifyUrl())
.setClientIp(payParam.getClientIp());
payOrderExtraManager.updateById(payOrderExtra);
}
/**
* 校验支付状态,支付成功则返回,支付失败则抛出对应的异常
*/
private PayOrder getAndCheckByBusinessId(String businessId) {
private PayOrder getAndCheckByBusinessNo(String businessNo) {
// 根据订单查询支付记录
PayOrder payment = payOrderService.findByBusinessId(businessId).orElse(null);
PayOrder payment = payOrderManager.findByBusinessNo(businessNo).orElse(null);
if (Objects.nonNull(payment)) {
// 支付失败类型状态
List<String> tradesStatus = Arrays.asList(PayStatusEnum.FAIL.getCode(), PayStatusEnum.CANCEL.getCode(),

View File

@@ -93,8 +93,7 @@ public class WeChatPayStrategy extends AbsPayStrategy {
*/
@Override
public void doPayHandler() {
weChatPayService.pay(this.getPayWayParam().getAmount(), this.getOrder(), this.weChatPayParam,
this.getPayWayParam(), this.weChatPayConfig);
weChatPayService.pay(this.getOrder(), this.weChatPayParam, this.getPayWayParam(), this.weChatPayConfig);
}
/**

View File

@@ -0,0 +1,114 @@
package cn.bootx.platform.daxpay.core.payment.refund.factory;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.payment.refund.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.core.payment.refund.strategy.*;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.val;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.bootx.platform.daxpay.code.PayChannelEnum.ASYNC_TYPE_CODE;
/**
* 退款策略工厂
* @author xxm
* @since 2023/7/4
*/
public class PayRefundStrategyFactory {
/**
* 根据传入的支付渠道创建策略
* @return 支付策略
*/
public static AbsPayRefundStrategy create(RefundChannelParam refundChannelParam) {
AbsPayRefundStrategy strategy = null;
PayChannelEnum channelEnum = PayChannelEnum.findByCode(refundChannelParam.getPayChannel());
switch (channelEnum) {
case ALI:
strategy = SpringUtil.getBean(AliPayRefundStrategy.class);
break;
case WECHAT:
strategy = SpringUtil.getBean(WeChatPayRefundStrategy.class);
break;
case UNION_PAY:
strategy = SpringUtil.getBean(UnionPayRefundStrategy.class);
break;
case CASH:
strategy = SpringUtil.getBean(CashPayRefundStrategy.class);
break;
case WALLET:
strategy = SpringUtil.getBean(WalletPayRefundStrategy.class);
break;
case VOUCHER:
strategy = SpringUtil.getBean(VoucherPayRefundStrategy.class);
break;
default:
throw new PayUnsupportedMethodException();
}
strategy.setChannelParam(refundChannelParam);
return strategy;
}
/**
* 根据传入的支付类型批量创建策略, 异步支付在后面
*/
public static List<AbsPayRefundStrategy> createDesc(List<RefundChannelParam> refundChannelParams) {
return create(refundChannelParams, true);
}
/**
* 根据传入的支付类型批量创建策略, 默认异步支付在前面
*/
public static List<AbsPayRefundStrategy> create(List<RefundChannelParam> refundChannelParams) {
return create(refundChannelParams, false);
}
/**
* 根据传入的支付类型批量创建策略
* @param refundChannelParams 支付类型
* @return 支付策略
*/
private static List<AbsPayRefundStrategy> create(List<RefundChannelParam> refundChannelParams, boolean description) {
if (CollectionUtil.isEmpty(refundChannelParams)) {
return Collections.emptyList();
}
List<AbsPayRefundStrategy> list = new ArrayList<>(refundChannelParams.size());
// 同步支付
val syncRefundModeParams = refundChannelParams.stream()
.filter(Objects::nonNull)
.filter(payModeParam -> !ASYNC_TYPE_CODE.contains(payModeParam.getPayChannel()))
.collect(Collectors.toList());
// 异步支付
val asyncRefundModeParams = refundChannelParams.stream()
.filter(Objects::nonNull)
.filter(payModeParam -> ASYNC_TYPE_CODE.contains(payModeParam.getPayChannel()))
.collect(Collectors.toList());
List<RefundChannelParam> sortList = new ArrayList<>(refundChannelParams.size());
// 异步在后面
if (description) {
sortList.addAll(syncRefundModeParams);
sortList.addAll(asyncRefundModeParams);
}
else {
sortList.addAll(asyncRefundModeParams);
sortList.addAll(syncRefundModeParams);
}
// 此处有一个根据Type的反转排序
sortList.stream().filter(Objects::nonNull).forEach(payMode -> list.add(create(payMode)));
return list;
}
}

View File

@@ -0,0 +1,61 @@
package cn.bootx.platform.daxpay.core.payment.refund.func;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import lombok.Getter;
import lombok.Setter;
/**
* 抽象支付退款策略基类
*
* @author xxm
* @since 2020/12/11
*/
@Getter
@Setter
public abstract class AbsPayRefundStrategy {
/** 支付对象 */
private PayOrder order = null;
/** 退款参数 */
private RefundParam refundParam = null;
/** 当前支付通道退款参数 退款参数中的与这个不一致, 以这个为准 */
private RefundChannelParam channelParam = null;
/**
* 策略标识
* @see PayChannelEnum
*/
public abstract PayChannelEnum getType();
/**
* 初始化支付的参数
*/
public void initPayParam(PayOrder payOrder, RefundParam refundParam) {
this.order = payOrder;
this.refundParam = refundParam;
}
/**
* 退款前对处理 包含必要的校验以及对Payment对象的创建和保存操作
*/
public void doBeforeRefundHandler() {
}
/**
* 退款操作
*/
public abstract void doRefundHandler();
/**
* 退款失败的处理方式
*/
public void doErrorHandler(ExceptionInfo exceptionInfo) {
}
}

View File

@@ -0,0 +1,17 @@
package cn.bootx.platform.daxpay.core.payment.refund.func;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import java.util.List;
/**
* 支付退款策略接口
* @author xxm
* @since 2023/7/5
*/
@FunctionalInterface
public interface PayRefundStrategyConsumer<T extends List<AbsPayRefundStrategy>, S extends PayOrder> {
void accept(T t, S s);
}

View File

@@ -1,5 +1,7 @@
package cn.bootx.platform.daxpay.core.payment.refund.service;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.result.pay.RefundResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -13,4 +15,14 @@ import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class PayRefundService {
/**
* 支付退款
*/
public RefundResult refund(RefundParam param){
return null;
}
}

View File

@@ -0,0 +1,62 @@
package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AlipayConfig;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayOrderService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayRefundService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AlipayConfigService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.payment.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 支付宝退款
* @author xxm
* @since 2023/7/4
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class AliPayRefundStrategy extends AbsPayRefundStrategy {
private final AlipayConfigService alipayConfigService;
private final AliPayOrderService aliPayOrderService;
private final AliPayRefundService aliRefundService;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.ALI;
}
private final PayOrderService payOrderService;
/**
* 退款前前操作
*/
@Override
public void doBeforeRefundHandler() {
AlipayConfig config = alipayConfigService.getConfig();
alipayConfigService.initConfig(config);
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
aliRefundService.refund(this.getOrder(), this.getChannelParam().getAmount());
aliPayOrderService.updatePayRefund(this.getOrder().getId(), this.getChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.ALI);
}
}

View File

@@ -0,0 +1,44 @@
package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.cash.service.CashService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.payment.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 现金支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class CashPayRefundStrategy extends AbsPayRefundStrategy {
private final CashService cashService;
private final PayOrderService paymentService;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.CASH;
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
cashService.refund(this.getOrder().getId(), this.getChannelParam().getAmount());
paymentService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.CASH);
}
}

View File

@@ -0,0 +1,37 @@
package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.payment.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 云闪付支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class UnionPayRefundStrategy extends AbsPayRefundStrategy {
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.UNION_PAY;
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
}
}

View File

@@ -0,0 +1,53 @@
package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherPayService;
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherPaymentService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.payment.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 储值卡支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class VoucherPayRefundStrategy extends AbsPayRefundStrategy {
private final VoucherPayService voucherPayService;
private final VoucherPaymentService voucherPaymentService;
private final PayOrderService payOrderService;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.VOUCHER;
}
/**
* 退款前对处理
*/
@Override
public void doBeforeRefundHandler() {
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
voucherPayService.refund(this.getOrder().getId(), this.getChannelParam().getAmount());
voucherPaymentService.updateRefund(this.getOrder().getId(), this.getChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.VOUCHER);
}
}

View File

@@ -0,0 +1,45 @@
package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletPayService;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletPaymentService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.payment.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 钱包支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class WalletPayRefundStrategy extends AbsPayRefundStrategy {
private final WalletPayService walletPayService;
private final WalletPaymentService walletPaymentService;
private final PayOrderService payOrderService;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.WALLET;
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
walletPayService.refund(this.getOrder().getId(), this.getChannelParam().getAmount());
walletPaymentService.updateRefund(this.getOrder().getId(), this.getChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.WALLET);
}
}

View File

@@ -0,0 +1,63 @@
package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayOrderService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WechatRefundService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.payment.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 微信支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class WeChatPayRefundStrategy extends AbsPayRefundStrategy {
private final WeChatPayConfigService weChatPayConfigService;
private final WechatRefundService wechatRefundService;
private final WeChatPayOrderService weChatPayOrderService;
private final PayOrderService payOrderService;
private WeChatPayConfig weChatPayConfig;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.WECHAT;
}
/**
* 退款前对处理, 初始化微信支付配置
*/
@Override
public void doBeforeRefundHandler() {
this.weChatPayConfig = weChatPayConfigService.getConfig();
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
wechatRefundService.refund(this.getOrder(), this.getChannelParam().getAmount(), this.weChatPayConfig);
weChatPayOrderService.updatePayRefund(this.getOrder().getId(), this.getChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.WECHAT);
}
}

View File

@@ -1,7 +1,8 @@
package cn.bootx.platform.daxpay.core.payment.sync.strategy;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AlipayConfigManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AlipayConfig;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AlipayConfigService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AlipaySyncService;
import cn.bootx.platform.daxpay.core.payment.sync.func.AbsPaySyncStrategy;
import cn.bootx.platform.daxpay.core.payment.sync.result.PaySyncResult;
@@ -21,7 +22,7 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class AliPaySyncStrategy extends AbsPaySyncStrategy {
private final AlipayConfigManager alipayConfigManager;
private final AlipayConfigService alipayConfigService;
private final AlipaySyncService alipaySyncService;
@@ -39,5 +40,7 @@ public class AliPaySyncStrategy extends AbsPaySyncStrategy {
*/
private void initAlipayConfig() {
// 检查并获取支付宝支付配置
AlipayConfig config = alipayConfigService.getConfig();
alipayConfigService.initConfig(config);
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.dto.channel.alipay;
import cn.bootx.platform.daxpay.dto.pay.order.BasePayOrderDto;
import cn.bootx.platform.daxpay.dto.order.pay.PayOrderDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -16,7 +16,7 @@ import java.io.Serializable;
@Data
@Accessors(chain = true)
@Schema(title = "支付宝支付记录")
public class AliPaymentDto extends BasePayOrderDto implements Serializable {
public class AliPaymentDto extends PayOrderDto implements Serializable {
private static final long serialVersionUID = 6883103229754466130L;

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.dto.channel.voucher;
import cn.bootx.platform.daxpay.dto.pay.order.BasePayOrderDto;
import cn.bootx.platform.daxpay.dto.order.pay.PayOrderDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -14,6 +14,6 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@Schema(title = "储值卡支付记录")
public class VoucherPayOrderDto extends BasePayOrderDto {
public class VoucherPayOrderDto extends PayOrderDto {
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.dto.channel.wallet;
import cn.bootx.platform.daxpay.dto.pay.order.BasePayOrderDto;
import cn.bootx.platform.daxpay.dto.order.pay.PayOrderDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -16,7 +16,7 @@ import java.io.Serializable;
@Data
@Accessors(chain = true)
@Schema(title = "钱包支付记录")
public class WalletPayOrderDto extends BasePayOrderDto implements Serializable {
public class WalletPayOrderDto extends PayOrderDto implements Serializable {
private static final long serialVersionUID = 8238920331255597223L;

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.dto.channel.wechat;
import cn.bootx.platform.daxpay.dto.pay.order.BasePayOrderDto;
import cn.bootx.platform.daxpay.dto.order.pay.PayOrderDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -16,7 +16,7 @@ import java.io.Serializable;
@Data
@Accessors(chain = true)
@Schema(title = "微信支付记录")
public class WeChatPayOrderDto extends BasePayOrderDto implements Serializable {
public class WeChatPayOrderDto extends PayOrderDto implements Serializable {
private static final long serialVersionUID = -2400358210732595795L;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.dto.pay.order;
package cn.bootx.platform.daxpay.dto.order.pay;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
@@ -18,7 +18,7 @@ import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
@Schema(title = "具体支付日志基类")
public class BasePayOrderDto extends BaseDto {
public class PayOrderDto extends BaseDto {
@Schema(description = "支付id")
private Long paymentId;

View File

@@ -0,0 +1,65 @@
package cn.bootx.platform.daxpay.dto.order.refund;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 退款记录
*
* @author xxm
* @since 2022/3/2
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "退款记录")
public class PayRefundOrderDto extends BaseDto {
@Schema(description = "关联的业务id")
private String businessNo;
@Schema(description = "付款付单号")
private Long paymentId;
@Schema(description = "异步方式关联退款请求号(部分退款情况)")
private String refundRequestNo;
@Schema(description = "标题")
private String title;
@Schema(description = "退款金额")
private BigDecimal amount;
@Schema(description = "剩余可退")
private BigDecimal refundableBalance;
@Schema(description = "退款终端ip")
private String clientIp;
@Schema(description = "退款时间")
private LocalDateTime refundTime;
@Schema(description = "退款信息列表")
private List<OrderRefundableInfo> refundableInfo;
/**
* @see PayStatusCode#REFUND_PROCESS_FAIL
*/
@Schema(description = "退款状态")
private String refundStatus;
@Schema(description = "错误码")
private String errorCode;
@Schema(description = "错误信息")
private String errorMsg;
}

View File

@@ -104,7 +104,7 @@ public abstract class AbsPayCallbackStrategy {
.setNotifyTime(LocalDateTime.now())
.setPaymentId(this.getPaymentId())
.setPayChannel(this.getPayChannel().getCode())
.setStatus(result.getCode())
.setPayStatus(result.getStatus())
.setMsg(result.getMsg());
callbackNotifyManager.save(payNotifyRecord);
}

View File

@@ -20,9 +20,6 @@ import java.util.List;
@Schema(title = "微信支付配置参数")
public class WeChatPayConfigParam {
@Schema(description = "主键")
private Long id;
@Schema(description = "名称")
private String name;

View File

@@ -1 +1 @@
cn.bootx.platform.daxpay.DaxPaySingleGatewayApp
cn.bootx.platform.daxpay.DaxpaySingleServiceApp

View File

@@ -9,6 +9,7 @@ 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;
@@ -30,8 +31,9 @@ class PaymentSignServiceTest {
payParam.setNotReturn(true);
payParam.setNotifyUrl("http://127.0.0.1:8080/pay/notify");
payParam.setReturnUrl("http://127.0.0.1:8080/pay/return");
payParam.setSignType("MD5");
payParam.setVersion("1.0");
// 传入的话需要传输时间戳
payParam.setReqTime(LocalDateTime.now());
PayWayParam p1 = new PayWayParam();
p1.setAmount(100);

View File

@@ -1 +1 @@
cn.bootx.platform.daxpay.DaxPaySingleGatewayApp
cn.bootx.platform.daxpay.DaxPaySingleAdminApp