Commit 5bf3045544ede01a9ced03a6040d7831cdc00b54

Authored by jason
1 parent 9a5f0853

优化退款逻辑

Showing 16 changed files with 62 additions and 156 deletions
sql/change_db.sql
... ... @@ -3,3 +3,6 @@ CHANGE COLUMN `channel_notify_data` `channel_notify_data` VARCHAR(2048) CHARACTE
3 3  
4 4 ALTER TABLE `ruoyi-vue-pro`.`pay_refund`
5 5 CHANGE COLUMN `req_no` `req_no` VARCHAR(64) NULL COMMENT '退款单请求号' ;
  6 +
  7 +ALTER TABLE `ruoyi-vue-pro`.`pay_refund`
  8 +DROP COLUMN `req_no`;
... ...
sql/ruoyi-vue-pro.sql
... ... @@ -607,7 +607,7 @@ CREATE TABLE `pay_order_extension` (
607 607 `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户 IP',
608 608 `status` tinyint NOT NULL COMMENT '支付状态',
609 609 `channel_extras` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '支付渠道的额外参数',
610   - `channel_notify_data` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '支付渠道异步通知的内容',
  610 + `channel_notify_data` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '支付渠道异步通知的内容',
611 611 `creator` varchar(64) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '创建者',
612 612 `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
613 613 `updater` varchar(64) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '更新者',
... ... @@ -720,7 +720,6 @@ COMMIT;
720 720 DROP TABLE IF EXISTS `pay_refund`;
721 721 CREATE TABLE `pay_refund` (
722 722 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '支付退款编号',
723   - `req_no` varchar(64) NOT NULL COMMENT '退款单请求号',
724 723 `merchant_id` bigint NOT NULL COMMENT '商户编号',
725 724 `app_id` bigint NOT NULL COMMENT '应用编号',
726 725 `channel_id` bigint NOT NULL COMMENT '渠道编号',
... ...
yudao-admin-server/src/test/java/cn/iocoder/yudao/adminserver/modules/pay/service/refund/PayRefundServiceTest.java
... ... @@ -43,7 +43,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
43 43 public void testGetRefundPage() {
44 44 // mock 数据
45 45 PayRefundDO dbRefund = randomPojo(PayRefundDO.class, o -> { // 等会查询到
46   - o.setReqNo("RF0000001");
47 46 o.setMerchantId(1L);
48 47 o.setAppId(1L);
49 48 o.setChannelId(1L);
... ... @@ -115,7 +114,6 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
115 114 public void testGetRefundList() {
116 115 // mock 数据
117 116 PayRefundDO dbRefund = randomPojo(PayRefundDO.class, o -> { // 等会查询到
118   - o.setReqNo("RF0000001");
119 117 o.setMerchantId(1L);
120 118 o.setAppId(1L);
121 119 o.setChannelId(1L);
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java
... ... @@ -36,20 +36,6 @@ public class PayRefundDO extends BaseDO {
36 36 @TableId
37 37 private Long id;
38 38  
39   - /**
40   - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no
41   - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no
42   - * 退款请求号。
43   - * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。
44   - * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更,
45   - * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。
46   - * 退款单请求号,根据规则生成
47   - * 例如说,R202109181134287570000
48   - * 废弃,使用 merchantRefundNo 做退款请求号
49   - */
50   - @Deprecated
51   - private String reqNo;
52   -
53 39 /**
54 40 * 商户编号
55 41 *
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundReqDTO.java
... ... @@ -6,6 +6,10 @@ import lombok.Data;
6 6 import lombok.NoArgsConstructor;
7 7 import lombok.experimental.Accessors;
8 8  
  9 +import javax.validation.constraints.DecimalMin;
  10 +import javax.validation.constraints.NotEmpty;
  11 +import javax.validation.constraints.NotNull;
  12 +
9 13 /**
10 14 * 退款申请单 Request DTO
11 15 */
... ... @@ -16,15 +20,17 @@ import lombok.experimental.Accessors;
16 20 @AllArgsConstructor
17 21 public class PayRefundReqDTO {
18 22  
19   - // TODO @jason:增加下 validation 注解哈
20 23 /**
21 24 * 支付订单编号
22 25 */
  26 + @NotNull(message = "支付订单编号不能为空")
23 27 private Long payOrderId;
24 28  
25 29 /**
26 30 * 退款金额
27 31 */
  32 + @NotNull(message = "退款金额不能为空")
  33 + @DecimalMin(value = "0", inclusive = false, message = "退款金额必须大于零")
28 34 private Long amount;
29 35  
30 36 /**
... ... @@ -35,8 +41,8 @@ public class PayRefundReqDTO {
35 41 /**
36 42 * 商户退款订单号
37 43 */
38   - // TODO @jason:merchantRefundNo=》merchantRefundId,保持和 PayOrder 的 merchantOrderId 一致哈
39   - private String merchantRefundNo;
  44 + @NotEmpty(message = "商户退款订单号不能为空")
  45 + private String merchantRefundId;
40 46  
41 47 /**
42 48 * 用户 IP
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundRespDTO.java
... ... @@ -17,24 +17,6 @@ import lombok.experimental.Accessors;
17 17 public class PayRefundRespDTO {
18 18  
19 19 /**
20   - * 渠道返回结果
21   - * 退款处理中和退款成功 返回 1
22   - * 失败和其他情况 返回 2
23   - */
24   - // TODO @jason:这个 result,可以使用 CommonResult 里呢
25   - private Integer channelReturnResult;
26   -
27   - /**
28   - * 渠道返回 code
29   - */
30   - private String channelReturnCode;
31   -
32   - /**
33   - * 渠道返回消息
34   - */
35   - private String channelReturnMsg;
36   -
37   - /**
38 20 * 支付退款单编号,自增
39 21 */
40 22 private Long refundId;
... ...
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundCoreServiceImpl.java
... ... @@ -23,11 +23,11 @@ import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundReqDT
23 23 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundRespDTO;
24 24 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
25 25 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
  26 +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
26 27 import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
27 28 import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundNotifyDTO;
28 29 import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO;
29 30 import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO;
30   -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRefundRespEnum;
31 31 import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum;
32 32 import lombok.extern.slf4j.Slf4j;
33 33 import org.springframework.stereotype.Service;
... ... @@ -88,11 +88,8 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
88 88 if (Objects.equals(req.getAmount(), order.getAmount())) {
89 89 refundType = PayRefundTypeEnum.ALL;
90 90 }
91   -
92 91 PayOrderExtensionDO orderExtensionDO = payOrderExtensionCoreMapper.selectById(order.getSuccessExtensionId());
93   - PayRefundDO payRefundDO = payRefundCoreMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), req.getMerchantRefundNo());
94   - // 构造渠道的统一的退款请求参数
95   - PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO();
  92 + PayRefundDO payRefundDO = payRefundCoreMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), req.getMerchantRefundId());
96 93 if(Objects.nonNull(payRefundDO)){
97 94 // 退款订单已经提交过。
98 95 //TODO 校验相同退款单的金额
... ... @@ -101,19 +98,10 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
101 98 || Objects.equals(PayRefundStatusEnum.CLOSE.getStatus(), payRefundDO.getStatus())) {
102 99 //已成功退款
103 100 throw exception(PAY_REFUND_SUCCEED);
104   - } else{
105   - // TODO @jason:这里不用 else,简洁一些
106   - // 保证商户退款单不变,重复向渠道发起退款。渠道保持幂等
107   - unifiedReqDTO.setUserIp(req.getUserIp())
108   - .setAmount(payRefundDO.getRefundAmount())
109   - .setChannelOrderNo(payRefundDO.getChannelOrderNo())
110   - .setPayTradeNo(payRefundDO.getTradeNo())
111   - .setRefundReqNo(payRefundDO.getMerchantRefundNo())
112   - .setReason(payRefundDO.getReason());
113 101 }
114   - }else{
115   - // 新生成退款单。 退款单入库 退款单状态:生成
116   - // TODO @jason:封装一个小方法。插入退款单
  102 + //可以重复提交,保证 退款请求号 一致,由渠道保证幂等
  103 + }else {
  104 + //成功,插入退款单 状态为生成.没有和渠道交互
117 105 payRefundDO = PayRefundDO.builder().channelOrderNo(order.getChannelOrderNo())
118 106 .appId(order.getAppId())
119 107 .channelOrderNo(order.getChannelOrderNo())
... ... @@ -121,7 +109,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
121 109 .channelId(order.getChannelId())
122 110 .merchantId(order.getMerchantId())
123 111 .orderId(order.getId())
124   - .merchantRefundNo(req.getMerchantRefundNo())
  112 + .merchantRefundNo(req.getMerchantRefundId())
125 113 .notifyUrl(app.getRefundNotifyUrl())
126 114 .payAmount(order.getAmount())
127 115 .refundAmount(req.getAmount())
... ... @@ -134,38 +122,21 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
134 122 .type(refundType.getStatus())
135 123 .build();
136 124 payRefundCoreMapper.insert(payRefundDO);
137   - // TODO @jason:这块的逻辑,和已存在的这块,貌似是统一的?
138   - unifiedReqDTO.setUserIp(req.getUserIp())
139   - .setAmount(payRefundDO.getRefundAmount())
140   - .setChannelOrderNo(payRefundDO.getChannelOrderNo())
141   - .setPayTradeNo(payRefundDO.getTradeNo())
142   - .setRefundReqNo(payRefundDO.getMerchantRefundNo())
143   - .setReason(req.getReason());
144 125 }
  126 + PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO();
  127 + unifiedReqDTO.setUserIp(req.getUserIp())
  128 + .setAmount(req.getAmount())
  129 + .setChannelOrderNo(order.getChannelOrderNo())
  130 + .setPayTradeNo(orderExtensionDO.getNo())
  131 + .setMerchantRefundId(req.getMerchantRefundId())
  132 + .setReason(req.getReason());
145 133 // 向渠道发起退款申请
146   - PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO);
147   - // 构造退款申请返回对象
148   - PayRefundRespDTO respDTO = new PayRefundRespDTO();
149   - if (refundUnifiedRespDTO.getChannelResp() == PayChannelRefundRespEnum.SUCCESS
150   - ||refundUnifiedRespDTO.getChannelResp() == PayChannelRefundRespEnum.PROCESSING) {
151   - // 成功处理,在退款通知中处理, 这里不处理
152   - respDTO.setChannelReturnResult(PayChannelRefundRespEnum.SUCCESS.getStatus());
153   - respDTO.setRefundId(payRefundDO.getId());
154   - }else {
155   - // 失败返回错误给前端,可以重新发起退款,保证退款请求号(这里是商户退款单号), 避免重复退款。
156   - // TODO @jason:失败的话,是不是可以跑出 ServiceException 业务异常。这样就是成功返回 refundId,失败业务异常
157   - respDTO.setChannelReturnResult(PayChannelRefundRespEnum.FAILURE.getStatus());
158   - // 更新退款单状态
159   - PayRefundDO updatePayRefund = new PayRefundDO();
160   - updatePayRefund.setId(payRefundDO.getId())
161   - .setChannelErrorMsg(refundUnifiedRespDTO.getChannelMsg())
162   - .setChannelErrorCode(refundUnifiedRespDTO.getChannelCode())
163   - .setStatus(PayRefundStatusEnum.FAILURE.getStatus());
164   - payRefundCoreMapper.updateById(updatePayRefund);
165   - }
166   - respDTO.setChannelReturnCode(refundUnifiedRespDTO.getChannelCode())
167   - .setChannelReturnMsg(refundUnifiedRespDTO.getChannelMsg());
168   - return respDTO;
  134 + PayCommonResult<PayRefundUnifiedRespDTO> refundUnifiedResult = client.unifiedRefund(unifiedReqDTO);
  135 + //检查是否失败,失败抛出业务异常。
  136 + //TODO 渠道的异常记录
  137 + refundUnifiedResult.checkError();
  138 + //成功在 退款回调中处理
  139 + return PayRefundRespDTO.builder().refundId(payRefundDO.getId()).build();
169 140 }
170 141  
171 142  
... ... @@ -187,7 +158,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
187 158 payRefundSuccess(refundNotify);
188 159 } else {
189 160 //TODO 支付异常, 支付宝似乎没有支付异常的通知。
190   - // TODO @jason:那这里可以考虑打个 error logger
  161 + // TODO @jason:那这里可以考虑打个 error logger @芋艿 微信是否存在支付异常通知
191 162 }
192 163 }
193 164  
... ... @@ -199,22 +170,22 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
199 170 throw exception(PAY_REFUND_NOT_FOUND);
200 171 }
201 172  
202   - // 计算订单的状态。如果全部退款,则订单处于关闭。TODO @jason:建议这里按照金额来判断,因为可能退款多次
203   - Integer type = refundDO.getType();
  173 + // 得到已退金额
  174 + PayOrderDO payOrderDO = payOrderCoreMapper.selectById(refundDO.getOrderId());
  175 + Long refundedAmount = payOrderDO.getRefundAmount();
  176 +
