重构退款逻辑,去掉退款后处理

This commit is contained in:
jason
2021-12-28 17:10:25 +08:00
parent 054c237b1f
commit 08103685f1
25 changed files with 234 additions and 720 deletions

View File

@ -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 = {

View File

@ -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;

View File

@ -15,4 +15,8 @@ public interface PayRefundCoreMapper extends BaseMapperX<PayRefundDO> {
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);
}
}

View File

@ -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 ==========
*/

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -16,6 +16,23 @@ import lombok.experimental.Accessors;
@AllArgsConstructor
public class PayRefundRespDTO {
/**
* 渠道返回结果
* 退款处理中和退款成功 返回 1
* 失败和其他情况 返回 2
*/
private Integer channelReturnResult;
/**
* 渠道返回code
*/
private String channelReturnCode;
/**
* 渠道返回消息
*/
private String channelReturnMsg;
/**
* 支付退款单编号, 自增
*/

View File

@ -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<PayRefundChannelPostHandler> handlerList;
private final EnumMap<PayChannelRespEnum, PayRefundChannelPostHandler> 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 退款申请信息

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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 发起查询任务
}
}

View File

@ -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 发起重试任务
}
}

View File

@ -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());
}
}