Commit 67aaf28832bd86a75e6fd9b54e286f7a94768428
1 parent
d556eae5
code review 退款逻辑
Showing
10 changed files
with
67 additions
and
52 deletions
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayRefundCoreConvert.java
| @@ -12,8 +12,6 @@ public interface PayRefundCoreConvert { | @@ -12,8 +12,6 @@ public interface PayRefundCoreConvert { | ||
| 12 | 12 | ||
| 13 | PayRefundCoreConvert INSTANCE = Mappers.getMapper(PayRefundCoreConvert.class); | 13 | PayRefundCoreConvert INSTANCE = Mappers.getMapper(PayRefundCoreConvert.class); |
| 14 | 14 | ||
| 15 | - | ||
| 16 | - | ||
| 17 | //TODO 太多需要处理了, 暂时不用 | 15 | //TODO 太多需要处理了, 暂时不用 |
| 18 | @Mappings(value = { | 16 | @Mappings(value = { |
| 19 | @Mapping(source = "amount", target = "payAmount"), | 17 | @Mapping(source = "amount", target = "payAmount"), |
| @@ -21,4 +19,5 @@ public interface PayRefundCoreConvert { | @@ -21,4 +19,5 @@ public interface PayRefundCoreConvert { | ||
| 21 | @Mapping(target = "status",ignore = true) | 19 | @Mapping(target = "status",ignore = true) |
| 22 | }) | 20 | }) |
| 23 | PayRefundDO convert(PayOrderDO orderDO); | 21 | PayRefundDO convert(PayOrderDO orderDO); |
| 22 | + | ||
| 24 | } | 23 | } |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java
| @@ -3,6 +3,8 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order; | @@ -3,6 +3,8 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order; | ||
| 3 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; | 3 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; |
| 4 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | 4 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; |
| 5 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; | 5 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; |
| 6 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; | ||
| 7 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum; | ||
| 6 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | 8 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; |
| 7 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 9 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 8 | import com.baomidou.mybatisplus.annotation.TableId; | 10 | import com.baomidou.mybatisplus.annotation.TableId; |
| @@ -79,7 +81,6 @@ public class PayRefundDO extends BaseDO { | @@ -79,7 +81,6 @@ public class PayRefundDO extends BaseDO { | ||
| 79 | */ | 81 | */ |
| 80 | private Long orderId; | 82 | private Long orderId; |
| 81 | 83 | ||
| 82 | - | ||
| 83 | /** | 84 | /** |
| 84 | * 交易订单号,根据规则生成 | 85 | * 交易订单号,根据规则生成 |
| 85 | * 调用支付渠道时,使用该字段作为对接的订单号。 | 86 | * 调用支付渠道时,使用该字段作为对接的订单号。 |
| @@ -110,7 +111,6 @@ public class PayRefundDO extends BaseDO { | @@ -110,7 +111,6 @@ public class PayRefundDO extends BaseDO { | ||
| 110 | * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 | 111 | * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 |
| 111 | * 退款单请求号,根据规则生成 | 112 | * 退款单请求号,根据规则生成 |
| 112 | * 例如说,R202109181134287570000 | 113 | * 例如说,R202109181134287570000 |
| 113 | - * | ||
| 114 | */ | 114 | */ |
| 115 | private String merchantRefundNo; | 115 | private String merchantRefundNo; |
| 116 | 116 | ||
| @@ -129,19 +129,22 @@ public class PayRefundDO extends BaseDO { | @@ -129,19 +129,22 @@ public class PayRefundDO extends BaseDO { | ||
| 129 | /** | 129 | /** |
| 130 | * 退款状态 | 130 | * 退款状态 |
| 131 | * | 131 | * |
| 132 | + * 枚举 {@link PayRefundStatusEnum} | ||
| 132 | */ | 133 | */ |
| 133 | private Integer status; | 134 | private Integer status; |
| 134 | 135 | ||
| 135 | /** | 136 | /** |
| 136 | * 退款类型(部分退款,全部退款) | 137 | * 退款类型(部分退款,全部退款) |
| 138 | + * | ||
| 139 | + * 枚举 {@link PayRefundTypeEnum} | ||
| 137 | */ | 140 | */ |
| 138 | private Integer type; | 141 | private Integer type; |
| 139 | /** | 142 | /** |
| 140 | - * 支付金额,单位分 | 143 | + * 支付金额,单位:分 |
| 141 | */ | 144 | */ |
| 142 | private Long payAmount; | 145 | private Long payAmount; |
| 143 | /** | 146 | /** |
| 144 | - * 退款金额,单位分 | 147 | + * 退款金额,单位:分 |
| 145 | */ | 148 | */ |
| 146 | private Long refundAmount; | 149 | private Long refundAmount; |
| 147 | 150 | ||
| @@ -150,7 +153,6 @@ public class PayRefundDO extends BaseDO { | @@ -150,7 +153,6 @@ public class PayRefundDO extends BaseDO { | ||
| 150 | */ | 153 | */ |
| 151 | private String reason; | 154 | private String reason; |
| 152 | 155 | ||
| 153 | - | ||
| 154 | /** | 156 | /** |
| 155 | * 用户 IP | 157 | * 用户 IP |
| 156 | */ | 158 | */ |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayRefundCoreMapper.java
| @@ -17,6 +17,7 @@ public interface PayRefundCoreMapper extends BaseMapperX<PayRefundDO> { | @@ -17,6 +17,7 @@ public interface PayRefundCoreMapper extends BaseMapperX<PayRefundDO> { | ||
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){ | 19 | default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){ |
| 20 | - return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); | 20 | + return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); |
| 21 | } | 21 | } |
| 22 | + | ||
| 22 | } | 23 | } |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayRefundStatusEnum.java
| @@ -6,6 +6,7 @@ import lombok.Getter; | @@ -6,6 +6,7 @@ import lombok.Getter; | ||
| 6 | @Getter | 6 | @Getter |
| 7 | @AllArgsConstructor | 7 | @AllArgsConstructor |
| 8 | public enum PayRefundStatusEnum { | 8 | public enum PayRefundStatusEnum { |
| 9 | + | ||
| 9 | CREATE(0, "退款订单生成"), | 10 | CREATE(0, "退款订单生成"), |
| 10 | SUCCESS(1, "退款成功"), | 11 | SUCCESS(1, "退款成功"), |
| 11 | FAILURE(2, "退款失败"), | 12 | FAILURE(2, "退款失败"), |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundReqDTO.java
| @@ -16,8 +16,9 @@ import lombok.experimental.Accessors; | @@ -16,8 +16,9 @@ import lombok.experimental.Accessors; | ||
| 16 | @AllArgsConstructor | 16 | @AllArgsConstructor |
| 17 | public class PayRefundReqDTO { | 17 | public class PayRefundReqDTO { |
| 18 | 18 | ||
| 19 | + // TODO @jason:增加下 validation 注解哈 | ||
| 19 | /** | 20 | /** |
| 20 | - * 支付订单编号自增 | 21 | + * 支付订单编号 |
| 21 | */ | 22 | */ |
| 22 | private Long payOrderId; | 23 | private Long payOrderId; |
| 23 | 24 | ||
| @@ -31,10 +32,10 @@ public class PayRefundReqDTO { | @@ -31,10 +32,10 @@ public class PayRefundReqDTO { | ||
| 31 | */ | 32 | */ |
| 32 | private String reason; | 33 | private String reason; |
| 33 | 34 | ||
| 34 | - | ||
| 35 | /** | 35 | /** |
| 36 | * 商户退款订单号 | 36 | * 商户退款订单号 |
| 37 | */ | 37 | */ |
| 38 | + // TODO @jason:merchantRefundNo=》merchantRefundId,保持和 PayOrder 的 merchantOrderId 一致哈 | ||
| 38 | private String merchantRefundNo; | 39 | private String merchantRefundNo; |
| 39 | 40 | ||
| 40 | /** | 41 | /** |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundRespDTO.java
| @@ -21,10 +21,11 @@ public class PayRefundRespDTO { | @@ -21,10 +21,11 @@ public class PayRefundRespDTO { | ||
| 21 | * 退款处理中和退款成功 返回 1 | 21 | * 退款处理中和退款成功 返回 1 |
| 22 | * 失败和其他情况 返回 2 | 22 | * 失败和其他情况 返回 2 |
| 23 | */ | 23 | */ |
| 24 | + // TODO @jason:这个 result,可以使用 CommonResult 里呢 | ||
| 24 | private Integer channelReturnResult; | 25 | private Integer channelReturnResult; |
| 25 | 26 | ||
| 26 | /** | 27 | /** |
| 27 | - * 渠道返回code | 28 | + * 渠道返回 code |
| 28 | */ | 29 | */ |
| 29 | private String channelReturnCode; | 30 | private String channelReturnCode; |
| 30 | 31 | ||
| @@ -34,7 +35,8 @@ public class PayRefundRespDTO { | @@ -34,7 +35,8 @@ public class PayRefundRespDTO { | ||
| 34 | private String channelReturnMsg; | 35 | private String channelReturnMsg; |
| 35 | 36 | ||
| 36 | /** | 37 | /** |
| 37 | - * 支付退款单编号, 自增 | 38 | + * 支付退款单编号,自增 |
| 38 | */ | 39 | */ |
| 39 | private Long refundId; | 40 | private Long refundId; |
| 41 | + | ||
| 40 | } | 42 | } |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundCoreServiceImpl.java
| @@ -11,20 +11,20 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderExtensio | @@ -11,20 +11,20 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderExtensio | ||
| 11 | import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundCoreMapper; | 11 | import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundCoreMapper; |
| 12 | import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; | 12 | import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; |
| 13 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum; | 13 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum; |
| 14 | -import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; | ||
| 15 | -import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; | ||
| 16 | -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundReqDTO; | ||
| 17 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundNotifyDTO; | ||
| 18 | -import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum; | ||
| 19 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum; | 14 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum; |
| 20 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; | 15 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; |
| 16 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum; | ||
| 21 | import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService; | 17 | import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService; |
| 22 | import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService; | 18 | import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService; |
| 19 | +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; | ||
| 20 | +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; | ||
| 23 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService; | 21 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService; |
| 22 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundReqDTO; | ||
| 24 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundRespDTO; | 23 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundRespDTO; |
| 25 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; | 24 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 26 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; | 25 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| 27 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; | 26 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; |
| 27 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundNotifyDTO; | ||
| 28 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO; | 28 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO; |
| 29 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; | 29 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; |
| 30 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRefundRespEnum; | 30 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRefundRespEnum; |
| @@ -34,7 +34,8 @@ import org.springframework.stereotype.Service; | @@ -34,7 +34,8 @@ import org.springframework.stereotype.Service; | ||
| 34 | import org.springframework.transaction.annotation.Transactional; | 34 | import org.springframework.transaction.annotation.Transactional; |
| 35 | 35 | ||
| 36 | import javax.annotation.Resource; | 36 | import javax.annotation.Resource; |
| 37 | -import java.util.*; | 37 | +import java.util.Date; |
| 38 | +import java.util.Objects; | ||
| 38 | 39 | ||
| 39 | import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; | 40 | import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; |
| 40 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 41 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| @@ -90,17 +91,19 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | @@ -90,17 +91,19 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | ||
| 90 | 91 | ||
| 91 | PayOrderExtensionDO orderExtensionDO = payOrderExtensionCoreMapper.selectById(order.getSuccessExtensionId()); | 92 | PayOrderExtensionDO orderExtensionDO = payOrderExtensionCoreMapper.selectById(order.getSuccessExtensionId()); |
| 92 | PayRefundDO payRefundDO = payRefundCoreMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), req.getMerchantRefundNo()); | 93 | PayRefundDO payRefundDO = payRefundCoreMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), req.getMerchantRefundNo()); |
| 93 | - //构造渠道的统一的退款请求参数 | 94 | + // 构造渠道的统一的退款请求参数 |
| 94 | PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); | 95 | PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); |
| 95 | if(Objects.nonNull(payRefundDO)){ | 96 | if(Objects.nonNull(payRefundDO)){ |
| 96 | - //退款订单已经提交过。 | 97 | + // 退款订单已经提交过。 |
| 97 | //TODO 校验相同退款单的金额 | 98 | //TODO 校验相同退款单的金额 |
| 98 | - if(Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus()) | ||
| 99 | - || Objects.equals(PayRefundStatusEnum.CLOSE.getStatus(), payRefundDO.getStatus())){ | 99 | + // TODO @jason:咱要不封装一个 ObjectUtils.equalsAny |
| 100 | + if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus()) | ||
| 101 | + || Objects.equals(PayRefundStatusEnum.CLOSE.getStatus(), payRefundDO.getStatus())) { | ||
| 100 | //已成功退款 | 102 | //已成功退款 |
| 101 | throw exception(PAY_REFUND_SUCCEED); | 103 | throw exception(PAY_REFUND_SUCCEED); |
| 102 | - }else{ | ||
| 103 | - //保证商户退款单不变,重复向渠道发起退款。渠道保持幂等 | 104 | + } else{ |
| 105 | + // TODO @jason:这里不用 else,简洁一些 | ||
| 106 | + // 保证商户退款单不变,重复向渠道发起退款。渠道保持幂等 | ||
| 104 | unifiedReqDTO.setUserIp(req.getUserIp()) | 107 | unifiedReqDTO.setUserIp(req.getUserIp()) |
| 105 | .setAmount(payRefundDO.getRefundAmount()) | 108 | .setAmount(payRefundDO.getRefundAmount()) |
| 106 | .setChannelOrderNo(payRefundDO.getChannelOrderNo()) | 109 | .setChannelOrderNo(payRefundDO.getChannelOrderNo()) |
| @@ -109,7 +112,8 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | @@ -109,7 +112,8 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | ||
| 109 | .setReason(payRefundDO.getReason()); | 112 | .setReason(payRefundDO.getReason()); |
| 110 | } | 113 | } |
| 111 | }else{ | 114 | }else{ |
| 112 | - //新生成退款单。 退款单入库 退款单状态:生成 | 115 | + // 新生成退款单。 退款单入库 退款单状态:生成 |
| 116 | + // TODO @jason:封装一个小方法。插入退款单 | ||
| 113 | payRefundDO = PayRefundDO.builder().channelOrderNo(order.getChannelOrderNo()) | 117 | payRefundDO = PayRefundDO.builder().channelOrderNo(order.getChannelOrderNo()) |
| 114 | .appId(order.getAppId()) | 118 | .appId(order.getAppId()) |
| 115 | .channelOrderNo(order.getChannelOrderNo()) | 119 | .channelOrderNo(order.getChannelOrderNo()) |
| @@ -130,6 +134,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | @@ -130,6 +134,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | ||
| 130 | .type(refundType.getStatus()) | 134 | .type(refundType.getStatus()) |
| 131 | .build(); | 135 | .build(); |
| 132 | payRefundCoreMapper.insert(payRefundDO); | 136 | payRefundCoreMapper.insert(payRefundDO); |
| 137 | + // TODO @jason:这块的逻辑,和已存在的这块,貌似是统一的? | ||
| 133 | unifiedReqDTO.setUserIp(req.getUserIp()) | 138 | unifiedReqDTO.setUserIp(req.getUserIp()) |
| 134 | .setAmount(payRefundDO.getRefundAmount()) | 139 | .setAmount(payRefundDO.getRefundAmount()) |
| 135 | .setChannelOrderNo(payRefundDO.getChannelOrderNo()) | 140 | .setChannelOrderNo(payRefundDO.getChannelOrderNo()) |
| @@ -137,19 +142,20 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | @@ -137,19 +142,20 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | ||
| 137 | .setRefundReqNo(payRefundDO.getMerchantRefundNo()) | 142 | .setRefundReqNo(payRefundDO.getMerchantRefundNo()) |
| 138 | .setReason(req.getReason()); | 143 | .setReason(req.getReason()); |
| 139 | } | 144 | } |
| 140 | - //向渠道发起退款申请 | 145 | + // 向渠道发起退款申请 |
| 141 | PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO); | 146 | PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO); |
| 142 | - //构造退款申请返回对象 | 147 | + // 构造退款申请返回对象 |
| 143 | PayRefundRespDTO respDTO = new PayRefundRespDTO(); | 148 | PayRefundRespDTO respDTO = new PayRefundRespDTO(); |
| 144 | - if(refundUnifiedRespDTO.getChannelResp() == PayChannelRefundRespEnum.SUCCESS | ||
| 145 | - ||refundUnifiedRespDTO.getChannelResp() == PayChannelRefundRespEnum.PROCESSING ){ | ||
| 146 | - //成功处理, 在退款通知中处理, 这里不处理 | 149 | + if (refundUnifiedRespDTO.getChannelResp() == PayChannelRefundRespEnum.SUCCESS |
| 150 | + ||refundUnifiedRespDTO.getChannelResp() == PayChannelRefundRespEnum.PROCESSING) { | ||
| 151 | + // 成功处理,在退款通知中处理, 这里不处理 | ||
| 147 | respDTO.setChannelReturnResult(PayChannelRefundRespEnum.SUCCESS.getStatus()); | 152 | respDTO.setChannelReturnResult(PayChannelRefundRespEnum.SUCCESS.getStatus()); |
| 148 | respDTO.setRefundId(payRefundDO.getId()); | 153 | respDTO.setRefundId(payRefundDO.getId()); |
| 149 | }else { | 154 | }else { |
| 150 | - //失败返回错误给前端,可以重新发起退款,保证退款请求号(这里是商户退款单号), 避免重复退款。 | 155 | + // 失败返回错误给前端,可以重新发起退款,保证退款请求号(这里是商户退款单号), 避免重复退款。 |
| 156 | + // TODO @jason:失败的话,是不是可以跑出 ServiceException 业务异常。这样就是成功返回 refundId,失败业务异常 | ||
| 151 | respDTO.setChannelReturnResult(PayChannelRefundRespEnum.FAILURE.getStatus()); | 157 | respDTO.setChannelReturnResult(PayChannelRefundRespEnum.FAILURE.getStatus()); |
| 152 | - //更新退款单状态 | 158 | + // 更新退款单状态 |
| 153 | PayRefundDO updatePayRefund = new PayRefundDO(); | 159 | PayRefundDO updatePayRefund = new PayRefundDO(); |
| 154 | updatePayRefund.setId(payRefundDO.getId()) | 160 | updatePayRefund.setId(payRefundDO.getId()) |
| 155 | .setChannelErrorMsg(refundUnifiedRespDTO.getChannelMsg()) | 161 | .setChannelErrorMsg(refundUnifiedRespDTO.getChannelMsg()) |
| @@ -181,35 +187,37 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | @@ -181,35 +187,37 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | ||
| 181 | payRefundSuccess(refundNotify); | 187 | payRefundSuccess(refundNotify); |
| 182 | } else { | 188 | } else { |
| 183 | //TODO 支付异常, 支付宝似乎没有支付异常的通知。 | 189 | //TODO 支付异常, 支付宝似乎没有支付异常的通知。 |
| 190 | + // TODO @jason:那这里可以考虑打个 error logger | ||
| 184 | } | 191 | } |
| 185 | } | 192 | } |
| 186 | 193 | ||
| 187 | private void payRefundSuccess(PayRefundNotifyDTO refundNotify) { | 194 | private void payRefundSuccess(PayRefundNotifyDTO refundNotify) { |
| 195 | + // 校验退款单存在 | ||
| 188 | PayRefundDO refundDO = payRefundCoreMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), refundNotify.getReqNo()); | 196 | PayRefundDO refundDO = payRefundCoreMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), refundNotify.getReqNo()); |
| 189 | if (refundDO == null) { | 197 | if (refundDO == null) { |
| 190 | - log.error("不存在 seqNo 为{} 的支付退款单",refundNotify.getReqNo()); | 198 | + log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo()); |
| 191 | throw exception(PAY_REFUND_NOT_FOUND); | 199 | throw exception(PAY_REFUND_NOT_FOUND); |
| 192 | } | 200 | } |
| 193 | - Long refundAmount = refundDO.getRefundAmount(); | 201 | + |
| 202 | + // 计算订单的状态。如果全部退款,则订单处于关闭。TODO @jason:建议这里按照金额来判断,因为可能退款多次 | ||
| 194 | Integer type = refundDO.getType(); | 203 | Integer type = refundDO.getType(); |
| 195 | PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS; | 204 | PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS; |
| 196 | - if(PayRefundTypeEnum.ALL.getStatus().equals(type)){ | 205 | + if (PayRefundTypeEnum.ALL.getStatus().equals(type)){ |
| 197 | orderStatus = PayOrderStatusEnum.CLOSED; | 206 | orderStatus = PayOrderStatusEnum.CLOSED; |
| 198 | } | 207 | } |
| 199 | - // 更新支付订单 | ||
| 200 | - PayOrderDO payOrderDO = payOrderCoreMapper.selectById(refundDO.getOrderId()); | ||
| 201 | // 需更新已退金额 | 208 | // 需更新已退金额 |
| 209 | + PayOrderDO payOrderDO = payOrderCoreMapper.selectById(refundDO.getOrderId()); | ||
| 202 | Long refundedAmount = payOrderDO.getRefundAmount(); | 210 | Long refundedAmount = payOrderDO.getRefundAmount(); |
| 211 | + // 更新支付订单 | ||
| 203 | PayOrderDO updateOrderDO = new PayOrderDO(); | 212 | PayOrderDO updateOrderDO = new PayOrderDO(); |
| 204 | updateOrderDO.setId(refundDO.getOrderId()) | 213 | updateOrderDO.setId(refundDO.getOrderId()) |
| 205 | - .setRefundAmount(refundedAmount + refundAmount) | 214 | + .setRefundAmount(refundedAmount + refundDO.getRefundAmount()) |
| 206 | .setStatus(orderStatus.getStatus()) | 215 | .setStatus(orderStatus.getStatus()) |
| 207 | - .setRefundTimes(payOrderDO.getRefundTimes()+1) | 216 | + .setRefundTimes(payOrderDO.getRefundTimes() + 1) |
| 208 | .setRefundStatus(type); | 217 | .setRefundStatus(type); |
| 209 | - | ||
| 210 | payOrderCoreMapper.updateById(updateOrderDO); | 218 | payOrderCoreMapper.updateById(updateOrderDO); |
| 211 | 219 | ||
| 212 | - // 跟新退款订单 | 220 | + // 更新退款订单 |
| 213 | PayRefundDO updateRefundDO = new PayRefundDO(); | 221 | PayRefundDO updateRefundDO = new PayRefundDO(); |
| 214 | updateRefundDO.setId(refundDO.getId()) | 222 | updateRefundDO.setId(refundDO.getId()) |
| 215 | .setSuccessTime(refundNotify.getRefundSuccessTime()) | 223 | .setSuccessTime(refundNotify.getRefundSuccessTime()) |
| @@ -219,7 +227,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | @@ -219,7 +227,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | ||
| 219 | .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); | 227 | .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); |
| 220 | payRefundCoreMapper.updateById(updateRefundDO); | 228 | payRefundCoreMapper.updateById(updateRefundDO); |
| 221 | 229 | ||
| 222 | - //插入退款通知记录 | 230 | + // 插入退款通知记录 |
| 223 | // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 | 231 | // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 |
| 224 | payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() | 232 | payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() |
| 225 | .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build()); | 233 | .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build()); |
| @@ -235,12 +243,12 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | @@ -235,12 +243,12 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { | ||
| 235 | if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { | 243 | if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { |
| 236 | throw exception(PAY_ORDER_STATUS_IS_NOT_SUCCESS); | 244 | throw exception(PAY_ORDER_STATUS_IS_NOT_SUCCESS); |
| 237 | } | 245 | } |
| 238 | - //是否已经全额退款 | 246 | + // 是否已经全额退款 |
| 239 | if (PayRefundTypeEnum.ALL.getStatus().equals(order.getRefundStatus())) { | 247 | if (PayRefundTypeEnum.ALL.getStatus().equals(order.getRefundStatus())) { |
| 240 | throw exception(PAY_REFUND_ALL_REFUNDED); | 248 | throw exception(PAY_REFUND_ALL_REFUNDED); |
| 241 | } | 249 | } |
| 242 | // 校验金额 退款金额不能大于 原定的金额 | 250 | // 校验金额 退款金额不能大于 原定的金额 |
| 243 | - if(req.getAmount() + order.getRefundAmount() > order.getAmount()){ | 251 | + if (req.getAmount() + order.getRefundAmount() > order.getAmount()){ |
| 244 | throw exception(PAY_REFUND_AMOUNT_EXCEED); | 252 | throw exception(PAY_REFUND_AMOUNT_EXCEED); |
| 245 | } | 253 | } |
| 246 | // 校验渠道订单号 | 254 | // 校验渠道订单号 |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java
| @@ -28,6 +28,7 @@ public class PayRefundUnifiedReqDTO { | @@ -28,6 +28,7 @@ public class PayRefundUnifiedReqDTO { | ||
| 28 | */ | 28 | */ |
| 29 | private String userIp; | 29 | private String userIp; |
| 30 | 30 | ||
| 31 | + // TODO @jason:这个是否为非必传字段呀,只需要传递 payTradeNo 字段即可。尽可能精简 | ||
| 31 | /** | 32 | /** |
| 32 | * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id | 33 | * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id |
| 33 | * https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no | 34 | * https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no |
| @@ -35,7 +36,6 @@ public class PayRefundUnifiedReqDTO { | @@ -35,7 +36,6 @@ public class PayRefundUnifiedReqDTO { | ||
| 35 | */ | 36 | */ |
| 36 | private String channelOrderNo; | 37 | private String channelOrderNo; |
| 37 | 38 | ||
| 38 | - | ||
| 39 | /** | 39 | /** |
| 40 | * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no | 40 | * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no |
| 41 | * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no | 41 | * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no |
| @@ -43,24 +43,22 @@ public class PayRefundUnifiedReqDTO { | @@ -43,24 +43,22 @@ public class PayRefundUnifiedReqDTO { | ||
| 43 | */ | 43 | */ |
| 44 | private String payTradeNo; | 44 | private String payTradeNo; |
| 45 | 45 | ||
| 46 | - | 46 | + // TODO @jason:这个字段,要不就使用 merchantRefundId,更直接 |
| 47 | /** | 47 | /** |
| 48 | * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no | 48 | * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no |
| 49 | - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no | 49 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no |
| 50 | * 退款请求单号 同一退款请求单号多次请求只退一笔。 | 50 | * 退款请求单号 同一退款请求单号多次请求只退一笔。 |
| 51 | * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo} | 51 | * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo} |
| 52 | */ | 52 | */ |
| 53 | @NotEmpty(message = "退款请求单号") | 53 | @NotEmpty(message = "退款请求单号") |
| 54 | private String refundReqNo; | 54 | private String refundReqNo; |
| 55 | 55 | ||
| 56 | - | ||
| 57 | /** | 56 | /** |
| 58 | * 退款原因 | 57 | * 退款原因 |
| 59 | */ | 58 | */ |
| 60 | @NotEmpty(message = "退款原因不能为空") | 59 | @NotEmpty(message = "退款原因不能为空") |
| 61 | private String reason; | 60 | private String reason; |
| 62 | 61 | ||
| 63 | - | ||
| 64 | /** | 62 | /** |
| 65 | * 退款金额,单位:分 | 63 | * 退款金额,单位:分 |
| 66 | */ | 64 | */ |
| @@ -68,12 +66,10 @@ public class PayRefundUnifiedReqDTO { | @@ -68,12 +66,10 @@ public class PayRefundUnifiedReqDTO { | ||
| 68 | @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") | 66 | @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") |
| 69 | private Long amount; | 67 | private Long amount; |
| 70 | 68 | ||
| 71 | - | ||
| 72 | - | ||
| 73 | - | ||
| 74 | /** | 69 | /** |
| 75 | * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要 | 70 | * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要 |
| 76 | */ | 71 | */ |
| 77 | @URL(message = "支付结果的 notify 回调地址必须是 URL 格式") | 72 | @URL(message = "支付结果的 notify 回调地址必须是 URL 格式") |
| 78 | private String notifyUrl; | 73 | private String notifyUrl; |
| 74 | + | ||
| 79 | } | 75 | } |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedRespDTO.java
| @@ -17,11 +17,14 @@ import lombok.experimental.Accessors; | @@ -17,11 +17,14 @@ import lombok.experimental.Accessors; | ||
| 17 | @AllArgsConstructor | 17 | @AllArgsConstructor |
| 18 | @Data | 18 | @Data |
| 19 | public class PayRefundUnifiedRespDTO { | 19 | public class PayRefundUnifiedRespDTO { |
| 20 | + | ||
| 21 | + // TODO @jason:可以合并下。退款处理中、成功,都是成功;其它就业务失败。这样,可以复用 PayCommonResult;这个 RespDTO 可以返回渠道的退款编号 | ||
| 20 | /** | 22 | /** |
| 21 | * 渠道的退款结果 | 23 | * 渠道的退款结果 |
| 22 | */ | 24 | */ |
| 23 | private PayChannelRefundRespEnum channelResp; | 25 | private PayChannelRefundRespEnum channelResp; |
| 24 | 26 | ||
| 27 | + // TODO @json:channelReturnCode 和 channelReturnMsg 放到 PayCommonResult 里噶 | ||
| 25 | /** | 28 | /** |
| 26 | * 渠道返回码 | 29 | * 渠道返回码 |
| 27 | */ | 30 | */ |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelRefundRespEnum.java
| @@ -11,6 +11,7 @@ import lombok.Getter; | @@ -11,6 +11,7 @@ import lombok.Getter; | ||
| 11 | @Getter | 11 | @Getter |
| 12 | @AllArgsConstructor | 12 | @AllArgsConstructor |
| 13 | public enum PayChannelRefundRespEnum { | 13 | public enum PayChannelRefundRespEnum { |
| 14 | + | ||
| 14 | SUCCESS(1, "退款成功"), | 15 | SUCCESS(1, "退款成功"), |
| 15 | FAILURE(2, "退款失败"), | 16 | FAILURE(2, "退款失败"), |
| 16 | PROCESSING(3,"退款处理中"), | 17 | PROCESSING(3,"退款处理中"), |
| @@ -18,4 +19,5 @@ public enum PayChannelRefundRespEnum { | @@ -18,4 +19,5 @@ public enum PayChannelRefundRespEnum { | ||
| 18 | 19 | ||
| 19 | private final Integer status; | 20 | private final Integer status; |
| 20 | private final String name; | 21 | private final String name; |
| 22 | + | ||
| 21 | } | 23 | } |