204 177 PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS;
205   - if (PayRefundTypeEnum.ALL.getStatus().equals(type)){
  178 + if(Objects.equals(payOrderDO.getAmount(), refundedAmount+ refundDO.getRefundAmount())){
  179 + //支付金额 = 已退金额 + 本次退款金额。
206 180 orderStatus = PayOrderStatusEnum.CLOSED;
207 181 }
208   - // 需更新已退金额
209   - PayOrderDO payOrderDO = payOrderCoreMapper.selectById(refundDO.getOrderId());
210   - Long refundedAmount = payOrderDO.getRefundAmount();
211 182 // 更新支付订单
212 183 PayOrderDO updateOrderDO = new PayOrderDO();
213 184 updateOrderDO.setId(refundDO.getOrderId())
214 185 .setRefundAmount(refundedAmount + refundDO.getRefundAmount())
215 186 .setStatus(orderStatus.getStatus())
216 187 .setRefundTimes(payOrderDO.getRefundTimes() + 1)
217   - .setRefundStatus(type);
  188 + .setRefundStatus(refundDO.getType());
218 189 payOrderCoreMapper.updateById(updateOrderDO);
219 190  
220 191 // 更新退款订单
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
... ... @@ -39,7 +39,7 @@ public interface PayClient {
39 39 * @param reqDTO 统一退款请求信息
40 40 * @return 各支付渠道的统一返回结果
41 41 */
42   - PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
  42 + PayCommonResult<PayRefundUnifiedRespDTO> unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
43 43  
44 44 /**
45 45 * 解析支付退款通知数据
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java
... ... @@ -43,7 +43,6 @@ public class PayRefundUnifiedReqDTO {
43 43 */
44 44 private String payTradeNo;
45 45  
46   - // TODO @jason:这个字段,要不就使用 merchantRefundId,更直接
47 46 /**
48 47 * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no
49 48 * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no
... ... @@ -51,7 +50,7 @@ public class PayRefundUnifiedReqDTO {
51 50 * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo}
52 51 */
53 52 @NotEmpty(message = "退款请求单号")
54   - private String refundReqNo;
  53 + private String merchantRefundId;
55 54  
56 55 /**
57 56 * 退款原因
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedRespDTO.java
... ... @@ -18,22 +18,8 @@ import lombok.experimental.Accessors;
18 18 @Data
19 19 public class PayRefundUnifiedRespDTO {
20 20  
21   - // TODO @jason:可以合并下。退款处理中、成功,都是成功;其它就业务失败。这样,可以复用 PayCommonResult;这个 RespDTO 可以返回渠道的退款编号
22 21 /**
23   - * 渠道的退款结果
  22 + * 渠道退款单编号
24 23 */
25   - private PayChannelRefundRespEnum channelResp;
26   -
27   - // TODO @json:channelReturnCode 和 channelReturnMsg 放到 PayCommonResult 里噶
28   - /**
29   - * 渠道返回码
30   - */
31   - private String channelCode;
32   -
33   - /**
34   - * 渠道返回信息
35   - */
36   - private String channelMsg;
37   -
38   - //TODO 退款资金渠 ???
  24 + private String channelRefundId;
39 25 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
... ... @@ -103,19 +103,19 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
103 103  
104 104  
105 105 @Override
106   - public PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
107   - PayRefundUnifiedRespDTO resp;
  106 + public PayCommonResult<PayRefundUnifiedRespDTO> unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  107 + PayCommonResult<PayRefundUnifiedRespDTO> resp;
108 108 try {
109 109 resp = doUnifiedRefund(reqDTO);
110 110 } catch (Throwable ex) {
111 111 // 记录异常日志
112 112 log.error("[unifiedRefund][request({}) 发起退款失败]", toJsonString(reqDTO), ex);
113   - throw exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR);
  113 + resp = PayCommonResult.error(ex);
114 114 }
115 115 return resp;
116 116 }
117 117  
118 118  
119   - protected abstract PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
  119 + protected abstract PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
