feat:退款的回调

This commit is contained in:
dzc committed 2025-12-09 14:03:11 +08:00
1 parent 44b26ffc6b
commit 87e5e30b78
3 files changed
+116 -19

No files matched your search

@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.member.controller.admin.opentype;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@@ -53,11 +54,10 @@ public class OpenTypeController {
return success(true);
}
@GetMapping("/refund")
@Operation(summary = "发起钱包充值退款")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<Boolean> refundWalletRecharge(@RequestParam("id") Long id) {
openTypeService.refundWalletRecharge(id, getClientIP());
@PostMapping("/update-refunded")
@Operation(summary = "更新钱包充值为已退款")
public CommonResult<Boolean> updateWalletRechargeRefunded(@RequestBody PayRefundNotifyReqDTO notifyReqDTO) {
openTypeService.updateWalletRechargeRefunded(Long.valueOf(notifyReqDTO.getMerchantOrderId()), notifyReqDTO.getPayRefundId());
return success(true);
}
@@ -51,6 +51,11 @@ public interface OpenTypeService {
*/
Long payOpenType(AppPayOpenReqType dto);
/**
* 发起退款
*/
void refundWalletRecharge(Long id, String ip);
/**
* 当前用户是否是会员
*/
@@ -86,5 +91,5 @@ public interface OpenTypeService {
/**
* 退款的回调
*/
void refundWalletRecharge(Long id, String ip);
void updateWalletRechargeRefunded(Long merchantOrderId, Long payRefundId);
}
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.member.service.opentype;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.client.service.ChatApi;
import cn.iocoder.yudao.module.member.client.vo.chat.privatemsg.ChatPrivateTotalMessageTotalReqVO;
@@ -11,22 +10,22 @@ import cn.iocoder.yudao.module.member.client.vo.chat.privatemsg.ChatPrivateTotal
import cn.iocoder.yudao.module.member.controller.app.opentype.vo.AppPayOpenReqType;
import cn.iocoder.yudao.module.member.dal.dataobject.config.MemberConfigDO;
import cn.iocoder.yudao.module.member.dal.dataobject.opentype.OpenTypeUserDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberRecentTagDO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.opentype.OpenTypeUserMapper;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberRecentTagMapper;
import cn.iocoder.yudao.module.member.enums.MemberErrorCode;
import cn.iocoder.yudao.module.member.enums.opentype.OpenTypeUserStatusEnum;
import cn.iocoder.yudao.module.member.enums.tag.MemberRecentTagEnum;
import cn.iocoder.yudao.module.member.service.config.MemberConfigService;
import cn.iocoder.yudao.module.member.service.opentype.BO.OpenTypeExtBO;
import cn.iocoder.yudao.module.member.service.user.MemberRecentTagService;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -45,7 +44,6 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.member.dal.mysql.opentype.OpenTypeMapper;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -56,6 +54,7 @@ import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.add
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum.*;
/**
* 会员类型 Service 实现类
@@ -81,6 +80,8 @@ public class OpenTypeServiceImpl implements OpenTypeService {
private ChatApi chatApi;
@Resource
private PayOrderApi payOrderApi;
@Resource
private PayRefundApi payRefundApi;
/**
@@ -189,6 +190,38 @@ public class OpenTypeServiceImpl implements OpenTypeService {
return payOrderId;
}
/**
* 发起退款
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void refundWalletRecharge(Long id, String clientIP) {
OpenTypeUserDO openTypeUserDO = openTypeUserMapper.selectById(id);
if (ObjectUtil.isEmpty(openTypeUserDO)) {
log.error("[会员开通退款][recharge({}) clientIP({}) 不存在开通会员的订单,请进行处理!]", id, clientIP);
throw exception(MemberErrorCode.EMPTY, "会员开通记录不存在");
}
// 校验钱包充值是否可以发起退款
validateOpenTypeCanRefund(openTypeUserDO);
String walletRechargeId = String.valueOf(id);
String refundId = walletRechargeId + "-refund";
// 创建退款订单
PayRefundCreateReqDTO refundDto = new PayRefundCreateReqDTO();
refundDto.setAppKey("vip");
refundDto.setUserIp(clientIP);
refundDto.setMerchantOrderId(String.valueOf(openTypeUserDO.getId()));
refundDto.setMerchantRefundId(refundId + "-refund");
refundDto.setReason("想退钱");
refundDto.setPrice(openTypeUserDO.getPayPrice());
Long payOrderRefundId = payRefundApi.createRefund(refundDto);
// 更新订单
OpenTypeUserDO updateObj = new OpenTypeUserDO();
updateObj.setId(openTypeUserDO.getId());
updateObj.setRefundStatus(WAITING.getStatus());
updateObj.setPayRefundId(payOrderRefundId);
openTypeUserMapper.updateById(updateObj);
}
/**
* 当前用户是否是会员
*/
@@ -312,7 +345,7 @@ public class OpenTypeServiceImpl implements OpenTypeService {
// 查询订单
PayOrderRespDTO payOrder = validatePayOrderPaid(openTypeUserDO, payOrderId);
// 更新钱包充值的支付状态
// 更新会员开通的支付状态
LambdaUpdateWrapper<OpenTypeUserDO> updateWrapper = new LambdaUpdateWrapper<OpenTypeUserDO>()
.set(OpenTypeUserDO::getPayChannelCode, payOrder.getChannelCode())
.set(OpenTypeUserDO::getPayStatus, true)
@@ -330,16 +363,33 @@ public class OpenTypeServiceImpl implements OpenTypeService {
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void refundWalletRecharge(Long id, String clientIP) {
public void updateWalletRechargeRefunded(Long id, Long payRefundId) {
OpenTypeUserDO openTypeUserDO = openTypeUserMapper.selectById(id);
if (ObjectUtil.isEmpty(openTypeUserDO)) {
log.error("[会员开通退款][recharge({}) clientIP({}) 不存在开通会员的订单,请进行处理!]", id, clientIP);
log.error("[会员开通支付,退款的回调][id({}) payRefundId({}) 不存在开通会员的订单,请进行处理!]", id, payRefundId);
}
PayRefundRespDTO payRefund = validateOpenTypeCanRefunded(openTypeUserDO, payRefundId);
OpenTypeUserDO updateObj = new OpenTypeUserDO().setId(id);
// 退款成功
if (PayRefundStatusEnum.isSuccess(payRefund.getStatus())) {
updateObj.setRefundStatus(SUCCESS.getStatus())
.setRefundTime(payRefund.getSuccessTime())
.setRefundPayPrice(openTypeUserDO.getPayPrice());
}
// 退款失败
if (PayRefundStatusEnum.isFailure(payRefund.getStatus())) {
updateObj.setRefundStatus(FAILURE.getStatus());
}
// 更新会员开通的退款状态
openTypeUserMapper.update(updateObj, new LambdaQueryWrapperX<OpenTypeUserDO>()
.eq(OpenTypeUserDO::getId, openTypeUserDO.getOpenTypeId())
.eq(OpenTypeUserDO::getRefundStatus, WAITING.getStatus())
);
}
/**
* 验证会员支付订单是否成功
* 验证开通会员支付订单是否成功
*/
private PayOrderRespDTO validatePayOrderPaid(OpenTypeUserDO recharge, Long payOrderId) {
// 1. 校验支付单是否存在
@@ -351,21 +401,63 @@ public class OpenTypeServiceImpl implements OpenTypeService {
// 2.1 校验支付单已支付
if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
log.error("[validatePayOrderPaid][验证会员支付订单是否成功({}) payOrder({}) 未支付,请进行处理!payOrder 数据是:{}]", recharge.getId(), payOrderId, toJsonString(payOrder));
throw exception(MemberErrorCode.EMPTY, "会员充值更新支付状态失败,支付单状态不是【支付成功】状态");
throw exception(MemberErrorCode.EMPTY, "开通会员更新支付状态失败,支付单状态不是【支付成功】状态");
}
// 2.2 校验支付金额一致
if (notEqual(payOrder.getPrice(), recharge.getPayPrice())) {
log.error("[validatePayOrderPaid][验证会员支付订单是否成功({}) payOrder({}) 支付金额不匹配,请进行处理!钱包 数据是:{},payOrder 数据是:{}]", recharge.getId(), payOrderId, toJsonString(recharge), toJsonString(payOrder));
throw exception(MemberErrorCode.EMPTY, "钱包充值更新支付状态失败,支付单金额不匹配");
throw exception(MemberErrorCode.EMPTY, "开通会员更新支付状态失败,支付单金额不匹配");
}
// 2.3 校验支付订单的商户订单匹配
if (notEqual(payOrder.getMerchantOrderId(), recharge.getId().toString())) {
log.error("[validatePayOrderPaid][验证会员支付订单是否成功({}) 支付单不匹配({}),请进行处理!payOrder 数据是:{}]", recharge.getId(), payOrderId, toJsonString(payOrder));
throw exception(MemberErrorCode.EMPTY, "钱包充值更新支付状态失败,支付单编号不匹配");
throw exception(MemberErrorCode.EMPTY, "开通会员更新支付状态失败,支付单编号不匹配");
}
return payOrder;
}
/**
* 校验开通会员是否已退款
*/
private PayRefundRespDTO validateOpenTypeCanRefunded(OpenTypeUserDO openTypeUserDO, Long payRefundId) {
// 校验退款订单匹配
if (notEqual(openTypeUserDO.getPayRefundId(), payRefundId)) {
log.error("[校验开通会员是否已退款][钱包充值({}) 退款单不匹配({}),请进行处理!钱包充值的数据是:{}]", openTypeUserDO.getId(), payRefundId, toJsonString(openTypeUserDO));
throw exception(MemberErrorCode.EMPTY, "开通会员退款更新失败,钱包退款单编号不匹配");
}
// 校验退款订单
PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId);
if (payRefund == null) {
log.error("[校验开通会员是否已退款][payRefund({})不存在]", payRefundId);
throw exception(MemberErrorCode.EMPTY, "开通会员退款更新失败,退款订单不存在");
}
// 校验退款金额一致
if (notEqual(payRefund.getRefundPrice(), openTypeUserDO.getPayPrice())) {
log.error("[校验开通会员是否已退款][开通会员({}) payRefund({}) 退款金额不匹配,请进行处理!钱包数据是:{},payRefund数据是:{}]", openTypeUserDO.getId(), payRefundId, toJsonString(openTypeUserDO), toJsonString(payRefund));
throw exception(MemberErrorCode.EMPTY, "开通会员退款更新失败,退款单金额不匹配");
}
// 校验退款订单商户订单是否匹配
if (notEqual(payRefund.getMerchantOrderId(), openTypeUserDO.getId().toString())) {
log.error("[校验开通会员是否已退款][开通会员({}) 退款单不匹配({}),请进行处理!payRefund 数据是:{}]", openTypeUserDO.getId(), payRefundId, toJsonString(payRefund));
throw exception(MemberErrorCode.EMPTY, "开通会员退款更新失败,钱包退款单编号不匹配");
}
return payRefund;
}
/**
* 校验开通会员是否可以发起退款
*/
private void validateOpenTypeCanRefund(OpenTypeUserDO walletRecharge) {
// 校验充值订单是否支付
if (!walletRecharge.getPayStatus()) {
throw exception(MemberErrorCode.EMPTY, "开通会员发起退款失败,钱包充值订单未支付");
}
// 校验充值订单是否已退款
if (walletRecharge.getPayRefundId() != null) {
throw exception(MemberErrorCode.EMPTY, "开通会员发起退款失败,钱包充值订单已退款");
}
}
private void validateOpenTypeExists(Long id) {
if (openTypeMapper.selectById(id) == null) {
throw exception(MemberErrorCode.OPEN_TYPE_NOT_EXISTS);