mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-05 20:18:22 +00:00
feat 支付退款改为二阶段提交, 支付宝支付查询
This commit is contained in:
@@ -89,10 +89,14 @@
|
||||
- 2024-01-28:
|
||||
- [x] 支付宝对账单下载异常排查-支付宝每日都会生成对账单, 哪怕为空, 也会生成
|
||||
- [x] 订单修复记录前端显示调整
|
||||
- 2044-01-29:
|
||||
- 2044-01-30:
|
||||
- [x] 退款接口更改为先落库, 后更新
|
||||
- [ ] 增加退款同步策略, 对退款中的状态的退款订单进行处理
|
||||
- [ ] 退款操作支持重试
|
||||
- [ ] 支付通道对出现疑似退款的订单进行**报错提醒**, 通过退款同步进行补偿
|
||||
- **任务池**
|
||||
2.0.1 版本内容
|
||||
- [ ] 支付流程也改为先落库后支付情况, 避免极端情况导致掉单
|
||||
**任务池**
|
||||
- [ ] 微信退款状态不一致补偿
|
||||
- [ ] 支付SDK编写
|
||||
- [ ] 接入支付网关的演示项目
|
||||
|
@@ -6,6 +6,7 @@ import cn.bootx.platform.common.core.rest.ResResult;
|
||||
import cn.bootx.platform.common.core.rest.param.PageParam;
|
||||
import cn.bootx.platform.common.spring.util.WebServletUtil;
|
||||
import cn.bootx.platform.daxpay.param.pay.RefundParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.service.PayRefundOrderQueryService;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.service.PayRefundOrderService;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.refund.service.PayRefundService;
|
||||
@@ -83,8 +84,7 @@ public class PayRefundOrderController {
|
||||
|
||||
@Operation(summary = "退款同步")
|
||||
@PostMapping("/syncById")
|
||||
public ResResult<Void> syncById(Long ID){
|
||||
payRefundOrderService.syncById(ID);
|
||||
return Res.ok();
|
||||
public ResResult<PaySyncResult> syncById(Long id){
|
||||
return Res.ok(payRefundOrderService.syncById(id));
|
||||
}
|
||||
}
|
||||
|
@@ -20,9 +20,8 @@ public enum PayRefundStatusEnum {
|
||||
* 接口调用成功不代表成功
|
||||
*/
|
||||
PROGRESS("progress","退款中"),
|
||||
/** 部分成功 */
|
||||
PART_SUCCESS("part_success","部分成功"),
|
||||
SUCCESS("success","成功"),
|
||||
CLOSE("close","关闭"),
|
||||
FAIL("fail","失败");
|
||||
|
||||
/** 编码 */
|
||||
|
@@ -33,7 +33,7 @@ public class PaySyncResult extends CommonResult {
|
||||
@Schema(description = "是否进行了修复")
|
||||
private boolean repair;
|
||||
|
||||
@Schema(description = "支付单修复ID")
|
||||
@Schema(description = "修复ID")
|
||||
private Long repairId;
|
||||
|
||||
@Schema(description = "失败原因")
|
||||
|
@@ -13,4 +13,15 @@ import lombok.experimental.Accessors;
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "退款同步结果")
|
||||
public class RefundSyncResult {
|
||||
|
||||
|
||||
|
||||
@Schema(description = "是否进行了修复")
|
||||
private boolean repair;
|
||||
|
||||
@Schema(description = "支付单修复ID")
|
||||
private Long repairId;
|
||||
|
||||
@Schema(description = "失败原因")
|
||||
private String errorMsg;
|
||||
}
|
||||
|
@@ -1,20 +0,0 @@
|
||||
package cn.bootx.platform.daxpay.service.code;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 回调类型枚举
|
||||
* @author xxm
|
||||
* @since 2024/1/24
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PayCallbackTypeEnum {
|
||||
|
||||
PAY("pay", "支付回调"),
|
||||
REFUND("refund", "退款回调");
|
||||
|
||||
public final String code;
|
||||
public final String name;
|
||||
}
|
@@ -4,16 +4,17 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 支付修复时的支付/退款类型
|
||||
* 支付系统中常见的操作类型, 如支付/退款/转账等
|
||||
* @author xxm
|
||||
* @since 2024/1/28
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PayRepairPayTypeEnum {
|
||||
public enum PaymentTypeEnum {
|
||||
|
||||
PAY("pay","支付"),
|
||||
REFUND("refund","退款");
|
||||
REFUND("refund","退款"),
|
||||
TRANSFER("transfer","转账");
|
||||
|
||||
private final String code;
|
||||
private final String name;
|
@@ -1,7 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.service.common.context;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@@ -19,11 +18,6 @@ public class RefundLocal {
|
||||
*/
|
||||
private String gatewayOrderNo;
|
||||
|
||||
/**
|
||||
* 支付退款ID, 用于异步支付时传入的退款号, 使用退款单ID
|
||||
*/
|
||||
private long refundId = IdUtil.getSnowflakeNextId();
|
||||
|
||||
/**
|
||||
* 退款状态, 默认为成功, 通常含有异步支付时, 才会出现别的状态
|
||||
*/
|
||||
|
@@ -4,7 +4,7 @@ import cn.bootx.platform.common.core.util.CertUtil;
|
||||
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayCallbackTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
|
||||
@@ -137,18 +137,18 @@ public class AliPayCallbackService extends AbsCallbackStrategy {
|
||||
/**
|
||||
* 判断类型 支付回调/退款回调
|
||||
*
|
||||
* @see PayCallbackTypeEnum
|
||||
* @see PaymentTypeEnum
|
||||
*/
|
||||
@Override
|
||||
public PayCallbackTypeEnum getCallbackType() {
|
||||
public PaymentTypeEnum getCallbackType() {
|
||||
CallbackLocal callback = PaymentContextLocal.get().getCallbackInfo();
|
||||
Map<String, String> callbackParam = callback.getCallbackParam();
|
||||
String refundFee = callbackParam.get(REFUND_FEE);
|
||||
// 如果有退款金额,说明是退款回调
|
||||
if (StrUtil.isNotBlank(refundFee)){
|
||||
return PayCallbackTypeEnum.REFUND;
|
||||
return PaymentTypeEnum.REFUND;
|
||||
} else {
|
||||
return PayCallbackTypeEnum.PAY;
|
||||
return PaymentTypeEnum.PAY;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.service.code.AliPayCode;
|
||||
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.domain.AlipayTradeRefundModel;
|
||||
import com.alipay.api.response.AlipayTradeRefundResponse;
|
||||
@@ -29,16 +29,16 @@ public class AliPayRefundService {
|
||||
/**
|
||||
* 退款, 调用支付宝退款
|
||||
*/
|
||||
public void refund(PayOrder payOrder, int amount) {
|
||||
public void refund(PayRefundOrder refundOrder, int amount) {
|
||||
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
|
||||
AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
|
||||
refundModel.setOutTradeNo(String.valueOf(payOrder.getId()));
|
||||
refundModel.setOutTradeNo(String.valueOf(refundOrder.getPaymentId()));
|
||||
refundModel.setOutRequestNo(String.valueOf(refundOrder.getId()));
|
||||
// 金额转换
|
||||
String refundAmount = String.valueOf(amount*0.01);
|
||||
refundModel.setRefundAmount(refundAmount);
|
||||
|
||||
// 设置退款信息
|
||||
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
|
||||
refundModel.setOutRequestNo(String.valueOf(refundInfo.getRefundId()));
|
||||
try {
|
||||
AlipayTradeRefundResponse response = AliPayApi.tradeRefundToResponse(refundModel);
|
||||
if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) {
|
||||
@@ -49,9 +49,12 @@ public class AliPayRefundService {
|
||||
}
|
||||
// 接口返回fund_change=Y为退款成功,fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
|
||||
if (response.getFundChange().equals("Y")){
|
||||
refundInfo.setStatus(PayRefundStatusEnum.SUCCESS)
|
||||
.setGatewayOrderNo(response.getTradeNo());
|
||||
// TODO 测试退款同步
|
||||
// refundInfo.setStatus(PayRefundStatusEnum.SUCCESS)
|
||||
// .setGatewayOrderNo(response.getTradeNo());
|
||||
}
|
||||
refundInfo.setStatus(PayRefundStatusEnum.PROGRESS)
|
||||
.setGatewayOrderNo(response.getTradeNo());
|
||||
}
|
||||
catch (AlipayApiException e) {
|
||||
log.error("订单退款失败:", e);
|
||||
|
@@ -51,6 +51,13 @@ public class AliPaySyncService {
|
||||
AlipayTradeQueryResponse response = AliPayApi.tradeQueryToResponse(queryModel);
|
||||
String tradeStatus = response.getTradeStatus();
|
||||
syncResult.setSyncInfo(JSONUtil.toJsonStr(response));
|
||||
// 失败
|
||||
if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) {
|
||||
syncResult.setSyncStatus(PaySyncStatusEnum.FAIL);
|
||||
syncResult.setErrorCode(response.getSubCode());
|
||||
syncResult.setErrorMsg(response.getSubMsg());
|
||||
return syncResult;
|
||||
}
|
||||
// 支付完成 TODO 部分退款也在这个地方, 但无法进行区分, 需要借助对账进行处理
|
||||
if (Objects.equals(tradeStatus, AliPayCode.NOTIFY_TRADE_SUCCESS) || Objects.equals(tradeStatus, AliPayCode.NOTIFY_TRADE_FINISHED)) {
|
||||
PaymentContextLocal.get().getPaySyncInfo().setGatewayOrderNo(response.getTradeNo());
|
||||
@@ -92,14 +99,25 @@ public class AliPaySyncService {
|
||||
RefundGatewaySyncResult syncResult = new RefundGatewaySyncResult().setSyncStatus(PayRefundSyncStatusEnum.FAIL);
|
||||
try {
|
||||
AlipayTradeFastpayRefundQueryModel queryModel = new AlipayTradeFastpayRefundQueryModel();
|
||||
queryModel.setOutTradeNo(String.valueOf(refundOrder.getId()));
|
||||
// 退款请求号
|
||||
queryModel.setOutRequestNo(String.valueOf(refundOrder.getId()));
|
||||
// 商户订单号
|
||||
queryModel.setOutTradeNo(String.valueOf(refundOrder.getPaymentId()));
|
||||
AlipayTradeFastpayRefundQueryResponse response = AliPayApi.tradeRefundQueryToResponse(queryModel);
|
||||
|
||||
syncResult.setSyncInfo(JSONUtil.toJsonStr(response));
|
||||
// 失败
|
||||
if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) {
|
||||
syncResult.setSyncStatus(PayRefundSyncStatusEnum.FAIL);
|
||||
syncResult.setErrorCode(response.getSubCode());
|
||||
syncResult.setErrorMsg(response.getSubMsg());
|
||||
return syncResult;
|
||||
}
|
||||
String tradeStatus = response.getRefundStatus();
|
||||
// 成功
|
||||
if (Objects.equals(tradeStatus, AliPayCode.NOTIFY_TRADE_SUCCESS)){
|
||||
if (Objects.equals(tradeStatus, AliPayCode.REFUND_SUCCESS)){
|
||||
return syncResult.setSyncStatus(PayRefundSyncStatusEnum.SUCCESS);
|
||||
} else {
|
||||
return syncResult.setSyncStatus(PayRefundSyncStatusEnum.FAIL).setErrorMsg("支付宝网关反正退款未成功");
|
||||
}
|
||||
} catch (AlipayApiException e) {
|
||||
log.error("退款订单同步失败:", e);
|
||||
|
@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
|
||||
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayCallbackTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
|
||||
@@ -62,8 +62,8 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy {
|
||||
}
|
||||
|
||||
// 退款回调不用进行校验
|
||||
PayCallbackTypeEnum callbackType = this.getCallbackType();
|
||||
if (callbackType == PayCallbackTypeEnum.REFUND){
|
||||
PaymentTypeEnum callbackType = this.getCallbackType();
|
||||
if (callbackType == PaymentTypeEnum.REFUND){
|
||||
return true;
|
||||
}
|
||||
// 支付回调信息校验
|
||||
@@ -141,16 +141,16 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy {
|
||||
/**
|
||||
* 判断类型 支付回调/退款回调
|
||||
*
|
||||
* @see PayCallbackTypeEnum
|
||||
* @see PaymentTypeEnum
|
||||
*/
|
||||
@Override
|
||||
public PayCallbackTypeEnum getCallbackType() {
|
||||
public PaymentTypeEnum getCallbackType() {
|
||||
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
|
||||
Map<String, String> callbackParam = callbackInfo.getCallbackParam();
|
||||
if (StrUtil.isNotBlank(callbackParam.get(REQ_INFO))){
|
||||
return PayCallbackTypeEnum.REFUND;
|
||||
return PaymentTypeEnum.REFUND;
|
||||
} else {
|
||||
return PayCallbackTypeEnum.PAY;
|
||||
return PaymentTypeEnum.PAY;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,8 +9,9 @@ import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundChannelOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
|
||||
@@ -20,7 +21,7 @@ import com.ijpay.core.enums.SignType;
|
||||
import com.ijpay.core.kit.WxPayKit;
|
||||
import com.ijpay.wxpay.WxPayApi;
|
||||
import com.ijpay.wxpay.model.OrderQueryModel;
|
||||
import com.ijpay.wxpay.model.UnifiedOrderModel;
|
||||
import com.ijpay.wxpay.model.RefundQueryModel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -40,6 +41,7 @@ import java.util.Objects;
|
||||
@RequiredArgsConstructor
|
||||
public class WeChatPaySyncService {
|
||||
private final PayChannelOrderManager payChannelOrderManager;
|
||||
private final PayRefundChannelOrderManager refundChannelOrderManager;
|
||||
|
||||
/**
|
||||
* 同步查询
|
||||
@@ -103,23 +105,25 @@ public class WeChatPaySyncService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款查询
|
||||
* 退款信息查询
|
||||
*/
|
||||
public RefundGatewaySyncResult syncRefundStatus(PayRefundOrder refundOrder, WeChatPayConfig weChatPayConfig){
|
||||
PayChannelOrder orderChannel = payChannelOrderManager.findByPaymentIdAndChannel(refundOrder.getId(), PayChannelEnum.WECHAT.getCode())
|
||||
PayRefundChannelOrder orderChannel = refundChannelOrderManager.findByRefundIdAndChannel(refundOrder.getId(), PayChannelEnum.WECHAT.getCode())
|
||||
.orElseThrow(() -> new PayFailureException("支付订单通道信息不存在"));
|
||||
|
||||
Map<String, String> params = UnifiedOrderModel.builder()
|
||||
Map<String, String> params = RefundQueryModel.builder()
|
||||
.appid(weChatPayConfig.getWxAppId())
|
||||
.mch_id(weChatPayConfig.getWxMchId())
|
||||
.nonce_str(WxPayKit.generateStr())
|
||||
.out_trade_no(String.valueOf(refundOrder.getId()))
|
||||
.out_refund_no(String.valueOf(refundOrder.getId()))
|
||||
.build()
|
||||
.createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256);
|
||||
String xmlResult = WxPayApi.orderRefundQuery(false, params);
|
||||
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
|
||||
// TODO 处理退款同步的情况
|
||||
|
||||
|
||||
|
||||
Integer refundFee = Integer.valueOf(result.get(WeChatPayCode.REFUND_FEE));
|
||||
if (Objects.equals(refundFee, orderChannel.getAmount())){
|
||||
return new RefundGatewaySyncResult().setSyncStatus(PayRefundSyncStatusEnum.REFUNDING);
|
||||
|
@@ -8,7 +8,7 @@ import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ijpay.core.enums.SignType;
|
||||
@@ -39,7 +39,7 @@ public class WechatRefundService {
|
||||
* 退款方法
|
||||
* 微信需要同时传输订单金额或退款金额
|
||||
*/
|
||||
public void refund(PayOrder payOrder, int amount, PayChannelOrder orderChannel, WeChatPayConfig weChatPayConfig) {
|
||||
public void refund(PayRefundOrder refundOrder, int amount, PayChannelOrder orderChannel, WeChatPayConfig weChatPayConfig) {
|
||||
String refundFee = String.valueOf(amount);
|
||||
String totalFee = String.valueOf(orderChannel.getAmount());
|
||||
// 设置退款信息
|
||||
@@ -48,8 +48,8 @@ public class WechatRefundService {
|
||||
.appid(weChatPayConfig.getWxAppId())
|
||||
.mch_id(weChatPayConfig.getWxMchId())
|
||||
.notify_url(weChatPayConfig.getNotifyUrl())
|
||||
.out_trade_no(String.valueOf(payOrder.getId()))
|
||||
.out_refund_no(String.valueOf(refundInfo.getRefundId()))
|
||||
.out_trade_no(String.valueOf(refundOrder.getPaymentId()))
|
||||
.out_refund_no(String.valueOf(refundOrder.getId()))
|
||||
.total_fee(totalFee)
|
||||
.refund_fee(refundFee)
|
||||
.nonce_str(WxPayKit.generateStr())
|
||||
|
@@ -87,7 +87,7 @@ public class PayChannelOrderService {
|
||||
* 更新异步支付通道退款余额和状态
|
||||
*/
|
||||
public void updateAsyncPayRefund(PayChannelOrder payChannelOrder, PayRefundChannelOrder refundChannelOrder){
|
||||
// 支付通道订单客可退余额
|
||||
// 支付通道订单可退余额
|
||||
int refundableBalance = payChannelOrder.getRefundableBalance() - refundChannelOrder.getAmount();
|
||||
payChannelOrder.setRefundableBalance(refundableBalance);
|
||||
// 支付通道订单状态
|
||||
|
@@ -31,7 +31,7 @@ public class PayReconcileOrderService {
|
||||
/**
|
||||
* 更新, 开启一个新事务进行更新
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void update(PayReconcileOrder order){
|
||||
reconcileOrderManager.updateById(order);
|
||||
}
|
||||
|
@@ -29,9 +29,9 @@ public class PayRefundChannelOrderManager extends BaseManager<PayRefundChannelOr
|
||||
/**
|
||||
* 根据退款单ID和退款通道查询
|
||||
*/
|
||||
public Optional<PayRefundChannelOrder> findByPaymentIdAndChannel(Long paymentId, String channel) {
|
||||
public Optional<PayRefundChannelOrder> findByRefundIdAndChannel(Long refundId, String channel) {
|
||||
return lambdaQuery()
|
||||
.eq(PayRefundChannelOrder::getRefundId,paymentId)
|
||||
.eq(PayRefundChannelOrder::getRefundId,refundId)
|
||||
.eq(PayRefundChannelOrder::getChannel,channel)
|
||||
.oneOpt();
|
||||
}
|
||||
|
@@ -8,6 +8,8 @@ import cn.bootx.platform.daxpay.service.core.order.refund.convert.PayRefundOrder
|
||||
import cn.bootx.platform.daxpay.service.dto.order.refund.PayRefundOrderDto;
|
||||
import cn.bootx.table.modify.annotation.DbColumn;
|
||||
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 lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -17,6 +19,7 @@ import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 退款记录
|
||||
* 主键作为退款的请求号
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2022/3/2
|
||||
@@ -71,6 +74,7 @@ public class PayRefundOrder extends MpBaseEntity implements EntityBaseFunction<P
|
||||
|
||||
/** 剩余可退 */
|
||||
@DbColumn(comment = "剩余可退")
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private Integer refundableBalance;
|
||||
|
||||
/** 请求链路ID */
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package cn.bootx.platform.daxpay.service.core.order.refund.service;
|
||||
|
||||
import cn.bootx.platform.common.core.exception.DataNotExistException;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PayRefundSyncService;
|
||||
@@ -24,10 +25,10 @@ public class PayRefundOrderService {
|
||||
/**
|
||||
* 退款同步
|
||||
*/
|
||||
public void syncById(Long id){
|
||||
public PaySyncResult syncById(Long id){
|
||||
PayRefundOrder refundOrder = refundOrderManager.findById(id)
|
||||
.orElseThrow(() -> new DataNotExistException("退款订单不存在"));
|
||||
refundSyncService.syncPayOrder(refundOrder);
|
||||
return refundSyncService.syncRefundOrder(refundOrder);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundChannelOr
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -31,6 +32,8 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.bootx.platform.daxpay.code.PayRefundStatusEnum.SUCCESS;
|
||||
|
||||
/**
|
||||
* 支付退款支撑服务
|
||||
* @author xxm
|
||||
@@ -109,10 +112,11 @@ public class PayRefundAssistService {
|
||||
List<String> tradesStatus = Arrays.asList(
|
||||
PayStatusEnum.PROGRESS.getCode(),
|
||||
PayStatusEnum.CLOSE.getCode(),
|
||||
PayStatusEnum.REFUNDING.getCode(),
|
||||
PayStatusEnum.FAIL.getCode());
|
||||
if (tradesStatus.contains(payOrder.getStatus())) {
|
||||
PayStatusEnum statusEnum = PayStatusEnum.findByCode(payOrder.getStatus());
|
||||
throw new PayFailureException("当前状态["+statusEnum.getName()+"]不允许状态非法, 无法退款");
|
||||
throw new PayFailureException("当前状态["+statusEnum.getName()+"]不允许发起退款操作");
|
||||
}
|
||||
|
||||
// 过滤掉金额为0的退款参数
|
||||
@@ -130,36 +134,29 @@ public class PayRefundAssistService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存退款订单 成不成功都记录
|
||||
* 预先创建退款相关订单并保存, 使用新事务, 防止丢单
|
||||
*/
|
||||
public PayRefundOrder generateRefundOrder(RefundParam refundParam, PayOrder payOrder){
|
||||
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PayRefundOrder createOrderAndChannel(RefundParam refundParam, PayOrder payOrder, List<PayRefundChannelOrder> refundChannelOrders) {
|
||||
// 此次的总退款金额
|
||||
Integer amount = refundParam.getRefundChannels()
|
||||
.stream()
|
||||
.map(RefundChannelParam::getAmount)
|
||||
.reduce(0, Integer::sum);
|
||||
int refundableBalance = payOrder.getRefundableBalance();
|
||||
|
||||
// 生成退款订单
|
||||
PayRefundOrder refundOrder = new PayRefundOrder()
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setStatus(PayRefundStatusEnum.PROGRESS.getCode())
|
||||
.setBusinessNo(payOrder.getBusinessNo())
|
||||
.setRefundNo(refundParam.getRefundNo())
|
||||
.setOrderAmount(payOrder.getAmount())
|
||||
.setAmount(amount)
|
||||
.setRefundableBalance(payOrder.getRefundableBalance())
|
||||
.setRefundTime(LocalDateTime.now())
|
||||
.setRefundableBalance(refundableBalance)
|
||||
.setTitle(payOrder.getTitle())
|
||||
.setGatewayOrderNo(asyncRefundInfo.getGatewayOrderNo())
|
||||
.setStatus(asyncRefundInfo.getStatus().getCode())
|
||||
.setClientIp(refundParam.getClientIp())
|
||||
.setReqId(PaymentContextLocal.get().getRequestInfo().getReqId());
|
||||
// 错误状态特殊处理
|
||||
if (asyncRefundInfo.getStatus() == PayRefundStatusEnum.FAIL){
|
||||
refundOrder.setErrorCode(asyncRefundInfo.getErrorCode());
|
||||
refundOrder.setErrorMsg(asyncRefundInfo.getErrorMsg());
|
||||
// 退款失败不保存剩余可退余额, 否则数据看起开会产生困惑
|
||||
refundOrder.setRefundableBalance(null);
|
||||
}
|
||||
|
||||
// 退款参数中是否存在异步通道
|
||||
RefundChannelParam asyncChannel = refundParam.getRefundChannels()
|
||||
@@ -173,33 +170,43 @@ public class PayRefundAssistService {
|
||||
}
|
||||
|
||||
// 主键使用预先生成的ID, 如果有异步通道, 关联的退款号就是这个ID
|
||||
long refundId = asyncRefundInfo.getRefundId();
|
||||
refundOrder.setId(refundId);
|
||||
refundOrder.setId(IdUtil.getSnowflakeNextId());
|
||||
|
||||
// 退款号, 如不传输, 使用ID作为退款号
|
||||
if(StrUtil.isBlank(refundOrder.getRefundNo())){
|
||||
refundOrder.setRefundNo(String.valueOf(refundId));
|
||||
refundOrder.setRefundNo(String.valueOf(refundOrder.getId()));
|
||||
}
|
||||
return refundOrder;
|
||||
refundChannelOrders.forEach(r->r.setRefundId(refundOrder.getId()));
|
||||
payRefundChannelOrderManager.saveAll(refundChannelOrders);
|
||||
return payRefundOrderManager.save(refundOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存退款记录和对应的通道记录
|
||||
* 更新退款成功信息
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveOrderAndChannels(PayRefundOrder refundOrder,List<PayRefundChannelOrder> refundChannelOrders){
|
||||
payRefundOrderManager.save(refundOrder);
|
||||
for (PayRefundChannelOrder refundOrderChannel : refundChannelOrders) {
|
||||
refundOrderChannel.setRefundId(refundOrder.getId());
|
||||
public void updateOrderAndChannel(PayRefundOrder refundOrder, List<PayRefundChannelOrder> refundChannelOrders){
|
||||
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
|
||||
refundOrder.setStatus(asyncRefundInfo.getStatus().getCode())
|
||||
.setGatewayOrderNo(asyncRefundInfo.getGatewayOrderNo());
|
||||
// 退款成功更新退款时间
|
||||
if (Objects.equals(refundOrder.getStatus(), SUCCESS.getCode())){
|
||||
refundOrder.setRefundTime(LocalDateTime.now());
|
||||
}
|
||||
payRefundChannelOrderManager.saveAll(refundChannelOrders);
|
||||
payRefundOrderManager.updateById(refundOrder);
|
||||
payRefundChannelOrderManager.updateAllById(refundChannelOrders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存退款记录, 开启新事物
|
||||
* 更新退款错误信息
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void saveOrder(PayRefundOrder refundOrder){
|
||||
payRefundOrderManager.save(refundOrder);
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void updateOrderByError(PayRefundOrder refundOrder){
|
||||
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
|
||||
refundOrder.setErrorCode(asyncRefundInfo.getErrorCode());
|
||||
refundOrder.setErrorMsg(asyncRefundInfo.getErrorMsg());
|
||||
// 退款失败不保存剩余可退余额, 否则数据看起开会产生困惑
|
||||
refundOrder.setRefundableBalance(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -23,11 +23,13 @@ import cn.bootx.platform.daxpay.service.core.payment.refund.factory.PayRefundStr
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundStrategy;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -126,78 +128,99 @@ public class PayRefundService {
|
||||
|
||||
/**
|
||||
* 分支付通道进行退款
|
||||
* TODO 增加错误处理, 目前出现错误后存储的数据不全
|
||||
* 1. 创建退款订单和通道订单并保存(单独事务)
|
||||
* 2. 调用API发起退款(异步退款)
|
||||
* 3. 根据API返回信息更新退款订单信息
|
||||
*/
|
||||
public RefundResult refundByChannel(RefundParam refundParam, PayOrder payOrder, List<PayChannelOrder> payChannelOrders){
|
||||
// 0.基础数据准备, 并比对通道支付单是否与可退款记录数量一致
|
||||
// 1.1 基础数据准备
|
||||
Map<String, PayChannelOrder> orderChannelMap = payChannelOrders.stream()
|
||||
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
|
||||
List<RefundChannelParam> refundChannels = refundParam.getRefundChannels();
|
||||
|
||||
// 1.2获取退款参数方式,通过工厂生成对应的策略组
|
||||
List<AbsRefundStrategy> payRefundStrategies = PayRefundStrategyFactory.createAsyncLast(refundChannels);
|
||||
if (CollectionUtil.isEmpty(payRefundStrategies)) {
|
||||
throw new PayUnsupportedMethodException();
|
||||
}
|
||||
// 1.3初始化退款策略的参数
|
||||
for (AbsRefundStrategy refundStrategy : payRefundStrategies) {
|
||||
PayChannelOrder payChannelOrder = orderChannelMap.get(refundStrategy.getChannel().getCode());
|
||||
if (Objects.isNull(payChannelOrder)){
|
||||
throw new PayFailureException("[数据异常]进行退款的通道没有对应的支付单, 无法退款");
|
||||
}
|
||||
refundStrategy.initRefundParam(payOrder, refundParam, payChannelOrder);
|
||||
}
|
||||
|
||||
// 对通道支付订单进行预扣款
|
||||
payRefundStrategies.forEach(AbsRefundStrategy::doPreDeductOrderHandler);
|
||||
|
||||
// 退款操作的预处理, 使用独立的新事物进行发起, 返回创建成功的退款订单, 成功后才可以进行下一阶段的操作
|
||||
PayRefundOrder refundOrder = SpringUtil.getBean(this.getClass()).preRefundMethod(refundParam, payOrder, payRefundStrategies);
|
||||
|
||||
// 设置退款订单对象
|
||||
payRefundStrategies.forEach(r->r.setRefundOrder(refundOrder));
|
||||
|
||||
try {
|
||||
// 1.获取退款参数方式,通过工厂生成对应的策略组
|
||||
List<AbsRefundStrategy> payRefundStrategies = PayRefundStrategyFactory.createAsyncLast(refundChannels);
|
||||
if (CollectionUtil.isEmpty(payRefundStrategies)) {
|
||||
throw new PayUnsupportedMethodException();
|
||||
}
|
||||
|
||||
// 2.初始化退款策略的参数
|
||||
for (AbsRefundStrategy refundStrategy : payRefundStrategies) {
|
||||
PayChannelOrder payChannelOrder = orderChannelMap.get(refundStrategy.getChannel().getCode());
|
||||
if (Objects.isNull(payChannelOrder)){
|
||||
throw new PayFailureException("[数据异常]进行退款的通道没有对应的支付单, 无法退款");
|
||||
}
|
||||
refundStrategy.initRefundParam(payOrder, refundParam, payChannelOrder);
|
||||
}
|
||||
|
||||
// 3.1 退款前准备操作
|
||||
payRefundStrategies.forEach(AbsRefundStrategy::doBeforeRefundHandler);
|
||||
// 3.2 生成各通道退款订单
|
||||
payRefundStrategies.forEach(AbsRefundStrategy::generateChannelOrder);
|
||||
// 3.3 执行退款策略
|
||||
// 3.2 执行退款策略
|
||||
payRefundStrategies.forEach(AbsRefundStrategy::doRefundHandler);
|
||||
// 3.4 执行退款发起成功后操作
|
||||
payRefundStrategies.forEach(AbsRefundStrategy::doSuccessHandler);
|
||||
|
||||
// 4 更新各支付通道订单的信息
|
||||
List<PayChannelOrder> channelOrders = payRefundStrategies.stream()
|
||||
.map(AbsRefundStrategy::getPayChannelOrder)
|
||||
.collect(Collectors.toList());
|
||||
payChannelOrderManager.updateAllById(channelOrders);
|
||||
|
||||
// 5 获取退款通道订单, 进行保存
|
||||
// 4.进行成功处理, 分别处理退款订单, 通道退款订单, 支付订单
|
||||
List<PayRefundChannelOrder> refundChannelOrders = payRefundStrategies.stream()
|
||||
.map(AbsRefundStrategy::getRefundChannelOrder)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 6.进行成功处理, 分别处理退款订单, 通道退款订单, 支付订单
|
||||
PayRefundOrder refundOrder = this.successHandler(refundParam, refundChannelOrders, payOrder);
|
||||
this.successHandler(refundOrder, refundChannelOrders, payOrder);
|
||||
return new RefundResult()
|
||||
.setRefundId(refundOrder.getId())
|
||||
.setRefundNo(refundParam.getRefundNo());
|
||||
}
|
||||
catch (Exception e) {
|
||||
// 失败处理
|
||||
// 5. 失败处理
|
||||
PaymentContextLocal.get().getRefundInfo().setStatus(PayRefundStatusEnum.FAIL);
|
||||
this.errorHandler(refundParam, payOrder);
|
||||
this.errorHandler(refundOrder);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款订单成功处理, 保存退款订单, 通道退款订单, 更新支付订单
|
||||
* 退款一阶段: 进行支付订单和支付通道订单的预扣, 预创建退款订单并保存, 使用独立的新事物进行发起
|
||||
*/
|
||||
private PayRefundOrder successHandler(RefundParam refundParam, List<PayRefundChannelOrder> refundChannelOrders, PayOrder payOrder) {
|
||||
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
|
||||
// ----------------------- 支付订单处理 ---------------------------------------
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class )
|
||||
public PayRefundOrder preRefundMethod(RefundParam refundParam, PayOrder payOrder, List<AbsRefundStrategy> payRefundStrategies) {
|
||||
// --------------------------- 支付订单 -------------------------------------
|
||||
// 预扣支付相关订单要退款的金额并进行更新
|
||||
payRefundStrategies.forEach(AbsRefundStrategy::generateChannelOrder);
|
||||
List<PayChannelOrder> channelOrders = payRefundStrategies.stream()
|
||||
.map(AbsRefundStrategy::getPayChannelOrder)
|
||||
.collect(Collectors.toList());
|
||||
payChannelOrderManager.updateAllById(channelOrders);
|
||||
// 此次的总退款金额
|
||||
Integer amount = refundParam.getRefundChannels().stream()
|
||||
Integer amount = refundParam.getRefundChannels()
|
||||
.stream()
|
||||
.map(RefundChannelParam::getAmount)
|
||||
.reduce(0, Integer::sum);
|
||||
// 剩余可退款余额
|
||||
int refundableBalance = payOrder.getRefundableBalance() - amount;
|
||||
payOrder.setRefundableBalance(refundableBalance);
|
||||
int orderRefundableBalance = payOrder.getRefundableBalance() - amount;
|
||||
payOrder.setRefundableBalance(orderRefundableBalance)
|
||||
.setStatus(PayStatusEnum.REFUNDING.getCode());
|
||||
payOrderService.updateById(payOrder);
|
||||
// ----------------------- 退款订单 -------------------------
|
||||
List<PayRefundChannelOrder> refundChannelOrders = payRefundStrategies.stream()
|
||||
.map(AbsRefundStrategy::getRefundChannelOrder)
|
||||
.collect(Collectors.toList());
|
||||
return payRefundAssistService.createOrderAndChannel(refundParam, payOrder,refundChannelOrders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功处理, 更新退款订单, 退款通道订单, 支付订单, 支付通道订单
|
||||
*/
|
||||
private void successHandler(PayRefundOrder payRefundOrder, List<PayRefundChannelOrder> refundChannelOrders, PayOrder payOrder) {
|
||||
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
|
||||
// 剩余可退款余额
|
||||
int refundableBalance = payRefundOrder.getRefundableBalance();
|
||||
// 设置支付订单状态
|
||||
if (asyncRefundInfo.getStatus() == PayRefundStatusEnum.PROGRESS) {
|
||||
// 设置为退款中
|
||||
@@ -209,22 +232,17 @@ public class PayRefundService {
|
||||
// 部分退款
|
||||
payOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
|
||||
}
|
||||
|
||||
// ----------------------- 退款订单处理 ---------------------------------------
|
||||
// 生成退款订单
|
||||
PayRefundOrder refundOrder = payRefundAssistService.generateRefundOrder(refundParam, payOrder);
|
||||
// 更新或保存相关订单
|
||||
payRefundAssistService.saveOrderAndChannels(refundOrder,refundChannelOrders);
|
||||
payOrderService.updateById(payOrder);
|
||||
return refundOrder;
|
||||
|
||||
// 更新退款订单和相关通道订单
|
||||
payRefundAssistService.updateOrderAndChannel(payRefundOrder,refundChannelOrders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 失败处理
|
||||
* 失败处理, 只更新退款订单, 通道订单不进行错误更新
|
||||
*/
|
||||
private void errorHandler(RefundParam refundParam, PayOrder payOrder) {
|
||||
private void errorHandler(PayRefundOrder refundOrder) {
|
||||
// 记录退款失败的记录
|
||||
PayRefundOrder refundOrder = payRefundAssistService.generateRefundOrder(refundParam, payOrder);
|
||||
payRefundAssistService.saveOrder(refundOrder);
|
||||
payRefundAssistService.updateOrderByError(refundOrder);
|
||||
}
|
||||
}
|
||||
|
@@ -12,8 +12,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
@@ -54,7 +52,7 @@ public class AliPayRefundStrategy extends AbsRefundStrategy {
|
||||
*/
|
||||
@Override
|
||||
public void doRefundHandler() {
|
||||
aliRefundService.refund(this.getPayOrder(), this.getRefundChannelParam().getAmount());
|
||||
aliRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,8 +1,8 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletPayService;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletPayOrderService;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletPayService;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@@ -12,8 +12,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
@@ -52,12 +50,13 @@ public class WeChatPayRefundStrategy extends AbsRefundStrategy {
|
||||
this.weChatPayConfig = weChatPayConfigService.getConfig();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退款操作
|
||||
*/
|
||||
@Override
|
||||
public void doRefundHandler() {
|
||||
wechatRefundService.refund(this.getPayOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), this.weChatPayConfig);
|
||||
wechatRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), this.weChatPayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.service.core.payment.repair.service;
|
||||
|
||||
import cn.bootx.platform.common.core.function.CollectorsFunction;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairPayTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairWayEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
|
||||
@@ -161,7 +161,7 @@ public class PayRepairService {
|
||||
.setOrderNo(order.getBusinessNo())
|
||||
.setBeforeStatus(repairResult.getBeforeStatus().getCode())
|
||||
.setAfterStatus(afterStatus)
|
||||
.setRepairType(PayRepairPayTypeEnum.PAY.getCode())
|
||||
.setRepairType(PaymentTypeEnum.PAY.getCode())
|
||||
.setRepairSource(source)
|
||||
.setRepairWay(recordType.getCode());
|
||||
payRepairRecord.setId(repairResult.getRepairId());
|
||||
|
@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.service.core.payment.repair.service;
|
||||
import cn.bootx.platform.common.core.exception.DataNotExistException;
|
||||
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairPayTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.RefundRepairWayEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
|
||||
@@ -57,15 +57,16 @@ public class RefundRepairService {
|
||||
// 获取关联支付单
|
||||
PayOrder payOrder = payOrderManager.findById(refundOrder.getPaymentId())
|
||||
.orElseThrow(() -> new RuntimeException("支付单不存在"));
|
||||
// 关联异步支付通道支付单
|
||||
// 关联支付通道支付单
|
||||
PayChannelOrder payChannelOrder = payChannelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), payOrder.getAsyncChannel())
|
||||
.orElseThrow(DataNotExistException::new);
|
||||
// 异步通道退款单
|
||||
PayRefundChannelOrder refundChannelOrder = refundChannelOrderManager.findByPaymentIdAndChannel(refundOrder.getId(), payOrder.getAsyncChannel())
|
||||
PayRefundChannelOrder refundChannelOrder = refundChannelOrderManager.findByRefundIdAndChannel(refundOrder.getId(), payOrder.getAsyncChannel())
|
||||
.orElseThrow(DataNotExistException::new);
|
||||
|
||||
// 根据不同的类型执行对应的修复逻辑
|
||||
RefundRepairResult repairResult = new RefundRepairResult();
|
||||
//TODO 整个退款单是一个状态, 最终结果要么全部成功, 要么全部回退
|
||||
if (Objects.requireNonNull(repairType) == RefundRepairWayEnum.SUCCESS) {
|
||||
repairResult = this.success(refundOrder,payOrder,refundChannelOrder,payChannelOrder);
|
||||
} else if (repairType == RefundRepairWayEnum.FAIL) {
|
||||
@@ -164,7 +165,6 @@ public class RefundRepairService {
|
||||
// 退款单设置为部分成功状态, 通道退款单设置为失败状态
|
||||
refundOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
|
||||
refundChannelOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
|
||||
repairResult.setAfterRefundStatus(PayRefundStatusEnum.PART_SUCCESS);
|
||||
}
|
||||
|
||||
// 更新订单和退款相关订单
|
||||
@@ -190,7 +190,7 @@ public class RefundRepairService {
|
||||
return new PayRepairRecord()
|
||||
.setRepairId(repairResult.getRepairId())
|
||||
.setOrderId(order.getId())
|
||||
.setRepairType(PayRepairPayTypeEnum.PAY.getCode())
|
||||
.setRepairType(PaymentTypeEnum.PAY.getCode())
|
||||
.setRepairSource(source)
|
||||
.setRepairWay(repairType.getCode())
|
||||
.setAsyncChannel(order.getAsyncChannel())
|
||||
@@ -214,7 +214,7 @@ public class RefundRepairService {
|
||||
.setOrderId(refundOrder.getId())
|
||||
.setRepairId(repairResult.getRepairId())
|
||||
.setOrderNo(refundOrder.getRefundNo())
|
||||
.setRepairType(PayRepairPayTypeEnum.REFUND.getCode())
|
||||
.setRepairType(PaymentTypeEnum.REFUND.getCode())
|
||||
.setBeforeStatus(repairResult.getBeforeRefundStatus().getCode())
|
||||
.setAfterStatus(afterStatus)
|
||||
.setRepairSource(source)
|
||||
|
@@ -4,10 +4,15 @@ import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
|
||||
import cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.RefundSyncParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.repair.result.RefundRepairResult;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.factory.RefundSyncStrategyFactory;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.core.record.sync.entity.PaySyncRecord;
|
||||
import cn.bootx.platform.daxpay.service.core.record.sync.service.PaySyncRecordService;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundSyncStrategy;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
@@ -30,12 +35,14 @@ import java.util.Objects;
|
||||
public class PayRefundSyncService {
|
||||
private final PayRefundOrderManager refundOrderManager;
|
||||
|
||||
private final PaySyncRecordService paySyncRecordService;
|
||||
|
||||
private final LockTemplate lockTemplate;
|
||||
|
||||
/**
|
||||
* 退款同步, 开启一个新的事务, 不受外部抛出异常的影响
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void sync(RefundSyncParam param){
|
||||
// 先获取退款单
|
||||
PayRefundOrder requestOrder;
|
||||
@@ -51,12 +58,15 @@ public class PayRefundSyncService {
|
||||
// TODO 需要限制同步的请求不进行同步
|
||||
return;
|
||||
}
|
||||
this.syncPayOrder(requestOrder);
|
||||
this.syncRefundOrder(requestOrder);
|
||||
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void syncPayOrder(PayRefundOrder refundOrder) {
|
||||
/**
|
||||
* 退款订单信息同步
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PaySyncResult syncRefundOrder(PayRefundOrder refundOrder) {
|
||||
// 加锁
|
||||
LockInfo lock = lockTemplate.lock("sync:refund:" + refundOrder.getId());
|
||||
if (Objects.isNull(lock)) {
|
||||
@@ -73,36 +83,66 @@ public class PayRefundSyncService {
|
||||
// 判断是否同步成功
|
||||
if (Objects.equals(syncResult.getSyncStatus(), PayRefundSyncStatusEnum.FAIL)) {
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
// return new PaySyncResult().setErrorMsg(syncResult.getErrorMsg());
|
||||
log.error("同步失败");
|
||||
return;
|
||||
return new PaySyncResult().setErrorMsg(syncResult.getErrorMsg());
|
||||
}
|
||||
|
||||
// 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态
|
||||
// boolean statusSync = this.checkAndAdjustSyncStatus(syncResult, payOrder);
|
||||
// PayRepairResult repairResult = new PayRepairResult();
|
||||
// try {
|
||||
// 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态
|
||||
boolean statusSync = this.checkAndAdjustSyncStatus(syncResult, refundOrder);
|
||||
RefundRepairResult repairResult = new RefundRepairResult();
|
||||
try {
|
||||
// // 状态不一致,执行支付单修复逻辑
|
||||
// if (!statusSync) {
|
||||
// repairResult = this.resultHandler(syncResult, payOrder);
|
||||
// }
|
||||
// } catch (PayFailureException e) {
|
||||
// // 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
// syncResult.setSyncStatus(PaySyncStatusEnum.FAIL);
|
||||
// this.saveRecord(payOrder, syncResult, false, null, e.getMessage());
|
||||
// return new PaySyncResult().setErrorMsg(e.getMessage());
|
||||
// }
|
||||
//
|
||||
if (!statusSync) {
|
||||
repairResult = this.repairHandler(syncResult, refundOrder);
|
||||
}
|
||||
} catch (PayFailureException e) {
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
syncResult.setSyncStatus(PayRefundSyncStatusEnum.FAIL);
|
||||
this.saveRecord(refundOrder, syncResult, false, null, e.getMessage());
|
||||
return new PaySyncResult().setErrorMsg(e.getMessage());
|
||||
}
|
||||
// // 同步成功记录日志
|
||||
// this.saveRecord(payOrder, syncResult, !statusSync, repairResult.getRepairId(), null);
|
||||
// return new PaySyncResult()
|
||||
// .setGatewayStatus(syncResult.getSyncStatus()
|
||||
// .getCode())
|
||||
// .setSuccess(true)
|
||||
// .setRepair(!statusSync)
|
||||
// .setRepairId(repairResult.getRepairId());
|
||||
this.saveRecord(refundOrder, syncResult, !statusSync, repairResult.getRepairId(), null);
|
||||
return new PaySyncResult()
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
.setSuccess(true)
|
||||
.setRepair(!statusSync)
|
||||
.setRepairId(repairResult.getRepairId());
|
||||
} finally {
|
||||
lockTemplate.releaseLock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean checkAndAdjustSyncStatus(RefundGatewaySyncResult syncResult, PayRefundOrder order){
|
||||
return true;
|
||||
}
|
||||
|
||||
private RefundRepairResult repairHandler(RefundGatewaySyncResult syncResult, PayRefundOrder order){
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 保存同步记录
|
||||
* @param payOrder 支付单
|
||||
* @param syncResult 同步结果
|
||||
* @param repair 是否修复
|
||||
* @param errorMsg 错误信息
|
||||
*/
|
||||
private void saveRecord(PayRefundOrder payOrder, RefundGatewaySyncResult syncResult, boolean repair, Long repairOrderId, String errorMsg){
|
||||
PaySyncRecord paySyncRecord = new PaySyncRecord()
|
||||
.setOrderId(payOrder.getId())
|
||||
.setOrderNo(payOrder.getBusinessNo())
|
||||
.setAsyncChannel(payOrder.getAsyncChannel())
|
||||
.setSyncInfo(syncResult.getSyncInfo())
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
.setRepairOrder(repair)
|
||||
.setRepairOrderId(repairOrderId)
|
||||
.setErrorMsg(errorMsg)
|
||||
.setClientIp(PaymentContextLocal.get().getRequestInfo().getClientIp())
|
||||
.setReqId(PaymentContextLocal.get().getRequestInfo().getReqId());
|
||||
paySyncRecordService.saveRecord(paySyncRecord);
|
||||
}
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@ public class PaySyncService {
|
||||
/**
|
||||
* 支付同步, 开启一个新的事务, 不受外部抛出异常的影响
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PaySyncResult sync(PaySyncParam param) {
|
||||
PayOrder payOrder = null;
|
||||
if (Objects.nonNull(param.getPaymentId())){
|
||||
@@ -81,7 +81,7 @@ public class PaySyncService {
|
||||
* 2. 如果状态不一致, 调用修复逻辑进行修复
|
||||
* todo 需要进行异常处理, 现在会有 Transaction rolled back because it has been marked as rollback-only 问题
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PaySyncResult syncPayOrder(PayOrder payOrder) {
|
||||
// 加锁
|
||||
LockInfo lock = lockTemplate.lock("sync:payment" + payOrder.getId());
|
||||
@@ -108,7 +108,7 @@ public class PaySyncService {
|
||||
try {
|
||||
// 状态不一致,执行支付单修复逻辑
|
||||
if (!statusSync){
|
||||
repairResult = this.resultHandler(syncResult, payOrder);
|
||||
repairResult = this.repairHandler(syncResult, payOrder);
|
||||
}
|
||||
} catch (PayFailureException e) {
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
@@ -173,9 +173,9 @@ public class PaySyncService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据同步的结果对支付单进行处理
|
||||
* 根据同步的结果对支付单进行修复处理
|
||||
*/
|
||||
private PayRepairResult resultHandler(PayGatewaySyncResult syncResult, PayOrder payOrder){
|
||||
private PayRepairResult repairHandler(PayGatewaySyncResult syncResult, PayOrder payOrder){
|
||||
PaySyncStatusEnum syncStatusEnum = syncResult.getSyncStatus();
|
||||
// 如果没有支付来源, 设置支付来源为同步
|
||||
RepairLocal repairInfo = PaymentContextLocal.get().getRepairInfo();
|
||||
@@ -195,6 +195,8 @@ public class PaySyncService {
|
||||
repair = repairService.repair(payOrder, PayRepairWayEnum.WAIT_PAY);
|
||||
break;
|
||||
}
|
||||
case REFUND:
|
||||
throw new PayFailureException("支付订单为退款状态,请通过执行对应的退款订单进行同步,来更新具体为什么类型退款状态");
|
||||
// 交易关闭和未找到, 都对本地支付订单进行关闭, 不需要再调用网关进行关闭
|
||||
case CLOSED:
|
||||
case NOT_FOUND: {
|
||||
@@ -222,7 +224,7 @@ public class PaySyncService {
|
||||
|
||||
|
||||
/**
|
||||
* 保存同步记录 TODO 目前出现一次请求多次与网关同步, 未全部记录
|
||||
* 保存同步记录
|
||||
* @param payOrder 支付单
|
||||
* @param syncResult 同步结果
|
||||
* @param repair 是否修复
|
||||
@@ -230,8 +232,8 @@ public class PaySyncService {
|
||||
*/
|
||||
private void saveRecord(PayOrder payOrder, PayGatewaySyncResult syncResult, boolean repair, Long repairOrderId, String errorMsg){
|
||||
PaySyncRecord paySyncRecord = new PaySyncRecord()
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setBusinessNo(payOrder.getBusinessNo())
|
||||
.setOrderId(payOrder.getId())
|
||||
.setOrderNo(payOrder.getBusinessNo())
|
||||
.setAsyncChannel(payOrder.getAsyncChannel())
|
||||
.setSyncInfo(syncResult.getSyncInfo())
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
|
@@ -1,6 +1,9 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.sync.strategy.Refund;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
|
||||
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPaySyncService;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundSyncStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -18,6 +21,8 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WeChatRefundSyncStrategy extends AbsRefundSyncStrategy {
|
||||
private final WeChatPaySyncService weChatPaySyncService;
|
||||
private final WeChatPayConfigService weChatPayConfigService;
|
||||
|
||||
/**
|
||||
* 策略标识
|
||||
@@ -32,7 +37,8 @@ public class WeChatRefundSyncStrategy extends AbsRefundSyncStrategy {
|
||||
*/
|
||||
@Override
|
||||
public RefundGatewaySyncResult doSyncStatus() {
|
||||
return null;
|
||||
WeChatPayConfig config = weChatPayConfigService.getConfig();
|
||||
return weChatPaySyncService.syncRefundStatus(this.getRefundOrder(), config);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ import cn.bootx.platform.common.core.function.EntityBaseFunction;
|
||||
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayCallbackStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayCallbackTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.core.record.callback.convert.PayCallbackRecordConvert;
|
||||
import cn.bootx.platform.daxpay.service.dto.record.callback.PayCallbackRecordDto;
|
||||
import cn.bootx.table.modify.annotation.DbColumn;
|
||||
@@ -46,7 +46,7 @@ public class PayCallbackRecord extends MpCreateEntity implements EntityBaseFunct
|
||||
|
||||
/**
|
||||
* 回调类型
|
||||
* @see PayCallbackTypeEnum
|
||||
* @see PaymentTypeEnum
|
||||
*/
|
||||
@DbColumn(comment = "回调类型")
|
||||
private String callbackType;
|
||||
|
@@ -42,7 +42,7 @@ public class PayCallbackRecordService {
|
||||
/**
|
||||
* 保存回调记录
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void save(PayCallbackRecord record) {
|
||||
callbackRecordManager.save(record);
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ public class PayCloseRecordService {
|
||||
/**
|
||||
* 新开事务进行记录保存
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void saveRecord(PayCloseRecord record){
|
||||
manager.save(record);
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.service.core.record.repair.entity;
|
||||
import cn.bootx.platform.common.core.function.EntityBaseFunction;
|
||||
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairPayTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairWayEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.RefundRepairWayEnum;
|
||||
@@ -48,7 +48,7 @@ public class PayRepairRecord extends MpCreateEntity implements EntityBaseFunctio
|
||||
|
||||
/**
|
||||
* 修复类型 支付修复/退款修复
|
||||
* @see PayRepairPayTypeEnum
|
||||
* @see PaymentTypeEnum
|
||||
*/
|
||||
@DbColumn(comment = "修复类型")
|
||||
private String repairType;
|
||||
|
@@ -44,14 +44,14 @@ public class PayRepairRecordService {
|
||||
/**
|
||||
* 保存记录
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void saveRecord(PayRepairRecord record){
|
||||
repairRecordManager.save(record);
|
||||
}
|
||||
/**
|
||||
* 保存记录
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void saveAllRecord(List<PayRepairRecord> records){
|
||||
repairRecordManager.saveAll(records);
|
||||
}
|
||||
|
@@ -27,13 +27,16 @@ import lombok.experimental.Accessors;
|
||||
@TableName("pay_sync_record")
|
||||
public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<PaySyncRecordDto> {
|
||||
|
||||
/** 支付记录id */
|
||||
@DbColumn(comment = "支付记录id")
|
||||
private Long paymentId;
|
||||
/** 本地订单ID */
|
||||
@DbColumn(comment = "本地订单ID")
|
||||
private Long orderId;
|
||||
|
||||
/** 业务号 */
|
||||
@DbColumn(comment = "业务号")
|
||||
private String businessNo;
|
||||
/** 本地业务号 */
|
||||
@DbColumn(comment = "本地业务号")
|
||||
private String orderNo;
|
||||
|
||||
@DbColumn(comment = "同步通道")
|
||||
private String syncChannel;
|
||||
|
||||
/**
|
||||
* 同步通道
|
||||
|
@@ -44,7 +44,7 @@ public class PaySyncRecordService {
|
||||
/**
|
||||
* 记录同步记录 同步支付单的不进行记录
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void saveRecord(PaySyncRecord paySyncRecord){
|
||||
orderManager.save(paySyncRecord);
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.service.dto.record.callback;
|
||||
import cn.bootx.platform.common.core.rest.dto.BaseDto;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayCallbackStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayCallbackTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -34,7 +34,7 @@ public class PayCallbackRecordDto extends BaseDto {
|
||||
|
||||
/**
|
||||
* 回调类型
|
||||
* @see PayCallbackTypeEnum
|
||||
* @see PaymentTypeEnum
|
||||
*/
|
||||
@Schema(description = "回调类型")
|
||||
private String callbackType;
|
||||
|
@@ -1,8 +1,8 @@
|
||||
package cn.bootx.platform.daxpay.service.func;
|
||||
|
||||
import cn.bootx.platform.daxpay.service.code.PayCallbackStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayCallbackTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.callback.service.PayCallbackService;
|
||||
@@ -51,8 +51,8 @@ public abstract class AbsCallbackStrategy implements PayStrategy {
|
||||
PaymentContextLocal.get().getRepairInfo().setSource(PayRepairSourceEnum.CALLBACK);
|
||||
|
||||
// 判断回调类型
|
||||
PayCallbackTypeEnum callbackType = this.getCallbackType();
|
||||
if (callbackType == PayCallbackTypeEnum.PAY){
|
||||
PaymentTypeEnum callbackType = this.getCallbackType();
|
||||
if (callbackType == PaymentTypeEnum.PAY){
|
||||
// 解析支付数据并放处理
|
||||
this.resolvePayData();
|
||||
payCallbackService.payCallback();
|
||||
@@ -78,9 +78,9 @@ public abstract class AbsCallbackStrategy implements PayStrategy {
|
||||
|
||||
/**
|
||||
* 判断类型 支付回调/退款回调
|
||||
* @see PayCallbackTypeEnum
|
||||
* @see PaymentTypeEnum
|
||||
*/
|
||||
public abstract PayCallbackTypeEnum getCallbackType();
|
||||
public abstract PaymentTypeEnum getCallbackType();
|
||||
|
||||
/**
|
||||
* 解析支付回调数据并放到上下文中
|
||||
@@ -105,7 +105,7 @@ public abstract class AbsCallbackStrategy implements PayStrategy {
|
||||
|
||||
// 回调类型
|
||||
String callbackType = Optional.ofNullable(this.getCallbackType())
|
||||
.map(PayCallbackTypeEnum::getCode)
|
||||
.map(PaymentTypeEnum::getCode)
|
||||
.orElse(null);
|
||||
|
||||
PayCallbackRecord payNotifyRecord = new PayCallbackRecord()
|
||||
|
@@ -7,6 +7,7 @@ import cn.bootx.platform.daxpay.param.pay.RefundParam;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -25,6 +26,9 @@ public abstract class AbsRefundStrategy implements PayStrategy{
|
||||
/** 支付订单 */
|
||||
private PayOrder payOrder = null;
|
||||
|
||||
/** 退款订单 已经持久化, 后续需要更新 */
|
||||
private PayRefundOrder refundOrder = null;
|
||||
|
||||
/** 当前通道的订单 */
|
||||
private PayChannelOrder payChannelOrder = null;
|
||||
|
||||
@@ -34,7 +38,7 @@ public abstract class AbsRefundStrategy implements PayStrategy{
|
||||
/** 当前通道的退款参数 退款参数中的与这个不一致, 以这个为准 */
|
||||
private RefundChannelParam refundChannelParam = null;
|
||||
|
||||
/** 当前通道的退款订单 */
|
||||
/** 当前通道的退款订单 未持久化, 需要后续更新 */
|
||||
private PayRefundChannelOrder refundChannelOrder;
|
||||
|
||||
/**
|
||||
@@ -46,8 +50,19 @@ public abstract class AbsRefundStrategy implements PayStrategy{
|
||||
this.refundParam = refundParam;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 退款前对处理 包含必要的校验以及对Payment对象的创建和保存操作
|
||||
* 退款前预扣通道和支付订单的金额
|
||||
*/
|
||||
public void doPreDeductOrderHandler(){
|
||||
PayChannelOrder payChannelOrder = this.getPayChannelOrder();
|
||||
int refundableBalance = payChannelOrder.getRefundableBalance() - this.getRefundChannelParam().getAmount();
|
||||
payChannelOrder.setRefundableBalance(refundableBalance)
|
||||
.setStatus(PayStatusEnum.REFUNDING.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款前对处理
|
||||
*/
|
||||
public void doBeforeRefundHandler() {}
|
||||
|
||||
@@ -64,7 +79,7 @@ public abstract class AbsRefundStrategy implements PayStrategy{
|
||||
this.refundChannelOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode())
|
||||
.setRefundTime(LocalDateTime.now());
|
||||
|
||||
// 支付通道订单客可退余额
|
||||
// 支付通道订单可退余额
|
||||
int refundableBalance = this.getPayChannelOrder().getRefundableBalance() - this.refundChannelOrder.getAmount();
|
||||
// 支付通道订单状态
|
||||
PayStatusEnum status = refundableBalance == 0 ? PayStatusEnum.REFUNDED : PayStatusEnum.PARTIAL_REFUND;
|
||||
|
@@ -22,6 +22,10 @@ public abstract class AbsRefundSyncStrategy implements PayStrategy{
|
||||
this.refundOrder = refundOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步前处理, 主要是预防请求过于迅速, 支付网关没有处理完退款请求, 导致返回的状态不正确
|
||||
*/
|
||||
public void doBeforeHandler(){}
|
||||
/**
|
||||
* 异步支付单与支付网关进行状态比对后的结果
|
||||
* @see PaySyncStatusEnum
|
||||
|
Reference in New Issue
Block a user