用户退款

This commit is contained in:
yanglei 2026-01-08 16:53:01 +08:00
parent 47b38537b4
commit f94acd035b
6 changed files with 84 additions and 95 deletions

View File

@ -12,17 +12,17 @@ public interface RefundStatus {
/**
* 退款成功
*/
int APPLYING = 0;
int SUCCESS = 0;
/**
* 关闭
*/
int PROCESSING = 1;
int CLOSED = 1;
/**
* 退款中
*/
int PROCESSED = 2;
int PROCESSING = 2;
/**
* 异常
*/
int CLOSED = 3;
int ABNORMAL = 3;
}

View File

@ -1,34 +0,0 @@
package com.cdzy.payment.model.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 退款信息
*
* @author yanglei
* @since 2025-10-21 10:26
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EbikeRefundInfoVo implements Serializable {
/**
* 退款金额单位为元
*/
private BigDecimal refund;
/**
* 原订单金额单位为元
*/
private BigDecimal total;
/**
* 退款币种 (默认CNY)
*/
private String currency = "CNY";
}

View File

@ -14,21 +14,21 @@ import com.mybatisflex.core.service.IService;
public interface EbikeOrderService extends IService<EbikeOrder> {
/**
* 订单退款完成
* 订单退款完成 由退款中 -> 退款成功
*
* @param orderId 订单ID
*/
void doneRefund(Long orderId);
/**
* 订单退款
* 订单退款 由退款申请中 -> 退款中
*
* @param orderId 订单ID
*/
void refund(Long orderId);
/**
* 订单退款失败
* 订单退款失败 由退款中 -> 退款失败
*
* @param orderId 订单ID
*/

View File

@ -36,7 +36,7 @@ public class EbikeOrderServiceImpl extends ServiceImpl<EbikeOrderMapper, EbikeOr
public void doneRefund(Long orderId) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(EBIKE_ORDER.ORDER_ID.eq(orderId))
.where(EBIKE_ORDER.ORDER_STATUS.eq(EbikeOrderStatus.REFUND_APPLYING));
.where(EBIKE_ORDER.ORDER_STATUS.eq(EbikeOrderStatus.REFUNDING));
EbikeOrder userOrders = this.mapper.selectOneByQuery(queryWrapper);
if (userOrders == null) {
log.warn("退款完成时未找到订单订单ID: {}, 或订单状态不是退款中", orderId);
@ -50,7 +50,7 @@ public class EbikeOrderServiceImpl extends ServiceImpl<EbikeOrderMapper, EbikeOr
public void failRefund(Long orderId) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(EBIKE_ORDER.ORDER_ID.eq(orderId))
.where(EBIKE_ORDER.ORDER_STATUS.eq(EbikeOrderStatus.REFUND_APPLYING));
.where(EBIKE_ORDER.ORDER_STATUS.eq(EbikeOrderStatus.REFUNDING));
EbikeOrder userOrders = this.mapper.selectOneByQuery(queryWrapper);
userOrders.setOrderStatus(EbikeOrderStatus.REFUND_FAILED);
this.mapper.update(userOrders);

View File

@ -45,7 +45,7 @@ public class EbikeRefundServiceImpl extends ServiceImpl<EbikeRefundMapper, Ebike
totalSeconds
);
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_REFUND.REFUND_STATUS.eq(RefundStatus.PROCESSED))
.where(EBIKE_REFUND.REFUND_STATUS.eq(RefundStatus.PROCESSING))
.and(timeFilter);
return list(query);
}
@ -99,10 +99,15 @@ public class EbikeRefundServiceImpl extends ServiceImpl<EbikeRefundMapper, Ebike
log.error("未找到对应的退款记录refundOrderId: {}", refundOrderId);
return false;
}
if (ebikeRefund.getRefundStatus() == RefundStatus.APPLYING) {
if (ebikeRefund.getRefundStatus() == RefundStatus.SUCCESS) {
log.info("退款已处理成功。refundOrderId: {}", refundOrderId);
return true;
}
if (ebikeRefund.getRefundStatus() == RefundStatus.CLOSED ||
ebikeRefund.getRefundStatus() == RefundStatus.ABNORMAL) {
log.info("退款单状态异常或关闭。refundOrderId: {}", refundOrderId);
return true;
}
log.info("退款回调订单Id:{}, 退款回调订单状态:{}", refundOrderId, refundNotification.getRefundStatus());
ebikeRefund.setRefundStatus(refundNotification.getRefundStatus().ordinal());
if (Status.SUCCESS.equals(refundNotification.getRefundStatus())) {
@ -115,9 +120,13 @@ public class EbikeRefundServiceImpl extends ServiceImpl<EbikeRefundMapper, Ebike
Long orderId = ebikeRefund.getOrderId();
// 更新订单退款状态
switch (refundNotification.getRefundStatus()) {
case PROCESSING, CLOSED -> ebikeOrderService.refund(orderId);
// 退款中
case PROCESSING -> ebikeOrderService.refund(orderId);
// 成功退款
case SUCCESS -> ebikeOrderService.doneRefund(orderId);
// 失败/关闭 订单退款失败
case ABNORMAL -> ebikeOrderService.failRefund(orderId);
default -> log.warn("未知的微信退款状态: {}, orderId: {}", refundNotification.getRefundStatus(), orderId);
}
return updateById(ebikeRefund);
}

View File

@ -1,11 +1,18 @@
package com.cdzy.payment.service.impl;
import com.cdzy.common.enums.GlobalConstants;
import com.cdzy.common.ex.EbikeException;
import com.cdzy.payment.config.WxPayConfig;
import com.cdzy.payment.enums.PaymentMethod;
import com.cdzy.payment.enums.RefundStatus;
import com.cdzy.payment.model.entity.EbikePayment;
import com.cdzy.payment.model.entity.EbikeRefund;
import com.cdzy.payment.model.vo.*;
import com.cdzy.payment.model.vo.EbikePaymentAmountVo;
import com.cdzy.payment.model.vo.EbikePaymentDetailVo;
import com.cdzy.payment.model.vo.EbikePaymentVo;
import com.cdzy.payment.model.vo.EbikeWxHandleNotifyVo;
import com.cdzy.payment.model.vo.EbikeWxJsapiPromptVo;
import com.cdzy.payment.service.EbikeOrderService;
import com.cdzy.payment.service.EbikePaymentService;
import com.cdzy.payment.service.EbikeRefundService;
import com.cdzy.payment.service.EbikeWxPayService;
@ -27,11 +34,22 @@ import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.util.NonceUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.CloseOrderRequest;
import com.wechat.pay.java.service.payments.jsapi.model.Detail;
import com.wechat.pay.java.service.payments.jsapi.model.GoodsDetail;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.jsapi.model.SettleInfo;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import com.wechat.pay.java.service.refund.model.Status;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
@ -44,8 +62,13 @@ import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static com.wechat.pay.java.core.http.Constant.*;
import static com.wechat.pay.java.core.http.Constant.REQUEST_ID;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_NONCE;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SERIAL;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SIGNATURE;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_TIMESTAMP;
/**
* 微信支付服务类JSAPI支付小程序
@ -81,6 +104,9 @@ public class EbikeWxPayServiceImpl implements EbikeWxPayService {
@Resource
private ObjectMapper objectMapper;
@Resource
private EbikeOrderService orderService;
@Override
public boolean closeOrder(String outTradeNo) {
try {
@ -385,31 +411,26 @@ public class EbikeWxPayServiceImpl implements EbikeWxPayService {
notifyVo.setSuccess(false);
// 检查退款记录是否存在
EbikeRefund ebikeRefund = ebikeRefundService.getById(refundDto.getRefundId());
if (ebikeRefund == null) {
log.error("退款refund失败{} 退款申请不存在", refundDto.getRefundId());
notifyVo.setMessage(String.format("{%s}退款申请不存在", refundDto.getRefundId()));
return notifyVo;
if (Objects.isNull(ebikeRefund)) {
throw new EbikeException("退款订单不存在");
}
// 检查退款状态
if (Status.SUCCESS.ordinal() == ebikeRefund.getRefundStatus()) {
log.error("退款refund失败{} 退款已经成功,不能重复退款", refundDto.getRefundId());
notifyVo.setMessage(String.format("{%s}退款已经成功,不能重复退款", refundDto.getRefundId()));
return notifyVo;
if (RefundStatus.SUCCESS == ebikeRefund.getRefundStatus()) {
throw new EbikeException("当前订单已完成退款");
}
if (Status.CLOSED.ordinal() == ebikeRefund.getRefundStatus() ||
Status.ABNORMAL.ordinal() == ebikeRefund.getRefundStatus()) {
//重新发起
if (RefundStatus.CLOSED == ebikeRefund.getRefundStatus() ||
RefundStatus.ABNORMAL == ebikeRefund.getRefundStatus()) {
//重新生成退款id并发起退款
String newOrder = StringUtils.generateSnowflakeId("refundId");
ebikeRefund.setRefundOrderId(newOrder);
}
// 查询支付记录
Long orderId = ebikeRefund.getOrderId();
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(orderId);
if (Objects.isNull(ebikePayment)) {
throw new EbikeException("支付记录不存在");
}
String transactionId = ebikePayment.getTransactionId();
EbikeRefundInfoVo amount = new EbikeRefundInfoVo();
amount.setTotal(ebikePayment.getCostPrice());
amount.setRefund(ebikePayment.getTotal());
amount.setCurrency(ebikeRefund.getCurrency());
// 发起退款
CreateRequest request = new CreateRequest();
request.setTransactionId(transactionId);
@ -418,43 +439,36 @@ public class EbikeWxPayServiceImpl implements EbikeWxPayService {
request.setReason(ebikeRefund.getProblemDescription());
request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
AmountReq amountReq = new AmountReq();
amountReq.setRefund(yuanToCent(amount.getRefund()));
amountReq.setTotal(yuanToCent(amount.getTotal()));
amountReq.setCurrency(amount.getCurrency());
amountReq.setRefund(yuanToCent(ebikePayment.getTotal()));
amountReq.setTotal(yuanToCent(ebikePayment.getTotal()));
amountReq.setCurrency(ebikePayment.getCurrency());
request.setAmount(amountReq);
try {
Refund result = wxRefundService.create(request);
if (result == null) {
log.error("调用微信退款接口返回空,订单号: {}", orderId);
notifyVo.setMessage("退款请求失败");
return notifyVo;
}
orderService.refund(orderId);
// 更新退款信息
if (result != null) {
ebikeRefund.setOrderId(orderId);
ebikeRefund.setTransactionId(transactionId);
ebikeRefund.setTotal(amount.getTotal());
ebikeRefund.setTotal(ebikePayment.getCostPrice());
ebikeRefund.setRefundStatus(result.getStatus().ordinal());
ebikeRefund.setCurrency(amount.getCurrency());
ebikeRefund.setCurrency(ebikePayment.getCurrency());
ebikeRefund.setRefund(new BigDecimal(result.getAmount().getRefund())
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
ebikeRefund.setRefundTime(LocalDateTime.now());
ebikeRefund.setRefundTransactionId(result.getRefundId());
ebikeRefundService.updateById(ebikeRefund);
notifyVo.setSuccess(true);
notifyVo.setMessage("退款成功");
return notifyVo;
}
log.error("退款refund失败订单号{}", orderId);
notifyVo.setMessage("退款失败");
notifyVo.setMessage("退款已提交");
return notifyVo;
} catch (Exception e) {
String err = logError("退款refund", e);
try {
JsonNode jsonNode = objectMapper.readTree(err);
if (jsonNode.has("message")) {
notifyVo.setMessage(jsonNode.get("message").asText());
} else {
notifyVo.setMessage(err);
}
} catch (Exception ex) {
notifyVo.setMessage(err);
}
log.error("发起退款异常refundId: {}, orderId: {}, error: {}",
refundDto.getRefundId(), orderId, e.getMessage(), e);
notifyVo.setMessage("退款失败: " + e.getMessage());
return notifyVo;
}
}