120 120  
121 121 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java
... ... @@ -3,9 +3,9 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
3 3 import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.date.DateUtil;
5 5 import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
  6 +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
6 7 import cn.iocoder.yudao.framework.pay.core.client.dto.*;
7 8 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
8   -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRefundRespEnum;
9 9 import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum;
10 10 import com.alipay.api.AlipayApiException;
11 11 import com.alipay.api.AlipayConfig;
... ... @@ -100,39 +100,32 @@ public abstract class AbstractAlipayClient extends AbstractPayClient&lt;AlipayPayCl
100 100 * @return 退款请求 Response
101 101 */
102 102 @Override
103   - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  103 + protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
104 104 AlipayTradeRefundModel model=new AlipayTradeRefundModel();
105 105 model.setTradeNo(reqDTO.getChannelOrderNo());
106 106 model.setOutTradeNo(reqDTO.getPayTradeNo());
107   - model.setOutRequestNo(reqDTO.getRefundReqNo());
  107 + model.setOutRequestNo(reqDTO.getMerchantRefundId());
108 108 model.setRefundAmount(calculateAmount(reqDTO.getAmount()).toString());
109 109 model.setRefundReason(reqDTO.getReason());
110 110 AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
111 111 refundRequest.setBizModel(model);
112   - PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO();
113 112 try {
114 113 AlipayTradeRefundResponse response = client.execute(refundRequest);
115 114 log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response));
116 115 if (response.isSuccess()) {
117   - //退款成功,更新为PROCESSING_NOTIFY, 而不是 SYNC_SUCCESS 通过支付宝回调接口处理。退款导致触发的异步通知,
118 116 //退款导致触发的异步通知是发送到支付接口中设置的notify_url
119   - //TODO 沙箱环境 返回 的tradeNo(渠道退款单号) 和 订单的tradNo 是一个值,是不是理解不对?
120   - respDTO.setChannelResp(PayChannelRefundRespEnum.SUCCESS)
121   - .setChannelCode(response.getCode())
122   - .setChannelMsg(response.getMsg());
  117 + //支付宝不返回退款单号,设置为空
  118 + PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO();
  119 + respDTO.setChannelRefundId("");
  120 + return PayCommonResult.build(response.getCode(), response.getMsg(), respDTO, codeMapping);
123 121 }else{
124   - respDTO.setChannelResp(PayChannelRefundRespEnum.FAILURE)
125   - .setChannelCode(response.getSubCode())
126   - .setChannelMsg(response.getSubMsg());
  122 + //失败。需要抛出异常
  123 + return PayCommonResult.build(response.getCode(), response.getMsg(), null, codeMapping);
127 124 }
128   - return respDTO;
129 125 } catch (AlipayApiException e) {
130 126 //TODO 记录异常日志
131 127 log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e);
132   - respDTO.setChannelResp(PayChannelRefundRespEnum.FAILURE)
133   - .setChannelCode(e.getErrCode())
134   - .setChannelMsg(e.getErrMsg());
135   - return respDTO;
  128 + return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping);
