@ -11,20 +11,20 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderExtensio
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.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.PayRefundReqDTO ;
import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundNotifyDTO ;
import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum ;
import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum ;
import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum ;
import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum ;
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.notify.PayNotifyCoreService ;
import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO ;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService ;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundReqDTO ;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayRefundRespDTO ;
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.PayRefundNotifyDTO ;
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 ;
@ -34,7 +34,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional ;
import javax.annotation.Resource ;
import java.util.* ;
import java.util.Date ;
import java.util.Objects ;
import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.* ;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception ;
@ -90,17 +91,19 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
PayOrderExtensionDO orderExtensionDO = payOrderExtensionCoreMapper . selectById ( order . getSuccessExtensionId ( ) ) ;
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 ( ) ) ) {
// TODO @jason: 咱要不封装一个 ObjectUtils.equalsAny
if ( Objects . equals ( PayRefundStatusEnum . SUCCESS . getStatus ( ) , payRefundDO . getStatus ( ) )
| | Objects . equals ( PayRefundStatusEnum . CLOSE . getStatus ( ) , payRefundDO . getStatus ( ) ) ) {
//已成功退款
throw exception ( PAY_REFUND_SUCCEED ) ;
} else {
//保证商户退款单不变,重复向渠道发起退款。渠道保持幂等
} else {
// TODO @jason: 这里不用 else, 简洁一些
// 保证商户退款单不变,重复向渠道发起退款。渠道保持幂等
unifiedReqDTO . setUserIp ( req . getUserIp ( ) )
. setAmount ( payRefundDO . getRefundAmount ( ) )
. setChannelOrderNo ( payRefundDO . getChannelOrderNo ( ) )
@ -109,7 +112,8 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
. setReason ( payRefundDO . getReason ( ) ) ;
}
} else {
//新生成退款单。 退款单入库 退款单状态:生成
// 新生成退款单。 退款单入库 退款单状态:生成
// TODO @jason: 封装一个小方法。插入退款单
payRefundDO = PayRefundDO . builder ( ) . channelOrderNo ( order . getChannelOrderNo ( ) )
. appId ( order . getAppId ( ) )
. channelOrderNo ( order . getChannelOrderNo ( ) )
@ -130,6 +134,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
. type ( refundType . getStatus ( ) )
. build ( ) ;
payRefundCoreMapper . insert ( payRefundDO ) ;
// TODO @jason: 这块的逻辑, 和已存在的这块, 貌似是统一的?
unifiedReqDTO . setUserIp ( req . getUserIp ( ) )
. setAmount ( payRefundDO . getRefundAmount ( ) )
. setChannelOrderNo ( payRefundDO . getChannelOrderNo ( ) )
@ -137,19 +142,20 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
. setRefundReqNo ( payRefundDO . getMerchantRefundNo ( ) )
. setReason ( req . getReason ( ) ) ;
}
//向渠道发起退款申请
// 向渠道发起退款申请
PayRefundUnifiedRespDTO refundUnifiedRespDTO = client . unifiedRefund ( unifiedReqDTO ) ;
//构造退款申请返回对象
// 构造退款申请返回对象
PayRefundRespDTO respDTO = new PayRefundRespDTO ( ) ;
if ( refundUnifiedRespDTO . getChannelResp ( ) = = PayChannelRefundRespEnum . SUCCESS
| | refundUnifiedRespDTO . getChannelResp ( ) = = PayChannelRefundRespEnum . PROCESSING ) {
//成功处理, 在退款通知中处理, 这里不处理
if ( refundUnifiedRespDTO . getChannelResp ( ) = = PayChannelRefundRespEnum . SUCCESS
| | refundUnifiedRespDTO . getChannelResp ( ) = = PayChannelRefundRespEnum . PROCESSING ) {
// 成功处理,在退款通知中处理, 这里不处理
respDTO . setChannelReturnResult ( PayChannelRefundRespEnum . SUCCESS . getStatus ( ) ) ;
respDTO . setRefundId ( payRefundDO . getId ( ) ) ;
} else {
//失败返回错误给前端,可以重新发起退款,保证退款请求号(这里是商户退款单号), 避免重复退款。
// 失败返回错误给前端,可以重新发起退款,保证退款请求号(这里是商户退款单号), 避免重复退款。
// TODO @jason: 失败的话, 是不是可以跑出 ServiceException 业务异常。这样就是成功返回 refundId, 失败业务异常
respDTO . setChannelReturnResult ( PayChannelRefundRespEnum . FAILURE . getStatus ( ) ) ;
//更新退款单状态
// 更新退款单状态
PayRefundDO updatePayRefund = new PayRefundDO ( ) ;
updatePayRefund . setId ( payRefundDO . getId ( ) )
. setChannelErrorMsg ( refundUnifiedRespDTO . getChannelMsg ( ) )
@ -181,35 +187,37 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
payRefundSuccess ( refundNotify ) ;
} else {
//TODO 支付异常, 支付宝似乎没有支付异常的通知。
// TODO @jason: 那这里可以考虑打个 error logger
}
}
private void payRefundSuccess ( PayRefundNotifyDTO refundNotify ) {
// 校验退款单存在
PayRefundDO refundDO = payRefundCoreMapper . selectByTradeNoAndMerchantRefundNo ( refundNotify . getTradeNo ( ) , refundNotify . getReqNo ( ) ) ;
if ( refundDO = = null ) {
log . error ( " 不存在 seqNo 为{} 的支付退款单 " , refundNotify . getReqNo ( ) ) ;
log . error ( " [payRefundSuccess][ 不存在 seqNo 为{} 的支付退款单] " , refundNotify . getReqNo ( ) ) ;
throw exception ( PAY_REFUND_NOT_FOUND ) ;
}
Long refundAmount = refundDO . getRefundAmount ( ) ;
// 计算订单的状态。如果全部退款, 则订单处于关闭。TODO @jason: 建议这里按照金额来判断, 因为可能退款多次
Integer type = refundDO . getType ( ) ;
PayOrderStatusEnum orderStatus = PayOrderStatusEnum . SUCCESS ;
if ( PayRefundTypeEnum . ALL . getStatus ( ) . equals ( type ) ) {
if ( PayRefundTypeEnum . ALL . getStatus ( ) . equals ( type ) ) {
orderStatus = PayOrderStatusEnum . CLOSED ;
}
// 更新支付订单
PayOrderDO payOrderDO = payOrderCoreMapper . selectById ( refundDO . getOrderId ( ) ) ;
// 需更新已退金额
PayOrderDO payOrderDO = payOrderCoreMapper . selectById ( refundDO . getOrderId ( ) ) ;
Long refundedAmount = payOrderDO . getRefundAmount ( ) ;
// 更新支付订单
PayOrderDO updateOrderDO = new PayOrderDO ( ) ;
updateOrderDO . setId ( refundDO . getOrderId ( ) )
. setRefundAmount ( refundedAmount + refundAmount )
. setRefundAmount ( refundedAmount + refundDO . getRefundAmount ( ) )
. setStatus ( orderStatus . getStatus ( ) )
. setRefundTimes ( payOrderDO . getRefundTimes ( ) + 1 )
. setRefundTimes ( payOrderDO . getRefundTimes ( ) + 1 )
. setRefundStatus ( type ) ;
payOrderCoreMapper . updateById ( updateOrderDO ) ;
// 跟 新退款订单
// 更 新退款订单
PayRefundDO updateRefundDO = new PayRefundDO ( ) ;
updateRefundDO . setId ( refundDO . getId ( ) )
. setSuccessTime ( refundNotify . getRefundSuccessTime ( ) )
@ -219,7 +227,7 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
. setStatus ( PayRefundStatusEnum . SUCCESS . getStatus ( ) ) ;
payRefundCoreMapper . updateById ( updateRefundDO ) ;
//插入退款通知记录
// 插入退款通知记录
// TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调
payNotifyCoreService . createPayNotifyTask ( PayNotifyTaskCreateReqDTO . builder ( )
. type ( PayNotifyTypeEnum . REFUND . getType ( ) ) . dataId ( refundDO . getId ( ) ) . build ( ) ) ;
@ -235,12 +243,12 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
if ( ! PayOrderStatusEnum . SUCCESS . getStatus ( ) . equals ( order . getStatus ( ) ) ) {
throw exception ( PAY_ORDER_STATUS_IS_NOT_SUCCESS ) ;
}
//是否已经全额退款
// 是否已经全额退款
if ( PayRefundTypeEnum . ALL . getStatus ( ) . equals ( order . getRefundStatus ( ) ) ) {
throw exception ( PAY_REFUND_ALL_REFUNDED ) ;
}
// 校验金额 退款金额不能大于 原定的金额
if ( req . getAmount ( ) + order . getRefundAmount ( ) > order . getAmount ( ) ) {
if ( req . getAmount ( ) + order . getRefundAmount ( ) > order . getAmount ( ) ) {
throw exception ( PAY_REFUND_AMOUNT_EXCEED ) ;
}
// 校验渠道订单号