Commit 288d8e313202362438b747fb5dabe970cd214665
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 | 36 | ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单不是【待发货】状态"); |
| 37 | 37 | ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订单创建失败,原因:存在未付款订单"); |
| 38 | 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 | 44 | // ========== After Sale 模块 1-011-000-100 ========== |
| 43 | 45 | ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在"); |
| ... | ... | @@ -51,9 +53,13 @@ public interface ErrorCodeConstants { |
| 51 | 53 | ErrorCode AFTER_SALE_DELIVERY_FAIL_STATUS_NOT_SELLER_AGREE = new ErrorCode(1_011_000_108, "退货失败,售后单状态不处于【待买家退货】"); |
| 52 | 54 | ErrorCode AFTER_SALE_CONFIRM_FAIL_STATUS_NOT_BUYER_DELIVERY = new ErrorCode(1_011_000_109, "确认收货失败,售后单状态不处于【待确认收货】"); |
| 53 | 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 | 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 | 64 | // ========== Cart 模块 1-011-002-000 ========== |
| 59 | 65 | ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1_011_002_000, "购物车项不存在"); |
| ... | ... | @@ -101,7 +107,6 @@ public interface ErrorCodeConstants { |
| 101 | 107 | ErrorCode BROKERAGE_WITHDRAW_STATUS_NOT_AUDITING = new ErrorCode(1_011_008_001, "佣金提现记录状态不是审核中"); |
| 102 | 108 | ErrorCode BROKERAGE_WITHDRAW_MIN_PRICE = new ErrorCode(1_011_008_002, "提现金额不能低于 {} 元"); |
| 103 | 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 | 110 | ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_ID_ERROR = new ErrorCode(1_011_008_005, "提现单更新转账状态失败,转账单不匹配"); |
| 106 | 111 | ErrorCode BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_TRANSFER_STATUS_NOT_SUCCESS_OR_CLOSED = new ErrorCode(1_011_008_006, "提现单更新转账状态失败,转账单状态不是成功或关闭状态"); |
| 107 | 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 | 19 | MEMBER_DELIVERY(20, "会员填写退货物流信息,快递公司:{deliveryName},快递单号:{logisticsNo}"), |
| 20 | 20 | ADMIN_AGREE_RECEIVE(21, "商家收货"), |
| 21 | 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 | 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 | 1 | package cn.iocoder.yudao.module.trade.controller.admin.aftersale; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 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 | 16 | import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService; |
| 16 | 17 | import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService; |
| 17 | 18 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; |
| 19 | +import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; | |
| 18 | 20 | import io.swagger.v3.oas.annotations.Operation; |
| 19 | 21 | import io.swagger.v3.oas.annotations.Parameter; |
| 20 | 22 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 21 | 23 | import lombok.extern.slf4j.Slf4j; |
| 24 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 22 | 25 | import org.springframework.security.access.prepost.PreAuthorize; |
| 23 | 26 | import org.springframework.validation.annotation.Validated; |
| 24 | 27 | import org.springframework.web.bind.annotation.*; |
| ... | ... | @@ -46,6 +49,8 @@ public class AfterSaleController { |
| 46 | 49 | @Resource |
| 47 | 50 | private TradeOrderQueryService tradeOrderQueryService; |
| 48 | 51 | @Resource |
| 52 | + private TradeOrderUpdateService tradeOrderUpdateService; | |
| 53 | + @Resource | |
| 49 | 54 | private AfterSaleLogService afterSaleLogService; |
| 50 | 55 | @Resource |
| 51 | 56 | private MemberUserApi memberUserApi; |
| ... | ... | @@ -133,11 +138,19 @@ public class AfterSaleController { |
| 133 | 138 | |
| 134 | 139 | @PostMapping("/update-refunded") |
| 135 | 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 | 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 | 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 | 88 | public CommonResult<Boolean> updateBrokerageWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { |
| 89 | 89 | log.info("[updateAfterRefund][notifyReqDTO({})]", notifyReqDTO); |
| 90 | 90 | brokerageWithdrawService.updateBrokerageWithdrawTransferred( |
| 91 | - Long.parseLong(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayTransferId()); | |
| 91 | + Long.parseLong(notifyReqDTO.getMerchantTransferId()), notifyReqDTO.getPayTransferId()); | |
| 92 | 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 | 1 | package cn.iocoder.yudao.module.trade.framework.aftersale.core.aop; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ObjUtil; | |
| 3 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 4 | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | 6 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 6 | 7 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderLogDO; |
| 8 | +import cn.iocoder.yudao.module.trade.enums.aftersale.AfterSaleOperateTypeEnum; | |
| 7 | 9 | import cn.iocoder.yudao.module.trade.framework.aftersale.core.annotations.AfterSaleLog; |
| 8 | 10 | import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleLogService; |
| 9 | 11 | import cn.iocoder.yudao.module.trade.service.aftersale.bo.AfterSaleLogCreateReqBO; |
| 12 | +import jakarta.annotation.Resource; | |
| 10 | 13 | import lombok.extern.slf4j.Slf4j; |
| 11 | 14 | import org.aspectj.lang.JoinPoint; |
| 12 | 15 | import org.aspectj.lang.annotation.AfterReturning; |
| 13 | 16 | import org.aspectj.lang.annotation.Aspect; |
| 14 | 17 | |
| 15 | -import jakarta.annotation.Resource; | |
| 16 | 18 | import java.util.Map; |
| 17 | 19 | |
| 18 | 20 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| ... | ... | @@ -51,6 +53,10 @@ public class AfterSaleLogAspect { |
| 51 | 53 | */ |
| 52 | 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 | 60 | * 拓展参数 Map,用于格式化操作内容 |
| 55 | 61 | */ |
| 56 | 62 | private static final ThreadLocal<Map<String, Object>> EXTS = new ThreadLocal<>(); |
| ... | ... | @@ -69,6 +75,7 @@ public class AfterSaleLogAspect { |
| 69 | 75 | if (afterSaleId == null) { // 如果未设置,只有注解,说明不需要记录日志 |
| 70 | 76 | return; |
| 71 | 77 | } |
| 78 | + AfterSaleOperateTypeEnum operateType = ObjUtil.defaultIfNull(OPERATE_TYPE.get(), afterSaleLog.operateType()); | |
| 72 | 79 | Integer beforeStatus = BEFORE_STATUS.get(); |
| 73 | 80 | Integer afterStatus = AFTER_STATUS.get(); |
| 74 | 81 | Map<String, Object> exts = ObjectUtil.defaultIfNull(EXTS.get(), emptyMap()); |
| ... | ... | @@ -78,7 +85,7 @@ public class AfterSaleLogAspect { |
| 78 | 85 | AfterSaleLogCreateReqBO createBO = new AfterSaleLogCreateReqBO() |
| 79 | 86 | .setUserId(userId).setUserType(userType) |
| 80 | 87 | .setAfterSaleId(afterSaleId).setBeforeStatus(beforeStatus).setAfterStatus(afterStatus) |
| 81 | - .setOperateType(afterSaleLog.operateType().getType()).setContent(content); | |
| 88 | + .setOperateType(operateType.getType()).setContent(content); | |
| 82 | 89 | afterSaleLogService.createAfterSaleLog(createBO); |
| 83 | 90 | } catch (Exception exception) { |
| 84 | 91 | log.error("[doAfterReturning][afterSaleLog({}) 日志记录错误]", toJsonString(afterSaleLog), exception); |
| ... | ... | @@ -116,6 +123,10 @@ public class AfterSaleLogAspect { |
| 116 | 123 | EXTS.set(exts); |
| 117 | 124 | } |
| 118 | 125 | |
| 126 | + public static void setAfterSaleOperateType(AfterSaleOperateTypeEnum operateType) { | |
| 127 | + OPERATE_TYPE.set(operateType); | |
| 128 | + } | |
| 129 | + | |
| 119 | 130 | public static void setUserInfo(Long userId, Integer userType) { |
| 120 | 131 | USER_ID.set(userId); |
| 121 | 132 | USER_TYPE.set(userType); |
| ... | ... | @@ -127,6 +138,7 @@ public class AfterSaleLogAspect { |
| 127 | 138 | AFTER_SALE_ID.remove(); |
| 128 | 139 | BEFORE_STATUS.remove(); |
| 129 | 140 | AFTER_STATUS.remove(); |
| 141 | + OPERATE_TYPE.remove(); | |
| 130 | 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 | 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 | 5 | import cn.iocoder.yudao.module.trade.framework.aftersale.core.aop.AfterSaleLogAspect; |
| 5 | 6 | |
| 6 | 7 | import java.util.Map; |
| ... | ... | @@ -13,6 +14,10 @@ import java.util.Map; |
| 13 | 14 | */ |
| 14 | 15 | public class AfterSaleLogUtils { |
| 15 | 16 | |
| 17 | + public static void setAfterSaleOperateType(AfterSaleOperateTypeEnum operateType) { | |
| 18 | + AfterSaleLogAspect.setAfterSaleOperateType(operateType); | |
| 19 | + } | |
| 20 | + | |
| 16 | 21 | public static void setAfterSaleInfo(Long id, Integer beforeStatus, Integer afterStatus) { |
| 17 | 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 | 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 | 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 | 7 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 8 | 8 | import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; |
| 9 | 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 | 12 | import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; |
| 11 | 13 | import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRespDTO; |
| 12 | 14 | import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; |
| ... | ... | @@ -41,13 +43,12 @@ import lombok.extern.slf4j.Slf4j; |
| 41 | 43 | import org.springframework.context.annotation.Lazy; |
| 42 | 44 | import org.springframework.stereotype.Service; |
| 43 | 45 | import org.springframework.transaction.annotation.Transactional; |
| 44 | -import org.springframework.transaction.support.TransactionSynchronization; | |
| 45 | -import org.springframework.transaction.support.TransactionSynchronizationManager; | |
| 46 | 46 | import org.springframework.validation.annotation.Validated; |
| 47 | 47 | |
| 48 | 48 | import java.time.LocalDateTime; |
| 49 | 49 | |
| 50 | 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 | 52 | import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*; |
| 52 | 53 | |
| 53 | 54 | /** |
| ... | ... | @@ -184,8 +185,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 184 | 185 | // 记录售后日志 |
| 185 | 186 | AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), null, |
| 186 | 187 | AfterSaleStatusEnum.APPLY.getStatus()); |
| 187 | - | |
| 188 | - // TODO 发送售后消息 | |
| 189 | 188 | return afterSale; |
| 190 | 189 | } |
| 191 | 190 | |
| ... | ... | @@ -206,8 +205,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 206 | 205 | |
| 207 | 206 | // 记录售后日志 |
| 208 | 207 | AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus); |
| 209 | - | |
| 210 | - // TODO 发送售后消息 | |
| 211 | 208 | } |
| 212 | 209 | |
| 213 | 210 | @Override |
| ... | ... | @@ -226,8 +223,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 226 | 223 | // 记录售后日志 |
| 227 | 224 | AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), newStatus); |
| 228 | 225 | |
| 229 | - // TODO 发送售后消息 | |
| 230 | - | |
| 231 | 226 | // 更新交易订单项的售后状态为【未申请】 |
| 232 | 227 | tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId()); |
| 233 | 228 | } |
| ... | ... | @@ -281,8 +276,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 281 | 276 | AfterSaleStatusEnum.BUYER_DELIVERY.getStatus(), |
| 282 | 277 | MapUtil.<String, Object>builder().put("deliveryName", express.getName()) |
| 283 | 278 | .put("logisticsNo", deliveryReqVO.getLogisticsNo()).build()); |
| 284 | - | |
| 285 | - // TODO 发送售后消息 | |
| 286 | 279 | } |
| 287 | 280 | |
| 288 | 281 | @Override |
| ... | ... | @@ -299,8 +292,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 299 | 292 | // 记录售后日志 |
| 300 | 293 | AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), |
| 301 | 294 | AfterSaleStatusEnum.WAIT_REFUND.getStatus()); |
| 302 | - | |
| 303 | - // TODO 发送售后消息 | |
| 304 | 295 | } |
| 305 | 296 | |
| 306 | 297 | @Override |
| ... | ... | @@ -326,8 +317,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 326 | 317 | AfterSaleStatusEnum.SELLER_REFUSE.getStatus(), |
| 327 | 318 | MapUtil.of("reason", refuseReqVO.getRefuseMemo())); |
| 328 | 319 | |
| 329 | - // TODO 发送售后消息 | |
| 330 | - | |
| 331 | 320 | // 更新交易订单项的售后状态为【未申请】 |
| 332 | 321 | tradeOrderUpdateService.updateOrderItemWhenAfterSaleCancel(afterSale.getOrderItemId()); |
| 333 | 322 | } |
| ... | ... | @@ -365,33 +354,90 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 365 | 354 | // 发起退款单。注意,需要在事务提交后,再进行发起,避免重复发起 |
| 366 | 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 | 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 | 443 | @Override |
| ... | ... | @@ -417,8 +463,6 @@ public class AfterSaleServiceImpl implements AfterSaleService { |
| 417 | 463 | AfterSaleLogUtils.setAfterSaleInfo(afterSale.getId(), afterSale.getStatus(), |
| 418 | 464 | AfterSaleStatusEnum.BUYER_CANCEL.getStatus()); |
| 419 | 465 | |
| 420 | - // TODO 发送售后消息 | |
| 421 | - | |
| 422 | 466 | // 更新交易订单项的售后状态为【未申请】 |
| 423 | 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 | 15 | import cn.iocoder.yudao.module.pay.api.wallet.dto.PayWalletRespDTO; |
| 16 | 16 | import cn.iocoder.yudao.module.pay.enums.PayChannelEnum; |
| 17 | 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 | 18 | import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.withdraw.BrokerageWithdrawPageReqVO; |
| 22 | 19 | import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.withdraw.AppBrokerageWithdrawCreateReqVO; |
| 23 | 20 | import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageWithdrawConvert; |
| ... | ... | @@ -71,8 +68,6 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 71 | 68 | @Resource |
| 72 | 69 | private PayTransferApi payTransferApi; |
| 73 | 70 | @Resource |
| 74 | - private SocialUserApi socialUserApi; | |
| 75 | - @Resource | |
| 76 | 71 | private PayWalletApi payWalletApi; |
| 77 | 72 | |
| 78 | 73 | @Resource |
| ... | ... | @@ -140,13 +135,8 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 140 | 135 | if (Objects.equal(withdraw.getType(), BrokerageWithdrawTypeEnum.ALIPAY_API.getType())) { |
| 141 | 136 | channelCode = PayChannelEnum.ALIPAY_PC.getCode(); |
| 142 | 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 | 138 | channelCode = PayChannelEnum.WX_LITE.getCode(); |
| 149 | - userAccount = socialUser.getOpenid(); | |
| 139 | + userAccount = withdraw.getUserAccount(); | |
| 150 | 140 | // 特殊:微信需要有报备信息 |
| 151 | 141 | channelExtras = PayTransferCreateReqDTO.buildWeiXinChannelExtra1000("佣金提现", "佣金提现"); |
| 152 | 142 | } else if (Objects.equal(withdraw.getType(), BrokerageWithdrawTypeEnum.WALLET.getType())) { |
| ... | ... | @@ -158,7 +148,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 158 | 148 | // 1.2 构建请求 |
| 159 | 149 | PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO() |
| 160 | 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 | 152 | .setUserAccount(userAccount).setUserName(userName).setUserIp(getClientIP()) |
| 163 | 153 | .setChannelExtras(channelExtras); |
| 164 | 154 | // 1.3 发起请求 |
| ... | ... | @@ -293,7 +283,7 @@ public class BrokerageWithdrawServiceImpl implements BrokerageWithdrawService { |
| 293 | 283 | throw exception(BROKERAGE_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH); |
| 294 | 284 | } |
| 295 | 285 | // 2.3 校验转账订单匹配 |
| 296 | - if (ObjectUtil.notEqual(payTransfer.getMerchantOrderId(), withdraw.getId().toString())) { | |
| 286 | + if (ObjectUtil.notEqual(payTransfer.getMerchantTransferId(), withdraw.getId().toString())) { | |
| 297 | 287 | log.error("[validateBrokerageTransferStatusCanUpdate][withdraw({}) 转账单不匹配({}),请进行处理!payTransfer 数据是:{}]", |
| 298 | 288 | withdraw.getId(), payTransferId, JsonUtils.toJsonString(payTransfer)); |
| 299 | 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 | 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 | 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 | 20 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; |
| 21 | 21 | import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; |
| 22 | 22 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; |
| 23 | +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; | |
| 23 | 24 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 25 | +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; | |
| 24 | 26 | import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; |
| 25 | 27 | import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; |
| 26 | 28 | import cn.iocoder.yudao.module.system.api.social.SocialClientApi; |
| ... | ... | @@ -938,11 +940,25 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 938 | 940 | .setAppKey(tradeOrderProperties.getPayAppKey()) // 支付应用 |
| 939 | 941 | .setUserIp(NetUtil.getLocalhostStr()) // 使用本机 IP,因为是服务器发起退款的 |
| 940 | 942 | .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号 |
| 941 | - .setMerchantRefundId(String.valueOf(order.getId())) | |
| 943 | + // 特殊:因为订单支持 AfterSale 单个售后退款,也支持整单退款,所以需要通过 order- 进行下区分 | |
| 944 | + // 具体可见 AfterSaleController 的 updateAfterSaleRefunded 方法 | |
| 945 | + .setMerchantRefundId("order-" + order.getId()) | |
| 942 | 946 | .setReason(TradeOrderCancelTypeEnum.COMBINATION_CLOSE.getName()).setPrice(order.getPayPrice())); // 价格信息 |
| 943 | 947 | } |
| 944 | 948 | |
| 945 | 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 | 962 | public void updateOrderGiveCouponIds(Long userId, Long orderId, List<Long> giveCouponIds) { |
| 947 | 963 | // 1. 检验订单存在 |
| 948 | 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 | 26 | private String merchantOrderId; |
| 27 | 27 | |
| 28 | 28 | /** |
| 29 | + * 商户退款编号 | |
| 30 | + */ | |
| 31 | + @NotEmpty(message = "商户退款编号不能为空") | |
| 32 | + private String merchantRefundId; | |
| 33 | + | |
| 34 | + /** | |
| 29 | 35 | * 支付退款编号 |
| 30 | 36 | */ |
| 31 | 37 | @NotNull(message = "支付退款编号不能为空") | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/notify/dto/PayTransferNotifyReqDTO.java
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 | 43 | */ |
| 44 | 44 | private String merchantOrderId; |
| 45 | 45 | /** |
| 46 | + * 商户退款编号 | |
| 47 | + */ | |
| 48 | + private String merchantRefundId; | |
| 49 | + /** | |
| 46 | 50 | * 退款成功时间 |
| 47 | 51 | */ |
| 48 | 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
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/transfer/dto/PayTransferRespDTO.java
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/refund/PayRefundApiImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.api.refund; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 3 | 4 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; |
| 4 | 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 | 7 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; |
| 8 | +import jakarta.annotation.Resource; | |
| 7 | 9 | import org.springframework.stereotype.Service; |
| 8 | 10 | import org.springframework.validation.annotation.Validated; |
| 9 | 11 | |
| 10 | -import jakarta.annotation.Resource; | |
| 11 | - | |
| 12 | 12 | /** |
| 13 | 13 | * 退款单 API 实现类 |
| 14 | 14 | * |
| ... | ... | @@ -23,12 +23,13 @@ public class PayRefundApiImpl implements PayRefundApi { |
| 23 | 23 | |
| 24 | 24 | @Override |
| 25 | 25 | public Long createRefund(PayRefundCreateReqDTO reqDTO) { |
| 26 | - return payRefundService.createPayRefund(reqDTO); | |
| 26 | + return payRefundService.createRefund(reqDTO); | |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | 29 | @Override |
| 30 | 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 | 66 | @Operation(summary = "更新示例订单为已退款") // 由 pay-module 支付服务,进行回调,可见 PayNotifyJob |
| 67 | 67 | @PermitAll // 无需登录,安全由 PayDemoOrderService 内部校验实现 |
| 68 | 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 | 72 | notifyReqDTO.getPayRefundId()); |
| 71 | 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 | 55 | @Operation(summary = "更新示例提现单的转账状态") // 由 pay-module 转账服务,进行回调 |
| 56 | 56 | @PermitAll // 无需登录,安全由 PayDemoTransferService 内部校验实现 |
| 57 | 57 | public CommonResult<Boolean> updateDemoWithdrawTransferred(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { |
| 58 | - demoWithdrawService.updateDemoWithdrawTransferred(Long.valueOf(notifyReqDTO.getMerchantOrderId()), | |
| 58 | + demoWithdrawService.updateDemoWithdrawTransferred(Long.valueOf(notifyReqDTO.getMerchantTransferId()), | |
| 59 | 59 | notifyReqDTO.getPayTransferId()); |
| 60 | 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 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 6 | 7 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 7 | 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; |
| 8 | 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 | 12 | import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO; |
| 12 | 13 | import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; |
| 13 | 14 | import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskRespVO; |
| 14 | -import cn.iocoder.yudao.module.pay.convert.notify.PayNotifyTaskConvert; | |
| 15 | 15 | import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; |
| 16 | 16 | import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; |
| 17 | 17 | import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; |
| ... | ... | @@ -138,7 +138,12 @@ public class PayNotifyController { |
| 138 | 138 | // 拼接返回 |
| 139 | 139 | PayAppDO app = appService.getApp(task.getAppId()); |
| 140 | 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 | 149 | @GetMapping("/page") |
| ... | ... | @@ -150,8 +155,15 @@ public class PayNotifyController { |
| 150 | 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 | 13 | @Data |
| 14 | 14 | @EqualsAndHashCode(callSuper = true) |
| 15 | 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 | 18 | @Schema(description = "回调日志列表") |
| 31 | 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 | 32 | @Schema(description = "商户订单编号", example = "26697") |
| 33 | 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 | 41 | @Schema(description = "创建时间") |
| 36 | 42 | @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
| 37 | 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 | 6 | |
| 7 | 7 | @Schema(description = "管理后台 - 回调通知 Response VO") |
| 8 | 8 | @Data |
| 9 | -@EqualsAndHashCode(callSuper = true) | |
| 10 | -@ToString(callSuper = true) | |
| 11 | -public class PayNotifyTaskRespVO extends PayNotifyTaskBaseVO { | |
| 9 | +public class PayNotifyTaskRespVO { | |
| 12 | 10 | |
| 13 | 11 | @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3380") |
| 14 | 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 | 17 | @Schema(description = "应用名称", example = "wx_pay") |
| 20 | 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 | 37 | return success(true); |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | - // TODO @jason:发起退款,要 post 操作哈; | |
| 41 | - @GetMapping("/refund") | |
| 40 | + @PostMapping("/refund") | |
| 42 | 41 | @Operation(summary = "发起钱包充值退款") |
| 43 | 42 | @Parameter(name = "id", description = "编号", required = true, example = "1024") |
| 44 | 43 | public CommonResult<Boolean> refundWalletRecharge(@RequestParam("id") Long id) { |
| ... | ... | @@ -51,7 +50,9 @@ public class PayWalletRechargeController { |
| 51 | 50 | @PermitAll // 无需登录, 内部校验实现 |
| 52 | 51 | public CommonResult<Boolean> updateWalletRechargeRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) { |
| 53 | 52 | walletRechargeService.updateWalletRechargeRefunded( |
| 54 | - Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId()); | |
| 53 | + Long.valueOf(notifyReqDTO.getMerchantOrderId()), | |
| 54 | + Long.valueOf(notifyReqDTO.getMerchantRefundId()), | |
| 55 | + notifyReqDTO.getPayRefundId()); | |
| 55 | 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 | 4 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 6 | 6 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; |
| 7 | -import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; | |
| 8 | 7 | import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundDetailsRespVO; |
| 9 | 8 | import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExcelVO; |
| 10 | 9 | import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageItemRespVO; |
| ... | ... | @@ -42,8 +41,6 @@ public interface PayRefundConvert { |
| 42 | 41 | |
| 43 | 42 | PayRefundDO convert(PayRefundCreateReqDTO bean); |
| 44 | 43 | |
| 45 | - PayRefundRespDTO convert02(PayRefundDO bean); | |
| 46 | - | |
| 47 | 44 | default List<PayRefundExcelVO> convertList(List<PayRefundDO> list, Map<Long, PayAppDO> appMap) { |
| 48 | 45 | return CollectionUtils.convertList(list, order -> { |
| 49 | 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 | 4 | import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; |
| 5 | 5 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 6 | 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 7 | +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; | |
| 7 | 8 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; |
| 8 | 9 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 9 | 10 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| ... | ... | @@ -60,6 +61,7 @@ public class PayNotifyTaskDO extends TenantBaseDO { |
| 60 | 61 | * |
| 61 | 62 | * 1. {@link PayNotifyTypeEnum#ORDER} 时,关联 {@link PayOrderDO#getId()} |
| 62 | 63 | * 2. {@link PayNotifyTypeEnum#REFUND} 时,关联 {@link PayRefundDO#getId()} |
| 64 | + * 3. {@link PayNotifyTypeEnum#TRANSFER} 时,关联 {@link PayTransferDO#getId()} | |
| 63 | 65 | */ |
| 64 | 66 | private Long dataId; |
| 65 | 67 | /** |
| ... | ... | @@ -67,6 +69,14 @@ public class PayNotifyTaskDO extends TenantBaseDO { |
| 67 | 69 | */ |
| 68 | 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 | 82 | * 枚举 {@link PayNotifyStatusEnum} | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/transfer/PayTransferDO.java
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<PayNotifyTaskDO> { |
| 37 | 37 | .eqIfPresent(PayNotifyTaskDO::getDataId, reqVO.getDataId()) |
| 38 | 38 | .eqIfPresent(PayNotifyTaskDO::getStatus, reqVO.getStatus()) |
| 39 | 39 | .eqIfPresent(PayNotifyTaskDO::getMerchantOrderId, reqVO.getMerchantOrderId()) |
| 40 | + .eqIfPresent(PayNotifyTaskDO::getMerchantRefundId, reqVO.getMerchantRefundId()) | |
| 41 | + .eqIfPresent(PayNotifyTaskDO::getMerchantTransferId, reqVO.getMerchantTransferId()) | |
| 40 | 42 | .betweenIfPresent(PayNotifyTaskDO::getCreateTime, reqVO.getCreateTime()) |
| 41 | 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<PayTransferDO> { |
| 28 | 28 | |
| 29 | 29 | default PayTransferDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) { |
| 30 | 30 | return selectOne(PayTransferDO::getAppId, appId, |
| 31 | - PayTransferDO::getMerchantOrderId, merchantOrderId); | |
| 31 | + PayTransferDO::getMerchantTransferId, merchantOrderId); | |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | 34 | default PageResult<PayTransferDO> selectPage(PayTransferPageReqVO reqVO) { |
| ... | ... | @@ -36,7 +36,7 @@ public interface PayTransferMapper extends BaseMapperX<PayTransferDO> { |
| 36 | 36 | .eqIfPresent(PayTransferDO::getNo, reqVO.getNo()) |
| 37 | 37 | .eqIfPresent(PayTransferDO::getAppId, reqVO.getAppId()) |
| 38 | 38 | .eqIfPresent(PayTransferDO::getChannelCode, reqVO.getChannelCode()) |
| 39 | - .eqIfPresent(PayTransferDO::getMerchantOrderId, reqVO.getMerchantOrderId()) | |
| 39 | + .eqIfPresent(PayTransferDO::getMerchantTransferId, reqVO.getMerchantOrderId()) | |
| 40 | 40 | .eqIfPresent(PayTransferDO::getStatus, reqVO.getStatus()) |
| 41 | 41 | .likeIfPresent(PayTransferDO::getUserName, reqVO.getUserName()) |
| 42 | 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 | 59 | * 更新示例订单为已退款 |
| 60 | 60 | * |
| 61 | 61 | * @param id 编号 |
| 62 | + * @param refundId 退款编号 | |
| 62 | 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 | 184 | |
| 185 | 185 | // 2.1 生成退款单号 |
| 186 | 186 | // 一般来说,用户发起退款的时候,都会单独插入一个售后维权表,然后使用该表的 id 作为 refundId |
| 187 | - // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示 | |
| 187 | + // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示 | |
| 188 | 188 | String refundId = order.getId() + "-refund"; |
| 189 | 189 | // 2.2 创建退款单 |
| 190 | 190 | Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO() |
| ... | ... | @@ -215,16 +215,18 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { |
| 215 | 215 | } |
| 216 | 216 | |
| 217 | 217 | @Override |
| 218 | - public void updateDemoOrderRefunded(Long id, Long payRefundId) { | |
| 218 | + public void updateDemoOrderRefunded(Long id, String refundId, Long payRefundId) { | |
| 219 | 219 | // 1. 校验并获得退款订单(可退款) |
| 220 | - PayRefundRespDTO payRefund = validateDemoOrderCanRefunded(id, payRefundId); | |
| 220 | + PayRefundRespDTO payRefund = validateDemoOrderCanRefunded(id, refundId, payRefundId); | |
| 221 | 221 | // 2.2 更新退款单到 demo 订单 |
| 222 | 222 | payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(id) |
| 223 | 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 | 227 | // 1.1 校验示例订单 |
| 228 | + // 一般来说,这里应该用 refundId 来查询退款单,然后再校验订单是否匹配 | |
| 229 | + // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id 来查询订单 | |
| 228 | 230 | PayDemoOrderDO order = payDemoOrderMapper.selectById(id); |
| 229 | 231 | if (order == null) { |
| 230 | 232 | throw exception(DEMO_ORDER_NOT_FOUND); |
| ... | ... | @@ -241,7 +243,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { |
| 241 | 243 | if (payRefund == null) { |
| 242 | 244 | throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND); |
| 243 | 245 | } |
| 244 | - // 2.2 | |
| 246 | + // 2.2 必须是退款成功状态 | |
| 245 | 247 | if (!PayRefundStatusEnum.isSuccess(payRefund.getStatus())) { |
| 246 | 248 | throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_NOT_SUCCESS); |
| 247 | 249 | } |
| ... | ... | @@ -252,7 +254,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { |
| 252 | 254 | throw exception(DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH); |
| 253 | 255 | } |
| 254 | 256 | // 2.4 校验退款订单匹配(二次) |
| 255 | - if (notEqual(payRefund.getMerchantOrderId(), id.toString())) { | |
| 257 | + if (notEqual(payRefund.getMerchantRefundId(), id.toString() + "-refund")) { | |
| 256 | 258 | log.error("[validateDemoOrderCanRefunded][order({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]", |
| 257 | 259 | id, payRefundId, toJsonString(payRefund)); |
| 258 | 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 | 75 | // 2.1 创建支付单 |
| 76 | 76 | PayTransferCreateReqDTO transferReqDTO = new PayTransferCreateReqDTO() |
| 77 | 77 | .setAppKey(PAY_APP_KEY).setChannelCode(withdraw.getTransferChannelCode()).setUserIp(getClientIP()) // 支付应用 |
| 78 | - .setMerchantOrderId(String.valueOf(withdraw.getId())) // 业务的订单编号 | |
| 78 | + .setMerchantTransferId(String.valueOf(withdraw.getId())) // 业务的订单编号 | |
| 79 | 79 | .setSubject(withdraw.getSubject()).setPrice(withdraw.getPrice()) // 价格信息 |
| 80 | 80 | .setUserAccount(withdraw.getUserAccount()).setUserName(withdraw.getUserName()); // 收款信息 |
| 81 | 81 | if (ObjectUtil.equal(withdraw.getType(), PayDemoWithdrawTypeEnum.WECHAT.getType())) { |
| ... | ... | @@ -177,7 +177,7 @@ public class PayDemoTransferServiceImpl implements PayDemoWithdrawService { |
| 177 | 177 | throw exception(DEMO_WITHDRAW_UPDATE_STATUS_FAIL_PAY_PRICE_NOT_MATCH); |
| 178 | 178 | } |
| 179 | 179 | // 2.3 校验转账订单匹配(二次) |
| 180 | - if (ObjectUtil.notEqual(payTransfer.getMerchantOrderId(), withdraw.getId().toString())) { | |
| 180 | + if (ObjectUtil.notEqual(payTransfer.getMerchantTransferId(), withdraw.getId().toString())) { | |
| 181 | 181 | log.error("[validateDemoTransferStatusCanUpdate][withdraw({}) 转账单不匹配({}),请进行处理!payTransfer 数据是:{}]", |
| 182 | 182 | withdraw.getId(), payTransferId, JsonUtils.toJsonString(payTransfer)); |
| 183 | 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 | 31 | import com.google.common.annotations.VisibleForTesting; |
| 32 | 32 | import lombok.extern.slf4j.Slf4j; |
| 33 | 33 | import org.springframework.context.annotation.Lazy; |
| 34 | +import org.springframework.scheduling.annotation.Async; | |
| 34 | 35 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| 35 | 36 | import org.springframework.stereotype.Service; |
| 36 | 37 | import org.springframework.transaction.annotation.Transactional; |
| ... | ... | @@ -97,19 +98,19 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 97 | 98 | PayNotifyTaskDO task = new PayNotifyTaskDO().setType(type).setDataId(dataId); |
| 98 | 99 | task.setStatus(PayNotifyStatusEnum.WAITING.getStatus()).setNextNotifyTime(LocalDateTime.now()) |
| 99 | 100 | .setNotifyTimes(0).setMaxNotifyTimes(PayNotifyTaskDO.NOTIFY_FREQUENCY.length + 1); |
| 100 | - // 补充 appId + notifyUrl 字段 | |
| 101 | + // 补充 appId + notifyUrl + merchant* 字段 | |
| 101 | 102 | if (Objects.equals(task.getType(), PayNotifyTypeEnum.ORDER.getType())) { |
| 102 | 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 | 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 | 110 | } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) { |
| 110 | 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 | 118 | |
| 118 | 119 | // 必须在事务提交后,在发起任务,否则 PayNotifyTaskDO 还没入库,就提前回调接入的业务 |
| 119 | 120 | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { |
| 121 | + | |
| 120 | 122 | @Override |
| 121 | 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 | 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 | 185 | * @param task 通知任务 |
| 172 | 186 | */ |
| ... | ... | @@ -223,9 +237,10 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 223 | 237 | .payOrderId(task.getDataId()).build(); |
| 224 | 238 | } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { |
| 225 | 239 | request = PayRefundNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId()) |
| 240 | + .merchantRefundId(task.getMerchantRefundId()) | |
| 226 | 241 | .payRefundId(task.getDataId()).build(); |
| 227 | 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 | 244 | .payTransferId(task.getDataId()).build(); |
| 230 | 245 | } else { |
| 231 | 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
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 | 91 | } |
| 92 | 92 | |
| 93 | 93 | @Override |
| 94 | - public Long createPayRefund(PayRefundCreateReqDTO reqDTO) { | |
| 94 | + public Long createRefund(PayRefundCreateReqDTO reqDTO) { | |
| 95 | 95 | // 1.1 校验 App |
| 96 | 96 | PayAppDO app = appService.validPayApp(reqDTO.getAppKey()); |
| 97 | 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 | 116 | } |
| 117 | 117 | |
| 118 | 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 | 120 | if (transfer != null) { |
| 121 | 121 | // 只有转账单状态为关闭,才能再次发起转账 |
| 122 | 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 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 5 | import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; |
| 6 | 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 | 9 | * 钱包充值 Service 接口 |
| ... | ... | @@ -16,7 +15,7 @@ public interface PayWalletRechargeService { |
| 16 | 15 | /** |
| 17 | 16 | * 创建钱包充值记录(发起充值) |
| 18 | 17 | * |
| 19 | - * @param userId 用户 id | |
| 18 | + * @param userId 用户编号 | |
| 20 | 19 | * @param userType 用户类型 |
| 21 | 20 | * @param createReqVO 钱包充值请求 VO |
| 22 | 21 | * @param userIp 用户Ip |
| ... | ... | @@ -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 | 45 | void updateWalletRechargerPaid(Long id, Long payOrderId); |
| 47 | 46 | |
| ... | ... | @@ -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 | 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 | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | 9 | import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; |
| 10 | 10 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; |
| 11 | +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; | |
| 11 | 12 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; |
| 13 | +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; | |
| 12 | 14 | import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; |
| 13 | 15 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 14 | -import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; | |
| 15 | 16 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; |
| 16 | 17 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; |
| 17 | 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 | 22 | import cn.iocoder.yudao.module.pay.enums.wallet.PayWalletBizTypeEnum; |
| 22 | 23 | import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; |
| 23 | 24 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 24 | -import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; | |
| 25 | 25 | import cn.iocoder.yudao.module.system.api.social.SocialClientApi; |
| 26 | 26 | import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; |
| 27 | 27 | import jakarta.annotation.Resource; |
| ... | ... | @@ -61,13 +61,15 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { |
| 61 | 61 | private PayWalletService payWalletService; |
| 62 | 62 | @Resource |
| 63 | 63 | private PayOrderService payOrderService; |
| 64 | - @Resource | |
| 65 | - private PayRefundService payRefundService; | |
| 64 | +// @Resource | |
| 65 | +// private PayRefundService payRefundService; | |
| 66 | 66 | @Resource |
| 67 | 67 | private PayWalletRechargePackageService payWalletRechargePackageService; |
| 68 | 68 | |
| 69 | 69 | @Resource |
| 70 | 70 | public SocialClientApi socialClientApi; |
| 71 | + @Resource | |
| 72 | + private PayRefundApi payRefundApi; | |
| 71 | 73 | |
| 72 | 74 | @Resource |
| 73 | 75 | private PayProperties payProperties; |
| ... | ... | @@ -186,7 +188,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { |
| 186 | 188 | // 3. 创建退款单 |
| 187 | 189 | String walletRechargeId = String.valueOf(id); |
| 188 | 190 | String refundId = walletRechargeId + "-refund"; |
| 189 | - Long payRefundId = payRefundService.createPayRefund(new PayRefundCreateReqDTO() | |
| 191 | + Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO() | |
| 190 | 192 | .setAppKey(payProperties.getWalletPayAppKey()).setUserIp(userIp) |
| 191 | 193 | .setMerchantOrderId(walletRechargeId) |
| 192 | 194 | .setMerchantRefundId(refundId) |
| ... | ... | @@ -199,18 +201,20 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { |
| 199 | 201 | |
| 200 | 202 | @Override |
| 201 | 203 | @Transactional(rollbackFor = Exception.class) |
| 202 | - public void updateWalletRechargeRefunded(Long id, Long payRefundId) { | |
| 204 | + public void updateWalletRechargeRefunded(Long id, Long refundId, Long payRefundId) { | |
| 203 | 205 | // 1.1 获取钱包充值记录 |
| 206 | + // 说明:因为 id 和 refundId 是相同的,所以直接使用 id 查询即可! | |
| 204 | 207 | PayWalletRechargeDO walletRecharge = walletRechargeMapper.selectById(id); |
| 205 | 208 | if (walletRecharge == null) { |
| 206 | 209 | log.error("[updateWalletRechargerPaid][钱包充值记录不存在,钱包充值记录 id({})]", id); |
| 207 | 210 | throw exception(WALLET_RECHARGE_NOT_FOUND); |
| 208 | 211 | } |
| 209 | 212 | // 1.2 校验钱包充值是否可以更新已退款 |
| 210 | - PayRefundDO payRefund = validateWalletRechargeCanRefunded(walletRecharge, payRefundId); | |
| 213 | + PayRefundRespDTO payRefund = validateWalletRechargeCanRefunded(walletRecharge, payRefundId); | |
| 211 | 214 | |
| 215 | + // 2. 处理退款结果 | |
| 212 | 216 | PayWalletRechargeDO updateObj = new PayWalletRechargeDO().setId(id); |
| 213 | - // 退款成功 | |
| 217 | + // 情况一:退款成功 | |
| 214 | 218 | if (PayRefundStatusEnum.isSuccess(payRefund.getStatus())) { |
| 215 | 219 | // 2.1 更新钱包余额 |
| 216 | 220 | payWalletService.reduceWalletBalance(walletRecharge.getWalletId(), id, |
| ... | ... | @@ -219,19 +223,19 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { |
| 219 | 223 | updateObj.setRefundStatus(SUCCESS.getStatus()).setRefundTime(payRefund.getSuccessTime()) |
| 220 | 224 | .setRefundTotalPrice(walletRecharge.getTotalPrice()).setRefundPayPrice(walletRecharge.getPayPrice()) |
| 221 | 225 | .setRefundBonusPrice(walletRecharge.getBonusPrice()); |
| 222 | - } | |
| 223 | - // 退款失败 | |
| 224 | - if (PayRefundStatusRespEnum.isFailure(payRefund.getStatus())) { | |
| 226 | + // 情况二:退款失败 | |
| 227 | + } else if (PayRefundStatusRespEnum.isFailure(payRefund.getStatus())) { | |
| 225 | 228 | // 2.2 解冻余额 |
| 226 | 229 | payWalletService.unfreezePrice(walletRecharge.getWalletId(), walletRecharge.getTotalPrice()); |
| 227 | 230 | |
| 228 | 231 | updateObj.setRefundStatus(FAILURE.getStatus()); |
| 229 | 232 | } |
| 233 | + | |
| 230 | 234 | // 3. 更新钱包充值的退款字段 |
| 231 | 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 | 239 | // 1. 校验退款订单匹配 |
| 236 | 240 | if (notEqual(walletRecharge.getPayRefundId(), payRefundId)) { |
| 237 | 241 | log.error("[validateWalletRechargeCanRefunded][钱包充值({}) 退款单不匹配({}),请进行处理!钱包充值的数据是:{}]", |
| ... | ... | @@ -240,7 +244,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { |
| 240 | 244 | } |
| 241 | 245 | |
| 242 | 246 | // 2.1 校验退款订单 |
| 243 | - PayRefundDO payRefund = payRefundService.getRefund(payRefundId); | |
| 247 | + PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId); | |
| 244 | 248 | if (payRefund == null) { |
| 245 | 249 | log.error("[validateWalletRechargeCanRefunded][payRefund({})不存在]", payRefundId); |
| 246 | 250 | throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_NOT_FOUND); |
| ... | ... | @@ -252,7 +256,7 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { |
| 252 | 256 | throw exception(WALLET_RECHARGE_REFUND_FAIL_REFUND_PRICE_NOT_MATCH); |
| 253 | 257 | } |
| 254 | 258 | // 2.3 校验退款订单商户订单是否匹配 |
| 255 | - if (notEqual(payRefund.getMerchantOrderId(), walletRecharge.getId().toString())) { | |
| 259 | + if (notEqual(payRefund.getMerchantRefundId(), walletRecharge.getId().toString())) { | |
| 256 | 260 | log.error("[validateWalletRechargeCanRefunded][钱包({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]", |
| 257 | 261 | walletRecharge.getId(), payRefundId, toJsonString(payRefund)); |
| 258 | 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 | 215 | when(appService.validPayApp(eq("demo"))).thenReturn(app); |
| 216 | 216 | |
| 217 | 217 | // 调用,并断言异常 |
| 218 | - assertServiceException(() -> refundService.createPayRefund(reqDTO), | |
| 218 | + assertServiceException(() -> refundService.createRefund(reqDTO), | |
| 219 | 219 | PAY_ORDER_NOT_FOUND); |
| 220 | 220 | } |
| 221 | 221 | |
| ... | ... | @@ -241,7 +241,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { |
| 241 | 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 | 245 | PAY_ORDER_REFUND_FAIL_STATUS_ERROR); |
| 246 | 246 | } |
| 247 | 247 | |
| ... | ... | @@ -260,7 +260,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { |
| 260 | 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 | 264 | REFUND_PRICE_EXCEED); |
| 265 | 265 | } |
| 266 | 266 | |
| ... | ... | @@ -283,7 +283,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { |
| 283 | 283 | refundMapper.insert(refund); |
| 284 | 284 | |
| 285 | 285 | // 调用,并断言异常 |
| 286 | - assertServiceException(() -> refundService.createPayRefund(reqDTO), | |
| 286 | + assertServiceException(() -> refundService.createRefund(reqDTO), | |
| 287 | 287 | REFUND_PRICE_EXCEED); |
| 288 | 288 | } |
| 289 | 289 | |
| ... | ... | @@ -307,7 +307,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { |
| 307 | 307 | when(channelService.validPayChannel(eq(1L))).thenReturn(channel); |
| 308 | 308 | |
| 309 | 309 | // 调用,并断言异常 |
| 310 | - assertServiceException(() -> refundService.createPayRefund(reqDTO), | |
| 310 | + assertServiceException(() -> refundService.createRefund(reqDTO), | |
| 311 | 311 | CHANNEL_NOT_FOUND); |
| 312 | 312 | } |
| 313 | 313 | |
| ... | ... | @@ -339,7 +339,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { |
| 339 | 339 | refundMapper.insert(refund); |
| 340 | 340 | |
| 341 | 341 | // 调用,并断言异常 |
| 342 | - assertServiceException(() -> refundService.createPayRefund(reqDTO), | |
| 342 | + assertServiceException(() -> refundService.createRefund(reqDTO), | |
| 343 | 343 | REFUND_EXISTS); |
| 344 | 344 | } |
| 345 | 345 | |
| ... | ... | @@ -369,7 +369,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { |
| 369 | 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 | 374 | PayRefundDO refundDO = refundMapper.selectById(refundId); |
| 375 | 375 | assertPojoEquals(reqDTO, refundDO); |
| ... | ... | @@ -422,7 +422,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest { |
| 422 | 422 | }))).thenReturn(refundRespDTO); |
| 423 | 423 | |
| 424 | 424 | // 调用 |
| 425 | - Long refundId = refundService.createPayRefund(reqDTO); | |
| 425 | + Long refundId = refundService.createRefund(reqDTO); | |
| 426 | 426 | // 断言 |
| 427 | 427 | PayRefundDO refundDO = refundMapper.selectById(refundId); |
| 428 | 428 | assertPojoEquals(reqDTO, refundDO); | ... | ... |