code review 退款逻辑

This commit is contained in:
YunaiV
2021-12-25 20:40:49 +08:00
parent d49ce4c81f
commit bcc2ff0f5b
17 changed files with 77 additions and 156 deletions

View File

@ -1,25 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.pay.service.order;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
/**
* 支付通用 Core Service
*
* @author jason
*/
public interface PayCommonCoreService {
/**
* 验证是否是渠道通知
* @param notifyData 通知数据
*/
void verifyNotifyData(Long channelId, PayNotifyDataDTO notifyData);
/**
* 支付宝的支付回调通知,和退款回调通知 地址是同一个
* 是否是退款回调通知
* @param notifyData 通知数据
* @return
*/
boolean isRefundNotify(Long channelId, PayNotifyDataDTO notifyData);
}

View File

@ -47,6 +47,4 @@ public interface PayOrderCoreService {
*/
void notifyPayOrder(Long channelId, PayNotifyDataDTO notifyData) throws Exception;
}

View File

@ -5,19 +5,21 @@ import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum;
/**
* 支付退款订单 ,渠道返回后 后置处理
*
* @author jason
*/
public interface PayRefundChannelPostHandler {
/**
* 支持的渠道返回值
*
* @return 支持的渠道返回值数组
*/
PayChannelRespEnum[] supportHandleResp();
/**
* 根据渠道返回, 处理支付退款单
* 根据渠道返回,处理支付退款单
*
* @param respBO
*/
void handleRefundChannelResp(PayRefundPostReqBO respBO);

View File

@ -11,23 +11,22 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
*/
public interface PayRefundCoreService {
// TODO @jason方法名改成submitRefundOrder发起退款订单。这样和发起支付单保持一致
/**
* 提交退款申请
*
* @param reqDTO 退款申请信息
* @return 退款申请返回信息
*/
PayRefundRespBO refund(PayRefundReqBO reqDTO);
/**
* 渠道的退款通知
*
* @param channelId 渠道编号
* @param notifyData 通知数据
* @throws Exception 退款通知异常
*/
void notifyPayRefund(Long channelId, PayNotifyDataDTO notifyData) throws Exception;
}

View File

@ -8,6 +8,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
// TODO @jason改到 dto 哈。我们项目,统一使用 DTO
@Data
@Accessors(chain = true)
@Builder

View File

@ -5,6 +5,8 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
// TODO @jason改到 dto 哈。我们项目,统一使用 DTO
/**
* 退款申请单 Request DTO
*/

View File

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
// TODO @jason改到 dto 哈。我们项目,统一使用 DTO
/**
* 退款申请单 Response DTO
*/

View File

@ -1,62 +0,0 @@
package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayCommonCoreService;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.PAY_CHANNEL_CLIENT_NOT_FOUND;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.PAY_CHANNEL_NOTIFY_VERIFY_FAILED;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 支付通用 Core Service 实现类
*
* @author jason
*/
@Service
@Slf4j
public class PayCommonCoreServiceImpl implements PayCommonCoreService {
@Resource
private PayChannelCoreService payChannelCoreService;
@Resource
private PayClientFactory payClientFactory;
@Override
public void verifyNotifyData(Long channelId, PayNotifyDataDTO notifyData) {
// 校验支付渠道是否有效
PayChannelDO channel = payChannelCoreService.validPayChannel(channelId);
// 校验支付客户端是否正确初始化
PayClient client = payClientFactory.getPayClient(channel.getId());
if (client == null) {
log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
}
boolean verifyResult = client.verifyNotifyData(notifyData);
if(!verifyResult){
//渠道通知验证失败
throw exception(PAY_CHANNEL_NOTIFY_VERIFY_FAILED);
}
}
@Override
public boolean isRefundNotify(Long channelId, PayNotifyDataDTO notifyData) {
// 校验支付渠道是否有效
PayChannelDO channel = payChannelCoreService.validPayChannel(channelId);
// 校验支付客户端是否正确初始化
PayClient client = payClientFactory.getPayClient(channel.getId());
if (client == null) {
log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
}
return client.isRefundNotify(notifyData);
}
}

View File

@ -50,18 +50,17 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
@Resource
private PayOrderCoreMapper payOrderCoreMapper;
@Resource
private PayRefundCoreMapper payRefundCoreMapper;
@Resource
private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper;
@Resource
private PayAppCoreService payAppCoreService;
@Resource
private PayChannelCoreService payChannelCoreService;
@Resource
private PayNotifyCoreService payNotifyCoreService;
@Resource
private PayClientFactory payClientFactory;
@ -72,25 +71,18 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
@Resource
private List<PayRefundChannelPostHandler> handlerList;
@Resource
private PayNotifyCoreService payNotifyCoreService;
// TODO @jsonmapHandlers
private final EnumMap<PayChannelRespEnum, PayRefundChannelPostHandler> mapHandler = new EnumMap<>(PayChannelRespEnum.class);
@PostConstruct
public void init(){
if (Objects.nonNull(handlerList)) {
handlerList.forEach(t->{
for (PayChannelRespEnum item : t.supportHandleResp()) {
mapHandler.put(item, t);
handlerList.forEach(handler -> {
for (PayChannelRespEnum item : handler.supportHandleResp()) {
mapHandler.put(item, handler);
}
});
}
}
@Override
@ -113,16 +105,16 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
}
//校验退款的条件
// 校验退款的条件
validatePayRefund(reqBO, order);
//退款类型
// 退款类型
PayRefundTypeEnum refundType = PayRefundTypeEnum.SOME;
if (Objects.equals(reqBO.getAmount(), order.getAmount())) {
refundType = PayRefundTypeEnum.ALL;
}
//退款单入库 退款单状态:生成, 没有和渠道产生交互
// 退款单入库 退款单状态:生成, 没有和渠道产生交互
PayOrderExtensionDO orderExtensionDO = payOrderExtensionCoreMapper.selectById(order.getSuccessExtensionId());
PayRefundDO refundDO = PayRefundDO.builder().channelOrderNo(order.getChannelOrderNo())
.appId(order.getAppId())
@ -144,9 +136,9 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
.reqNo(PaySeqUtils.genRefundReqNo())
.type(refundType.getStatus())
.build();
payRefundCoreMapper.insert(refundDO);
// TODO @jason可以把“调用渠道进行退款"写到这里,这样分块更明确
PayRefundUnifiedReqDTO unifiedReqDTO = PayRefundUnifiedReqDTO.builder()
.userIp(reqBO.getUserIp())
.channelOrderNo(refundDO.getChannelOrderNo())
@ -156,13 +148,14 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
.reason(refundDO.getReason())
.build();
//调用渠道进行退款
// 调用渠道进行退款
PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO);
//根据渠道返回获取退款后置处理由postHandler 进行处理
// TODO @jason下面这块是一整块逻辑不要空开。不然阅读的时候会以为不是一块逻辑
// 根据渠道返回获取退款后置处理由postHandler 进行处理
PayRefundChannelPostHandler payRefundChannelPostHandler = mapHandler.get(refundUnifiedRespDTO.getRespEnum());
if(Objects.isNull(payRefundChannelPostHandler)){
if (Objects.isNull(payRefundChannelPostHandler)) {
throw exception(PAY_REFUND_POST_HANDLER_NOT_FOUND);
}
@ -192,11 +185,12 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
}
//解析渠道退款通知数据, 统一处理
// 解析渠道退款通知数据, 统一处理
PayRefundNotifyDTO refundNotify = client.parseRefundNotify(notifyData);
if(Objects.equals(PayNotifyRefundStatusEnum.SUCCESS,refundNotify.getStatus())){
//退款成功。 支付宝只有退款成功才会发通知
// TODO @jason抽一个 notifyPayRefundSuccess 方法
if (Objects.equals(PayNotifyRefundStatusEnum.SUCCESS,refundNotify.getStatus())){
// 退款成功。 支付宝只有退款成功才会发通知
PayRefundDO refundDO = payRefundCoreMapper.selectByReqNo(refundNotify.getReqNo());
if (refundDO == null) {
log.error("不存在 seqNo 为{} 的支付退款单",refundNotify.getReqNo());
@ -208,9 +202,9 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
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())
@ -219,7 +213,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
.setRefundStatus(type);
payOrderCoreMapper.updateById(updateOrderDO);
//跟新退款订单
// 跟新退款订单
PayRefundDO updateRefundDO = new PayRefundDO();
updateRefundDO.setId(refundDO.getId())
.setSuccessTime(refundNotify.getRefundSuccessTime())
@ -233,10 +227,9 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
// TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调
payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
.type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build());
}else{
} else {
//TODO 退款失败
}
}
/**
@ -245,7 +238,6 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
* @param order 原始支付订单信息
*/
private void validatePayRefund(PayRefundReqBO reqBO, PayOrderDO order) {
// 校验状态,必须是支付状态
if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) {
throw exception(PAY_ORDER_STATUS_IS_NOT_SUCCESS);
@ -258,10 +250,11 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
if(reqBO.getAmount() + order.getRefundAmount() > order.getAmount()){
throw exception(PAY_REFUND_AMOUNT_EXCEED);
}
//校验渠道订单号
// 校验渠道订单号
if (StrUtil.isEmpty(order.getChannelOrderNo())) {
throw exception(PAY_REFUND_CHN_ORDER_NO_IS_NULL);
}
//TODO 退款的期限 退款次数的控制
}
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
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;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
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;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
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;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
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;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
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;