Commit 288d8e313202362438b747fb5dabe970cd214665

Authored by YunaiV
1 parent fe8871b5

reactor:【PAY 支付】优化退款回调接口,增加 merchantRefundId 退款编号参数

reactor:【MALL 商城】售后退款时,基于回调处理
Showing 41 changed files with 356 additions and 244 deletions
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -36,8 +36,10 @@ public interface ErrorCodeConstants { @@ -36,8 +36,10 @@ public interface ErrorCodeConstants {
36 ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单不是【待发货】状态"); 36 ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单不是【待发货】状态");
37 ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订单创建失败,原因:存在未付款订单"); 37 ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订单创建失败,原因:存在未付款订单");
38 ErrorCode ORDER_CANCEL_PAID_FAIL = new ErrorCode(1_011_000_033, "交易订单取消支付失败,原因:订单不是【{}】状态"); 38 ErrorCode ORDER_CANCEL_PAID_FAIL = new ErrorCode(1_011_000_033, "交易订单取消支付失败,原因:订单不是【{}】状态");
39 - ErrorCode ORDER_PICK_UP_FAIL_NOT_VERIFY_USER = new ErrorCode(1_011_000_034, "交易订单自提失败,原因:你没有核销该门店订单的权限");  
40 - ErrorCode ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS = new ErrorCode(1_011_000_035, "交易订单创建失败,原因:用户积分不足"); 39 + ErrorCode ORDER_UPDATE_PAID_ORDER_REFUNDED_FAIL_REFUND_NOT_FOUND = new ErrorCode(1_011_000_034, "交易订单更新支付订单退款状态失败,原因:退款单不存在");
  40 + ErrorCode ORDER_UPDATE_PAID_ORDER_REFUNDED_FAIL_REFUND_STATUS_NOT_SUCCESS = new ErrorCode(1_011_000_035, "交易订单更新支付订单退款状态失败,原因:退款单状态不是【退款成功】");
  41 + ErrorCode ORDER_PICK_UP_FAIL_NOT_VERIFY_USER = new ErrorCode(1_011_000_036, "交易订单自提失败,原因:你没有核销该门店订单的权限");
  42 + ErrorCode ORDER_CREATE_FAIL_INSUFFICIENT_USER_POINTS = new ErrorCode(1_011_000_037, "交易订单创建失败,原因:用户积分不足");
41 43
42 // ========== After Sale 模块 1-011-000-100 ========== 44 // ========== After Sale 模块 1-011-000-100 ==========
43 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在"); 45 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
@@ -51,9 +53,13 @@ public interface ErrorCodeConstants { @@ -51,9 +53,13 @@ public interface ErrorCodeConstants {
51 ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1_011_000_108, "退货失败,售后单状态不处于【待买家退货】"); 53 ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1_011_000_108, "退货失败,售后单状态不处于【待买家退货】");
52 ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1_011_000_109, "确认收货失败,售后单状态不处于【待确认收货】"); 54 ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1_011_000_109, "确认收货失败,售后单状态不处于【待确认收货】");
53 ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售后单状态不是【待退款】"); 55 ErrorCode AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND = new ErrorCode(1_011_000_110, "退款失败,售后单状态不是【待退款】");
  56 + ErrorCode AFTER_SALE_REFUND_FAIL_REFUND_NOT_FOUND = new ErrorCode(1_011_000_111, "退款失败,退款单不存在");
  57 + ErrorCode AFTER_SALE_REFUND_FAIL_REFUND_NOT_SUCCESS_OR_FAILURE = new ErrorCode(1_011_000_112, "退款失败,退款单未退款");
  58 + ErrorCode AFTER_SALE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1_011_000_113, "退款失败,退款金额不匹配");
  59 + ErrorCode AFTER_SALE_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1_011_000_114, "退款失败,退款单不匹配");
