code review 退款逻辑

pull/2/head
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; 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 * @author jason
*/ */
public interface PayRefundChannelPostHandler { public interface PayRefundChannelPostHandler {
/** /**
* *
*
* @return * @return
*/ */
PayChannelRespEnum[] supportHandleResp(); PayChannelRespEnum[] supportHandleResp();
/** /**
* 退 * 退
*
* @param respBO * @param respBO
*/ */
void handleRefundChannelResp(PayRefundPostReqBO respBO); void handleRefundChannelResp(PayRefundPostReqBO respBO);

View File

@ -11,23 +11,22 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
*/ */
public interface PayRefundCoreService { public interface PayRefundCoreService {
// TODO @jason方法名改成submitRefundOrder发起退款订单。这样和发起支付单保持一致
/** /**
* 退 * 退
*
* @param reqDTO 退 * @param reqDTO 退
* @return 退 * @return 退
*/ */
PayRefundRespBO refund(PayRefundReqBO reqDTO); PayRefundRespBO refund(PayRefundReqBO reqDTO);
/** /**
* 退 * 退
*
* @param channelId * @param channelId
* @param notifyData * @param notifyData
* @throws Exception 退 * @throws Exception 退
*/ */
void notifyPayRefund(Long channelId, PayNotifyDataDTO 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.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
// TODO @jason改到 dto 哈。我们项目,统一使用 DTO
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
@Builder @Builder

View File

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

View File

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
// TODO @jason改到 dto 哈。我们项目,统一使用 DTO
/** /**
* 退 Response 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 @Resource
private PayOrderCoreMapper payOrderCoreMapper; private PayOrderCoreMapper payOrderCoreMapper;
@Resource @Resource
private PayRefundCoreMapper payRefundCoreMapper; private PayRefundCoreMapper payRefundCoreMapper;
@Resource @Resource
private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper; private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper;
@Resource @Resource
private PayAppCoreService payAppCoreService; private PayAppCoreService payAppCoreService;
@Resource @Resource
private PayChannelCoreService payChannelCoreService; private PayChannelCoreService payChannelCoreService;
@Resource
private PayNotifyCoreService payNotifyCoreService;
@Resource @Resource
private PayClientFactory payClientFactory; private PayClientFactory payClientFactory;
@ -72,25 +71,18 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
@Resource @Resource
private List<PayRefundChannelPostHandler> handlerList; private List<PayRefundChannelPostHandler> handlerList;
@Resource // TODO @jsonmapHandlers
private PayNotifyCoreService payNotifyCoreService;
private final EnumMap<PayChannelRespEnum, PayRefundChannelPostHandler> mapHandler = new EnumMap<>(PayChannelRespEnum.class); private final EnumMap<PayChannelRespEnum, PayRefundChannelPostHandler> mapHandler = new EnumMap<>(PayChannelRespEnum.class);
@PostConstruct @PostConstruct
public void init(){ public void init(){
if (Objects.nonNull(handlerList)) { if (Objects.nonNull(handlerList)) {
handlerList.forEach(t->{ handlerList.forEach(handler -> {
for (PayChannelRespEnum item : t.supportHandleResp()) { for (PayChannelRespEnum item : handler.supportHandleResp()) {
mapHandler.put(item, t); mapHandler.put(item, handler);
} }
}); });
} }
} }
@Override @Override
@ -144,9 +136,9 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
.reqNo(PaySeqUtils.genRefundReqNo()) .reqNo(PaySeqUtils.genRefundReqNo())
.type(refundType.getStatus()) .type(refundType.getStatus())
.build(); .build();
payRefundCoreMapper.insert(refundDO); payRefundCoreMapper.insert(refundDO);
// TODO @jason可以把“调用渠道进行退款"写到这里,这样分块更明确
PayRefundUnifiedReqDTO unifiedReqDTO = PayRefundUnifiedReqDTO.builder() PayRefundUnifiedReqDTO unifiedReqDTO = PayRefundUnifiedReqDTO.builder()
.userIp(reqBO.getUserIp()) .userIp(reqBO.getUserIp())
.channelOrderNo(refundDO.getChannelOrderNo()) .channelOrderNo(refundDO.getChannelOrderNo())
@ -159,6 +151,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
// 调用渠道进行退款 // 调用渠道进行退款
PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO); PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO);
// TODO @jason下面这块是一整块逻辑不要空开。不然阅读的时候会以为不是一块逻辑
// 根据渠道返回获取退款后置处理由postHandler 进行处理 // 根据渠道返回获取退款后置处理由postHandler 进行处理
PayRefundChannelPostHandler payRefundChannelPostHandler = mapHandler.get(refundUnifiedRespDTO.getRespEnum()); PayRefundChannelPostHandler payRefundChannelPostHandler = mapHandler.get(refundUnifiedRespDTO.getRespEnum());
@ -195,6 +188,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
// 解析渠道退款通知数据, 统一处理 // 解析渠道退款通知数据, 统一处理
PayRefundNotifyDTO refundNotify = client.parseRefundNotify(notifyData); PayRefundNotifyDTO refundNotify = client.parseRefundNotify(notifyData);
// TODO @jason抽一个 notifyPayRefundSuccess 方法
if (Objects.equals(PayNotifyRefundStatusEnum.SUCCESS,refundNotify.getStatus())){ if (Objects.equals(PayNotifyRefundStatusEnum.SUCCESS,refundNotify.getStatus())){
// 退款成功。 支付宝只有退款成功才会发通知 // 退款成功。 支付宝只有退款成功才会发通知
PayRefundDO refundDO = payRefundCoreMapper.selectByReqNo(refundNotify.getReqNo()); PayRefundDO refundDO = payRefundCoreMapper.selectByReqNo(refundNotify.getReqNo());
@ -236,7 +230,6 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
} else { } else {
//TODO 退款失败 //TODO 退款失败
} }
} }
/** /**
@ -245,7 +238,6 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
* @param order * @param order
*/ */
private void validatePayRefund(PayRefundReqBO reqBO, PayOrderDO order) { private void validatePayRefund(PayRefundReqBO reqBO, PayOrderDO order) {
// 校验状态,必须是支付状态 // 校验状态,必须是支付状态
if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) {
throw exception(PAY_ORDER_STATUS_IS_NOT_SUCCESS); throw exception(PAY_ORDER_STATUS_IS_NOT_SUCCESS);
@ -264,4 +256,5 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
} }
//TODO 退款的期限 退款次数的控制 //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.PayOrderDO;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; 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.PayOrderDO;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; 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.PayOrderDO;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; 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.PayOrderDO;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; 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.PayOrderDO;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO;

View File

@ -34,7 +34,6 @@ public interface PayClient {
*/ */
PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception; PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception;
/** /**
* 退 * 退
* @param reqDTO 退 * @param reqDTO 退
@ -49,9 +48,10 @@ public interface PayClient {
*/ */
PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData); PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData);
// TODO @芋艿:后续改成非 default避免不知道去实现
/** /**
* *
*
* @param notifyData * @param notifyData
* @return true * @return true
*/ */
@ -59,12 +59,15 @@ public interface PayClient {
return true; return true;
} }
// TODO @芋艿:后续改成非 default避免不知道去实现
/** /**
* 退 * 退
*
* @param notifyData * @param notifyData
* @return false * @return false
*/ */
default boolean isRefundNotify(PayNotifyDataDTO notifyData){ default boolean isRefundNotify(PayNotifyDataDTO notifyData){
return false; return false;
} }
} }

View File

@ -2,27 +2,27 @@ package cn.iocoder.yudao.userserver.modules.pay.controller.order;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayCommonCoreService;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService; import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService; import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO; import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitRespDTO; import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitRespDTO;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.PayNotifyDataDTO;
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
import cn.iocoder.yudao.userserver.modules.pay.controller.order.vo.PayOrderSubmitReqVO; import cn.iocoder.yudao.userserver.modules.pay.controller.order.vo.PayOrderSubmitReqVO;
import cn.iocoder.yudao.userserver.modules.pay.controller.order.vo.PayOrderSubmitRespVO; import cn.iocoder.yudao.userserver.modules.pay.controller.order.vo.PayOrderSubmitRespVO;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.PAY_CHANNEL_CLIENT_NOT_FOUND;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
@ -35,11 +35,11 @@ public class PayOrderController {
@Resource @Resource
private PayOrderCoreService payOrderCoreService; private PayOrderCoreService payOrderCoreService;
@Resource @Resource
private PayRefundCoreService payRefundCoreService; private PayRefundCoreService payRefundCoreService;
@Resource PayCommonCoreService commonCoreService; @Resource
private PayClientFactory payClientFactory;
@PostMapping("/submit") @PostMapping("/submit")
@ -81,11 +81,12 @@ public class PayOrderController {
public String returnAliPayOrder(@PathVariable("channelId") Long channelId, @RequestParam Map<String, String> params){ public String returnAliPayOrder(@PathVariable("channelId") Long channelId, @RequestParam Map<String, String> params){
//TODO 可以根据渠道和 app_id 返回不同的页面 //TODO 可以根据渠道和 app_id 返回不同的页面
log.info("app_id is {}", params.get("app_id")); log.info("app_id is {}", params.get("app_id"));
return String.format("渠道[%s]支付成功", String.valueOf(channelId)); return String.format("渠道[%s]支付成功", channelId);
} }
/** /**
* 退 * 退
*
* @param channelId * @param channelId
* @param params form * @param params form
* @param originData http request body * @param originData http request body
@ -96,18 +97,25 @@ public class PayOrderController {
public String notifyChannelPay(@PathVariable("channelId") Long channelId, public String notifyChannelPay(@PathVariable("channelId") Long channelId,
@RequestParam Map<String, String> params, @RequestParam Map<String, String> params,
@RequestBody String originData) throws Exception { @RequestBody String originData) throws Exception {
//校验是否是渠道回调 // 校验支付渠道是否存在
commonCoreService.verifyNotifyData(channelId, PayNotifyDataDTO.builder().params(params).body(originData).build()); PayClient payClient = payClientFactory.getPayClient(channelId);
//支付宝退款交易也会触发支付回调接口 if (payClient == null) {
//参考 https://opensupport.alipay.com/support/helpcenter/193/201602484851 log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channelId);
//判断是否为退款通知 throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
if(commonCoreService.isRefundNotify(channelId, PayNotifyDataDTO.builder().params(params).body(originData).build())) {
//退款通知
payRefundCoreService.notifyPayRefund(channelId,PayNotifyDataDTO.builder().params(params).body(originData).build());
}else{
//支付通知
payOrderCoreService.notifyPayOrder(channelId,PayNotifyDataDTO.builder().params(params).body(originData).build());
} }
// 校验通知数据是否合法
PayNotifyDataDTO notifyData = PayNotifyDataDTO.builder().params(params).body(originData).build();
payClient.verifyNotifyData(notifyData);
// 如果是退款,则发起退款通知
if (payClient.isRefundNotify(notifyData)) {
payRefundCoreService.notifyPayRefund(channelId, PayNotifyDataDTO.builder().params(params).body(originData).build());
return "success";
}
// 如果非退款,则发起支付通知
payOrderCoreService.notifyPayOrder(channelId, PayNotifyDataDTO.builder().params(params).body(originData).build());
return "success"; return "success";
} }

View File

@ -40,4 +40,5 @@ public class PayRefundController {
//reqBO.setMerchantRefundNo("MO202111210814084370000"); //reqBO.setMerchantRefundNo("MO202111210814084370000");
return CommonResult.success( PayRefundConvert.INSTANCE.convert(payRefundCoreService.refund(reqBO))); return CommonResult.success( PayRefundConvert.INSTANCE.convert(payRefundCoreService.refund(reqBO)));
} }
} }