diff --git a/sql/change_db.sql b/sql/change_db.sql new file mode 100644 index 0000000000..4cffc8cfff --- /dev/null +++ b/sql/change_db.sql @@ -0,0 +1,5 @@ +ALTER TABLE `ruoyi-vue-pro`.`pay_order_extension` +CHANGE COLUMN `channel_notify_data` `channel_notify_data` VARCHAR(2048) CHARACTER SET 'utf8mb4' NULL DEFAULT NULL COMMENT '支付渠道异步通知的内容' ; + +ALTER TABLE `ruoyi-vue-pro`.`pay_refund` +CHANGE COLUMN `req_no` `req_no` VARCHAR(64) NULL COMMENT '退款单请求号' ; diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayRefundCoreConvert.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayRefundCoreConvert.java index 8bc035b515..f8e6a78a95 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayRefundCoreConvert.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayRefundCoreConvert.java @@ -2,19 +2,17 @@ package cn.iocoder.yudao.coreservice.modules.pay.convert.order; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundPostReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; import org.mapstruct.Mapping; import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; @Mapper public interface PayRefundCoreConvert { PayRefundCoreConvert INSTANCE = Mappers.getMapper(PayRefundCoreConvert.class); - PayRefundPostReqDTO convert(PayRefundUnifiedRespDTO respDTO); + //TODO 太多需要处理了, 暂时不用 @Mappings(value = { diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java index 98e6b55f25..6015d39abd 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java @@ -42,9 +42,10 @@ public class PayRefundDO extends BaseDO { * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更, * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 * 退款单请求号,根据规则生成 - * * 例如说,R202109181134287570000 + * 废弃,使用 merchantRefundNo 做退款请求号 */ + @Deprecated private String reqNo; /** @@ -82,7 +83,7 @@ public class PayRefundDO extends BaseDO { /** * 交易订单号,根据规则生成 * 调用支付渠道时,使用该字段作为对接的订单号。 - * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no + * 1. 调用微信支付 https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 时,使用该字段作为 out_trade_no * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no * 这里对应 pay_extension 里面的 no * 例如说,P202110132239124200055 @@ -95,10 +96,20 @@ public class PayRefundDO extends BaseDO { * 商户订单编号 */ private String merchantOrderId; + /** - * 商户退款订单号, 由商户系统产生, 由他们保证唯一,不能为空,通知商户时会传该字段。发送channel 使用 reqNo + * 商户退款订单号, 由商户系统产生, 由他们保证唯一,不能为空,通知商户时会传该字段。 * 例如说,内部系统 A 的退款订单号。需要保证每个 PayMerchantDO 唯一 - * TODO 芋艿:我理解 一个商户退款订单,可以对应多条退款记录, 因为有可能失败。但是 退款请求号 reqNo 必须唯一 + * 个商户退款订单,对应一条退款请求记录。可多次提交。 渠道保持幂等 + * 使用商户退款单,作为退款请求号 + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml 中的 out_refund_no + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no + * 退款请求号。 + * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 + * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更, + * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 + * 退款单请求号,根据规则生成 + * 例如说,R202109181134287570000 * */ private String merchantRefundNo; @@ -151,6 +162,9 @@ public class PayRefundDO extends BaseDO { */ private String channelOrderNo; /** + * 微信中的 refund_id + * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml + * 支付宝没有 * 渠道退款单号,渠道返回 */ private String channelRefundNo; diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayRefundCoreMapper.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayRefundCoreMapper.java index dba8dd86b2..ad157071b2 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayRefundCoreMapper.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayRefundCoreMapper.java @@ -15,4 +15,8 @@ public interface PayRefundCoreMapper extends BaseMapperX { default PayRefundDO selectByReqNo(String reqNo) { return selectOne("req_no", reqNo); } + + default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){ + return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); + } } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java index 687d1d28e0..9eefa3f877 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java @@ -46,9 +46,10 @@ public interface PayErrorCodeCoreConstants { ErrorCode PAY_REFUND_AMOUNT_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额"); ErrorCode PAY_REFUND_ALL_REFUNDED = new ErrorCode(1007006001, "订单已经全额退款"); ErrorCode PAY_REFUND_CHN_ORDER_NO_IS_NULL = new ErrorCode(1007006002, "该订单的渠道订单为空"); - ErrorCode PAY_REFUND_POST_HANDLER_NOT_FOUND = new ErrorCode(1007006003, "未找到对应的退款后置处理类"); + ErrorCode PAY_REFUND_SUCCEED = new ErrorCode(1007006003, "已经退款成功"); ErrorCode PAY_REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在"); + /** * ========== 支付商户信息 1-007-004-000 ========== */ diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayRefundStatusEnum.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayRefundStatusEnum.java index 3b95849efe..35d99b0ec9 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayRefundStatusEnum.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayRefundStatusEnum.java @@ -9,10 +9,6 @@ public enum PayRefundStatusEnum { CREATE(0, "退款订单生成"), SUCCESS(1, "退款成功"), FAILURE(2, "退款失败"), - PROCESSING_NOTIFY(3,"退款中, 渠道通知结果"), - PROCESSING_QUERY(4,"退款中, 系统查询结果"), - UNKNOWN_RETRY(5,"状态未知,需要重试"), - UNKNOWN_QUERY(6,"状态未知,系统查询结果"), CLOSE(99, "退款关闭"); private final Integer status; diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundAbstractChannelPostHandler.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundAbstractChannelPostHandler.java deleted file mode 100644 index 47ded02b09..0000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundAbstractChannelPostHandler.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.pay.service.order; - -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundCoreMapper; - -/** - * 支付退款订单渠道返回后 , 后置处理抽象类, 处理公用的逻辑 - * @author jason - */ -public abstract class PayRefundAbstractChannelPostHandler implements PayRefundChannelPostHandler { - - private final PayOrderCoreMapper payOrderCoreMapper; - private final PayRefundCoreMapper payRefundMapper; - - public PayRefundAbstractChannelPostHandler(PayOrderCoreMapper payOrderCoreMapper, - PayRefundCoreMapper payRefundMapper){ - this.payOrderCoreMapper = payOrderCoreMapper; - this.payRefundMapper = payRefundMapper; - } - - - /** - * 更新退款单 - * @param refundDO 需要更新的退款单信息 - */ - protected void updatePayRefund(PayRefundDO refundDO){ - payRefundMapper.updateById(refundDO); - } - - - /** - * 更新原始支付订单 - * @param payOrderDO 支付订单信息 - */ - protected void updatePayOrder(PayOrderDO payOrderDO){ - payOrderCoreMapper.updateById(payOrderDO); - } -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundChannelPostHandler.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundChannelPostHandler.java deleted file mode 100644 index 8c0820acb5..0000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundChannelPostHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.pay.service.order; - -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundPostReqDTO; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; - -/** - * 支付退款订单 ,渠道返回后 后置处理 - * - * @author jason - */ -public interface PayRefundChannelPostHandler { - - /** - * 支持的渠道返回值 - * - * @return 支持的渠道返回值数组 - */ - PayChannelRespEnum[] supportHandleResp(); - - /** - * 根据渠道返回,处理支付退款单 - * - * @param req 退款后置处理请求 - */ - void handleRefundChannelResp(PayRefundPostReqDTO req); -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundPostReqDTO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundPostReqDTO.java deleted file mode 100644 index ea32c32bfa..0000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundPostReqDTO.java +++ /dev/null @@ -1,99 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.pay.service.order.dto; - -import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; - -/** - * 退款后置处理请求 Request DTO - */ -@Data -@Accessors(chain = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class PayRefundPostReqDTO { - - - /** - * 渠道的通用返回结果 - */ - private PayChannelRespEnum respEnum; - - - - private PayRefundTypeEnum refundTypeEnum; - - /** - * 已退款的总金额 - */ - private Long refundedAmount; - - /** - * 本次退款金额 - */ - private Long refundAmount; - - /** - * 已退款次数 - */ - private Integer refundedTimes; - - - /** - * 订单编号 - */ - private Long orderId; - - /** - * 退款单编号 - */ - private Long refundId; - - - /** - * 渠道退款单号 - */ - private String channelRefundNo; - - - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no - * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空 - */ - private String payTradeNo; - - - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no - * 退款请求单号 同一退款请求单号多次请求只退一笔。 - */ - private String refundReqNo; - - - - /** - * 调用异常错误信息 - */ - private String exceptionMsg; - - - /** - * 渠道的错误码 - */ - private String channelErrCode; - - - /** - * 渠道的错误描述 - */ - private String channelErrMsg; - - -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundRespDTO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundRespDTO.java index 8a13212ac8..3000b5b22a 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundRespDTO.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayRefundRespDTO.java @@ -16,6 +16,23 @@ import lombok.experimental.Accessors; @AllArgsConstructor public class PayRefundRespDTO { + /** + * 渠道返回结果 + * 退款处理中和退款成功 返回 1 + * 失败和其他情况 返回 2 + */ + private Integer channelReturnResult; + + /** + * 渠道返回code + */ + private String channelReturnCode; + + /** + * 渠道返回消息 + */ + private String channelReturnMsg; + /** * 支付退款单编号, 自增 */ diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundCoreServiceImpl.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundCoreServiceImpl.java index 1c1f384331..0cda8ad09c 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundCoreServiceImpl.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundCoreServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayRefundCoreConvert; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; @@ -14,7 +13,6 @@ import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum; import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundPostReqDTO; import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundNotifyDTO; import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum; @@ -23,21 +21,18 @@ import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService; import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService; import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundChannelPostHandler; import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundRespDTO; -import cn.iocoder.yudao.coreservice.modules.pay.util.PaySeqUtils; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRefundRespEnum; import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.util.*; @@ -65,25 +60,6 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { @Resource private PayClientFactory payClientFactory; - /** - * 处理渠道返回结果的后置处理器 集合 - */ - @Resource - private List handlerList; - - private final EnumMap mapHandlers = new EnumMap<>(PayChannelRespEnum.class); - - @PostConstruct - public void init(){ - if (Objects.nonNull(handlerList)) { - handlerList.forEach(handler -> { - for (PayChannelRespEnum item : handler.supportHandleResp()) { - mapHandlers.put(item, handler); - } - }); - } - } - @Override @Transactional(rollbackFor = Exception.class) public PayRefundRespDTO submitRefundOrder(PayRefundReqDTO req) { @@ -106,66 +82,89 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { // 校验退款的条件 validatePayRefund(req, order); - // 退款类型 PayRefundTypeEnum refundType = PayRefundTypeEnum.SOME; if (Objects.equals(req.getAmount(), order.getAmount())) { refundType = PayRefundTypeEnum.ALL; } - // 退款单入库 退款单状态:生成, 没有和渠道产生交互 + PayOrderExtensionDO orderExtensionDO = payOrderExtensionCoreMapper.selectById(order.getSuccessExtensionId()); - PayRefundDO refundDO = PayRefundDO.builder().channelOrderNo(order.getChannelOrderNo()) - .appId(order.getAppId()) - .channelOrderNo(order.getChannelOrderNo()) - .channelCode(order.getChannelCode()) - .channelId(order.getChannelId()) - .merchantId(order.getMerchantId()) - .orderId(order.getId()) - .merchantRefundNo(req.getMerchantRefundNo()) - .notifyUrl(app.getRefundNotifyUrl()) - .payAmount(order.getAmount()) - .refundAmount(req.getAmount()) - .userIp(req.getUserIp()) - .merchantOrderId(order.getMerchantOrderId()) - .tradeNo(orderExtensionDO.getNo()) - .status(PayRefundStatusEnum.CREATE.getStatus()) - .reason(req.getReason()) - .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) - .reqNo(PaySeqUtils.genRefundReqNo()) - .type(refundType.getStatus()) - .build(); - payRefundCoreMapper.insert(refundDO); - - // 调用渠道进行退款 - PayRefundUnifiedReqDTO unifiedReqDTO = PayRefundUnifiedReqDTO.builder() - .userIp(req.getUserIp()) - .channelOrderNo(refundDO.getChannelOrderNo()) - .payTradeNo(refundDO.getTradeNo()) - .refundReqNo(refundDO.getReqNo()) - .amount(req.getAmount()) - .reason(refundDO.getReason()) - .build(); - PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO); - // 根据渠道返回,获取退款后置处理,由postHandler 进行处理 - PayRefundChannelPostHandler payRefundChannelPostHandler = mapHandlers.get(refundUnifiedRespDTO.getRespEnum()); - if (Objects.isNull(payRefundChannelPostHandler)) { - throw exception(PAY_REFUND_POST_HANDLER_NOT_FOUND); - } - PayRefundPostReqDTO dto = PayRefundCoreConvert.INSTANCE.convert(refundUnifiedRespDTO); - dto.setRefundAmount(req.getAmount()) - .setRefundedAmount(order.getRefundAmount()) - .setRefundedTimes(order.getRefundTimes()) - .setRefundId(refundDO.getId()) - .setOrderId(order.getId()) - .setRefundTypeEnum(refundType); - //调用退款的后置处理 - payRefundChannelPostHandler.handleRefundChannelResp(dto); - - return PayRefundRespDTO.builder().refundId(refundDO.getId()).build(); + PayRefundDO payRefundDO = payRefundCoreMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), req.getMerchantRefundNo()); + //构造渠道的统一的退款请求参数 + PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); + if(Objects.nonNull(payRefundDO)){ + //退款订单已经提交过。 + //TODO 校验相同退款单的金额 + if(Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus()) + || Objects.equals(PayRefundStatusEnum.CLOSE.getStatus(), payRefundDO.getStatus())){ + //已成功退款 + throw exception(PAY_REFUND_SUCCEED); + }else{ + //保证商户退款单不变,重复向渠道发起退款。渠道保持幂等 + unifiedReqDTO.setUserIp(req.getUserIp()) + .setAmount(payRefundDO.getRefundAmount()) + .setChannelOrderNo(payRefundDO.getChannelOrderNo()) + .setPayTradeNo(payRefundDO.getTradeNo()) + .setRefundReqNo(payRefundDO.getMerchantRefundNo()) + .setReason(payRefundDO.getReason()); + } + }else{ + //新生成退款单。 退款单入库 退款单状态:生成 + payRefundDO = PayRefundDO.builder().channelOrderNo(order.getChannelOrderNo()) + .appId(order.getAppId()) + .channelOrderNo(order.getChannelOrderNo()) + .channelCode(order.getChannelCode()) + .channelId(order.getChannelId()) + .merchantId(order.getMerchantId()) + .orderId(order.getId()) + .merchantRefundNo(req.getMerchantRefundNo()) + .notifyUrl(app.getRefundNotifyUrl()) + .payAmount(order.getAmount()) + .refundAmount(req.getAmount()) + .userIp(req.getUserIp()) + .merchantOrderId(order.getMerchantOrderId()) + .tradeNo(orderExtensionDO.getNo()) + .status(PayRefundStatusEnum.CREATE.getStatus()) + .reason(req.getReason()) + .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) + .type(refundType.getStatus()) + .build(); + payRefundCoreMapper.insert(payRefundDO); + unifiedReqDTO.setUserIp(req.getUserIp()) + .setAmount(payRefundDO.getRefundAmount()) + .setChannelOrderNo(payRefundDO.getChannelOrderNo()) + .setPayTradeNo(payRefundDO.getTradeNo()) + .setRefundReqNo(payRefundDO.getMerchantRefundNo()) + .setReason(req.getReason()); + } + //向渠道发起退款申请 + PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO); + //构造退款申请返回对象 + PayRefundRespDTO respDTO = new PayRefundRespDTO(); + if(refundUnifiedRespDTO.getChannelResp() == PayChannelRefundRespEnum.SUCCESS + ||refundUnifiedRespDTO.getChannelResp() == PayChannelRefundRespEnum.PROCESSING ){ + //成功处理, 在退款通知中处理, 这里不处理 + respDTO.setChannelReturnResult(PayChannelRefundRespEnum.SUCCESS.getStatus()); + respDTO.setRefundId(payRefundDO.getId()); + }else { + //失败返回错误给前端,可以重新发起退款,保证退款请求号(这里是商户退款单号), 避免重复退款。 + respDTO.setChannelReturnResult(PayChannelRefundRespEnum.FAILURE.getStatus()); + //更新退款单状态 + PayRefundDO updatePayRefund = new PayRefundDO(); + updatePayRefund.setId(payRefundDO.getId()) + .setChannelErrorMsg(refundUnifiedRespDTO.getChannelMsg()) + .setChannelErrorCode(refundUnifiedRespDTO.getChannelCode()) + .setStatus(PayRefundStatusEnum.FAILURE.getStatus()); + payRefundCoreMapper.updateById(updatePayRefund); + } + respDTO.setChannelReturnCode(refundUnifiedRespDTO.getChannelCode()) + .setChannelReturnMsg(refundUnifiedRespDTO.getChannelMsg()); + return respDTO; } @Override + @Transactional(rollbackFor = Exception.class) public void notifyPayRefund(Long channelId, PayNotifyDataDTO notifyData) { log.info("[notifyPayRefund][channelId({}) 回调数据({})]", channelId, notifyData.getBody()); // 校验支付渠道是否有效 @@ -178,51 +177,54 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService { } // 解析渠道退款通知数据, 统一处理 PayRefundNotifyDTO refundNotify = client.parseRefundNotify(notifyData); - - // TODO @jason:抽一个 notifyPayRefundSuccess 方法 if (Objects.equals(PayNotifyRefundStatusEnum.SUCCESS,refundNotify.getStatus())){ - // 退款成功。 支付宝只有退款成功才会发通知 - PayRefundDO refundDO = payRefundCoreMapper.selectByReqNo(refundNotify.getReqNo()); - if (refundDO == null) { - log.error("不存在 seqNo 为{} 的支付退款单",refundNotify.getReqNo()); - throw exception(PAY_REFUND_NOT_FOUND); - } - Long refundAmount = refundDO.getRefundAmount(); - Integer type = refundDO.getType(); - PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS; - if(PayRefundTypeEnum.ALL.getStatus().equals(type)){ - orderStatus = PayOrderStatusEnum.CLOSED; - } - // 更新支付订单 - PayOrderDO payOrderDO = payOrderCoreMapper.selectById(refundDO.getOrderId()); - // 需更新已退金额 - Long refundedAmount = payOrderDO.getRefundAmount(); - PayOrderDO updateOrderDO = new PayOrderDO(); - updateOrderDO.setId(refundDO.getOrderId()) - .setRefundAmount(refundedAmount + refundAmount) - .setStatus(orderStatus.getStatus()) - .setRefundStatus(type); - payOrderCoreMapper.updateById(updateOrderDO); - - // 跟新退款订单 - PayRefundDO updateRefundDO = new PayRefundDO(); - updateRefundDO.setId(refundDO.getId()) - .setSuccessTime(refundNotify.getRefundSuccessTime()) - .setChannelRefundNo(refundNotify.getChannelOrderNo()) - .setTradeNo(refundNotify.getTradeNo()) - .setNotifyTime(new Date()) - .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); - payRefundCoreMapper.updateById(updateRefundDO); - - //插入退款通知记录 - // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 - payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build()); + payRefundSuccess(refundNotify); } else { - //TODO 退款失败 + //TODO 支付异常, 支付宝似乎没有支付异常的通知。 } } + private void payRefundSuccess(PayRefundNotifyDTO refundNotify) { + PayRefundDO refundDO = payRefundCoreMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), refundNotify.getReqNo()); + if (refundDO == null) { + log.error("不存在 seqNo 为{} 的支付退款单",refundNotify.getReqNo()); + throw exception(PAY_REFUND_NOT_FOUND); + } + Long refundAmount = refundDO.getRefundAmount(); + Integer type = refundDO.getType(); + PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS; + if(PayRefundTypeEnum.ALL.getStatus().equals(type)){ + orderStatus = PayOrderStatusEnum.CLOSED; + } + // 更新支付订单 + PayOrderDO payOrderDO = payOrderCoreMapper.selectById(refundDO.getOrderId()); + // 需更新已退金额 + Long refundedAmount = payOrderDO.getRefundAmount(); + PayOrderDO updateOrderDO = new PayOrderDO(); + updateOrderDO.setId(refundDO.getOrderId()) + .setRefundAmount(refundedAmount + refundAmount) + .setStatus(orderStatus.getStatus()) + .setRefundTimes(payOrderDO.getRefundTimes()+1) + .setRefundStatus(type); + + payOrderCoreMapper.updateById(updateOrderDO); + + // 跟新退款订单 + PayRefundDO updateRefundDO = new PayRefundDO(); + updateRefundDO.setId(refundDO.getId()) + .setSuccessTime(refundNotify.getRefundSuccessTime()) + .setChannelRefundNo(refundNotify.getChannelOrderNo()) + .setTradeNo(refundNotify.getTradeNo()) + .setNotifyTime(new Date()) + .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); + payRefundCoreMapper.updateById(updateRefundDO); + + //插入退款通知记录 + // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 + payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() + .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build()); + } + /** * 校验是否进行退款 * @param req 退款申请信息 diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelFailedHandler.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelFailedHandler.java deleted file mode 100644 index 02a121b924..0000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelFailedHandler.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl.handler; - -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; -import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; -import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; -import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundPostReqDTO; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.Optional; - -/** - * 支付退款订单渠道返回失败的后置处理类 - * {@link PayChannelRespEnum#CALL_EXCEPTION} - * {@link PayChannelRespEnum#CAN_NOT_RETRY_FAILURE} - */ -@Service -public class PayRefundChannelFailedHandler extends PayRefundAbstractChannelPostHandler { - - @Resource - private PayNotifyCoreService payNotifyCoreService; - - public PayRefundChannelFailedHandler(PayOrderCoreMapper payOrderCoreMapper, PayRefundCoreMapper payRefundMapper) { - super(payOrderCoreMapper, payRefundMapper); - } - - @Override - public PayChannelRespEnum[] supportHandleResp() { - return new PayChannelRespEnum[] {PayChannelRespEnum.CALL_EXCEPTION, PayChannelRespEnum.CAN_NOT_RETRY_FAILURE}; - } - - - @Override - public void handleRefundChannelResp(PayRefundPostReqDTO req) { - //退款失败 - //更新退款单表 - PayRefundDO updateRefundDO = new PayRefundDO(); - - updateRefundDO.setId(req.getRefundId()) - .setStatus(PayRefundStatusEnum.FAILURE.getStatus()) - .setChannelErrorCode(req.getChannelErrCode()) - .setChannelErrorMsg(Optional.ofNullable(req.getChannelErrMsg()) - .orElse(req.getExceptionMsg())); - updatePayRefund(updateRefundDO); - PayOrderDO updateOrderDO = new PayOrderDO(); - //更新订单表 - updateOrderDO.setId(req.getOrderId()) - .setRefundTimes(req.getRefundedTimes() + 1); - updatePayOrder(updateOrderDO); - // 立刻插入退款通知记录 - // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 - payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(req.getRefundId()).build()); - } -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelNotifyHandler.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelNotifyHandler.java deleted file mode 100644 index 32cf48754b..0000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelNotifyHandler.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl.handler; - -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundPostReqDTO; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; -import org.springframework.stereotype.Service; - -/** - * 支付退款订单渠道返回通知 {@link PayChannelRespEnum#PROCESSING_NOTIFY},后置处理类 - * 支付宝退款单好像没有回调, 微信会触发回调 - */ -@Service -public class PayRefundChannelNotifyHandler extends PayRefundAbstractChannelPostHandler { - - public PayRefundChannelNotifyHandler(PayOrderCoreMapper payOrderCoreMapper, - PayRefundCoreMapper payRefundMapper) { - super(payOrderCoreMapper, payRefundMapper); - } - - @Override - public PayChannelRespEnum[] supportHandleResp() { - return new PayChannelRespEnum[] {PayChannelRespEnum.PROCESSING_NOTIFY}; - } - - @Override - public void handleRefundChannelResp(PayRefundPostReqDTO req) { - PayRefundDO updateRefundDO = new PayRefundDO(); - //更新退款单表 - updateRefundDO.setId(req.getRefundId()) - .setStatus(PayRefundStatusEnum.PROCESSING_NOTIFY.getStatus()); - updatePayRefund(updateRefundDO); - - PayOrderDO updateOrderDO = new PayOrderDO(); - //更新订单表 - updateOrderDO.setId(req.getOrderId()) - .setRefundTimes(req.getRefundedTimes() + 1); - updatePayOrder(updateOrderDO); - - } -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelQueryHandler.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelQueryHandler.java deleted file mode 100644 index 58e840bf72..0000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelQueryHandler.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl.handler; - -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundPostReqDTO; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; -import org.springframework.stereotype.Service; - -import java.util.Objects; - -/** - * 支付退款订单渠道返回需调用查询接口的后置处理类 - * {@link PayChannelRespEnum#PROCESSING_QUERY} //TODO 芋道源码 是不是微信有这样的情况 - * {@link PayChannelRespEnum#READ_TIME_OUT_EXCEPTION} - */ -@Service -public class PayRefundChannelQueryHandler extends PayRefundAbstractChannelPostHandler { - - - public PayRefundChannelQueryHandler(PayOrderCoreMapper payOrderCoreMapper, - PayRefundCoreMapper payRefundMapper) { - super(payOrderCoreMapper, payRefundMapper); - } - - @Override - public PayChannelRespEnum[] supportHandleResp() { - return new PayChannelRespEnum[]{PayChannelRespEnum.PROCESSING_QUERY, PayChannelRespEnum.READ_TIME_OUT_EXCEPTION}; - } - - @Override - public void handleRefundChannelResp(PayRefundPostReqDTO req) { - final PayChannelRespEnum respEnum = req.getRespEnum(); - PayRefundStatusEnum refundStatus = - Objects.equals(PayChannelRespEnum.PROCESSING_QUERY, respEnum) ? PayRefundStatusEnum.PROCESSING_QUERY - : PayRefundStatusEnum.UNKNOWN_QUERY; - //更新退款单表 - PayRefundDO updateRefundDO = new PayRefundDO(); - updateRefundDO.setId(req.getRefundId()) - .setStatus(refundStatus.getStatus()) - .setChannelErrorCode(req.getChannelErrCode()) - .setChannelErrorMsg(req.getChannelErrMsg()); - updatePayRefund(updateRefundDO); - - PayOrderDO updateOrderDO = new PayOrderDO(); - //更新订单表 - updateOrderDO.setId(req.getOrderId()) - .setRefundTimes(req.getRefundedTimes() + 1); - updatePayOrder(updateOrderDO); - - //TODO 发起查询任务 - } -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelRetryHandler.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelRetryHandler.java deleted file mode 100644 index 2046adfb7b..0000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelRetryHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl.handler; - -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundPostReqDTO; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; -import org.springframework.stereotype.Service; - -/** - * 支付退款订单渠道返回重试的后置处理类 - * {@link PayChannelRespEnum#RETRY_FAILURE} - */ -@Service -public class PayRefundChannelRetryHandler extends PayRefundAbstractChannelPostHandler { - - - public PayRefundChannelRetryHandler(PayOrderCoreMapper payOrderCoreMapper, - PayRefundCoreMapper payRefundMapper) { - super(payOrderCoreMapper, payRefundMapper); - } - - @Override - public PayChannelRespEnum[] supportHandleResp() { - return new PayChannelRespEnum[] {PayChannelRespEnum.RETRY_FAILURE}; - } - - @Override - public void handleRefundChannelResp(PayRefundPostReqDTO req) { - - PayRefundDO updateRefundDO = new PayRefundDO(); - //更新退款单表 - updateRefundDO.setId(req.getRefundId()) - .setStatus(PayRefundStatusEnum.UNKNOWN_RETRY.getStatus()) - .setChannelErrorCode(req.getChannelErrCode()) - .setChannelErrorMsg(req.getChannelErrMsg()); - updatePayRefund(updateRefundDO); - - PayOrderDO updateOrderDO = new PayOrderDO(); - //更新订单表 - updateOrderDO.setId(req.getOrderId()) - .setRefundTimes(req.getRefundedTimes() + 1); - updatePayOrder(updateOrderDO); - - //TODO 发起重试任务 - } -} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelSuccessHandler.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelSuccessHandler.java deleted file mode 100644 index 444974cc83..0000000000 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/handler/PayRefundChannelSuccessHandler.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl.handler; - -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundCoreMapper; -import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; -import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; -import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; -import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundPostReqDTO; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.Date; -/** - * 支付退款订单渠道返回退款成功的后置处理类 - * {@link PayChannelRespEnum#SYNC_SUCCESS} - */ -@Service -public class PayRefundChannelSuccessHandler extends PayRefundAbstractChannelPostHandler { - - - @Resource - private PayNotifyCoreService payNotifyCoreService; - - - public PayRefundChannelSuccessHandler(PayOrderCoreMapper payOrderCoreMapper, - PayRefundCoreMapper payRefundMapper) { - super(payOrderCoreMapper, payRefundMapper); - } - - @Override - public PayChannelRespEnum[] supportHandleResp() { - return new PayChannelRespEnum[]{PayChannelRespEnum.SYNC_SUCCESS}; - } - - @Override - public void handleRefundChannelResp(PayRefundPostReqDTO req) { - //退款成功 - PayRefundDO updateRefundDO = new PayRefundDO(); - //更新退款单表 - updateRefundDO.setId(req.getRefundId()) - .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()) - .setChannelRefundNo(req.getChannelRefundNo()) - .setSuccessTime(new Date()); - updatePayRefund(updateRefundDO); - - PayOrderDO updateOrderDO = new PayOrderDO(); - //更新订单表 - updateOrderDO.setId(req.getOrderId()) - .setRefundTimes(req.getRefundedTimes() + 1) - .setRefundStatus(req.getRefundTypeEnum().getStatus()) - .setRefundAmount(req.getRefundedAmount()+ req.getRefundAmount()); - updatePayOrder(updateOrderDO); - - // 立刻插入退款通知记录 - // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 - payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() - .type(PayNotifyTypeEnum.REFUND.getType()).dataId(req.getRefundId()).build()); - } -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java index 585400b40d..c173a68c99 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java @@ -48,6 +48,7 @@ public class PayRefundUnifiedReqDTO { * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no * 退款请求单号 同一退款请求单号多次请求只退一笔。 + * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo} */ @NotEmpty(message = "退款请求单号") private String refundReqNo; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedRespDTO.java index 956e31a55e..a7c0c2ecd0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedRespDTO.java @@ -1,15 +1,13 @@ package cn.iocoder.yudao.framework.pay.core.client.dto; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRefundRespEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; - -import javax.validation.constraints.NotEmpty; /** - * 统一 退款 Response DTO + * 统一退款 Response DTO * * @author jason */ @@ -19,55 +17,20 @@ import javax.validation.constraints.NotEmpty; @AllArgsConstructor @Data public class PayRefundUnifiedRespDTO { - - + /** + * 渠道的退款结果 + */ + private PayChannelRefundRespEnum channelResp; /** - * 渠道的通用返回结果 + * 渠道返回码 */ - private PayChannelRespEnum respEnum; - - + private String channelCode; /** - * 渠道退款单号 + * 渠道返回信息 */ - private String channelRefundNo; - - - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no - * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空 - */ - private String payTradeNo; - - - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no - * 退款请求单号 同一退款请求单号多次请求只退一笔。 - */ - private String refundReqNo; - - - - /** - * 调用异常错误信息 - */ - private String exceptionMsg; - - - /** - * 渠道的错误码 - */ - private String channelErrCode; - - - /** - * 渠道的错误描述 - */ - private String channelErrMsg; + private String channelMsg; //TODO 退款资金渠 ??? } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java index bcffa71a87..b265bd2ac2 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl; import cn.hutool.extra.validation.ValidationUtil; +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; @@ -8,11 +9,9 @@ import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; import lombok.extern.slf4j.Slf4j; -import java.net.SocketTimeoutException; - +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; /** @@ -108,20 +107,10 @@ public abstract class AbstractPayClient implemen PayRefundUnifiedRespDTO resp; try { resp = doUnifiedRefund(reqDTO); - } catch (SocketTimeoutException ex){ - // 网络 read time out 异常 - log.error("[unifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), ex); - return PayRefundUnifiedRespDTO.builder() - .exceptionMsg(ex.getMessage()) - .respEnum(PayChannelRespEnum.READ_TIME_OUT_EXCEPTION) - .build(); - } catch (Throwable ex) { - // 打印异常日志 + } catch (Throwable ex) { + // 记录异常日志 log.error("[unifiedRefund][request({}) 发起退款失败]", toJsonString(reqDTO), ex); - return PayRefundUnifiedRespDTO.builder() - .exceptionMsg(ex.getMessage()) - .respEnum(PayChannelRespEnum.CALL_EXCEPTION) - .build(); + throw exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR); } return resp; } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java index 117019440b..8dca7cbece 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java @@ -5,7 +5,7 @@ import cn.hutool.core.date.DateUtil; import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; import cn.iocoder.yudao.framework.pay.core.client.dto.*; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRefundRespEnum; import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayConfig; @@ -17,7 +17,6 @@ import com.alipay.api.response.AlipayTradeRefundResponse; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -118,43 +117,21 @@ public abstract class AbstractAlipayClient extends AbstractPayClient