136 129 }
137 130 }
138 131  
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java
... ... @@ -149,7 +149,7 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
149 149  
150 150  
151 151 @Override
152   - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
  152 + protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
153 153 //TODO 需要实现
154 154 throw new UnsupportedOperationException();
155 155 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/PayRefundController.java
... ... @@ -37,8 +37,8 @@ public class PayRefundController {
37 37 PayRefundReqDTO req = PayRefundConvert.INSTANCE.convert(reqVO);
38 38 req.setUserIp(getClientIP());
39 39 //TODO 测试暂时模拟生成商户退款订单
40   - if(StrUtil.isEmpty(reqVO.getMerchantRefundNo())) {
41   - req.setMerchantRefundNo(PaySeqUtils.genMerchantRefundNo());
  40 + if(StrUtil.isEmpty(reqVO.getMerchantRefundId())) {
  41 + req.setMerchantRefundId(PaySeqUtils.genMerchantRefundNo());
42 42 }
43 43 return CommonResult.success( PayRefundConvert.INSTANCE.convert(payRefundCoreService.submitRefundOrder(req)));
44 44 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/vo/PayRefundReqVO.java
... ... @@ -29,6 +29,6 @@ public class PayRefundReqVO {
29 29 @ApiModelProperty(value = "商户退款订单号", required = true, example = "MR202111180000000001")
30 30 //TODO 测试暂时模拟生成
31 31 //@NotEmpty(message = "商户退款订单号")
32   - private String merchantRefundNo;
  32 + private String merchantRefundId;
33 33  
34 34 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/vo/PayRefundRespVO.java
... ... @@ -16,23 +16,6 @@ import lombok.experimental.Accessors;
16 16 public class PayRefundRespVO {
17 17  
18 18 /**
19   - * 渠道返回结果
20   - * 退款处理中和退款成功 返回 1
21   - * 失败和其他情况 返回 2
22   - */
23   - private Integer channelReturnResult;
24   -
25   - /**
26   - * 渠道返回code
27   - */
28   - private String channelReturnCode;
29   -
30   - /**
31   - * 渠道返回消息
32   - */
33   - private String channelReturnMsg;
34   -
35   - /**
36 19 * 支付退款单编号, 自增
37 20 */
38 21 private Long refundId;
... ...