54 ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY = 60 ErrorCode AFTER_SALE_CANCEL_FAIL_STATUS_NOT_APPLY_OR_AGREE_OR_BUYER_DELIVERY =
55 - new ErrorCode(1_011_000_111, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");  
56 - ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS = new ErrorCode(1_011_000_112, "订单拼团中,无法申请售后"); 61 + new ErrorCode(1_011_000_115, "取消售后单失败,售后单状态不是【待审核】或【卖家同意】或【商家待收货】");
  62 + ErrorCode AFTER_SALE_CREATE_FAIL_ORDER_STATUS_COMBINATION_IN_PROGRESS = new ErrorCode(1_011_000_116, "订单拼团中,无法申请售后");
57 63
58 // ========== Cart 模块 1-011-002-000 ========== 64 // ========== Cart 模块 1-011-002-000 ==========
59 ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在"); 65 ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在");
@@ -101,7 +107,6 @@ public interface ErrorCodeConstants { @@ -101,7 +107,6 @@ public interface ErrorCodeConstants {
101 ErrorCode BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING = new ErrorCode(1_011_008_001, "佣金提现记录状态不是审核中"); 107 ErrorCode BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING = new ErrorCode(1_011_008_001, "佣金提现记录状态不是审核中");
102 ErrorCode BROKERAGE_WITHDRAW_MIN_PRICE = new ErrorCode(1_011_008_002, "提现金额不能低于 {} 元"); 108 ErrorCode BROKERAGE_WITHDRAW_MIN_PRICE = new ErrorCode(1_011_008_002, "提现金额不能低于 {} 元");
103 ErrorCode BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH = new ErrorCode(1_011_008_003, "您当前最多可提现 {} 元"); 109 ErrorCode BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH = new ErrorCode(1_011_008_003, "您当前最多可提现 {} 元");
104 - ErrorCode BROKERAGE_WITHDRAW_TRANSFER_FAIL_WECHAT_NOT_BIND = new ErrorCode(1_011_008_004, "提现失败,原因:用户未绑定微信");  
105 ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_ID_ERROR = new ErrorCode(1_011_008_005, "提现单更新转账状态失败,转账单不匹配"); 110 ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_ID_ERROR = new ErrorCode(1_011_008_005, "提现单更新转账状态失败,转账单不匹配");
106 ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_STATUS_NOT_SUCCESS_OR_CLOSED = new ErrorCode(1_011_008_006, "提现单更新转账状态失败,转账单状态不是成功或关闭状态"); 111 ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_STATUS_NOT_SUCCESS_OR_CLOSED = new ErrorCode(1_011_008_006, "提现单更新转账状态失败,转账单状态不是成功或关闭状态");
107 ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1_011_008_007, "提现单更新转账状态失败,转账单金额不匹配"); 112 ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH = new ErrorCode(1_011_008_007, "提现单更新转账状态失败,转账单金额不匹配");
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/aftersale/AfterSaleOperateTypeEnum.java
@@ -19,7 +19,9 @@ public enum AfterSaleOperateTypeEnum { @@ -19,7 +19,9 @@ public enum AfterSaleOperateTypeEnum {
19 MEMBER_DELIVERY(20, "会员填写退货物流信息,快递公司:{deliveryName},快递单号:{logisticsNo}"), 19 MEMBER_DELIVERY(20, "会员填写退货物流信息,快递公司:{deliveryName},快递单号:{logisticsNo}"),
20 ADMIN_AGREE_RECEIVE(21, "商家收货"), 20 ADMIN_AGREE_RECEIVE(21, "商家收货"),
21 ADMIN_DISAGREE_RECEIVE(22, "商家拒绝收货,原因:{reason}"), 21 ADMIN_DISAGREE_RECEIVE(22, "商家拒绝收货,原因:{reason}"),
22 - ADMIN_REFUND(30, "商家退款"), 22 + ADMIN_REFUND(30, "商家发起退款"),
  23 + SYSTEM_REFUND_SUCCESS(31, "退款成功"),
  24 + SYSTEM_REFUND_FAIL(32, "退款失败"),
23 MEMBER_CANCEL(40, "会员取消退款"), 25 MEMBER_CANCEL(40, "会员取消退款"),
24 ; 26 ;
25 27
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/AfterSaleController.java
1 package cn.iocoder.yudao.module.trade.controller.admin.aftersale; 1 package cn.iocoder.yudao.module.trade.controller.admin.aftersale;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 7 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
@@ -15,10 +16,12 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; @@ -15,10 +16,12 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
15 import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService; 16 import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService;
16 import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService; 17 import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
17 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 18 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
  19 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
18 import io.swagger.v3.oas.annotations.Operation; 20 import io.swagger.v3.oas.annotations.Operation;
19 import io.swagger.v3.oas.annotations.Parameter; 21 import io.swagger.v3.oas.annotations.Parameter;
20 import io.swagger.v3.oas.annotations.tags.Tag; 22 import io.swagger.v3.oas.annotations.tags.Tag;
21 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.beans.factory.annotation.Autowired;
22 import org.springframework.security.access.prepost.PreAuthorize; 25 import org.springframework.security.access.prepost.PreAuthorize;
23 import org.springframework.validation.annotation.Validated; 26 import org.springframework.validation.annotation.Validated;
24 import org.springframework.web.bind.annotation.*; 27 import org.springframework.web.bind.annotation.*;
@@ -46,6 +49,8 @@ public class AfterSaleController { @@ -46,6 +49,8 @@ public class AfterSaleController {
46 @Resource 49 @Resource
47 private TradeOrderQueryService tradeOrderQueryService; 50 private TradeOrderQueryService tradeOrderQueryService;
48 @Resource 51 @Resource
  52 + private TradeOrderUpdateService tradeOrderUpdateService;
  53 + @Resource
49 private AfterSaleLogService afterSaleLogService; 54 private AfterSaleLogService afterSaleLogService;
50 @Resource 55 @Resource
51 private MemberUserApi memberUserApi; 56 private MemberUserApi memberUserApi;
@@ -133,11 +138,19 @@ public class AfterSaleController { @@ -133,11 +138,19 @@ public class AfterSaleController {
133 138
134 @PostMapping("/update-refunded") 139 @PostMapping("/update-refunded")
135 @Operation(summary = "更新售后订单为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob 140 @Operation(summary = "更新售后订单为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob
136 - @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现  
137 - public CommonResult<Boolean> updateAfterRefund(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {  
138 - // 目前业务逻辑,不需要做任何事情  
139 - // 当然,退款会有小概率会失败的情况,可以监控失败状态,进行告警 141 + @PermitAll // 无需登录,安全由 AfterSaleService 内部校验实现
  142 + public CommonResult<Boolean> updateAfterSaleRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {
140 log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO); 143 log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO);
  144 + if (StrUtil.startWithAny(notifyReqDTO.getMerchantRefundId(), "order-")) {
  145 + tradeOrderUpdateService.updatePaidOrderRefunded(
  146 + Long.parseLong(notifyReqDTO.getMerchantRefundId()),
  147 + notifyReqDTO.getPayRefundId());
  148 + } else {
  149 + afterSaleService.updateAfterSaleRefunded(
  150 + Long.parseLong(notifyReqDTO.getMerchantRefundId()),
  151 + Long.parseLong(notifyReqDTO.getMerchantOrderId()),
  152 + notifyReqDTO.getPayRefundId());
  153 + }
141 return success(true); 154 return success(true);
142 } 155 }
143 156
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/brokerage/BrokerageWithdrawController.java
@@ -88,7 +88,7 @@ public class BrokerageWithdrawController { @@ -88,7 +88,7 @@ public class BrokerageWithdrawController {
88 public CommonResult<Boolean> updateBrokerageWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { 88 public CommonResult<Boolean> updateBrokerageWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) {
89 log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO); 89 log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO);
90 brokerageWithdrawService.updateBrokerageWithdrawTransferred( 90 brokerageWithdrawService.updateBrokerageWithdrawTransferred(
91 - Long.parseLong(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayTransferId()); 91 + Long.parseLong(notifyReqDTO.getMerchantTransferId()), notifyReqDTO.getPayTransferId());
92 return success(true); 92 return success(true);
93 } 93 }
94 94
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/aftersale/core/aop/AfterSaleLogAspect.java
1 package cn.iocoder.yudao.module.trade.framework.aftersale.core.aop; 1 package cn.iocoder.yudao.module.trade.framework.aftersale.core.aop;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
3 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
4 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 6 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO;
  8 +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum;
7 import cn.iocoder.yudao.module.trade.framework.aftersale.core.annotations.AfterSaleLog; 9 import cn.iocoder.yudao.module.trade.framework.aftersale.core.annotations.AfterSaleLog;
8 import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService; 10 import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService;
9 import cn.iocoder.yudao.module.trade.service.aftersale.bo.AfterSaleLogCreateReqBO; 11 import cn.iocoder.yudao.module.trade.service.aftersale.bo.AfterSaleLogCreateReqBO;
  12 +import jakarta.annotation.Resource;
10 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
11 import org.aspectj.lang.JoinPoint; 14 import org.aspectj.lang.JoinPoint;
12 import org.aspectj.lang.annotation.AfterReturning; 15 import org.aspectj.lang.annotation.AfterReturning;
13 import org.aspectj.lang.annotation.Aspect; 16 import org.aspectj.lang.annotation.Aspect;
14 17
15 -import jakarta.annotation.Resource;  
16 import java.util.Map; 18 import java.util.Map;
17 19
18 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 20 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
@@ -51,6 +53,10 @@ public class AfterSaleLogAspect { @@ -51,6 +53,10 @@ public class AfterSaleLogAspect {
51 */ 53 */
52 private static final ThreadLocal<Integer> AFTER_STATUS = new ThreadLocal<>(); 54 private static final ThreadLocal<Integer> AFTER_STATUS = new ThreadLocal<>();
53 /** 55 /**
  56 + * 操作类型(仅“动态场景”需要使用)
  57 + */
  58 + private static final ThreadLocal<AfterSaleOperateTypeEnum> OPERATE_TYPE = new ThreadLocal<>();
  59 + /**
54 * 拓展参数 Map,用于格式化操作内容 60 * 拓展参数 Map,用于格式化操作内容
55 */ 61 */
56 private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>(); 62 private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>();
@@ -69,6 +75,7 @@ public class AfterSaleLogAspect { @@ -69,6 +75,7 @@ public class AfterSaleLogAspect {
69 if (afterSaleId == null) { // 如果未设置,只有注解,说明不需要记录日志 75 if (afterSaleId == null) { // 如果未设置,只有注解,说明不需要记录日志
70 return; 76 return;
71 } 77 }
  78 + AfterSaleOperateTypeEnum operateType = ObjUtil.defaultIfNull(OPERATE_TYPE.get(), afterSaleLog.operateType());
72 Integer beforeStatus = BEFORE_STATUS.get(); 79 Integer beforeStatus = BEFORE_STATUS.get();
73 Integer afterStatus = AFTER_STATUS.get(); 80 Integer afterStatus = AFTER_STATUS.get();
74 Map<String, Object> exts = ObjectUtil.defaultIfNull(EXTS.get(), emptyMap()); 81 Map<String, Object> exts = ObjectUtil.defaultIfNull(EXTS.get(), emptyMap());
@@ -78,7 +85,7 @@ public class AfterSaleLogAspect { @@ -78,7 +85,7 @@ public class AfterSaleLogAspect {
78 AfterSaleLogCreateReqBO createBO = new AfterSaleLogCreateReqBO() 85 AfterSaleLogCreateReqBO createBO = new AfterSaleLogCreateReqBO()
79 .setUserId(userId).setUserType(userType) 86 .setUserId(userId).setUserType(userType)
80 .setAfterSaleId(afterSaleId).setBeforeStatus(beforeStatus).setAfterStatus(afterStatus) 87 .setAfterSaleId(afterSaleId).setBeforeStatus(beforeStatus).setAfterStatus(afterStatus)
81 - .setOperateType(afterSaleLog.operateType().getType()).setContent(content); 88 + .setOperateType(operateType.getType()).setContent(content);
82 afterSaleLogService.createAfterSaleLog(createBO); 89 afterSaleLogService.createAfterSaleLog(createBO);
83 } catch (Exception exception) { 90 } catch (Exception exception) {
84 log.error("[doAfterReturning][afterSaleLog({}) 日志记录错误]", toJsonString(afterSaleLog), exception); 91 log.error("[doAfterReturning][afterSaleLog({}) 日志记录错误]", toJsonString(afterSaleLog), exception);
@@ -116,6 +123,10 @@ public class AfterSaleLogAspect { @@ -116,6 +123,10 @@ public class AfterSaleLogAspect {
116 EXTS.set(exts); 123 EXTS.set(exts);
117 } 124 }
118 125
  126 + public static void setAfterSaleOperateType(AfterSaleOperateTypeEnum operateType) {
  127 + OPERATE_TYPE.set(operateType);
  128 + }
  129 +
119 public static void setUserInfo(Long userId, Integer userType) { 130 public static void setUserInfo(Long userId, Integer userType) {
120 USER_ID.set(userId); 131 USER_ID.set(userId);
121 USER_TYPE.set(userType); 132 USER_TYPE.set(userType);
@@ -127,6 +138,7 @@ public class AfterSaleLogAspect { @@ -127,6 +138,7 @@ public class AfterSaleLogAspect {
127 AFTER_SALE_ID.remove(); 138 AFTER_SALE_ID.remove();
128 BEFORE_STATUS.remove(); 139 BEFORE_STATUS.remove();
129 AFTER_STATUS.remove(); 140 AFTER_STATUS.remove();
  141 + OPERATE_TYPE.remove();
130 EXTS.remove(); 142 EXTS.remove();
131 } 143 }
132 144
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/aftersale/core/utils/AfterSaleLogUtils.java
1 package cn.iocoder.yudao.module.trade.framework.aftersale.core.utils; 1 package cn.iocoder.yudao.module.trade.framework.aftersale.core.utils;
2 2
3 3
  4 +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum;
4 import cn.iocoder.yudao.module.trade.framework.aftersale.core.aop.AfterSaleLogAspect; 5 import cn.iocoder.yudao.module.trade.framework.aftersale.core.aop.AfterSaleLogAspect;
5 6
6 import java.util.Map; 7 import java.util.Map;
@@ -13,6 +14,10 @@ import java.util.Map; @@ -13,6 +14,10 @@ import java.util.Map;
13 */ 14 */
14 public class AfterSaleLogUtils { 15 public class AfterSaleLogUtils {
15 16
  17 + public static void setAfterSaleOperateType(AfterSaleOperateTypeEnum operateType) {
  18 + AfterSaleLogAspect.setAfterSaleOperateType(operateType);
  19 + }
  20 +
16 public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus) { 21 public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus) {
17 setAfterSaleInfo(id, beforeStatus, afterStatus, null); 22 setAfterSaleInfo(id, beforeStatus, afterStatus, null);
18 } 23 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleService.java
@@ -109,6 +109,15 @@ public interface AfterSaleService { @@ -109,6 +109,15 @@ public interface AfterSaleService {
109 void refundAfterSale(Long userId, String userIp, Long id); 109 void refundAfterSale(Long userId, String userIp, Long id);
110 110
111 /** 111 /**
  112 + * 更新售后订单为已退款
  113 + *
  114 + * @param id 售后编号
  115 + * @param orderId 订单编号
  116 + * @param payRefundId 支付退款编号
  117 + */
  118 + void updateAfterSaleRefunded(Long id, Long orderId, Long payRefundId);
  119 +
  120 + /**
112 * 【会员】取消售后 121 * 【会员】取消售后
113 * 122 *
114 * @param userId 会员用户编号 123 * @param userId 会员用户编号
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
@@ -7,6 +7,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -7,6 +7,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
8 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; 8 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
9 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 9 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
  10 +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
  11 +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
10 import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; 12 import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
11 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; 13 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO;
12 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; 14 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
@@ -41,13 +43,12 @@ import lombok.extern.slf4j.Slf4j; @@ -41,13 +43,12 @@ import lombok.extern.slf4j.Slf4j;
41 import org.springframework.context.annotation.Lazy; 43 import org.springframework.context.annotation.Lazy;
42 import org.springframework.stereotype.Service; 44 import org.springframework.stereotype.Service;
43 import org.springframework.transaction.annotation.Transactional; 45 import org.springframework.transaction.annotation.Transactional;
44 -import org.springframework.transaction.support.TransactionSynchronization;  
45 -import org.springframework.transaction.support.TransactionSynchronizationManager;  
46 import org.springframework.validation.annotation.Validated; 46 import org.springframework.validation.annotation.Validated;
47 47
48 import java.time.LocalDateTime; 48 import java.time.LocalDateTime;
49 49
50 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 50 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  51 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
51 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; 52 import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
52 53
53 /** 54 /**
@@ -184,8 +185,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -184,8 +185,6 @@ public class AfterSaleServiceImpl implements AfterSaleService {
184 // 记录售后日志 185 // 记录售后日志
185 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), null, 186 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), null,
186 AfterSaleStatusEnum.APPLY.getStatus()); 187 AfterSaleStatusEnum.APPLY.getStatus());
187 -  
188 - // TODO 发送售后消息  
189 return afterSale; 188 return afterSale;
190 } 189 }
191 190
@@ -206,8 +205,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -206,8 +205,6 @@ public class AfterSaleServiceImpl implements AfterSaleService {
206 205
207 // 记录售后日志 206 // 记录售后日志
208 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus); 207 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus);
209 -  
210 - // TODO 发送售后消息  
211 } 208 }
212 209
213 @Override 210 @Override
@@ -226,8 +223,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -226,8 +223,6 @@ public class AfterSaleServiceImpl implements AfterSaleService {
226 // 记录售后日志 223 // 记录售后日志
227 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus); 224 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus);
228 225
229 - // TODO 发送售后消息  
230 -  
231 // 更新交易订单项的售后状态为【未申请】 226 // 更新交易订单项的售后状态为【未申请】
232 tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId()); 227 tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
233 } 228 }
@@ -281,8 +276,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -281,8 +276,6 @@ public class AfterSaleServiceImpl implements AfterSaleService {
281 AfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), 276 AfterSaleStatusEnum.BUYER_DELIVERY.getStatus(),
282 MapUtil.<String, Object>builder().put("deliveryName", express.getName()) 277 MapUtil.<String, Object>builder().put("deliveryName", express.getName())
283 .put("logisticsNo", deliveryReqVO.getLogisticsNo()).build()); 278 .put("logisticsNo", deliveryReqVO.getLogisticsNo()).build());
284 -  
285 - // TODO 发送售后消息  
286 } 279 }
287 280
288 @Override 281 @Override
@@ -299,8 +292,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -299,8 +292,6 @@ public class AfterSaleServiceImpl implements AfterSaleService {
299 // 记录售后日志 292 // 记录售后日志
300 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), 293 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
301 AfterSaleStatusEnum.WAIT_REFUND.getStatus()); 294 AfterSaleStatusEnum.WAIT_REFUND.getStatus());
302 -  
303 - // TODO 发送售后消息  
304 } 295 }
305 296
306 @Override 297 @Override
@@ -326,8 +317,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -326,8 +317,6 @@ public class AfterSaleServiceImpl implements AfterSaleService {
326 AfterSaleStatusEnum.SELLER_REFUSE.getStatus(), 317 AfterSaleStatusEnum.SELLER_REFUSE.getStatus(),
327 MapUtil.of("reason", refuseReqVO.getRefuseMemo())); 318 MapUtil.of("reason", refuseReqVO.getRefuseMemo()));
328 319
329 - // TODO 发送售后消息  
330 -  
331 // 更新交易订单项的售后状态为【未申请】 320 // 更新交易订单项的售后状态为【未申请】
332 tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId()); 321 tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
333 } 322 }
@@ -365,33 +354,90 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -365,33 +354,90 @@ public class AfterSaleServiceImpl implements AfterSaleService {
365 // 发起退款单。注意,需要在事务提交后,再进行发起,避免重复发起 354 // 发起退款单。注意,需要在事务提交后,再进行发起,避免重复发起
366 createPayRefund(userIp, afterSale); 355 createPayRefund(userIp, afterSale);
367 356
368 - // 更新售后单的状态为【已完成】  
369 - updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.WAIT_REFUND.getStatus(), new AfterSaleDO()  
370 - .setStatus(AfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(LocalDateTime.now()));  
371 -  
372 // 记录售后日志 357 // 记录售后日志
373 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), 358 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
374 - AfterSaleStatusEnum.COMPLETE.getStatus()); 359 + afterSale.getStatus()); // 特殊:这里状态不变,而是最终 updateAfterSaleRefunded 处理!!!
  360 + }
375 361
376 - // TODO 发送售后消息 362 + private void createPayRefund(String userIp, AfterSaleDO afterSale) {
  363 + // 创建退款单
  364 + PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties)
  365 + .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));
  366 + Long payRefundId = payRefundApi.createRefund(createReqDTO);
377 367
378 - // 更新交易订单项的售后状态为【已完成】  
379 - tradeOrderUpdateService.updateOrderItemWhenAfterSaleSuccess(afterSale.getOrderItemId(), afterSale.getRefundPrice()); 368 + // 更新售后单的退款单号
  369 + tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
380 } 370 }
381 371
382 - private void createPayRefund(String userIp, AfterSaleDO afterSale) {  
383 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {  
384 -  
385 - @Override  
386 - public void afterCommit() {  
387 - // 创建退款单  
388 - PayRefundCreateReqDTO createReqDTO = AfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties)  
389 - .setReason(StrUtil.format("退款【{}】", afterSale.getSpuName()));  
390 - Long payRefundId = payRefundApi.createRefund(createReqDTO);  
391 - // 更新售后单的退款单号  
392 - tradeAfterSaleMapper.updateById(new AfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));  
393 - }  
394 - }); 372 + @Override
  373 + @Transactional(rollbackFor = Exception.class)
  374 + @AfterSaleLog(operateType = AfterSaleOperateTypeEnum.SYSTEM_REFUND_SUCCESS)
  375 + public void updateAfterSaleRefunded(Long id, Long orderId, Long payRefundId) {
  376 + // 1. 校验售后单的状态,并状态待退款
  377 + AfterSaleDO afterSale = tradeAfterSaleMapper.selectById(id);
  378 + if (afterSale == null) {
  379 + throw exception(AFTER_SALE_NOT_FOUND);
  380 + }
  381 + if (ObjectUtil.notEqual(afterSale.getStatus(), AfterSaleStatusEnum.WAIT_REFUND.getStatus())) {
  382 + throw exception(AFTER_SALE_REFUND_FAIL_STATUS_NOT_WAIT_REFUND);
  383 + }
  384 +
  385 + // 2. 校验退款单
  386 + PayRefundRespDTO payRefund = validatePayRefund(afterSale, payRefundId);
  387 +
  388 + // 3. 处理退款结果
  389 + if (PayRefundStatusEnum.isSuccess(payRefund.getStatus())) {
  390 + // 【情况一:退款成功】
  391 + updateAfterSaleStatus(afterSale.getId(), AfterSaleStatusEnum.WAIT_REFUND.getStatus(), new AfterSaleDO()
  392 + .setStatus(AfterSaleStatusEnum.COMPLETE.getStatus()).setRefundTime(LocalDateTime.now()));
  393 +
  394 + // 记录售后日志
  395 + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), AfterSaleStatusEnum.COMPLETE.getStatus());
  396 +
  397 + // 更新交易订单项的售后状态为【已完成】
  398 + tradeOrderUpdateService.updateOrderItemWhenAfterSaleSuccess(afterSale.getOrderItemId(), afterSale.getRefundPrice());
  399 + // 【情况二:退款失败】
  400 + } else if (PayRefundStatusEnum.isFailure(payRefund.getStatus())) {
  401 + // 记录售后日志
  402 + AfterSaleLogUtils.setAfterSaleOperateType(AfterSaleOperateTypeEnum.SYSTEM_REFUND_FAIL);
  403 + AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), afterSale.getStatus());
  404 + }
  405 + }
  406 +
  407 + /**
  408 + * 校验退款单的合法性
  409 + *
  410 + * @param afterSale 售后单
  411 + * @param payRefundId 退款单编号
  412 + * @return 退款单
  413 + */
  414 + private PayRefundRespDTO validatePayRefund(AfterSaleDO afterSale, Long payRefundId) {
  415 + // 1. 校验退款单是否存在
  416 + PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId);
  417 + if (payRefund == null) {
  418 + log.error("[validatePayRefund][afterSale({}) payRefund({}) 不存在,请进行处理!]", afterSale.getId(), payRefundId);
  419 + throw exception(AFTER_SALE_REFUND_FAIL_REFUND_NOT_FOUND);
  420 + }
  421 + // 2.1 校验退款单无退款结果(成功、失败)
  422 + if (!PayRefundStatusEnum.isSuccess(payRefund.getStatus())
  423 + && !PayRefundStatusEnum.isFailure(payRefund.getStatus())) {
  424 + log.error("[validatePayRefund][afterSale({}) payRefund({}) 无退款结果,请进行处理!payRefund 数据是:{}]",
  425 + afterSale.getId(), payRefundId, toJsonString(payRefund));
  426 + throw exception(AFTER_SALE_REFUND_FAIL_REFUND_NOT_SUCCESS_OR_FAILURE);
  427 + }
  428 + // 2.2 校验退款金额一致
  429 + if (ObjectUtil.notEqual(payRefund.getRefundPrice(), afterSale.getRefundPrice())) {
  430 + log.error("[validatePayRefund][afterSale({}) payRefund({}) 退款金额不匹配,请进行处理!afterSale 数据是:{},payRefund 数据是:{}]",
  431 + afterSale.getId(), payRefundId, toJsonString(afterSale), toJsonString(payRefund));
  432 + throw exception(AFTER_SALE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH);
  433 + }
  434 + // 2.3 校验退款订单匹配(二次)
  435 + if (ObjectUtil.notEqual(payRefund.getMerchantRefundId(), afterSale.getId().toString())) {
  436 + log.error("[validatePayRefund][afterSale({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]",
  437 + afterSale.getId(), payRefundId, toJsonString(payRefund));
  438 + throw exception(AFTER_SALE_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
  439 + }
  440 + return payRefund;
395 } 441 }
396 442
397 @Override 443 @Override
@@ -417,8 +463,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -417,8 +463,6 @@ public class AfterSaleServiceImpl implements AfterSaleService {
417 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), 463 AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(),
418 AfterSaleStatusEnum.BUYER_CANCEL.getStatus()); 464 AfterSaleStatusEnum.BUYER_CANCEL.getStatus());
419 465
420 - // TODO 发送售后消息  
421 -  
422 // 更新交易订单项的售后状态为【未申请】 466 // 更新交易订单项的售后状态为【未申请】
423 tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId()); 467 tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId());
424 } 468 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageWithdrawServiceImpl.java
@@ -15,9 +15,6 @@ import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi; @@ -15,9 +15,6 @@ import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi;
15 import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletRespDTO; 15 import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletRespDTO;
16 import cn.iocoder.yudao.module.pay.enums.PayChannelEnum; 16 import cn.iocoder.yudao.module.pay.enums.PayChannelEnum;
17 import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; 17 import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum;
18 -import cn.iocoder.yudao.module.system.api.social.SocialUserApi;  
19 -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO;  
20 -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;  
21 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; 18 import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO;
22 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; 19 import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO;
23 import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert; 20 import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert;
@@ -71,8 +68,6 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -71,8 +68,6 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
71 @Resource 68 @Resource
72 private PayTransferApi payTransferApi; 69 private PayTransferApi payTransferApi;
73 @Resource 70 @Resource
74 - private SocialUserApi socialUserApi;  
75 - @Resource  
76 private PayWalletApi payWalletApi; 71 private PayWalletApi payWalletApi;
77 72
78 @Resource 73 @Resource
@@ -140,13 +135,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -140,13 +135,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
140 if (Objects.equal(withdraw.getType(), BrokerageWithdrawTypeEnum.ALIPAY_API.getType())) { 135 if (Objects.equal(withdraw.getType(), BrokerageWithdrawTypeEnum.ALIPAY_API.getType())) {
141 channelCode = PayChannelEnum.ALIPAY_PC.getCode(); 136 channelCode = PayChannelEnum.ALIPAY_PC.getCode();
142 } else if (Objects.equal(withdraw.getType(), BrokerageWithdrawTypeEnum.WECHAT_API.getType())) { 137 } else if (Objects.equal(withdraw.getType(), BrokerageWithdrawTypeEnum.WECHAT_API.getType())) {
143 - SocialUserRespDTO socialUser = socialUserApi.getSocialUserByUserId(  
144 - UserTypeEnum.MEMBER.getValue(), withdraw.getUserId(), SocialTypeEnum.WECHAT_MINI_PROGRAM.getType());  
145 - if (socialUser == null) {  
146 - throw exception(BROKERAGE_WITHDRAW_TRANSFER_FAIL_WECHAT_NOT_BIND);  
147 - }  
148 channelCode = PayChannelEnum.WX_LITE.getCode(); 138 channelCode = PayChannelEnum.WX_LITE.getCode();
149 - userAccount = socialUser.getOpenid(); 139 + userAccount = withdraw.getUserAccount();
150 // 特殊:微信需要有报备信息 140 // 特殊:微信需要有报备信息
151 channelExtras = PayTransferCreateReqDTO.buildWeiXinChannelExtra1000("佣金提现", "佣金提现"); 141 channelExtras = PayTransferCreateReqDTO.buildWeiXinChannelExtra1000("佣金提现", "佣金提现");
152 } else if (Objects.equal(withdraw.getType(), BrokerageWithdrawTypeEnum.WALLET.getType())) { 142 } else if (Objects.equal(withdraw.getType(), BrokerageWithdrawTypeEnum.WALLET.getType())) {
@@ -158,7 +148,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -158,7 +148,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
158 // 1.2 构建请求 148 // 1.2 构建请求
159 PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO() 149 PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO()
160 .setAppKey(tradeOrderProperties.getPayAppKey()).setChannelCode(channelCode) 150 .setAppKey(tradeOrderProperties.getPayAppKey()).setChannelCode(channelCode)
161 - .setMerchantOrderId(withdraw.getId().toString()).setSubject("佣金提现").setPrice(withdraw.getPrice()) 151 + .setMerchantTransferId(withdraw.getId().toString()).setSubject("佣金提现").setPrice(withdraw.getPrice())
162 .setUserAccount(userAccount).setUserName(userName).setUserIp(getClientIP()) 152 .setUserAccount(userAccount).setUserName(userName).setUserIp(getClientIP())
163 .setChannelExtras(channelExtras); 153 .setChannelExtras(channelExtras);
164 // 1.3 发起请求 154 // 1.3 发起请求
@@ -293,7 +283,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { @@ -293,7 +283,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService {
293 throw exception(BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH); 283 throw exception(BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH);
294 } 284 }
295 // 2.3 校验转账订单匹配 285 // 2.3 校验转账订单匹配
296 - if (ObjectUtil.notEqual(payTransfer.getMerchantOrderId(), withdraw.getId().toString())) { 286 + if (ObjectUtil.notEqual(payTransfer.getMerchantTransferId(), withdraw.getId().toString())) {
297 log.error("[validateBrokerageTransferStatusCanUpdate][withdraw({}) 转账单不匹配({}),请进行处理!payTransfer 数据是:{}]", 287 log.error("[validateBrokerageTransferStatusCanUpdate][withdraw({}) 转账单不匹配({}),请进行处理!payTransfer 数据是:{}]",
298 withdraw.getId(), payTransferId, JsonUtils.toJsonString(payTransfer)); 288 withdraw.getId(), payTransferId, JsonUtils.toJsonString(payTransfer));
299 throw exception(BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_MERCHANT_EXISTS); 289 throw exception(BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_MERCHANT_EXISTS);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java
@@ -210,6 +210,14 @@ public interface TradeOrderUpdateService { @@ -210,6 +210,14 @@ public interface TradeOrderUpdateService {
210 void cancelPaidOrder(Long userId, Long orderId, Integer cancelType); 210 void cancelPaidOrder(Long userId, Long orderId, Integer cancelType);
211 211
212 /** 212 /**
  213 + * 取消支付订单的退款回调
  214 + *
  215 + * @param id 订单编号
  216 + * @param payRefundId 支付退款编号
  217 + */
  218 + void updatePaidOrderRefunded(Long id, Long payRefundId);
  219 +
  220 + /**
213 * 更新下单赠送的优惠券编号到订单 221 * 更新下单赠送的优惠券编号到订单
214 * 222 *
215 * @param userId 用户编号 223 * @param userId 用户编号
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -20,7 +20,9 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; @@ -20,7 +20,9 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
20 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; 20 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
21 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; 21 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
22 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 22 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
  23 +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
23 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 24 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
  25 +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
24 import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; 26 import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
25 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; 27 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
26 import cn.iocoder.yudao.module.system.api.social.SocialClientApi; 28 import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
@@ -938,11 +940,25 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -938,11 +940,25 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
938 .setAppKey(tradeOrderProperties.getPayAppKey()) // 支付应用 940 .setAppKey(tradeOrderProperties.getPayAppKey()) // 支付应用
939 .setUserIp(NetUtil.getLocalhostStr()) // 使用本机 IP,因为是服务器发起退款的 941 .setUserIp(NetUtil.getLocalhostStr()) // 使用本机 IP,因为是服务器发起退款的
940 .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号 942 .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
941 - .setMerchantRefundId(String.valueOf(order.getId())) 943 + // 特殊:因为订单支持 AfterSale 单个售后退款,也支持整单退款,所以需要通过 order- 进行下区分
  944 + // 具体可见 AfterSaleController 的 updateAfterSaleRefunded 方法
  945 + .setMerchantRefundId("order-" + order.getId())
942 .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice())); // 价格信息 946 .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice())); // 价格信息
943 } 947 }
944 948
945 @Override 949 @Override
  950 + public void updatePaidOrderRefunded(Long id, Long payRefundId) {
  951 + PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId);
  952 + if (payRefund == null) {
  953 + throw exception(ORDER_UPDATE_PAID_ORDER_REFUNDED_FAIL_REFUND_NOT_FOUND);
  954 + }
  955 + // 特殊:因为在 cancelPaidOrder 已经进行订单的取消,所以这里必须退款成功!!!
  956 + if (!PayRefundStatusEnum.isSuccess(payRefund.getStatus())) {
  957 + throw exception(ORDER_UPDATE_PAID_ORDER_REFUNDED_FAIL_REFUND_STATUS_NOT_SUCCESS);
  958 + }
  959 + }
  960 +
  961 + @Override
946 public void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds) { 962 public void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds) {
947 // 1. 检验订单存在 963 // 1. 检验订单存在
948 TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId); 964 TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/notify/dto/PayRefundNotifyReqDTO.java
@@ -26,6 +26,12 @@ public class PayRefundNotifyReqDTO { @@ -26,6 +26,12 @@ public class PayRefundNotifyReqDTO {
26 private String merchantOrderId; 26 private String merchantOrderId;
27 27
28 /** 28 /**
  29 + * 商户退款编号
  30 + */
  31 + @NotEmpty(message = "商户退款编号不能为空")
  32 + private String merchantRefundId;
  33 +
  34 + /**
29 * 支付退款编号 35 * 支付退款编号
30 */ 36 */
31 @NotNull(message = "支付退款编号不能为空") 37 @NotNull(message = "支付退款编号不能为空")
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/notify/dto/PayTransferNotifyReqDTO.java
@@ -23,7 +23,7 @@ public class PayTransferNotifyReqDTO { @@ -23,7 +23,7 @@ public class PayTransferNotifyReqDTO {
23 * 商户转账单号 23 * 商户转账单号
24 */ 24 */
25 @NotEmpty(message = "商户转账单号不能为空") 25 @NotEmpty(message = "商户转账单号不能为空")
26 - private String merchantOrderId; 26 + private String merchantTransferId;
27 27
28 /** 28 /**
29 * 转账订单编号 29 * 转账订单编号
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundRespDTO.java
@@ -43,8 +43,23 @@ public class PayRefundRespDTO { @@ -43,8 +43,23 @@ public class PayRefundRespDTO {
43 */ 43 */
44 private String merchantOrderId; 44 private String merchantOrderId;
45 /** 45 /**
  46 + * 商户退款编号
  47 + */
  48 + private String merchantRefundId;
  49 + /**
46 * 退款成功时间 50 * 退款成功时间
47 */ 51 */
48 private LocalDateTime successTime; 52 private LocalDateTime successTime;
49 53
  54 + // ========== 渠道相关字段 ==========
  55 +
  56 + /**
  57 + * 调用渠道的错误码
  58 + */
  59 + private String channelErrorCode;
  60 + /**
  61 + * 调用渠道的错误提示
  62 + */
  63 + private String channelErrorMsg;
  64 +
50 } 65 }
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferCreateReqDTO.java
@@ -47,7 +47,7 @@ public class PayTransferCreateReqDTO { @@ -47,7 +47,7 @@ public class PayTransferCreateReqDTO {
47 * 商户转账单编号 47 * 商户转账单编号
48 */ 48 */
49 @NotEmpty(message = "商户转账单编号能为空") 49 @NotEmpty(message = "商户转账单编号能为空")
50 - private String merchantOrderId; 50 + private String merchantTransferId;
51 51
52 /** 52 /**
53 * 转账金额,单位:分 53 * 转账金额,单位:分
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferRespDTO.java
@@ -31,7 +31,7 @@ public class PayTransferRespDTO { @@ -31,7 +31,7 @@ public class PayTransferRespDTO {
31 /** 31 /**
32 * 商户转账单编号 32 * 商户转账单编号
33 */ 33 */
34 - private String merchantOrderId; 34 + private String merchantTransferId;
35 35
36 // ========== 转账相关字段 ========== 36 // ========== 转账相关字段 ==========
37 37
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/refund/PayRefundApiImpl.java
1 package cn.iocoder.yudao.module.pay.api.refund; 1 package cn.iocoder.yudao.module.pay.api.refund;
2 2
  3 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
3 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 4 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
4 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; 5 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
5 -import cn.iocoder.yudao.module.pay.convert.refund.PayRefundConvert; 6 +import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
6 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; 7 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
  8 +import jakarta.annotation.Resource;
7 import org.springframework.stereotype.Service; 9 import org.springframework.stereotype.Service;
8 import org.springframework.validation.annotation.Validated; 10 import org.springframework.validation.annotation.Validated;
9 11
10 -import jakarta.annotation.Resource;  
11 -  
12 /** 12 /**
13 * 退款单 API 实现类 13 * 退款单 API 实现类
14 * 14 *
@@ -23,12 +23,13 @@ public class PayRefundApiImpl implements PayRefundApi { @@ -23,12 +23,13 @@ public class PayRefundApiImpl implements PayRefundApi {
23 23
24 @Override 24 @Override
25 public Long createRefund(PayRefundCreateReqDTO reqDTO) { 25 public Long createRefund(PayRefundCreateReqDTO reqDTO) {
26 - return payRefundService.createPayRefund(reqDTO); 26 + return payRefundService.createRefund(reqDTO);
27 } 27 }
28 28
29 @Override 29 @Override
30 public PayRefundRespDTO getRefund(Long id) { 30 public PayRefundRespDTO getRefund(Long id) {
31 - return PayRefundConvert.INSTANCE.convert02(payRefundService.getRefund(id)); 31 + PayRefundDO refund = payRefundService.getRefund(id);
  32 + return BeanUtils.toBean(refund, PayRefundRespDTO.class);
32 } 33 }
33 34
34 } 35 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoOrderController.java
@@ -66,7 +66,9 @@ public class PayDemoOrderController { @@ -66,7 +66,9 @@ public class PayDemoOrderController {
66 @Operation(summary = "更新示例订单为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob 66 @Operation(summary = "更新示例订单为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob
67 @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 67 @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现
68 public CommonResult<Boolean> updateDemoOrderRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { 68 public CommonResult<Boolean> updateDemoOrderRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {
69 - payDemoOrderService.updateDemoOrderRefunded(Long.valueOf(notifyReqDTO.getMerchantOrderId()), 69 + payDemoOrderService.updateDemoOrderRefunded(
  70 + Long.valueOf(notifyReqDTO.getMerchantOrderId()),
  71 + notifyReqDTO.getMerchantRefundId(),
70 notifyReqDTO.getPayRefundId()); 72 notifyReqDTO.getPayRefundId());
71 return success(true); 73 return success(true);
72 } 74 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoWithdrawController.java
@@ -55,7 +55,7 @@ public class PayDemoWithdrawController { @@ -55,7 +55,7 @@ public class PayDemoWithdrawController {
55 @Operation(summary = "更新示例提现单的转账状态") // 由 pay-module 转账服务,进行回调 55 @Operation(summary = "更新示例提现单的转账状态") // 由 pay-module 转账服务,进行回调
56 @PermitAll // 无需登录,安全由 PayDemoTransferService 内部校验实现 56 @PermitAll // 无需登录,安全由 PayDemoTransferService 内部校验实现
57 public CommonResult<Boolean> updateDemoWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { 57 public CommonResult<Boolean> updateDemoWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) {
58 - demoWithdrawService.updateDemoWithdrawTransferred(Long.valueOf(notifyReqDTO.getMerchantOrderId()), 58 + demoWithdrawService.updateDemoWithdrawTransferred(Long.valueOf(notifyReqDTO.getMerchantTransferId()),
59 notifyReqDTO.getPayTransferId()); 59 notifyReqDTO.getPayTransferId());
60 return success(true); 60 return success(true);
61 } 61 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.controller.admin.notify; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.pay.controller.admin.notify;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 7 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
@@ -11,7 +12,6 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; @@ -11,7 +12,6 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
11 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO; 12 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
12 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; 13 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
13 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO; 14 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;
14 -import cn.iocoder.yudao.module.pay.convert.notify.PayNotifyTaskConvert;  
15 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; 15 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
16 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; 16 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
17 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; 17 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
@@ -138,7 +138,12 @@ public class PayNotifyController { @@ -138,7 +138,12 @@ public class PayNotifyController {
138 // 拼接返回 138 // 拼接返回
139 PayAppDO app = appService.getApp(task.getAppId()); 139 PayAppDO app = appService.getApp(task.getAppId());
140 List<PayNotifyLogDO> logs = notifyService.getNotifyLogList(id); 140 List<PayNotifyLogDO> logs = notifyService.getNotifyLogList(id);
141 - return success(PayNotifyTaskConvert.INSTANCE.convert(task, app, logs)); 141 + return success(BeanUtils.toBean(task, PayNotifyTaskDetailRespVO.class, respVO -> {
  142 + if (app != null) {
  143 + respVO.setAppName(app.getName());
  144 + }
  145 + respVO.setLogs(BeanUtils.toBean(logs, PayNotifyTaskDetailRespVO.Log.class));
  146 + }));
142 } 147 }
143 148
144 @GetMapping("/page") 149 @GetMapping("/page")
@@ -150,8 +155,15 @@ public class PayNotifyController { @@ -150,8 +155,15 @@ public class PayNotifyController {
150 return success(PageResult.empty()); 155 return success(PageResult.empty());
151 } 156 }
152 // 拼接返回 157 // 拼接返回
153 - Map<Long, PayAppDO> appMap = appService.getAppMap(convertList(pageResult.getList(), PayNotifyTaskDO::getAppId));  
154 - return success(PayNotifyTaskConvert.INSTANCE.convertPage(pageResult, appMap)); 158 + Map<Long, PayAppDO> apps = appService.getAppMap(convertList(pageResult.getList(), PayNotifyTaskDO::getAppId));
  159 +
  160 + // 转换对象
  161 + return success(BeanUtils.toBean(pageResult, PayNotifyTaskRespVO.class, order -> {
  162 + PayAppDO app = apps.get(order.getAppId());
  163 + if (app != null) {
  164 + order.setAppName(app.getName());
  165 + }
  166 + }));
155 } 167 }
156 168
157 } 169 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskBaseVO.java deleted
1 -package cn.iocoder.yudao.module.pay.controller.admin.notify.vo;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.Data;  
5 -  
6 -import java.time.LocalDateTime;  
7 -  
8 -/**  
9 - * 回调通知 Base VO,提供给添加、修改、详细的子 VO 使用  
10 - * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成  
11 - */  
12 -@Data  
13 -public class PayNotifyTaskBaseVO {  
14 -  
15 - @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10636")  
16 - private Long appId;  
17 -  
18 - @Schema(description = "通知类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
19 - private Byte type;  
20 -  
21 - @Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6722")  
22 - private Long dataId;  
23 -  
24 - @Schema(description = "通知状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
25 - private Byte status;  
26 -  
27 - @Schema(description = "商户订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26697")  
28 - private String merchantOrderId;  
29 -  
30 - @Schema(description = "下一次通知时间", requiredMode = Schema.RequiredMode.REQUIRED)  
31 - private LocalDateTime nextNotifyTime;  
32 -  
33 - @Schema(description = "最后一次执行时间", requiredMode = Schema.RequiredMode.REQUIRED)  
34 - private LocalDateTime lastExecuteTime;  
35 -  
36 - @Schema(description = "当前通知次数", requiredMode = Schema.RequiredMode.REQUIRED)  
37 - private Byte notifyTimes;  
38 -  
39 - @Schema(description = "最大可通知次数", requiredMode = Schema.RequiredMode.REQUIRED)  
40 - private Byte maxNotifyTimes;  
41 -  
42 - @Schema(description = "异步通知地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn")  
43 - private String notifyUrl;  
44 -  
45 -}  
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskDetailRespVO.java
@@ -13,19 +13,7 @@ import java.util.List; @@ -13,19 +13,7 @@ import java.util.List;
13 @Data 13 @Data
14 @EqualsAndHashCode(callSuper = true) 14 @EqualsAndHashCode(callSuper = true)
15 @ToString(callSuper = true) 15 @ToString(callSuper = true)
16 -public class PayNotifyTaskDetailRespVO extends PayNotifyTaskBaseVO {  
17 -  
18 - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380")  
19 - private Long id;  
20 -  
21 - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)  
22 - private LocalDateTime createTime;  
23 -  
24 - @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)  
25 - private LocalDateTime updateTime;  
26 -  
27 - @Schema(description = "应用名称", example = "wx_pay")  
28 - private String appName; 16 +public class PayNotifyTaskDetailRespVO extends PayNotifyTaskRespVO {
29 17
30 @Schema(description = "回调日志列表") 18 @Schema(description = "回调日志列表")
31 private List<Log> logs; 19 private List<Log> logs;
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java
@@ -32,6 +32,12 @@ public class PayNotifyTaskPageReqVO extends PageParam { @@ -32,6 +32,12 @@ public class PayNotifyTaskPageReqVO extends PageParam {
32 @Schema(description = "商户订单编号", example = "26697") 32 @Schema(description = "商户订单编号", example = "26697")
33 private String merchantOrderId; 33 private String merchantOrderId;
34 34
  35 + @Schema(description = "商户退款编号", example = "26697")
  36 + private String merchantRefundId;
  37 +
  38 + @Schema(description = "商户转账编号", example = "26697")
  39 + private String merchantTransferId;
  40 +
35 @Schema(description = "创建时间") 41 @Schema(description = "创建时间")
36 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 42 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
37 private LocalDateTime[] createTime; 43 private LocalDateTime[] createTime;
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskRespVO.java
@@ -6,17 +6,51 @@ import java.time.LocalDateTime; @@ -6,17 +6,51 @@ import java.time.LocalDateTime;
6 6
7 @Schema(description = "管理后台 - 回调通知 Response VO") 7 @Schema(description = "管理后台 - 回调通知 Response VO")
8 @Data 8 @Data
9 -@EqualsAndHashCode(callSuper = true)  
10 -@ToString(callSuper = true)  
11 -public class PayNotifyTaskRespVO extends PayNotifyTaskBaseVO { 9 +public class PayNotifyTaskRespVO {
12 10
13 @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380") 11 @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380")
14 private Long id; 12 private Long id;
15 13
16 - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)  
17 - private LocalDateTime createTime; 14 + @Schema(description = "应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10636")
  15 + private Long appId;
18 16
19 @Schema(description = "应用名称", example = "wx_pay") 17 @Schema(description = "应用名称", example = "wx_pay")
20 private String appName; 18 private String appName;
21 19
  20 + @Schema(description = "通知类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  21 + private Byte type;
  22 +
  23 + @Schema(description = "数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6722")
  24 + private Long dataId;
  25 +
  26 + @Schema(description = "商户订单编号", example = "26697")
  27 + private String merchantOrderId;
  28 +
  29 + @Schema(description = "商户退款编号", example = "26697")
  30 + private String merchantRefundId;
  31 +
  32 + @Schema(description = "商户转账编号", example = "26697")
  33 + private String merchantTransferId;
  34 +
  35 + @Schema(description = "通知状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  36 + private Byte status;
  37 +
  38 + @Schema(description = "下一次通知时间", requiredMode = Schema.RequiredMode.REQUIRED)
  39 + private LocalDateTime nextNotifyTime;
  40 +
  41 + @Schema(description = "最后一次执行时间", requiredMode = Schema.RequiredMode.REQUIRED)
  42 + private LocalDateTime lastExecuteTime;
  43 +
  44 + @Schema(description = "当前通知次数", requiredMode = Schema.RequiredMode.REQUIRED)
  45 + private Byte notifyTimes;
  46 +
  47 + @Schema(description = "最大可通知次数", requiredMode = Schema.RequiredMode.REQUIRED)
  48 + private Byte maxNotifyTimes;
  49 +
  50 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  51 + private LocalDateTime createTime;
  52 +
  53 + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
  54 + private LocalDateTime updateTime;
  55 +
22 } 56 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/wallet/PayWalletRechargeController.java
@@ -37,8 +37,7 @@ public class PayWalletRechargeController { @@ -37,8 +37,7 @@ public class PayWalletRechargeController {
37 return success(true); 37 return success(true);
38 } 38 }
39 39
40 - // TODO @jason:发起退款,要 post 操作哈;  
41 - @GetMapping("/refund") 40 + @PostMapping("/refund")
42 @Operation(summary = "发起钱包充值退款") 41 @Operation(summary = "发起钱包充值退款")
43 @Parameter(name = "id", description = "编号", required = true, example = "1024") 42 @Parameter(name = "id", description = "编号", required = true, example = "1024")
44 public CommonResult<Boolean> refundWalletRecharge(@RequestParam("id") Long id) { 43 public CommonResult<Boolean> refundWalletRecharge(@RequestParam("id") Long id) {
@@ -51,7 +50,9 @@ public class PayWalletRechargeController { @@ -51,7 +50,9 @@ public class PayWalletRechargeController {
51 @PermitAll // 无需登录, 内部校验实现 50 @PermitAll // 无需登录, 内部校验实现
52 public CommonResult<Boolean> updateWalletRechargeRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { 51 public CommonResult<Boolean> updateWalletRechargeRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {
53 walletRechargeService.updateWalletRechargeRefunded( 52 walletRechargeService.updateWalletRechargeRefunded(
54 - Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId()); 53 + Long.valueOf(notifyReqDTO.getMerchantOrderId()),
  54 + Long.valueOf(notifyReqDTO.getMerchantRefundId()),
  55 + notifyReqDTO.getPayRefundId());
55 return success(true); 56 return success(true);
56 } 57 }
57 58
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/notify/PayNotifyTaskConvert.java deleted
1 -package cn.iocoder.yudao.module.pay.convert.notify;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;  
5 -import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;  
6 -import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO;  
7 -import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;  
8 -import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;  
9 -import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;  
10 -import org.mapstruct.Mapper;  
11 -import org.mapstruct.factory.Mappers;  
12 -  
13 -import java.util.List;  
14 -import java.util.Map;  
15 -  
16 -/**  
17 - * 支付通知 Convert  
18 - *  
19 - * @author 芋道源码  
20 - */  
21 -@Mapper  
22 -public interface PayNotifyTaskConvert {  
23 -  
24 - PayNotifyTaskConvert INSTANCE = Mappers.getMapper(PayNotifyTaskConvert.class);  
25 -  
26 - PayNotifyTaskRespVO convert(PayNotifyTaskDO bean);  
27 -  
28 - default PageResult<PayNotifyTaskRespVO> convertPage(PageResult<PayNotifyTaskDO> page, Map<Long, PayAppDO> appMap){  
29 - PageResult<PayNotifyTaskRespVO> result = convertPage(page);  
30 - result.getList().forEach(order -> MapUtils.findAndThen(appMap, order.getAppId(), app -> order.setAppName(app.getName())));  
31 - return result;  
32 - }  
33 - PageResult<PayNotifyTaskRespVO> convertPage(PageResult<PayNotifyTaskDO> page);  
34 -  
35 - default PayNotifyTaskDetailRespVO convert(PayNotifyTaskDO task, PayAppDO app, List<PayNotifyLogDO> logs) {  
36 - PayNotifyTaskDetailRespVO respVO = convert(task, logs);  
37 - if (app != null) {  
38 - respVO.setAppName(app.getName());  
39 - }  
40 - return respVO;  
41 - }  
42 - PayNotifyTaskDetailRespVO convert(PayNotifyTaskDO task, List<PayNotifyLogDO> logs);  
43 -}  
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java
@@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
6 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 6 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
7 -import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;  
8 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundDetailsRespVO; 7 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundDetailsRespVO;
9 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExcelVO; 8 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExcelVO;
10 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageItemRespVO; 9 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageItemRespVO;
@@ -42,8 +41,6 @@ public interface PayRefundConvert { @@ -42,8 +41,6 @@ public interface PayRefundConvert {
42 41
43 PayRefundDO convert(PayRefundCreateReqDTO bean); 42 PayRefundDO convert(PayRefundCreateReqDTO bean);
44 43
45 - PayRefundRespDTO convert02(PayRefundDO bean);  
46 -  
47 default List<PayRefundExcelVO> convertList(List<PayRefundDO> list, Map<Long, PayAppDO> appMap) { 44 default List<PayRefundExcelVO> convertList(List<PayRefundDO> list, Map<Long, PayAppDO> appMap) {
48 return CollectionUtils.convertList(list, order -> { 45 return CollectionUtils.convertList(list, order -> {
49 PayRefundExcelVO excelVO = convertExcel(order); 46 PayRefundExcelVO excelVO = convertExcel(order);
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
4 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; 4 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
  7 +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO;
7 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; 8 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
8 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; 9 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
9 import com.baomidou.mybatisplus.annotation.KeySequence; 10 import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -60,6 +61,7 @@ public class PayNotifyTaskDO extends TenantBaseDO { @@ -60,6 +61,7 @@ public class PayNotifyTaskDO extends TenantBaseDO {
60 * 61 *
61 * 1. {@link PayNotifyTypeEnum#ORDER} 时,关联 {@link PayOrderDO#getId()} 62 * 1. {@link PayNotifyTypeEnum#ORDER} 时,关联 {@link PayOrderDO#getId()}
62 * 2. {@link PayNotifyTypeEnum#REFUND} 时,关联 {@link PayRefundDO#getId()} 63 * 2. {@link PayNotifyTypeEnum#REFUND} 时,关联 {@link PayRefundDO#getId()}
  64 + * 3. {@link PayNotifyTypeEnum#TRANSFER} 时,关联 {@link PayTransferDO#getId()}
63 */ 65 */
64 private Long dataId; 66 private Long dataId;
65 /** 67 /**
@@ -67,6 +69,14 @@ public class PayNotifyTaskDO extends TenantBaseDO { @@ -67,6 +69,14 @@ public class PayNotifyTaskDO extends TenantBaseDO {
67 */ 69 */
68 private String merchantOrderId; 70 private String merchantOrderId;
69 /** 71 /**
  72 + * 商户退款编号
  73 + */
  74 + private String merchantRefundId;
  75 + /**
  76 + * 商户转账编号
  77 + */
  78 + private String merchantTransferId;
  79 + /**
70 * 通知状态 80 * 通知状态
71 * 81 *
72 * 枚举 {@link PayNotifyStatusEnum} 82 * 枚举 {@link PayNotifyStatusEnum}
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/transfer/PayTransferDO.java
@@ -63,7 +63,7 @@ public class PayTransferDO extends BaseDO { @@ -63,7 +63,7 @@ public class PayTransferDO extends BaseDO {
63 * 63 *
64 * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一 64 * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
65 */ 65 */
66 - private String merchantOrderId; 66 + private String merchantTransferId;
67 67
68 // ========== 转账相关字段 ========== 68 // ========== 转账相关字段 ==========
69 69
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/notify/PayNotifyTaskMapper.java
@@ -37,6 +37,8 @@ public interface PayNotifyTaskMapper extends BaseMapperX&lt;PayNotifyTaskDO&gt; { @@ -37,6 +37,8 @@ public interface PayNotifyTaskMapper extends BaseMapperX&lt;PayNotifyTaskDO&gt; {
37 .eqIfPresent(PayNotifyTaskDO::getDataId, reqVO.getDataId()) 37 .eqIfPresent(PayNotifyTaskDO::getDataId, reqVO.getDataId())
38 .eqIfPresent(PayNotifyTaskDO::getStatus, reqVO.getStatus()) 38 .eqIfPresent(PayNotifyTaskDO::getStatus, reqVO.getStatus())
39 .eqIfPresent(PayNotifyTaskDO::getMerchantOrderId, reqVO.getMerchantOrderId()) 39 .eqIfPresent(PayNotifyTaskDO::getMerchantOrderId, reqVO.getMerchantOrderId())
  40 + .eqIfPresent(PayNotifyTaskDO::getMerchantRefundId, reqVO.getMerchantRefundId())
  41 + .eqIfPresent(PayNotifyTaskDO::getMerchantTransferId, reqVO.getMerchantTransferId())
40 .betweenIfPresent(PayNotifyTaskDO::getCreateTime, reqVO.getCreateTime()) 42 .betweenIfPresent(PayNotifyTaskDO::getCreateTime, reqVO.getCreateTime())
41 .orderByDesc(PayNotifyTaskDO::getId)); 43 .orderByDesc(PayNotifyTaskDO::getId));
42 } 44 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java
@@ -28,7 +28,7 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; { @@ -28,7 +28,7 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; {
28 28
29 default PayTransferDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) { 29 default PayTransferDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) {
30 return selectOne(PayTransferDO::getAppId, appId, 30 return selectOne(PayTransferDO::getAppId, appId,
31 - PayTransferDO::getMerchantOrderId, merchantOrderId); 31 + PayTransferDO::getMerchantTransferId, merchantOrderId);
32 } 32 }
33 33
34 default PageResult<PayTransferDO> selectPage(PayTransferPageReqVO reqVO) { 34 default PageResult<PayTransferDO> selectPage(PayTransferPageReqVO reqVO) {
@@ -36,7 +36,7 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; { @@ -36,7 +36,7 @@ public interface PayTransferMapper extends BaseMapperX&lt;PayTransferDO&gt; {
36 .eqIfPresent(PayTransferDO::getNo, reqVO.getNo()) 36 .eqIfPresent(PayTransferDO::getNo, reqVO.getNo())
37 .eqIfPresent(PayTransferDO::getAppId, reqVO.getAppId()) 37 .eqIfPresent(PayTransferDO::getAppId, reqVO.getAppId())
38 .eqIfPresent(PayTransferDO::getChannelCode, reqVO.getChannelCode()) 38 .eqIfPresent(PayTransferDO::getChannelCode, reqVO.getChannelCode())
39 - .eqIfPresent(PayTransferDO::getMerchantOrderId, reqVO.getMerchantOrderId()) 39 + .eqIfPresent(PayTransferDO::getMerchantTransferId, reqVO.getMerchantOrderId())
40 .eqIfPresent(PayTransferDO::getStatus, reqVO.getStatus()) 40 .eqIfPresent(PayTransferDO::getStatus, reqVO.getStatus())
41 .likeIfPresent(PayTransferDO::getUserName, reqVO.getUserName()) 41 .likeIfPresent(PayTransferDO::getUserName, reqVO.getUserName())
42 .eqIfPresent(PayTransferDO::getChannelTransferNo, reqVO.getChannelTransferNo()) 42 .eqIfPresent(PayTransferDO::getChannelTransferNo, reqVO.getChannelTransferNo())
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderService.java
@@ -59,8 +59,9 @@ public interface PayDemoOrderService { @@ -59,8 +59,9 @@ public interface PayDemoOrderService {
59 * 更新示例订单为已退款 59 * 更新示例订单为已退款
60 * 60 *
61 * @param id 编号 61 * @param id 编号
  62 + * @param refundId 退款编号
62 * @param payRefundId 退款订单号 63 * @param payRefundId 退款订单号
63 */ 64 */
64 - void updateDemoOrderRefunded(Long id, Long payRefundId); 65 + void updateDemoOrderRefunded(Long id, String refundId, Long payRefundId);
65 66
66 } 67 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
@@ -184,7 +184,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { @@ -184,7 +184,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
184 184
185 // 2.1 生成退款单号 185 // 2.1 生成退款单号
186 // 一般来说,用户发起退款的时候,都会单独插入一个售后维权表,然后使用该表的 id 作为 refundId 186 // 一般来说,用户发起退款的时候,都会单独插入一个售后维权表,然后使用该表的 id 作为 refundId
187 - // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示 187 + // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示
188 String refundId = order.getId() + "-refund"; 188 String refundId = order.getId() + "-refund";
189 // 2.2 创建退款单 189 // 2.2 创建退款单
190 Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO() 190 Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO()
@@ -215,16 +215,18 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { @@ -215,16 +215,18 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
215 } 215 }
216 216
217 @Override 217 @Override
218 - public void updateDemoOrderRefunded(Long id, Long payRefundId) { 218 + public void updateDemoOrderRefunded(Long id, String refundId, Long payRefundId) {
219 // 1. 校验并获得退款订单(可退款) 219 // 1. 校验并获得退款订单(可退款)
220 - PayRefundRespDTO payRefund = validateDemoOrderCanRefunded(id, payRefundId); 220 + PayRefundRespDTO payRefund = validateDemoOrderCanRefunded(id, refundId, payRefundId);
221 // 2.2 更新退款单到 demo 订单 221 // 2.2 更新退款单到 demo 订单
222 payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(id) 222 payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(id)
223 .setRefundTime(payRefund.getSuccessTime())); 223 .setRefundTime(payRefund.getSuccessTime()));
224 } 224 }
225 225
226 - private PayRefundRespDTO validateDemoOrderCanRefunded(Long id, Long payRefundId) { 226 + private PayRefundRespDTO validateDemoOrderCanRefunded(Long id, String refundId, Long payRefundId) {
227 // 1.1 校验示例订单 227 // 1.1 校验示例订单
  228 + // 一般来说,这里应该用 refundId 来查询退款单,然后再校验订单是否匹配
  229 + // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id 来查询订单
228 PayDemoOrderDO order = payDemoOrderMapper.selectById(id); 230 PayDemoOrderDO order = payDemoOrderMapper.selectById(id);
229 if (order == null) { 231 if (order == null) {
230 throw exception(DEMO_ORDER_NOT_FOUND); 232 throw exception(DEMO_ORDER_NOT_FOUND);
@@ -241,7 +243,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { @@ -241,7 +243,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
241 if (payRefund == null) { 243 if (payRefund == null) {
242 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND); 244 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND);
243 } 245 }
244 - // 2.2 246 + // 2.2 必须是退款成功状态
245 if (!PayRefundStatusEnum.isSuccess(payRefund.getStatus())) { 247 if (!PayRefundStatusEnum.isSuccess(payRefund.getStatus())) {
246 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS); 248 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS);
247 } 249 }
@@ -252,7 +254,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { @@ -252,7 +254,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
252 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH); 254 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH);
253 } 255 }
254 // 2.4 校验退款订单匹配(二次) 256 // 2.4 校验退款订单匹配(二次)
255 - if (notEqual(payRefund.getMerchantOrderId(), id.toString())) { 257 + if (notEqual(payRefund.getMerchantRefundId(), id.toString() + "-refund")) {
256 log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]", 258 log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]",
257 id, payRefundId, toJsonString(payRefund)); 259 id, payRefundId, toJsonString(payRefund));
258 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR); 260 throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java
@@ -75,7 +75,7 @@ public class PayDemoTransferServiceImpl implements PayDemoWithdrawService { @@ -75,7 +75,7 @@ public class PayDemoTransferServiceImpl implements PayDemoWithdrawService {
75 // 2.1 创建支付单 75 // 2.1 创建支付单
76 PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO() 76 PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO()
77 .setAppKey(PAY_APP_KEY).setChannelCode(withdraw.getTransferChannelCode()).setUserIp(getClientIP()) // 支付应用 77 .setAppKey(PAY_APP_KEY).setChannelCode(withdraw.getTransferChannelCode()).setUserIp(getClientIP()) // 支付应用
78 - .setMerchantOrderId(String.valueOf(withdraw.getId())) // 业务的订单编号 78 + .setMerchantTransferId(String.valueOf(withdraw.getId())) // 业务的订单编号
79 .setSubject(withdraw.getSubject()).setPrice(withdraw.getPrice()) // 价格信息 79 .setSubject(withdraw.getSubject()).setPrice(withdraw.getPrice()) // 价格信息
80 .setUserAccount(withdraw.getUserAccount()).setUserName(withdraw.getUserName()); // 收款信息 80 .setUserAccount(withdraw.getUserAccount()).setUserName(withdraw.getUserName()); // 收款信息
81 if (ObjectUtil.equal(withdraw.getType(), PayDemoWithdrawTypeEnum.WECHAT.getType())) { 81 if (ObjectUtil.equal(withdraw.getType(), PayDemoWithdrawTypeEnum.WECHAT.getType())) {
@@ -177,7 +177,7 @@ public class PayDemoTransferServiceImpl implements PayDemoWithdrawService { @@ -177,7 +177,7 @@ public class PayDemoTransferServiceImpl implements PayDemoWithdrawService {
177 throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH); 177 throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH);
178 } 178 }
179 // 2.3 校验转账订单匹配(二次) 179 // 2.3 校验转账订单匹配(二次)
180 - if (ObjectUtil.notEqual(payTransfer.getMerchantOrderId(), withdraw.getId().toString())) { 180 + if (ObjectUtil.notEqual(payTransfer.getMerchantTransferId(), withdraw.getId().toString())) {
181 log.error("[validateDemoTransferStatusCanUpdate][withdraw({}) 转账单不匹配({}),请进行处理!payTransfer 数据是:{}]", 181 log.error("[validateDemoTransferStatusCanUpdate][withdraw({}) 转账单不匹配({}),请进行处理!payTransfer 数据是:{}]",
182 withdraw.getId(), payTransferId, JsonUtils.toJsonString(payTransfer)); 182 withdraw.getId(), payTransferId, JsonUtils.toJsonString(payTransfer));
183 throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_MERCHANT_EXISTS); 183 throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_MERCHANT_EXISTS);
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java
@@ -31,6 +31,7 @@ import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; @@ -31,6 +31,7 @@ import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService;
31 import com.google.common.annotations.VisibleForTesting; 31 import com.google.common.annotations.VisibleForTesting;
32 import lombok.extern.slf4j.Slf4j; 32 import lombok.extern.slf4j.Slf4j;
33 import org.springframework.context.annotation.Lazy; 33 import org.springframework.context.annotation.Lazy;
  34 +import org.springframework.scheduling.annotation.Async;
34 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 35 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
35 import org.springframework.stereotype.Service; 36 import org.springframework.stereotype.Service;
36 import org.springframework.transaction.annotation.Transactional; 37 import org.springframework.transaction.annotation.Transactional;
@@ -97,19 +98,19 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -97,19 +98,19 @@ public class PayNotifyServiceImpl implements PayNotifyService {
97 PayNotifyTaskDO task = new PayNotifyTaskDO().setType(type).setDataId(dataId); 98 PayNotifyTaskDO task = new PayNotifyTaskDO().setType(type).setDataId(dataId);
98 task.setStatus(PayNotifyStatusEnum.WAITING.getStatus()).setNextNotifyTime(LocalDateTime.now()) 99 task.setStatus(PayNotifyStatusEnum.WAITING.getStatus()).setNextNotifyTime(LocalDateTime.now())
99 .setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1); 100 .setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1);
100 - // 补充 appId + notifyUrl 字段 101 + // 补充 appId + notifyUrl + merchant* 字段
101 if (Objects.equals(task.getType(), PayNotifyTypeEnum.ORDER.getType())) { 102 if (Objects.equals(task.getType(), PayNotifyTypeEnum.ORDER.getType())) {
102 PayOrderDO order = orderService.getOrder(task.getDataId()); // 不进行非空判断,有问题直接异常 103 PayOrderDO order = orderService.getOrder(task.getDataId()); // 不进行非空判断,有问题直接异常
103 - task.setAppId(order.getAppId()).  
104 - setMerchantOrderId(order.getMerchantOrderId()).setNotifyUrl(order.getNotifyUrl()); 104 + task.setAppId(order.getAppId()).setNotifyUrl(order.getNotifyUrl())
  105 + .setMerchantOrderId(order.getMerchantOrderId());
105 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { 106 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) {
106 - PayRefundDO refundDO = refundService.getRefund(task.getDataId());  
107 - task.setAppId(refundDO.getAppId())  
108 - .setMerchantOrderId(refundDO.getMerchantOrderId()).setNotifyUrl(refundDO.getNotifyUrl()); 107 + PayRefundDO refund = refundService.getRefund(task.getDataId());
  108 + task.setAppId(refund.getAppId()).setNotifyUrl(refund.getNotifyUrl())
  109 + .setMerchantOrderId(refund.getMerchantOrderId()).setMerchantRefundId(refund.getMerchantRefundId());
109 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) { 110 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) {
110 PayTransferDO transfer = transferService.getTransfer(task.getDataId()); 111 PayTransferDO transfer = transferService.getTransfer(task.getDataId());
111 - task.setAppId(transfer.getAppId())  
112 - .setMerchantOrderId(transfer.getMerchantOrderId()).setNotifyUrl(transfer.getNotifyUrl()); 112 + task.setAppId(transfer.getAppId()).setNotifyUrl(transfer.getNotifyUrl())
  113 + .setMerchantTransferId(transfer.getMerchantTransferId());
113 } 114 }
114 115
115 // 执行插入 116 // 执行插入
@@ -117,10 +118,13 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -117,10 +118,13 @@ public class PayNotifyServiceImpl implements PayNotifyService {
117 118
118 // 必须在事务提交后,在发起任务,否则 PayNotifyTaskDO 还没入库,就提前回调接入的业务 119 // 必须在事务提交后,在发起任务,否则 PayNotifyTaskDO 还没入库,就提前回调接入的业务
119 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { 120 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
  121 +
120 @Override 122 @Override
121 public void afterCommit() { 123 public void afterCommit() {
122 - executeNotify(task); 124 + // 异步的原因:避免阻塞当前事务,无需等待结果
  125 + getSelf().executeNotifyAsync(task);
123 } 126 }
  127 +
124 }); 128 });
125 } 129 }
126 130
@@ -166,7 +170,17 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -166,7 +170,17 @@ public class PayNotifyServiceImpl implements PayNotifyService {
166 } 170 }
167 171
168 /** 172 /**
169 - * 同步执行单个支付通知 173 + * 异步执行单个支付通知
  174 + *
  175 + * @param task 通知任务
  176 + */
  177 + @Async
  178 + public void executeNotifyAsync(PayNotifyTaskDO task) {
  179 + executeNotify(task);
  180 + }
  181 +
  182 + /**
  183 + * 【加锁】执行单个支付通知
170 * 184 *
171 * @param task 通知任务 185 * @param task 通知任务
172 */ 186 */
@@ -223,9 +237,10 @@ public class PayNotifyServiceImpl implements PayNotifyService { @@ -223,9 +237,10 @@ public class PayNotifyServiceImpl implements PayNotifyService {
223 .payOrderId(task.getDataId()).build(); 237 .payOrderId(task.getDataId()).build();
224 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { 238 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) {
225 request = PayRefundNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId()) 239 request = PayRefundNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId())
  240 + .merchantRefundId(task.getMerchantRefundId())
226 .payRefundId(task.getDataId()).build(); 241 .payRefundId(task.getDataId()).build();
227 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) { 242 } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) {
228 - request = PayTransferNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId()) 243 + request = PayTransferNotifyReqDTO.builder().merchantTransferId(task.getMerchantTransferId())
229 .payTransferId(task.getDataId()).build(); 244 .payTransferId(task.getDataId()).build();
230 } else { 245 } else {
231 throw new RuntimeException("未知的通知任务类型:" + JsonUtils.toJsonString(task)); 246 throw new RuntimeException("未知的通知任务类型:" + JsonUtils.toJsonString(task));
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java
@@ -62,7 +62,7 @@ public interface PayRefundService { @@ -62,7 +62,7 @@ public interface PayRefundService {
62 * @param reqDTO 退款申请信息 62 * @param reqDTO 退款申请信息
63 * @return 退款单号 63 * @return 退款单号
64 */ 64 */
65 - Long createPayRefund(PayRefundCreateReqDTO reqDTO); 65 + Long createRefund(PayRefundCreateReqDTO reqDTO);
66 66
67 /** 67 /**
68 * 渠道的退款通知 68 * 渠道的退款通知
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
@@ -91,7 +91,7 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -91,7 +91,7 @@ public class PayRefundServiceImpl implements PayRefundService {
91 } 91 }
92 92
93 @Override 93 @Override
94 - public Long createPayRefund(PayRefundCreateReqDTO reqDTO) { 94 + public Long createRefund(PayRefundCreateReqDTO reqDTO) {
95 // 1.1 校验 App 95 // 1.1 校验 App
96 PayAppDO app = appService.validPayApp(reqDTO.getAppKey()); 96 PayAppDO app = appService.validPayApp(reqDTO.getAppKey());
97 // 1.2 校验支付订单 97 // 1.2 校验支付订单
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java
@@ -116,7 +116,7 @@ public class PayTransferServiceImpl implements PayTransferService { @@ -116,7 +116,7 @@ public class PayTransferServiceImpl implements PayTransferService {
116 } 116 }
117 117
118 private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO reqDTO, Long appId) { 118 private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO reqDTO, Long appId) {
119 - PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantOrderId(appId, reqDTO.getMerchantOrderId()); 119 + PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantOrderId(appId, reqDTO.getMerchantTransferId());
120 if (transfer != null) { 120 if (transfer != null) {
121 // 只有转账单状态为关闭,才能再次发起转账 121 // 只有转账单状态为关闭,才能再次发起转账
122 if (!PayTransferStatusEnum.isClosed(transfer.getStatus())) { 122 if (!PayTransferStatusEnum.isClosed(transfer.getStatus())) {
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeService.java
@@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; @@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; 5 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO;
7 -import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO;  
8 7
9 /** 8 /**
10 * 钱包充值 Service 接口 9 * 钱包充值 Service 接口
@@ -16,7 +15,7 @@ public interface PayWalletRechargeService { @@ -16,7 +15,7 @@ public interface PayWalletRechargeService {
16 /** 15 /**
17 * 创建钱包充值记录(发起充值) 16 * 创建钱包充值记录(发起充值)
18 * 17 *
19 - * @param userId 用户 id 18 + * @param userId 用户编号
20 * @param userType 用户类型 19 * @param userType 用户类型
21 * @param createReqVO 钱包充值请求 VO 20 * @param createReqVO 钱包充值请求 VO
22 * @param userIp 用户Ip 21 * @param userIp 用户Ip
@@ -40,8 +39,8 @@ public interface PayWalletRechargeService { @@ -40,8 +39,8 @@ public interface PayWalletRechargeService {
40 /** 39 /**
41 * 更新钱包充值成功 40 * 更新钱包充值成功
42 * 41 *
43 - * @param id 钱包充值记录 id  
44 - * @param payOrderId 支付订单 id 42 + * @param id 钱包充值记录编号
  43 + * @param payOrderId 支付订单编号
45 */ 44 */
46 void updateWalletRechargerPaid(Long id, Long payOrderId); 45 void updateWalletRechargerPaid(Long id, Long payOrderId);
47 46
@@ -56,9 +55,10 @@ public interface PayWalletRechargeService { @@ -56,9 +55,10 @@ public interface PayWalletRechargeService {
56 /** 55 /**
57 * 更新钱包充值记录为已退款 56 * 更新钱包充值记录为已退款
58 * 57 *
59 - * @param id 钱包充值 id 58 + * @param id 钱包充值记录编号
  59 + * @param refundId 钱包充值退款编号(实际和 id 相同)
60 * @param payRefundId 退款单id 60 * @param payRefundId 退款单id
61 */ 61 */
62 - void updateWalletRechargeRefunded(Long id, Long payRefundId); 62 + void updateWalletRechargeRefunded(Long id, Long refundId, Long payRefundId);
63 63
64 } 64 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java
@@ -8,10 +8,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; @@ -8,10 +8,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; 9 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
10 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 10 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
  11 +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
11 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 12 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
  13 +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
12 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; 14 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO;
13 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 15 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
14 -import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;  
15 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 16 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
16 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; 17 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO;
17 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO; 18 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargePackageDO;
@@ -21,7 +22,6 @@ import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; @@ -21,7 +22,6 @@ import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
21 import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; 22 import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum;
22 import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; 23 import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
23 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 24 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
24 -import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;  
25 import cn.iocoder.yudao.module.system.api.social.SocialClientApi; 25 import cn.iocoder.yudao.module.system.api.social.SocialClientApi;
26 import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; 26 import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO;
27 import jakarta.annotation.Resource; 27 import jakarta.annotation.Resource;
@@ -61,13 +61,15 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -61,13 +61,15 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
61 private PayWalletService payWalletService; 61 private PayWalletService payWalletService;
62 @Resource 62 @Resource
63 private PayOrderService payOrderService; 63 private PayOrderService payOrderService;
64 - @Resource  
65 - private PayRefundService payRefundService; 64 +// @Resource
  65 +// private PayRefundService payRefundService;
66 @Resource 66 @Resource
67 private PayWalletRechargePackageService payWalletRechargePackageService; 67 private PayWalletRechargePackageService payWalletRechargePackageService;
68 68
69 @Resource 69 @Resource
70 public SocialClientApi socialClientApi; 70 public SocialClientApi socialClientApi;
  71 + @Resource
  72 + private PayRefundApi payRefundApi;
71 73
72 @Resource 74 @Resource
73 private PayProperties payProperties; 75 private PayProperties payProperties;
@@ -186,7 +188,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -186,7 +188,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
186 // 3. 创建退款单 188 // 3. 创建退款单
187 String walletRechargeId = String.valueOf(id); 189 String walletRechargeId = String.valueOf(id);
188 String refundId = walletRechargeId + "-refund"; 190 String refundId = walletRechargeId + "-refund";
189 - Long payRefundId = payRefundService.createPayRefund(new PayRefundCreateReqDTO() 191 + Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO()
190 .setAppKey(payProperties.getWalletPayAppKey()).setUserIp(userIp) 192 .setAppKey(payProperties.getWalletPayAppKey()).setUserIp(userIp)
191 .setMerchantOrderId(walletRechargeId) 193 .setMerchantOrderId(walletRechargeId)
192 .setMerchantRefundId(refundId) 194 .setMerchantRefundId(refundId)
@@ -199,18 +201,20 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -199,18 +201,20 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
199 201
200 @Override 202 @Override
201 @Transactional(rollbackFor = Exception.class) 203 @Transactional(rollbackFor = Exception.class)
202 - public void updateWalletRechargeRefunded(Long id, Long payRefundId) { 204 + public void updateWalletRechargeRefunded(Long id, Long refundId, Long payRefundId) {
203 // 1.1 获取钱包充值记录 205 // 1.1 获取钱包充值记录
  206 + // 说明:因为 id 和 refundId 是相同的,所以直接使用 id 查询即可!
204 PayWalletRechargeDO walletRecharge = walletRechargeMapper.selectById(id); 207 PayWalletRechargeDO walletRecharge = walletRechargeMapper.selectById(id);
205 if (walletRecharge == null) { 208 if (walletRecharge == null) {
206 log.error("[updateWalletRechargerPaid][钱包充值记录不存在,钱包充值记录 id({})]", id); 209 log.error("[updateWalletRechargerPaid][钱包充值记录不存在,钱包充值记录 id({})]", id);
207 throw exception(WALLET_RECHARGE_NOT_FOUND); 210 throw exception(WALLET_RECHARGE_NOT_FOUND);
208 } 211 }
209 // 1.2 校验钱包充值是否可以更新已退款 212 // 1.2 校验钱包充值是否可以更新已退款
210 - PayRefundDO payRefund = validateWalletRechargeCanRefunded(walletRecharge, payRefundId); 213 + PayRefundRespDTO payRefund = validateWalletRechargeCanRefunded(walletRecharge, payRefundId);
211 214
  215 + // 2. 处理退款结果
212 PayWalletRechargeDO updateObj = new PayWalletRechargeDO().setId(id); 216 PayWalletRechargeDO updateObj = new PayWalletRechargeDO().setId(id);
213 - // 退款成功 217 + // 情况一:退款成功
214 if (PayRefundStatusEnum.isSuccess(payRefund.getStatus())) { 218 if (PayRefundStatusEnum.isSuccess(payRefund.getStatus())) {
215 // 2.1 更新钱包余额 219 // 2.1 更新钱包余额
216 payWalletService.reduceWalletBalance(walletRecharge.getWalletId(), id, 220 payWalletService.reduceWalletBalance(walletRecharge.getWalletId(), id,
@@ -219,19 +223,19 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -219,19 +223,19 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
219 updateObj.setRefundStatus(SUCCESS.getStatus()).setRefundTime(payRefund.getSuccessTime()) 223 updateObj.setRefundStatus(SUCCESS.getStatus()).setRefundTime(payRefund.getSuccessTime())
220 .setRefundTotalPrice(walletRecharge.getTotalPrice()).setRefundPayPrice(walletRecharge.getPayPrice()) 224 .setRefundTotalPrice(walletRecharge.getTotalPrice()).setRefundPayPrice(walletRecharge.getPayPrice())
221 .setRefundBonusPrice(walletRecharge.getBonusPrice()); 225 .setRefundBonusPrice(walletRecharge.getBonusPrice());
222 - }  
223 - // 退款失败  
224 - if (PayRefundStatusRespEnum.isFailure(payRefund.getStatus())) { 226 + // 情况二:退款失败
  227 + } else if (PayRefundStatusRespEnum.isFailure(payRefund.getStatus())) {
225 // 2.2 解冻余额 228 // 2.2 解冻余额
226 payWalletService.unfreezePrice(walletRecharge.getWalletId(), walletRecharge.getTotalPrice()); 229 payWalletService.unfreezePrice(walletRecharge.getWalletId(), walletRecharge.getTotalPrice());
227 230
228 updateObj.setRefundStatus(FAILURE.getStatus()); 231 updateObj.setRefundStatus(FAILURE.getStatus());
229 } 232 }
  233 +
230 // 3. 更新钱包充值的退款字段 234 // 3. 更新钱包充值的退款字段
231 walletRechargeMapper.updateByIdAndRefunded(id, WAITING.getStatus(), updateObj); 235 walletRechargeMapper.updateByIdAndRefunded(id, WAITING.getStatus(), updateObj);
232 } 236 }
233 237
234 - private PayRefundDO validateWalletRechargeCanRefunded(PayWalletRechargeDO walletRecharge, Long payRefundId) { 238 + private PayRefundRespDTO validateWalletRechargeCanRefunded(PayWalletRechargeDO walletRecharge, Long payRefundId) {
235 // 1. 校验退款订单匹配 239 // 1. 校验退款订单匹配
236 if (notEqual(walletRecharge.getPayRefundId(), payRefundId)) { 240 if (notEqual(walletRecharge.getPayRefundId(), payRefundId)) {
237 log.error("[validateWalletRechargeCanRefunded][钱包充值({}) 退款单不匹配({}),请进行处理!钱包充值的数据是:{}]", 241 log.error("[validateWalletRechargeCanRefunded][钱包充值({}) 退款单不匹配({}),请进行处理!钱包充值的数据是:{}]",
@@ -240,7 +244,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -240,7 +244,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
240 } 244 }
241 245
242 // 2.1 校验退款订单 246 // 2.1 校验退款订单
243 - PayRefundDO payRefund = payRefundService.getRefund(payRefundId); 247 + PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId);
244 if (payRefund == null) { 248 if (payRefund == null) {
245 log.error("[validateWalletRechargeCanRefunded][payRefund({})不存在]", payRefundId); 249 log.error("[validateWalletRechargeCanRefunded][payRefund({})不存在]", payRefundId);
246 throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND); 250 throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND);
@@ -252,7 +256,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -252,7 +256,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
252 throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH); 256 throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH);
253 } 257 }
254 // 2.3 校验退款订单商户订单是否匹配 258 // 2.3 校验退款订单商户订单是否匹配
255 - if (notEqual(payRefund.getMerchantOrderId(), walletRecharge.getId().toString())) { 259 + if (notEqual(payRefund.getMerchantRefundId(), walletRecharge.getId().toString())) {
256 log.error("[validateWalletRechargeCanRefunded][钱包({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]", 260 log.error("[validateWalletRechargeCanRefunded][钱包({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]",
257 walletRecharge.getId(), payRefundId, toJsonString(payRefund)); 261 walletRecharge.getId(), payRefundId, toJsonString(payRefund));
258 throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR); 262 throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_ORDER_ID_ERROR);
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
@@ -215,7 +215,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -215,7 +215,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
215 when(appService.validPayApp(eq("demo"))).thenReturn(app); 215 when(appService.validPayApp(eq("demo"))).thenReturn(app);
216 216
217 // 调用,并断言异常 217 // 调用,并断言异常
218 - assertServiceException(() -> refundService.createPayRefund(reqDTO), 218 + assertServiceException(() -> refundService.createRefund(reqDTO),
219 PAY_ORDER_NOT_FOUND); 219 PAY_ORDER_NOT_FOUND);
220 } 220 }
221 221
@@ -241,7 +241,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -241,7 +241,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
241 when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); 241 when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
242 242
243 // 调用,并断言异常 243 // 调用,并断言异常
244 - assertServiceException(() -> refundService.createPayRefund(reqDTO), 244 + assertServiceException(() -> refundService.createRefund(reqDTO),
245 PAY_ORDER_REFUND_FAIL_STATUS_ERROR); 245 PAY_ORDER_REFUND_FAIL_STATUS_ERROR);
246 } 246 }
247 247
@@ -260,7 +260,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -260,7 +260,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
260 when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order); 260 when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
261 261
262 // 调用,并断言异常 262 // 调用,并断言异常
263 - assertServiceException(() -> refundService.createPayRefund(reqDTO), 263 + assertServiceException(() -> refundService.createRefund(reqDTO),
264 REFUND_PRICE_EXCEED); 264 REFUND_PRICE_EXCEED);
265 } 265 }
266 266
@@ -283,7 +283,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -283,7 +283,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
283 refundMapper.insert(refund); 283 refundMapper.insert(refund);
284 284
285 // 调用,并断言异常 285 // 调用,并断言异常
286 - assertServiceException(() -> refundService.createPayRefund(reqDTO), 286 + assertServiceException(() -> refundService.createRefund(reqDTO),
287 REFUND_PRICE_EXCEED); 287 REFUND_PRICE_EXCEED);
288 } 288 }
289 289
@@ -307,7 +307,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -307,7 +307,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
307 when(channelService.validPayChannel(eq(1L))).thenReturn(channel); 307 when(channelService.validPayChannel(eq(1L))).thenReturn(channel);
308 308
309 // 调用,并断言异常 309 // 调用,并断言异常
310 - assertServiceException(() -> refundService.createPayRefund(reqDTO), 310 + assertServiceException(() -> refundService.createRefund(reqDTO),
311 CHANNEL_NOT_FOUND); 311 CHANNEL_NOT_FOUND);
312 } 312 }
313 313
@@ -339,7 +339,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -339,7 +339,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
339 refundMapper.insert(refund); 339 refundMapper.insert(refund);
340 340
341 // 调用,并断言异常 341 // 调用,并断言异常
342 - assertServiceException(() -> refundService.createPayRefund(reqDTO), 342 + assertServiceException(() -> refundService.createRefund(reqDTO),
343 REFUND_EXISTS); 343 REFUND_EXISTS);
344 } 344 }
345 345
@@ -369,7 +369,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -369,7 +369,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
369 when(client.unifiedRefund(any(PayRefundUnifiedReqDTO.class))).thenThrow(new RuntimeException()); 369 when(client.unifiedRefund(any(PayRefundUnifiedReqDTO.class))).thenThrow(new RuntimeException());
370 370
371 // 调用 371 // 调用
372 - Long refundId = refundService.createPayRefund(reqDTO); 372 + Long refundId = refundService.createRefund(reqDTO);
373 // 断言 373 // 断言
374 PayRefundDO refundDO = refundMapper.selectById(refundId); 374 PayRefundDO refundDO = refundMapper.selectById(refundId);
375 assertPojoEquals(reqDTO, refundDO); 375 assertPojoEquals(reqDTO, refundDO);
@@ -422,7 +422,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { @@ -422,7 +422,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
422 }))).thenReturn(refundRespDTO); 422 }))).thenReturn(refundRespDTO);
423 423
424 // 调用 424 // 调用
425 - Long refundId = refundService.createPayRefund(reqDTO); 425 + Long refundId = refundService.createRefund(reqDTO);
426 // 断言 426 // 断言
427 PayRefundDO refundDO = refundMapper.selectById(refundId); 427 PayRefundDO refundDO = refundMapper.selectById(refundId);
428 assertPojoEquals(reqDTO, refundDO); 428 assertPojoEquals(reqDTO, refundDO);