Commit c44ace601160ae562e85e38240c318591257f93b
1 parent
518e89dc
mall + pay:
1. 完成支付宝的退款重构 2. 完成 demo 模块的退款接入
Showing
23 changed files
with
333 additions
and
274 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
| ... | ... | @@ -68,7 +68,6 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa |
| 68 | 68 | model.setOutTradeNo(reqDTO.getOutTradeNo()); |
| 69 | 69 | model.setOutRequestNo(reqDTO.getOutRefundNo()); |
| 70 | 70 | model.setRefundAmount(formatAmount(reqDTO.getPrice())); |
| 71 | -// model.setRefundAmount(formatAmount(reqDTO.getPrice() / 2)); | |
| 72 | 71 | model.setRefundReason(reqDTO.getReason()); |
| 73 | 72 | // 1.2 构建 AlipayTradePayRequest 请求 |
| 74 | 73 | AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); |
| ... | ... | @@ -77,6 +76,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa |
| 77 | 76 | // 2.1 执行请求 |
| 78 | 77 | AlipayTradeRefundResponse response = client.execute(request); |
| 79 | 78 | PayRefundRespDTO refund = new PayRefundRespDTO() |
| 79 | + .setOutRefundNo(reqDTO.getOutRefundNo()) | |
| 80 | 80 | .setRawData(response); |
| 81 | 81 | // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。 |
| 82 | 82 | // 另外,支付宝没有退款单号,所以不用设置 | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundCreateReqDTO.java
| ... | ... | @@ -27,26 +27,32 @@ public class PayRefundCreateReqDTO { |
| 27 | 27 | private String userIp; |
| 28 | 28 | |
| 29 | 29 | // ========== 商户相关字段 ========== |
| 30 | + /** | |
| 31 | + * 商户订单编号 | |
| 32 | + */ | |
| 33 | + @NotEmpty(message = "商户订单编号不能为空") | |
| 34 | + private String merchantOrderId; | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * 商户退款编号 | |
| 38 | + */ | |
| 39 | + @NotEmpty(message = "商户退款编号不能为空") | |
| 40 | + private String merchantRefundId; | |
| 30 | 41 | |
| 31 | 42 | /** |
| 32 | 43 | * 退款描述 |
| 33 | 44 | */ |
| 34 | 45 | @NotEmpty(message = "退款描述不能为空") |
| 35 | - @Length(max = 128, message = "退款描述长度不能超过128") | |
| 46 | + @Length(max = 128, message = "退款描述长度不能超过 128") | |
| 36 | 47 | private String reason; |
| 37 | 48 | |
| 38 | 49 | // ========== 订单相关字段 ========== |
| 39 | 50 | |
| 40 | 51 | /** |
| 41 | - * 支付单号 | |
| 42 | - */ | |
| 43 | - @NotNull(message = "支付单号不能为空") | |
| 44 | - private Long payOrderId; | |
| 45 | - | |
| 46 | - /** | |
| 47 | 52 | * 退款金额,单位:分 |
| 48 | 53 | */ |
| 49 | 54 | @NotNull(message = "退款金额不能为空") |
| 50 | 55 | @Min(value = 1, message = "退款金额必须大于零") |
| 51 | 56 | private Integer price; |
| 57 | + | |
| 52 | 58 | } | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
| ... | ... | @@ -33,11 +33,12 @@ public interface ErrorCodeConstants { |
| 33 | 33 | ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付"); |
| 34 | 34 | |
| 35 | 35 | // ========== 支付模块(退款) 1007006000 ========== |
| 36 | - ErrorCode PAY_PRICE_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额"); | |
| 36 | + ErrorCode PAY_REFUND_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额"); | |
| 37 | 37 | ErrorCode PAY_REFUND_ALL_REFUNDED = new ErrorCode(1007006001, "订单已经全额退款"); |
| 38 | - ErrorCode PAY_REFUND_CHN_ORDER_NO_IS_NULL = new ErrorCode(1007006002, "该订单的渠道订单为空"); | |
| 39 | - ErrorCode PAY_REFUND_SUCCEED = new ErrorCode(1007006003, "已经退款成功"); | |
| 38 | + ErrorCode PAY_REFUND_HAS_REFUNDING = new ErrorCode(1007006002, "已经有退款在处理中"); | |
| 39 | + ErrorCode PAY_REFUND_EXISTS = new ErrorCode(1007006003, "已经存在退款单"); | |
| 40 | 40 | ErrorCode PAY_REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在"); |
| 41 | + ErrorCode PAY_REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款"); | |
| 41 | 42 | |
| 42 | 43 | // ========== 示例订单 1007900000 ========== |
| 43 | 44 | ErrorCode PAY_DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在"); | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java
| ... | ... | @@ -38,4 +38,14 @@ public enum PayOrderStatusEnum implements IntArrayValuable { |
| 38 | 38 | return Objects.equals(status, SUCCESS.getStatus()); |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | + /** | |
| 42 | + * 判断是否支付关闭 | |
| 43 | + * | |
| 44 | + * @param status 状态 | |
| 45 | + * @return 是否支付关闭 | |
| 46 | + */ | |
| 47 | + public static boolean isClosed(Integer status) { | |
| 48 | + return Objects.equals(status, CLOSED.getStatus()); | |
| 49 | + } | |
| 50 | + | |
| 41 | 51 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java
| ... | ... | @@ -22,7 +22,7 @@ public class PayOrderApiImpl implements PayOrderApi { |
| 22 | 22 | |
| 23 | 23 | @Override |
| 24 | 24 | public Long createOrder(PayOrderCreateReqDTO reqDTO) { |
| 25 | - return payOrderService.createPayOrder(reqDTO); | |
| 25 | + return payOrderService.createOrder(reqDTO); | |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | 28 | @Override | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/refund/PayRefundApiImpl.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.api.refund; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; |
| 4 | 4 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; |
| 5 | +import cn.iocoder.yudao.module.pay.convert.refund.PayRefundConvert; | |
| 5 | 6 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; |
| 6 | 7 | import org.springframework.stereotype.Service; |
| 7 | 8 | import org.springframework.validation.annotation.Validated; |
| ... | ... | @@ -27,8 +28,7 @@ public class PayRefundApiImpl implements PayRefundApi { |
| 27 | 28 | |
| 28 | 29 | @Override |
| 29 | 30 | public PayRefundRespDTO getPayRefund(Long id) { |
| 30 | - // TODO 芋艿:暂未实现 | |
| 31 | - return null; | |
| 31 | + return PayRefundConvert.INSTANCE.convert02(payRefundService.getRefund(id)); | |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | 34 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
| ... | ... | @@ -65,12 +65,12 @@ public class PayNotifyController { |
| 65 | 65 | // 3. 处理通知 |
| 66 | 66 | // 3.1:退款通知 |
| 67 | 67 | if (notify instanceof PayRefundRespDTO) { |
| 68 | - refundService.notifyPayRefund(channelId, (PayRefundRespDTO) notify); | |
| 68 | + refundService.notifyRefund(channelId, (PayRefundRespDTO) notify); | |
| 69 | 69 | return "success"; |
| 70 | 70 | } |
| 71 | 71 | // 3.2:支付通知 |
| 72 | 72 | if (notify instanceof PayOrderRespDTO) { |
| 73 | - orderService.notifyPayOrder(channelId, (PayOrderRespDTO) notify); | |
| 73 | + orderService.notifyOrder(channelId, (PayOrderRespDTO) notify); | |
| 74 | 74 | return "success"; |
| 75 | 75 | } |
| 76 | 76 | throw new UnsupportedOperationException("未知通知:" + toJsonString(notify)); | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java
| ... | ... | @@ -86,7 +86,7 @@ public class PayOrderController { |
| 86 | 86 | @PostMapping("/submit") |
| 87 | 87 | @Operation(summary = "提交支付订单") |
| 88 | 88 | public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) { |
| 89 | - PayOrderSubmitRespVO respVO = payOrderService.submitPayOrder(reqVO, getClientIP()); | |
| 89 | + PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP()); | |
| 90 | 90 | return success(respVO); |
| 91 | 91 | } |
| 92 | 92 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java
| ... | ... | @@ -40,7 +40,7 @@ public class AppPayOrderController { |
| 40 | 40 | @PostMapping("/submit") |
| 41 | 41 | @Operation(summary = "提交支付订单") |
| 42 | 42 | public CommonResult<AppPayOrderSubmitRespVO> submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) { |
| 43 | - PayOrderSubmitRespVO respVO = payOrderService.submitPayOrder(reqVO, getClientIP()); | |
| 43 | + PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP()); | |
| 44 | 44 | return success(PayOrderConvert.INSTANCE.convert3(respVO)); |
| 45 | 45 | } |
| 46 | 46 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.convert.refund; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; | |
| 5 | +import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; | |
| 4 | 6 | import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*; |
| 5 | -import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; | |
| 6 | 7 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 7 | 8 | import org.mapstruct.Mapper; |
| 8 | -import org.mapstruct.Mapping; | |
| 9 | -import org.mapstruct.Mappings; | |
| 10 | 9 | import org.mapstruct.factory.Mappers; |
| 11 | 10 | |
| 12 | 11 | import java.math.BigDecimal; |
| ... | ... | @@ -44,8 +43,6 @@ public interface PayRefundConvert { |
| 44 | 43 | |
| 45 | 44 | PageResult<PayRefundRespVO> convertPage(PageResult<PayRefundDO> page); |
| 46 | 45 | |
| 47 | - List<PayRefundExcelVO> convertList02(List<PayRefundDO> list); | |
| 48 | - | |
| 49 | 46 | /** |
| 50 | 47 | * 退款订单DO 转 导出excel VO |
| 51 | 48 | * |
| ... | ... | @@ -67,7 +64,6 @@ public interface PayRefundConvert { |
| 67 | 64 | payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl()); |
| 68 | 65 | payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus()); |
| 69 | 66 | payRefundExcelVO.setStatus(bean.getStatus()); |
| 70 | - payRefundExcelVO.setType(bean.getType()); | |
| 71 | 67 | payRefundExcelVO.setReason(bean.getReason()); |
| 72 | 68 | payRefundExcelVO.setUserIp(bean.getUserIp()); |
| 73 | 69 | payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo()); |
| ... | ... | @@ -84,12 +80,8 @@ public interface PayRefundConvert { |
| 84 | 80 | return payRefundExcelVO; |
| 85 | 81 | } |
| 86 | 82 | |
| 87 | - //TODO 太多需要处理了, 暂时不用 | |
| 88 | - @Mappings(value = { | |
| 89 | - @Mapping(source = "price", target = "payPrice"), | |
| 90 | - @Mapping(source = "id", target = "orderId"), | |
| 91 | - @Mapping(target = "status",ignore = true) | |
| 92 | - }) | |
| 93 | - PayRefundDO convert(PayOrderDO orderDO); | |
| 83 | + PayRefundDO convert(PayRefundCreateReqDTO bean); | |
| 84 | + | |
| 85 | + PayRefundRespDTO convert02(PayRefundDO bean); | |
| 94 | 86 | |
| 95 | 87 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java
| ... | ... | @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; |
| 6 | 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; |
| 7 | 7 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; |
| 8 | 8 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 9 | -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; | |
| 9 | +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; | |
| 10 | 10 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 11 | 11 | import com.baomidou.mybatisplus.annotation.TableName; |
| 12 | 12 | import lombok.*; |
| ... | ... | @@ -127,7 +127,7 @@ public class PayOrderDO extends BaseDO { |
| 127 | 127 | /** |
| 128 | 128 | * 退款状态 |
| 129 | 129 | * |
| 130 | - * 枚举 {@link PayRefundTypeEnum} | |
| 130 | + * 枚举 {@link PayOrderRefundStatusEnum} | |
| 131 | 131 | */ |
| 132 | 132 | private Integer refundStatus; |
| 133 | 133 | /** |
| ... | ... | @@ -137,7 +137,7 @@ public class PayOrderDO extends BaseDO { |
| 137 | 137 | /** |
| 138 | 138 | * 退款总金额,单位:分 |
| 139 | 139 | */ |
| 140 | - private Long refundPrice; | |
| 140 | + private Integer refundPrice; | |
| 141 | 141 | |
| 142 | 142 | // ========== 渠道相关字段 ========== |
| 143 | 143 | /** | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
| ... | ... | @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; |
| 7 | 7 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 8 | 8 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; |
| 9 | 9 | import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; |
| 10 | -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; | |
| 11 | 10 | import com.baomidou.mybatisplus.annotation.KeySequence; |
| 12 | 11 | import com.baomidou.mybatisplus.annotation.TableId; |
| 13 | 12 | import com.baomidou.mybatisplus.annotation.TableName; |
| ... | ... | @@ -105,12 +104,6 @@ public class PayRefundDO extends BaseDO { |
| 105 | 104 | private Integer status; |
| 106 | 105 | |
| 107 | 106 | /** |
| 108 | - * 退款类型(部分退款,全部退款) | |
| 109 | - * | |
| 110 | - * 枚举 {@link PayRefundTypeEnum} | |
| 111 | - */ | |
| 112 | - private Integer type; | |
| 113 | - /** | |
| 114 | 107 | * 支付金额,单位:分 |
| 115 | 108 | */ |
| 116 | 109 | private Integer payPrice; |
| ... | ... | @@ -158,12 +151,6 @@ public class PayRefundDO extends BaseDO { |
| 158 | 151 | private String channelErrorMsg; |
| 159 | 152 | |
| 160 | 153 | /** |
| 161 | - * 支付渠道的额外参数 | |
| 162 | - * | |
| 163 | - * 参见 <a href="https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html">参数说明</> | |
| 164 | - */ | |
| 165 | - private String channelExtras; | |
| 166 | - /** | |
| 167 | 154 | * 支付渠道异步通知的内容 |
| 168 | 155 | * |
| 169 | 156 | * 在退款成功后,会记录回调的数据 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.dal.mysql.refund; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; | |
| 4 | -import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; | |
| 5 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 7 | 6 | import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; |
| 7 | +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; | |
| 8 | +import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; | |
| 8 | 9 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 10 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 9 | 11 | import org.apache.ibatis.annotations.Mapper; |
| 10 | 12 | |
| 11 | 13 | import java.util.List; |
| ... | ... | @@ -13,6 +15,34 @@ import java.util.List; |
| 13 | 15 | @Mapper |
| 14 | 16 | public interface PayRefundMapper extends BaseMapperX<PayRefundDO> { |
| 15 | 17 | |
| 18 | + default Long selectCountByAppId(Long appId) { | |
| 19 | + return selectCount(PayRefundDO::getAppId, appId); | |
| 20 | + } | |
| 21 | + | |
| 22 | + default PayRefundDO selectByAppIdAndMerchantRefundId(Long appId, String merchantRefundId) { | |
| 23 | + return selectOne(new LambdaQueryWrapperX<PayRefundDO>() | |
| 24 | + .eq(PayRefundDO::getAppId, appId) | |
| 25 | + .eq(PayRefundDO::getMerchantRefundId, merchantRefundId)); | |
| 26 | + } | |
| 27 | + | |
| 28 | + default Long selectCountByAppIdAndOrderId(Long appId, Long orderId, Integer status) { | |
| 29 | + return selectCount(new LambdaQueryWrapperX<PayRefundDO>() | |
| 30 | + .eq(PayRefundDO::getAppId, appId) | |
| 31 | + .eq(PayRefundDO::getOrderId, orderId) | |
| 32 | + .eq(PayRefundDO::getStatus, status)); | |
| 33 | + } | |
| 34 | + | |
| 35 | + default PayRefundDO selectByAppIdAndNo(Long appId, String no) { | |
| 36 | + return selectOne(new LambdaQueryWrapperX<PayRefundDO>() | |
| 37 | + .eq(PayRefundDO::getAppId, appId) | |
| 38 | + .eq(PayRefundDO::getNo, no)); | |
| 39 | + } | |
| 40 | + | |
| 41 | + default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) { | |
| 42 | + return update(update, new LambdaQueryWrapper<PayRefundDO>() | |
| 43 | + .eq(PayRefundDO::getId, id).eq(PayRefundDO::getStatus, status)); | |
| 44 | + } | |
| 45 | + | |
| 16 | 46 | default PageResult<PayRefundDO> selectPage(PayRefundPageReqVO reqVO) { |
| 17 | 47 | return selectPage(reqVO, new QueryWrapperX<PayRefundDO>() |
| 18 | 48 | .eqIfPresent("app_id", reqVO.getAppId()) |
| ... | ... | @@ -37,18 +67,4 @@ public interface PayRefundMapper extends BaseMapperX<PayRefundDO> { |
| 37 | 67 | .orderByDesc("id")); |
| 38 | 68 | } |
| 39 | 69 | |
| 40 | - default Long selectCountByApp(Long appId) { | |
| 41 | - return selectCount(PayRefundDO::getAppId, appId); | |
| 42 | - } | |
| 43 | - | |
| 44 | - default PayRefundDO selectByReqNo(String reqNo) { | |
| 45 | - return selectOne("req_no", reqNo); | |
| 46 | - } | |
| 47 | - | |
| 48 | - // TODO 芋艿:要重构 | |
| 49 | - default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){ | |
| 50 | -// return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); | |
| 51 | - return null; | |
| 52 | - } | |
| 53 | - | |
| 54 | 70 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundTypeEnum.java renamed to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java
| 1 | -package cn.iocoder.yudao.module.pay.enums.refund; | |
| 1 | +package cn.iocoder.yudao.module.pay.enums.order; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.core.IntArrayValuable; |
| 4 | 4 | import lombok.AllArgsConstructor; |
| ... | ... | @@ -11,10 +11,10 @@ import lombok.Getter; |
| 11 | 11 | */ |
| 12 | 12 | @Getter |
| 13 | 13 | @AllArgsConstructor |
| 14 | -public enum PayRefundTypeEnum implements IntArrayValuable { | |
| 14 | +public enum PayOrderRefundStatusEnum implements IntArrayValuable { | |
| 15 | 15 | |
| 16 | 16 | NO(0, "未退款"), |
| 17 | - SOME(10, "部分退款"), | |
| 17 | + PART(10, "部分退款"), | |
| 18 | 18 | ALL(20, "全部退款") |
| 19 | 19 | ; |
| 20 | 20 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
| ... | ... | @@ -184,12 +184,17 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { |
| 184 | 184 | // 1. 校验订单是否可以退款 |
| 185 | 185 | PayDemoOrderDO order = validateDemoOrderCanRefund(id); |
| 186 | 186 | |
| 187 | - // 2.1 创建退款单 | |
| 187 | + // 2.1 生成退款单号 | |
| 188 | + // 一般来说,用户发起退款的时候,都会单独插入一个售后维权表,然后使用该表的 id 作为 refundId | |
| 189 | + // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示 | |
| 190 | + String refundId = order.getId() + "-refund"; | |
| 191 | + // 2.2 创建退款单 | |
| 188 | 192 | Long payRefundId = payRefundApi.createPayRefund(new PayRefundCreateReqDTO() |
| 189 | 193 | .setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用 |
| 190 | - .setPayOrderId(order.getPayOrderId()) // 支付单号 | |
| 194 | + .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号 | |
| 195 | + .setMerchantRefundId(refundId) | |
| 191 | 196 | .setReason("想退钱").setPrice(order.getPrice()));// 价格信息 |
| 192 | - // 2.2 更新退款单到 demo 订单 | |
| 197 | + // 2.3 更新退款单到 demo 订单 | |
| 193 | 198 | payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(id) |
| 194 | 199 | .setPayRefundId(payRefundId).setRefundPrice(order.getPrice())); |
| 195 | 200 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java
| ... | ... | @@ -32,6 +32,15 @@ public interface PayOrderService { |
| 32 | 32 | PayOrderDO getOrder(Long id); |
| 33 | 33 | |
| 34 | 34 | /** |
| 35 | + * 获得支付订单 | |
| 36 | + * | |
| 37 | + * @param appId 应用编号 | |
| 38 | + * @param merchantOrderId 商户订单编号 | |
| 39 | + * @return 支付订单 | |
| 40 | + */ | |
| 41 | + PayOrderDO getOrder(Long appId, String merchantOrderId); | |
| 42 | + | |
| 43 | + /** | |
| 35 | 44 | * 获得指定应用的订单数量 |
| 36 | 45 | * |
| 37 | 46 | * @param appId 应用编号 |
| ... | ... | @@ -70,11 +79,11 @@ public interface PayOrderService { |
| 70 | 79 | /** |
| 71 | 80 | * 根据订单 ID 集合获取订单商品名称Map集合 |
| 72 | 81 | * |
| 73 | - * @param idList 订单 ID 集合 | |
| 82 | + * @param ids 订单 ID 集合 | |
| 74 | 83 | * @return 订单商品 map 集合 |
| 75 | 84 | */ |
| 76 | - default Map<Long, PayOrderDO> getOrderSubjectMap(Collection<Long> idList) { | |
| 77 | - List<PayOrderDO> list = getOrderSubjectList(idList); | |
| 85 | + default Map<Long, PayOrderDO> getOrderSubjectMap(Collection<Long> ids) { | |
| 86 | + List<PayOrderDO> list = getOrderSubjectList(ids); | |
| 78 | 87 | return CollectionUtils.convertMap(list, PayOrderDO::getId); |
| 79 | 88 | } |
| 80 | 89 | |
| ... | ... | @@ -84,7 +93,7 @@ public interface PayOrderService { |
| 84 | 93 | * @param reqDTO 创建请求 |
| 85 | 94 | * @return 支付单编号 |
| 86 | 95 | */ |
| 87 | - Long createPayOrder(@Valid PayOrderCreateReqDTO reqDTO); | |
| 96 | + Long createOrder(@Valid PayOrderCreateReqDTO reqDTO); | |
| 88 | 97 | |
| 89 | 98 | /** |
| 90 | 99 | * 提交支付 |
| ... | ... | @@ -94,8 +103,8 @@ public interface PayOrderService { |
| 94 | 103 | * @param userIp 提交 IP |
| 95 | 104 | * @return 提交结果 |
| 96 | 105 | */ |
| 97 | - PayOrderSubmitRespVO submitPayOrder(@Valid PayOrderSubmitReqVO reqVO, | |
| 98 | - @NotEmpty(message = "提交 IP 不能为空") String userIp); | |
| 106 | + PayOrderSubmitRespVO submitOrder(@Valid PayOrderSubmitReqVO reqVO, | |
| 107 | + @NotEmpty(message = "提交 IP 不能为空") String userIp); | |
| 99 | 108 | |
| 100 | 109 | /** |
| 101 | 110 | * 通知支付单成功 |
| ... | ... | @@ -103,6 +112,14 @@ public interface PayOrderService { |
| 103 | 112 | * @param channelId 渠道编号 |
| 104 | 113 | * @param notify 通知 |
| 105 | 114 | */ |
| 106 | - void notifyPayOrder(Long channelId, PayOrderRespDTO notify); | |
| 115 | + void notifyOrder(Long channelId, PayOrderRespDTO notify); | |
| 116 | + | |
| 117 | + /** | |
| 118 | + * 更新支付订单的退款金额 | |
| 119 | + * | |
| 120 | + * @param id 编号 | |
| 121 | + * @param incrRefundPrice 增加的退款金额 | |
| 122 | + */ | |
| 123 | + void updateOrderRefundPrice(Long id, Integer incrRefundPrice); | |
| 107 | 124 | |
| 108 | 125 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
| ... | ... | @@ -26,11 +26,10 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 26 | 26 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; |
| 27 | 27 | import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper; |
| 28 | 28 | import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; |
| 29 | -import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; | |
| 30 | 29 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 31 | 30 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; |
| 31 | +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; | |
| 32 | 32 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 33 | -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; | |
| 34 | 33 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 35 | 34 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 36 | 35 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| ... | ... | @@ -48,6 +47,7 @@ import java.util.Objects; |
| 48 | 47 | |
| 49 | 48 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 50 | 49 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| 50 | +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; | |
| 51 | 51 | |
| 52 | 52 | /** |
| 53 | 53 | * 支付订单 Service 实现类 |
| ... | ... | @@ -83,6 +83,11 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | @Override |
| 86 | + public PayOrderDO getOrder(Long appId, String merchantOrderId) { | |
| 87 | + return orderMapper.selectByAppIdAndMerchantOrderId(appId, merchantOrderId); | |
| 88 | + } | |
| 89 | + | |
| 90 | + @Override | |
| 86 | 91 | public Long getOrderCountByAppId(Long appId) { |
| 87 | 92 | return orderMapper.selectCountByAppId(appId); |
| 88 | 93 | } |
| ... | ... | @@ -104,7 +109,7 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 104 | 109 | } |
| 105 | 110 | |
| 106 | 111 | @Override |
| 107 | - public Long createPayOrder(PayOrderCreateReqDTO reqDTO) { | |
| 112 | + public Long createOrder(PayOrderCreateReqDTO reqDTO) { | |
| 108 | 113 | // 校验 App |
| 109 | 114 | PayAppDO app = appService.validPayApp(reqDTO.getAppId()); |
| 110 | 115 | |
| ... | ... | @@ -112,7 +117,7 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 112 | 117 | PayOrderDO order = orderMapper.selectByAppIdAndMerchantOrderId( |
| 113 | 118 | reqDTO.getAppId(), reqDTO.getMerchantOrderId()); |
| 114 | 119 | if (order != null) { |
| 115 | - log.warn("[createPayOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(), | |
| 120 | + log.warn("[createOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(), | |
| 116 | 121 | order.getMerchantOrderId(), toJsonString(order)); // 理论来说,不会出现这个情况 |
| 117 | 122 | return order.getId(); |
| 118 | 123 | } |
| ... | ... | @@ -124,16 +129,16 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 124 | 129 | // 订单相关字段 |
| 125 | 130 | .setStatus(PayOrderStatusEnum.WAITING.getStatus()) |
| 126 | 131 | // 退款相关字段 |
| 127 | - .setRefundStatus(PayRefundTypeEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0L); | |
| 132 | + .setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0); | |
| 128 | 133 | orderMapper.insert(order); |
| 129 | 134 | return order.getId(); |
| 130 | 135 | } |
| 131 | 136 | |
| 132 | 137 | @Override |
| 133 | 138 | @Transactional(rollbackFor = Exception.class) |
| 134 | - public PayOrderSubmitRespVO submitPayOrder(PayOrderSubmitReqVO reqVO, String userIp) { | |
| 139 | + public PayOrderSubmitRespVO submitOrder(PayOrderSubmitReqVO reqVO, String userIp) { | |
| 135 | 140 | // 1. 获得 PayOrderDO ,并校验其是否存在 |
| 136 | - PayOrderDO order = validatePayOrderCanSubmit(reqVO.getId()); | |
| 141 | + PayOrderDO order = validateOrderCanSubmit(reqVO.getId()); | |
| 137 | 142 | // 1.2 校验支付渠道是否有效 |
| 138 | 143 | PayChannelDO channel = validatePayChannelCanSubmit(order.getAppId(), reqVO.getChannelCode()); |
| 139 | 144 | PayClient client = payClientFactory.getPayClient(channel.getId()); |
| ... | ... | @@ -167,16 +172,16 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 167 | 172 | return PayOrderConvert.INSTANCE.convert(order, unifiedOrderRespDTO); |
| 168 | 173 | } |
| 169 | 174 | |
| 170 | - private PayOrderDO validatePayOrderCanSubmit(Long id) { | |
| 175 | + private PayOrderDO validateOrderCanSubmit(Long id) { | |
| 171 | 176 | PayOrderDO order = orderMapper.selectById(id); |
| 172 | 177 | if (order == null) { // 是否存在 |
| 173 | - throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); | |
| 178 | + throw exception(PAY_ORDER_NOT_FOUND); | |
| 174 | 179 | } |
| 175 | 180 | if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 |
| 176 | - throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); | |
| 181 | + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); | |
| 177 | 182 | } |
| 178 | 183 | if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) { // 校验是否过期 |
| 179 | - throw exception(ErrorCodeConstants.PAY_ORDER_IS_EXPIRED); | |
| 184 | + throw exception(PAY_ORDER_IS_EXPIRED); | |
| 180 | 185 | } |
| 181 | 186 | return order; |
| 182 | 187 | } |
| ... | ... | @@ -184,14 +189,12 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 184 | 189 | private PayChannelDO validatePayChannelCanSubmit(Long appId, String channelCode) { |
| 185 | 190 | // 校验 App |
| 186 | 191 | appService.validPayApp(appId); |
| 187 | - | |
| 188 | 192 | // 校验支付渠道是否有效 |
| 189 | 193 | PayChannelDO channel = channelService.validPayChannel(appId, channelCode); |
| 190 | - // 校验支付客户端是否正确初始化 | |
| 191 | 194 | PayClient client = payClientFactory.getPayClient(channel.getId()); |
| 192 | 195 | if (client == null) { |
| 193 | 196 | log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); |
| 194 | - throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND); | |
| 197 | + throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); | |
| 195 | 198 | } |
| 196 | 199 | return channel; |
| 197 | 200 | } |
| ... | ... | @@ -226,28 +229,55 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 226 | 229 | |
| 227 | 230 | @Override |
| 228 | 231 | @Transactional(rollbackFor = Exception.class) |
| 229 | - public void notifyPayOrder(Long channelId, PayOrderRespDTO notify) { | |
| 232 | + public void notifyOrder(Long channelId, PayOrderRespDTO notify) { | |
| 230 | 233 | // 校验支付渠道是否有效 |
| 231 | 234 | PayChannelDO channel = channelService.validPayChannel(channelId); |
| 232 | 235 | // 更新支付订单为已支付 |
| 233 | 236 | TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify)); |
| 234 | 237 | } |
| 235 | 238 | |
| 239 | + @Override | |
| 240 | + public void updateOrderRefundPrice(Long id, Integer incrRefundPrice) { | |
| 241 | + PayOrderDO order = orderMapper.selectById(id); | |
| 242 | + if (order == null) { | |
| 243 | + throw exception(PAY_ORDER_NOT_FOUND); | |
| 244 | + } | |
| 245 | + if (!PayOrderStatusEnum.isSuccess(order.getStatus())) { | |
| 246 | + throw exception(PAY_REFUND_PRICE_EXCEED); | |
| 247 | + } | |
| 248 | + if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) { | |
| 249 | + throw exception(PAY_REFUND_PRICE_EXCEED); | |
| 250 | + } | |
| 251 | + | |
| 252 | + // 更新订单 | |
| 253 | + PayOrderDO updateObj = new PayOrderDO() | |
| 254 | + .setRefundPrice(order.getRefundPrice() + incrRefundPrice) | |
| 255 | + .setRefundTimes(order.getRefundTimes() + 1); | |
| 256 | + if (Objects.equals(updateObj.getRefundPrice(), order.getPrice())) { | |
| 257 | + updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) | |
| 258 | + .setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()); | |
| 259 | + } else { | |
| 260 | + updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus()) | |
| 261 | + .setRefundStatus(PayOrderRefundStatusEnum.PART.getStatus()); | |
| 262 | + } | |
| 263 | + orderMapper.updateByIdAndStatus(id, PayOrderStatusEnum.SUCCESS.getStatus(), updateObj); | |
| 264 | + } | |
| 265 | + | |
| 236 | 266 | private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) { |
| 237 | 267 | // 情况一:支付成功的回调 |
| 238 | 268 | if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) { |
| 239 | - notifyPayOrderSuccess(channel, notify); | |
| 269 | + notifyOrderSuccess(channel, notify); | |
| 240 | 270 | return; |
| 241 | 271 | } |
| 242 | 272 | // 情况二:非支付成功的回调,进行忽略 |
| 243 | 273 | log.info("[notifyPayOrder][非支付成功的回调({}),直接忽略]", toJsonString(notify)); |
| 244 | 274 | } |
| 245 | 275 | |
| 246 | - private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { | |
| 276 | + private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { | |
| 247 | 277 | // 1. 更新 PayOrderExtensionDO 支付成功 |
| 248 | - PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify); | |
| 278 | + PayOrderExtensionDO orderExtension = updateOrderExtensionSuccess(notify); | |
| 249 | 279 | // 2. 更新 PayOrderDO 支付成功 |
| 250 | - Pair<Boolean, PayOrderDO> order = updatePayOrderSuccess(channel, orderExtension, notify); | |
| 280 | + Pair<Boolean, PayOrderDO> order = updateOrderExtensionSuccess(channel, orderExtension, notify); | |
| 251 | 281 | if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 |
| 252 | 282 | return; |
| 253 | 283 | } |
| ... | ... | @@ -263,27 +293,27 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 263 | 293 | * @param notify 通知 |
| 264 | 294 | * @return PayOrderExtensionDO 对象 |
| 265 | 295 | */ |
| 266 | - private PayOrderExtensionDO updatePayOrderExtensionSuccess(PayOrderRespDTO notify) { | |
| 296 | + private PayOrderExtensionDO updateOrderExtensionSuccess(PayOrderRespDTO notify) { | |
| 267 | 297 | // 1. 查询 PayOrderExtensionDO |
| 268 | 298 | PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); |
| 269 | 299 | if (orderExtension == null) { |
| 270 | - throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND); | |
| 300 | + throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); | |
| 271 | 301 | } |
| 272 | 302 | if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 |
| 273 | - log.info("[updatePayOrderSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId()); | |
| 303 | + log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId()); | |
| 274 | 304 | return orderExtension; |
| 275 | 305 | } |
| 276 | 306 | if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付 |
| 277 | - throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); | |
| 307 | + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); | |
| 278 | 308 | } |
| 279 | 309 | |
| 280 | 310 | // 2. 更新 PayOrderExtensionDO |
| 281 | - int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(), | |
| 311 | + int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(), | |
| 282 | 312 | PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build()); |
| 283 | 313 | if (updateCounts == 0) { // 校验状态,必须是待支付 |
| 284 | - throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); | |
| 314 | + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); | |
| 285 | 315 | } |
| 286 | - log.info("[updatePayOrderSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId()); | |
| 316 | + log.info("[updateOrderExtensionSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId()); | |
| 287 | 317 | return orderExtension; |
| 288 | 318 | } |
| 289 | 319 | |
| ... | ... | @@ -296,20 +326,20 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 296 | 326 | * @return key:是否之前已经成功回调 |
| 297 | 327 | * value:PayOrderDO 对象 |
| 298 | 328 | */ |
| 299 | - private Pair<Boolean, PayOrderDO> updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, | |
| 329 | + private Pair<Boolean, PayOrderDO> updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, | |
| 300 | 330 | PayOrderRespDTO notify) { |
| 301 | 331 | // 1. 判断 PayOrderDO 是否处于待支付 |
| 302 | 332 | PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); |
| 303 | 333 | if (order == null) { |
| 304 | - throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); | |
| 334 | + throw exception(PAY_ORDER_NOT_FOUND); | |
| 305 | 335 | } |
| 306 | 336 | if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新 |
| 307 | 337 | && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) { |
| 308 | - log.info("[updatePayOrderSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId()); | |
| 338 | + log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId()); | |
| 309 | 339 | return Pair.of(true, order); |
| 310 | 340 | } |
| 311 | 341 | if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 |
| 312 | - throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); | |
| 342 | + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); | |
| 313 | 343 | } |
| 314 | 344 | |
| 315 | 345 | // 2. 更新 PayOrderDO |
| ... | ... | @@ -320,9 +350,9 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 320 | 350 | .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId()) |
| 321 | 351 | .notifyTime(LocalDateTime.now()).build()); |
| 322 | 352 | if (updateCounts == 0) { // 校验状态,必须是待支付 |
| 323 | - throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); | |
| 353 | + throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); | |
| 324 | 354 | } |
| 325 | - log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId()); | |
| 355 | + log.info("[updateOrderExtensionSuccess][支付订单({}) 更新为已支付]", order.getId()); | |
| 326 | 356 | return Pair.of(false, order); |
| 327 | 357 | } |
| 328 | 358 | ... | ... |
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
| 1 | 1 | package cn.iocoder.yudao.module.pay.service.refund; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.date.DateUtil; | |
| 3 | 4 | import cn.hutool.core.util.RandomUtil; |
| 4 | -import cn.hutool.core.util.StrUtil; | |
| 5 | -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | |
| 6 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | 6 | import cn.iocoder.yudao.framework.pay.config.PayProperties; |
| 8 | 7 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| ... | ... | @@ -10,22 +9,23 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| 10 | 9 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; |
| 11 | 10 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; |
| 12 | 11 | import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; |
| 12 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | |
| 13 | 13 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; |
| 14 | 14 | import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; |
| 15 | 15 | import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; |
| 16 | +import cn.iocoder.yudao.module.pay.convert.refund.PayRefundConvert; | |
| 16 | 17 | import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; |
| 17 | 18 | import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; |
| 18 | 19 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 19 | 20 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; |
| 20 | 21 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 21 | -import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; | |
| 22 | 22 | import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; |
| 23 | 23 | import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; |
| 24 | +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; | |
| 24 | 25 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 25 | -import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; | |
| 26 | +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; | |
| 26 | 27 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 27 | 28 | import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; |
| 28 | -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; | |
| 29 | 29 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 30 | 30 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 31 | 31 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| ... | ... | @@ -38,8 +38,11 @@ import org.springframework.transaction.annotation.Transactional; |
| 38 | 38 | import org.springframework.validation.annotation.Validated; |
| 39 | 39 | |
| 40 | 40 | import javax.annotation.Resource; |
| 41 | +import java.time.LocalDateTime; | |
| 41 | 42 | import java.util.List; |
| 42 | -import java.util.Objects; | |
| 43 | + | |
| 44 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 45 | +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | |
| 43 | 46 | |
| 44 | 47 | /** |
| 45 | 48 | * 退款订单 Service 实现类 |
| ... | ... | @@ -59,8 +62,6 @@ public class PayRefundServiceImpl implements PayRefundService { |
| 59 | 62 | |
| 60 | 63 | @Resource |
| 61 | 64 | private PayRefundMapper refundMapper; |
| 62 | - @Resource | |
| 63 | - private PayOrderMapper orderMapper; // TODO @jason:需要改成不直接操作 db; | |
| 64 | 65 | |
| 65 | 66 | @Resource |
| 66 | 67 | private PayOrderService orderService; |
| ... | ... | @@ -80,7 +81,7 @@ public class PayRefundServiceImpl implements PayRefundService { |
| 80 | 81 | |
| 81 | 82 | @Override |
| 82 | 83 | public Long getRefundCountByAppId(Long appId) { |
| 83 | - return refundMapper.selectCountByApp(appId); | |
| 84 | + return refundMapper.selectCountByAppId(appId); | |
| 84 | 85 | } |
| 85 | 86 | |
| 86 | 87 | @Override |
| ... | ... | @@ -96,82 +97,82 @@ public class PayRefundServiceImpl implements PayRefundService { |
| 96 | 97 | @Override |
| 97 | 98 | @Transactional(rollbackFor = Exception.class) |
| 98 | 99 | public Long createPayRefund(PayRefundCreateReqDTO reqDTO) { |
| 99 | - // 获得 PayOrderDO | |
| 100 | - PayOrderDO order = orderService.getOrder(reqDTO.getPayOrderId()); | |
| 101 | - // 校验订单是否存在 | |
| 102 | - if (Objects.isNull(order) ) { | |
| 103 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); | |
| 104 | - } | |
| 105 | - // 校验 App | |
| 106 | - PayAppDO app = appService.validPayApp(order.getAppId()); | |
| 107 | - // 校验支付渠道是否有效 | |
| 100 | + // 1.1 校验 App | |
| 101 | + PayAppDO app = appService.validPayApp(reqDTO.getAppId()); | |
| 102 | + // 1.2 校验支付订单 | |
| 103 | + PayOrderDO order = validatePayOrderCanRefund(reqDTO); | |
| 104 | + // 1.3 校验支付渠道是否有效 | |
| 108 | 105 | PayChannelDO channel = channelService.validPayChannel(order.getChannelId()); |
| 109 | - // 校验支付客户端是否正确初始化 | |
| 110 | 106 | PayClient client = payClientFactory.getPayClient(channel.getId()); |
| 111 | 107 | if (client == null) { |
| 112 | 108 | log.error("[refund][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); |
| 113 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND); | |
| 109 | + throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND); | |
| 114 | 110 | } |
| 115 | - | |
| 116 | - // TODO 芋艿:待实现 | |
| 117 | - String merchantRefundId = "rrr" + RandomUtil.randomNumbers(16); | |
| 118 | - | |
| 119 | - // 校验退款的条件 | |
| 120 | - validatePayRefund(reqDTO, order); | |
| 121 | - // 退款类型 | |
| 122 | - PayRefundTypeEnum refundType = PayRefundTypeEnum.SOME; | |
| 123 | - if (Objects.equals(reqDTO.getPrice(), order.getPrice())) { | |
| 124 | - refundType = PayRefundTypeEnum.ALL; | |
| 111 | + // 1.4 校验退款订单是否已经存在 | |
| 112 | + PayRefundDO refund = refundMapper.selectByAppIdAndMerchantRefundId( | |
| 113 | + app.getId(), reqDTO.getMerchantRefundId()); | |
| 114 | + if (refund != null) { | |
| 115 | + throw exception(ErrorCodeConstants.PAY_REFUND_EXISTS); | |
| 125 | 116 | } |
| 126 | - PayOrderExtensionDO orderExtensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); | |
| 127 | - PayRefundDO payRefundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), | |
| 128 | - merchantRefundId); // TODO 芋艿:需要优化 | |
| 129 | - if (Objects.nonNull(payRefundDO)) { | |
| 130 | - // 退款订单已经提交过。 | |
| 131 | - //TODO 校验相同退款单的金额 | |
| 132 | - // TODO @jason:咱要不封装一个 ObjectUtils.equalsAny | |
| 133 | - if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus())) { | |
| 134 | - //已成功退款 | |
| 135 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_SUCCEED); | |
| 136 | - } | |
| 137 | - //可以重复提交,保证 退款请求号 一致,由渠道保证幂等 | |
| 138 | - } else { | |
| 139 | - // 成功,插入退款单 状态为生成.没有和渠道交互 | |
| 140 | - // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下; | |
| 141 | - payRefundDO = PayRefundDO.builder() | |
| 142 | - .appId(order.getAppId()) | |
| 143 | - .channelOrderNo(order.getChannelOrderNo()) | |
| 144 | - .channelCode(order.getChannelCode()) | |
| 145 | - .channelId(order.getChannelId()) | |
| 146 | - .orderId(order.getId()) | |
| 147 | - .merchantRefundId(merchantRefundId) | |
| 148 | - .notifyUrl(app.getRefundNotifyUrl()) | |
| 149 | - .payPrice(order.getPrice()) | |
| 150 | - .refundPrice(reqDTO.getPrice()) | |
| 151 | - .userIp(reqDTO.getUserIp()) | |
| 152 | - .merchantOrderId(order.getMerchantOrderId()) | |
| 153 | - .no(orderExtensionDO.getNo()) | |
| 154 | - .status(PayRefundStatusEnum.WAITING.getStatus()) | |
| 155 | - .reason(reqDTO.getReason()) | |
| 156 | - .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) | |
| 157 | - .type(refundType.getStatus()) | |
| 158 | - .build(); | |
| 159 | - refundMapper.insert(payRefundDO); | |
| 160 | - } | |
| 161 | - // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下; | |
| 117 | + | |
| 118 | + // 2.1 插入退款单 | |
| 119 | + refund = PayRefundConvert.INSTANCE.convert(reqDTO) | |
| 120 | + .setNo(generateRefundNo()).setOrderId(order.getId()) | |
| 121 | + .setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode()) | |
| 122 | + // 商户相关的字段 | |
| 123 | + .setNotifyUrl(app.getRefundNotifyUrl()).setNotifyStatus(PayNotifyStatusEnum.WAITING.getStatus()) | |
| 124 | + // 渠道相关字段 | |
| 125 | + .setChannelOrderNo(order.getChannelOrderNo()) | |
| 126 | + // 退款相关字段 | |
| 127 | + .setStatus(PayRefundStatusEnum.WAITING.getStatus()) | |
| 128 | + .setPayPrice(order.getPrice()).setRefundPrice(reqDTO.getPrice()); | |
| 129 | + refundMapper.insert(refund); | |
| 130 | + // 2.2 向渠道发起退款申请 | |
| 131 | + PayOrderExtensionDO orderExtension = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); | |
| 162 | 132 | PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); |
| 163 | 133 | unifiedReqDTO.setPrice(reqDTO.getPrice()) |
| 164 | - .setOutTradeNo(orderExtensionDO.getNo()) | |
| 165 | - .setOutRefundNo(merchantRefundId) // TODO 芋艿:需要优化 | |
| 134 | + .setOutTradeNo(orderExtension.getNo()) | |
| 135 | + .setOutRefundNo(refund.getNo()) | |
| 166 | 136 | .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl |
| 167 | 137 | .setReason(reqDTO.getReason()); |
| 168 | - // 向渠道发起退款申请 | |
| 169 | - client.unifiedRefund(unifiedReqDTO); | |
| 170 | - // 检查是否失败,失败抛出业务异常。 | |
| 171 | - // TODO 渠道的异常记录。 | |
| 172 | - // TODO @jason:可以先打个 warn log 哈; | |
| 138 | + PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO); // TODO 增加一个 channelErrorCode、channelErrorMsg 字段 | |
| 139 | + // 2.3 处理退款返回 | |
| 140 | + notifyRefund(channel, refundRespDTO); | |
| 141 | + | |
| 173 | 142 | // 成功在 退款回调中处理 |
| 174 | - return payRefundDO.getId(); | |
| 143 | + return refund.getId(); | |
| 144 | + } | |
| 145 | + | |
| 146 | + /** | |
| 147 | + * 校验支付订单是否可以退款 | |
| 148 | + * | |
| 149 | + * @param reqDTO 退款申请信息 | |
| 150 | + * @return 支付订单 | |
| 151 | + */ | |
| 152 | + private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO) { | |
| 153 | + PayOrderDO order = orderService.getOrder(reqDTO.getAppId(), reqDTO.getMerchantOrderId()); | |
| 154 | + if (order == null) { | |
| 155 | + throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); | |
| 156 | + } | |
| 157 | + // 校验状态,必须是支付状态 | |
| 158 | + if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { | |
| 159 | + throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_SUCCESS); | |
| 160 | + } | |
| 161 | + | |
| 162 | + // 是否已经全额退款 | |
| 163 | + if (PayOrderRefundStatusEnum.ALL.getStatus().equals(order.getRefundStatus())) { | |
| 164 | + throw exception(ErrorCodeConstants.PAY_REFUND_ALL_REFUNDED); | |
| 165 | + } | |
| 166 | + // 校验金额 退款金额不能大于原定的金额 | |
| 167 | + if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){ | |
| 168 | + throw exception(ErrorCodeConstants.PAY_REFUND_PRICE_EXCEED); | |
| 169 | + } | |
| 170 | + // 是否有退款中的订单 | |
| 171 | + if (refundMapper.selectCountByAppIdAndOrderId(reqDTO.getAppId(), order.getId(), | |
| 172 | + PayRefundStatusEnum.WAITING.getStatus()) > 0) { | |
| 173 | + throw exception(ErrorCodeConstants.PAY_REFUND_HAS_REFUNDING); | |
| 174 | + } | |
| 175 | + return order; | |
| 175 | 176 | } |
| 176 | 177 | |
| 177 | 178 | /** |
| ... | ... | @@ -184,88 +185,80 @@ public class PayRefundServiceImpl implements PayRefundService { |
| 184 | 185 | return payProperties.getCallbackUrl() + "/" + channel.getId(); |
| 185 | 186 | } |
| 186 | 187 | |
| 188 | + private String generateRefundNo() { | |
| 189 | +// wx | |
| 190 | +// 2014 | |
| 191 | +// 10 | |
| 192 | +// 27 | |
| 193 | +// 20 | |
| 194 | +// 09 | |
| 195 | +// 39 | |
| 196 | +// 5522657 | |
| 197 | +// a690389285100 | |
| 198 | + // 目前的算法 | |
| 199 | + // 时间序列,年月日时分秒 14 位 | |
| 200 | + // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整 | |
| 201 | + return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + // 时间序列 | |
| 202 | + RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒 | |
| 203 | + ; | |
| 204 | + } | |
| 205 | + | |
| 187 | 206 | @Override |
| 188 | 207 | @Transactional(rollbackFor = Exception.class) |
| 189 | - public void notifyPayRefund(Long channelId, PayRefundRespDTO notify) { | |
| 208 | + public void notifyRefund(Long channelId, PayRefundRespDTO notify) { | |
| 209 | + // 校验支付渠道是否有效 | |
| 210 | + channelService.validPayChannel(channelId); | |
| 211 | + // 通知结果 | |
| 212 | + | |
| 190 | 213 | // 校验支付渠道是否有效 |
| 191 | - // TODO 芋艿:需要重构下这块的逻辑 | |
| 192 | 214 | PayChannelDO channel = channelService.validPayChannel(channelId); |
| 215 | + // 更新退款订单 | |
| 216 | + TenantUtils.execute(channel.getTenantId(), () -> notifyRefund(channel, notify)); | |
| 217 | + } | |
| 218 | + | |
| 219 | + private void notifyRefund(PayChannelDO channel, PayRefundRespDTO notify) { | |
| 193 | 220 | if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) { |
| 194 | - payRefundSuccess(notify); | |
| 221 | + notifyRefundSuccess(channel, notify); | |
| 195 | 222 | } else { |
| 196 | - // TODO @jason:那这里可以考虑打个 error logger @芋艿 微信是否存在支付异常通知 | |
| 223 | + notifyRefundFailure(channel, notify); | |
| 197 | 224 | } |
| 198 | 225 | } |
| 199 | 226 | |
| 200 | - private void payRefundSuccess(PayRefundRespDTO refundNotify) { | |
| 201 | - // 校验退款单存在 | |
| 202 | - PayRefundDO refundDO = null; // TODO 芋艿:临时注释 | |
| 203 | -// PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), | |
| 204 | -// refundNotify.getReqNo()); | |
| 205 | - if (refundDO == null) { | |
| 206 | - // TODO 芋艿:临时注释 | |
| 207 | -// log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo()); | |
| 208 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND); | |
| 227 | + private void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) { | |
| 228 | + // 1.1 查询 PayRefundDO | |
| 229 | + PayRefundDO refund = refundMapper.selectByAppIdAndNo( | |
| 230 | + channel.getAppId(), notify.getOutRefundNo()); | |
| 231 | + if (refund == null) { | |
| 232 | + throw exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND); | |
| 233 | + } | |
| 234 | + if (PayRefundStatusEnum.isSuccess(refund.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 | |
| 235 | + return; | |
| 236 | + } | |
| 237 | + if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) { | |
| 238 | + throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING); | |
| 209 | 239 | } |
| 210 | 240 | |
| 211 | - // 得到已退金额 | |
| 212 | - PayOrderDO payOrderDO = orderService.getOrder(refundDO.getOrderId()); | |
| 213 | - Long refundedAmount = payOrderDO.getRefundPrice(); | |
| 214 | - | |
| 215 | - PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS; | |
| 216 | - if(Objects.equals(payOrderDO.getPrice(), refundedAmount+ refundDO.getRefundPrice())){ | |
| 217 | - //支付金额 = 已退金额 + 本次退款金额。 | |
| 218 | - orderStatus = PayOrderStatusEnum.CLOSED; | |
| 241 | + // 1.2 更新 PayRefundDO | |
| 242 | + PayRefundDO updateRefundObj = new PayRefundDO() | |
| 243 | + .setSuccessTime(notify.getSuccessTime()) | |
| 244 | + .setChannelRefundNo(notify.getChannelRefundNo()) | |
| 245 | + .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()) | |
| 246 | + .setChannelNotifyData(toJsonString(notify)); | |
| 247 | + int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj); | |
| 248 | + if (updateCounts == 0) { // 校验状态,必须是等待状态 | |
| 249 | + throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING); | |
| 219 | 250 | } |
| 220 | - // 更新支付订单 | |
| 221 | - PayOrderDO updateOrderDO = new PayOrderDO(); | |
| 222 | - updateOrderDO.setId(refundDO.getOrderId()) | |
| 223 | - .setRefundPrice(refundedAmount + refundDO.getRefundPrice()) | |
| 224 | - .setStatus(orderStatus.getStatus()) | |
| 225 | - .setRefundTimes(payOrderDO.getRefundTimes() + 1) | |
| 226 | - .setRefundStatus(refundDO.getType()); | |
| 227 | - orderMapper.updateById(updateOrderDO); | |
| 228 | 251 | |
| 229 | - // 更新退款订单 | |
| 230 | - PayRefundDO updateRefundDO = new PayRefundDO(); | |
| 231 | - updateRefundDO.setId(refundDO.getId()) | |
| 232 | - .setSuccessTime(refundNotify.getSuccessTime()) | |
| 233 | - // TODO 芋艿:如下两行,临时注释 | |
| 234 | -// .setChannelRefundNo(refundNotify.getChannelOrderNo()) | |
| 235 | -// .setNo(refundNotify.getTradeNo()) | |
| 236 | - .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); | |
| 237 | - refundMapper.updateById(updateRefundDO); | |
| 252 | + // 2. 更新订单 | |
| 253 | + orderService.updateOrderRefundPrice(refund.getOrderId(), refund.getRefundPrice()); | |
| 238 | 254 | |
| 239 | - // 插入退款通知记录 | |
| 240 | - // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 | |
| 255 | + // 3. 插入退款通知记录 | |
| 241 | 256 | notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() |
| 242 | - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build()); | |
| 257 | + .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()); | |
| 243 | 258 | } |
| 244 | 259 | |
| 245 | - /** | |
| 246 | - * 校验是否进行退款 | |
| 247 | - * | |
| 248 | - * @param reqDTO 退款申请信息 | |
| 249 | - * @param order 原始支付订单信息 | |
| 250 | - */ | |
| 251 | - private void validatePayRefund(PayRefundCreateReqDTO reqDTO, PayOrderDO order) { | |
| 252 | - // 校验状态,必须是支付状态 | |
| 253 | - if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { | |
| 254 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_SUCCESS); | |
| 255 | - } | |
| 256 | - // 是否已经全额退款 | |
| 257 | - if (PayRefundTypeEnum.ALL.getStatus().equals(order.getRefundStatus())) { | |
| 258 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_ALL_REFUNDED); | |
| 259 | - } | |
| 260 | - // 校验金额 退款金额不能大于 原定的金额 | |
| 261 | - if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){ | |
| 262 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_PRICE_PRICE_EXCEED); | |
| 263 | - } | |
| 264 | - // 校验渠道订单号 | |
| 265 | - if (StrUtil.isEmpty(order.getChannelOrderNo())) { | |
| 266 | - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_CHN_ORDER_NO_IS_NULL); | |
| 267 | - } | |
| 268 | - //TODO 退款的期限 退款次数的控制 | |
| 260 | + private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) { | |
| 261 | + // TODO 芋艿:未实现 | |
| 269 | 262 | } |
| 270 | 263 | |
| 271 | 264 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java
| ... | ... | @@ -13,7 +13,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 13 | 13 | import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper; |
| 14 | 14 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; |
| 15 | 15 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 16 | -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; | |
| 16 | +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; | |
| 17 | 17 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 18 | 18 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 19 | 19 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| ... | ... | @@ -85,7 +85,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 85 | 85 | o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2)); |
| 86 | 86 | o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); |
| 87 | 87 | o.setSuccessExtensionId(1L); |
| 88 | - o.setRefundStatus(PayRefundTypeEnum.NO.getStatus()); | |
| 88 | + o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); | |
| 89 | 89 | o.setRefundTimes(0); |
| 90 | 90 | o.setRefundPrice(0L); |
| 91 | 91 | o.setChannelUserId("1008611"); |
| ... | ... | @@ -106,7 +106,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 106 | 106 | // 测试 status 不匹配 |
| 107 | 107 | orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()))); |
| 108 | 108 | // 测试 refundStatus 不匹配 |
| 109 | - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayRefundTypeEnum.ALL.getStatus()))); | |
| 109 | + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()))); | |
| 110 | 110 | // 测试 createTime 不匹配 |
| 111 | 111 | orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10, |
| 112 | 112 | 1)))); |
| ... | ... | @@ -118,7 +118,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 118 | 118 | reqVO.setMerchantOrderId(merchantOrderId); |
| 119 | 119 | reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); |
| 120 | 120 | reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); |
| 121 | - reqVO.setRefundStatus(PayRefundTypeEnum.NO.getStatus()); | |
| 121 | + reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); | |
| 122 | 122 | reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)})); |
| 123 | 123 | // 调用 |
| 124 | 124 | PageResult<PayOrderDO> pageResult = orderService.getOrderPage(reqVO); |
| ... | ... | @@ -153,7 +153,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 153 | 153 | o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2)); |
| 154 | 154 | o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15)); |
| 155 | 155 | o.setSuccessExtensionId(1L); |
| 156 | - o.setRefundStatus(PayRefundTypeEnum.NO.getStatus()); | |
| 156 | + o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); | |
| 157 | 157 | o.setRefundTimes(0); |
| 158 | 158 | o.setRefundPrice(0L); |
| 159 | 159 | o.setChannelUserId("1008611"); |
| ... | ... | @@ -175,7 +175,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 175 | 175 | // 测试 status 不匹配 |
| 176 | 176 | orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus()))); |
| 177 | 177 | // 测试 refundStatus 不匹配 |
| 178 | - orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayRefundTypeEnum.ALL.getStatus()))); | |
| 178 | + orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus()))); | |
| 179 | 179 | // 测试 createTime 不匹配 |
| 180 | 180 | orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10, |
| 181 | 181 | 1)))); |
| ... | ... | @@ -187,7 +187,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest { |
| 187 | 187 | reqVO.setMerchantOrderId(merchantOrderId); |
| 188 | 188 | reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); |
| 189 | 189 | reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus()); |
| 190 | - reqVO.setRefundStatus(PayRefundTypeEnum.NO.getStatus()); | |
| 190 | + reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()); | |
| 191 | 191 | reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)})); |
| 192 | 192 | |
| 193 | 193 | // 调用 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
| ... | ... | @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 11 | 11 | import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; |
| 12 | 12 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; |
| 13 | 13 | import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; |
| 14 | -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; | |
| 14 | +import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum; | |
| 15 | 15 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 16 | 16 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 17 | 17 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| ... | ... | @@ -64,11 +64,11 @@ public class PayRefundServiceTest extends BaseDbUnitTest { |
| 64 | 64 | o.setOrderId(1L); |
| 65 | 65 | o.setNo("OT0000001"); |
| 66 | 66 | o.setMerchantOrderId("MOT0000001"); |
| 67 | - o.setMerchantRefundNo("MRF0000001"); | |
| 67 | + o.setMerchantRefundId("MRF0000001"); | |
| 68 | 68 | o.setNotifyUrl("https://www.cancanzi.com"); |
| 69 | 69 | o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); |
| 70 | 70 | o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); |
| 71 | - o.setType(PayRefundTypeEnum.SOME.getStatus()); | |
| 71 | + o.setType(PayOrderRefundStatusEnum.PART.getStatus()); | |
| 72 | 72 | o.setPayPrice(100); |
| 73 | 73 | o.setRefundPrice(500); |
| 74 | 74 | o.setReason("就是想退款了,你有意见吗"); |
| ... | ... | @@ -77,10 +77,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { |
| 77 | 77 | o.setChannelRefundNo("CHR0000001"); |
| 78 | 78 | o.setChannelErrorCode(""); |
| 79 | 79 | o.setChannelErrorMsg(""); |
| 80 | - o.setChannelExtras(""); | |
| 81 | - o.setExpireTime(LocalDateTime.of(2021, 1, 1, 10, 10, 30)); | |
| 82 | 80 | o.setSuccessTime(LocalDateTime.of(2021, 1, 1, 10, 10, 15)); |
| 83 | - o.setNotifyTime(LocalDateTime.of(2021, 1, 1, 10, 10, 20)); | |
| 84 | 81 | o.setCreateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 10)); |
| 85 | 82 | o.setUpdateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 35)); |
| 86 | 83 | }); |
| ... | ... | @@ -90,14 +87,14 @@ public class PayRefundServiceTest extends BaseDbUnitTest { |
| 90 | 87 | // 测试 channelCode 不匹配 |
| 91 | 88 | refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); |
| 92 | 89 | // 测试 merchantRefundNo 不匹配 |
| 93 | - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundNo("MRF1111112"))); | |
| 90 | + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112"))); | |
| 94 | 91 | // 测试 notifyStatus 不匹配 |
| 95 | 92 | refundMapper.insert( |
| 96 | 93 | cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); |
| 97 | 94 | // 测试 status 不匹配 |
| 98 | - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.CLOSE.getStatus()))); | |
| 95 | + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()))); | |
| 99 | 96 | // 测试 type 不匹配 |
| 100 | - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayRefundTypeEnum.ALL.getStatus()))); | |
| 97 | + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayOrderRefundStatusEnum.ALL.getStatus()))); | |
| 101 | 98 | // 测试 createTime 不匹配 |
| 102 | 99 | refundMapper.insert(cloneIgnoreId(dbRefund, o -> |
| 103 | 100 | o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10)))); |
| ... | ... | @@ -108,7 +105,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { |
| 108 | 105 | reqVO.setMerchantRefundNo("MRF0000001"); |
| 109 | 106 | reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); |
| 110 | 107 | reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); |
| 111 | - reqVO.setType(PayRefundTypeEnum.SOME.getStatus()); | |
| 108 | + reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus()); | |
| 112 | 109 | reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)})); |
| 113 | 110 | |
| 114 | 111 | // 调用 |
| ... | ... | @@ -129,11 +126,11 @@ public class PayRefundServiceTest extends BaseDbUnitTest { |
| 129 | 126 | o.setOrderId(1L); |
| 130 | 127 | o.setNo("OT0000001"); |
| 131 | 128 | o.setMerchantOrderId("MOT0000001"); |
| 132 | - o.setMerchantRefundNo("MRF0000001"); | |
| 129 | + o.setMerchantRefundId("MRF0000001"); | |
| 133 | 130 | o.setNotifyUrl("https://www.cancanzi.com"); |
| 134 | 131 | o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); |
| 135 | 132 | o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); |
| 136 | - o.setType(PayRefundTypeEnum.SOME.getStatus()); | |
| 133 | + o.setType(PayOrderRefundStatusEnum.PART.getStatus()); | |
| 137 | 134 | o.setPayPrice(100); |
| 138 | 135 | o.setRefundPrice(500); |
| 139 | 136 | o.setReason("就是想退款了,你有意见吗"); |
| ... | ... | @@ -142,10 +139,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { |
| 142 | 139 | o.setChannelRefundNo("CHR0000001"); |
| 143 | 140 | o.setChannelErrorCode(""); |
| 144 | 141 | o.setChannelErrorMsg(""); |
| 145 | - o.setChannelExtras(""); | |
| 146 | - o.setExpireTime(LocalDateTime.of(2021, 1, 1, 10, 10, 30)); | |
| 147 | 142 | o.setSuccessTime(LocalDateTime.of(2021, 1, 1, 10, 10, 15)); |
| 148 | - o.setNotifyTime(LocalDateTime.of(2021, 1, 1, 10, 10, 20)); | |
| 149 | 143 | o.setCreateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 10)); |
| 150 | 144 | o.setUpdateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 35)); |
| 151 | 145 | }); |
| ... | ... | @@ -155,14 +149,14 @@ public class PayRefundServiceTest extends BaseDbUnitTest { |
| 155 | 149 | // 测试 channelCode 不匹配 |
| 156 | 150 | refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()))); |
| 157 | 151 | // 测试 merchantRefundNo 不匹配 |
| 158 | - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundNo("MRF1111112"))); | |
| 152 | + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112"))); | |
| 159 | 153 | // 测试 notifyStatus 不匹配 |
| 160 | 154 | refundMapper.insert( |
| 161 | 155 | cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus()))); |
| 162 | 156 | // 测试 status 不匹配 |
| 163 | - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.CLOSE.getStatus()))); | |
| 157 | + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()))); | |
| 164 | 158 | // 测试 type 不匹配 |
| 165 | - refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayRefundTypeEnum.ALL.getStatus()))); | |
| 159 | + refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayOrderRefundStatusEnum.ALL.getStatus()))); | |
| 166 | 160 | // 测试 createTime 不匹配 |
| 167 | 161 | refundMapper.insert(cloneIgnoreId(dbRefund, o -> |
| 168 | 162 | o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10)))); |
| ... | ... | @@ -174,7 +168,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { |
| 174 | 168 | reqVO.setMerchantRefundNo("MRF0000001"); |
| 175 | 169 | reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus()); |
| 176 | 170 | reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); |
| 177 | - reqVO.setType(PayRefundTypeEnum.SOME.getStatus()); | |
| 171 | + reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus()); | |
| 178 | 172 | reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)})); |
| 179 | 173 | |
| 180 | 174 | // 调用 | ... | ... |