订单退款
parent
89b1679992
commit
c6f6c8c6a5
|
|
@ -15,6 +15,7 @@ public enum MoneyChangeCategory implements DictStr {
|
||||||
PinTaiDaKuan("PinTaiDaKuan", "平台打款"),
|
PinTaiDaKuan("PinTaiDaKuan", "平台打款"),
|
||||||
DingDanTiaoZhang("DingDanTiaoZhang", "订单调账"),
|
DingDanTiaoZhang("DingDanTiaoZhang", "订单调账"),
|
||||||
DingDanKouKuan("DingDanKouKuan", "订单扣款"),
|
DingDanKouKuan("DingDanKouKuan", "订单扣款"),
|
||||||
|
DingDanTuiKuan("DingDanTuiKuan", "订单退款"),
|
||||||
;
|
;
|
||||||
private final String val;
|
private final String val;
|
||||||
private final String txt;
|
private final String txt;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import com.njzscloud.supervisory.order.pojo.entity.OrderInfoEntity;
|
||||||
import com.njzscloud.supervisory.order.pojo.param.PaymentItemParam;
|
import com.njzscloud.supervisory.order.pojo.param.PaymentItemParam;
|
||||||
import com.njzscloud.supervisory.order.pojo.param.PaymentParam;
|
import com.njzscloud.supervisory.order.pojo.param.PaymentParam;
|
||||||
import com.njzscloud.supervisory.order.pojo.result.PaymentContextResult;
|
import com.njzscloud.supervisory.order.pojo.result.PaymentContextResult;
|
||||||
|
import com.njzscloud.supervisory.order.pojo.dto.RefundRequestDto;
|
||||||
import com.njzscloud.supervisory.order.service.OrderExpenseItemsService;
|
import com.njzscloud.supervisory.order.service.OrderExpenseItemsService;
|
||||||
import com.njzscloud.supervisory.order.service.OrderInfoService;
|
import com.njzscloud.supervisory.order.service.OrderInfoService;
|
||||||
import com.njzscloud.supervisory.order.service.WechatPayService;
|
import com.njzscloud.supervisory.order.service.WechatPayService;
|
||||||
|
|
@ -37,6 +38,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付相关接口
|
* 支付相关接口
|
||||||
|
|
@ -83,8 +85,8 @@ public class PaymentController {
|
||||||
|
|
||||||
// 校验支付方式
|
// 校验支付方式
|
||||||
if (paymentParam.getPaymentCategory() == null ||
|
if (paymentParam.getPaymentCategory() == null ||
|
||||||
!("wx".equals(paymentParam.getPaymentCategory()) ||
|
!(PaymentWay.WX.getVal().equals(paymentParam.getPaymentCategory()) ||
|
||||||
"company".equals(paymentParam.getPaymentCategory()))) {
|
PaymentWay.COMPANY.getVal().equals(paymentParam.getPaymentCategory()))) {
|
||||||
throw Exceptions.clierr("支付方式不合法");
|
throw Exceptions.clierr("支付方式不合法");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,6 +303,154 @@ public class PaymentController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一退款接口 - 支持微信退款和公司退款
|
||||||
|
*/
|
||||||
|
@PostMapping("/refund")
|
||||||
|
public R<?> refund(@RequestBody RefundRequestDto refundRequest) {
|
||||||
|
try {
|
||||||
|
// 验证订单存在性
|
||||||
|
OrderInfoEntity orderInfo = orderInfoService.getById(refundRequest.getOrderId());
|
||||||
|
if (orderInfo == null) {
|
||||||
|
throw Exceptions.clierr("订单不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证订单状态
|
||||||
|
if (!PaymentStatus.YiZhiFu.getVal().equals(orderInfo.getPaymentStatus().getVal())) {
|
||||||
|
throw Exceptions.clierr("订单未支付,无法退款");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取支付上下文
|
||||||
|
PaymentContextResult ctx = orderInfoService.paymentContext(refundRequest.getOrderId());
|
||||||
|
if (ctx == null) {
|
||||||
|
throw Exceptions.clierr("获取支付上下文失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据支付方式处理退款
|
||||||
|
if (PaymentWay.WX.getVal().equals(ctx.getPaymentCategory())) {
|
||||||
|
// 微信退款
|
||||||
|
// return processWechatRefund(refundRequest, orderInfo, ctx);
|
||||||
|
return R.success("退款成功");
|
||||||
|
} else if (PaymentWay.COMPANY.getVal().equals(ctx.getPaymentCategory())) {
|
||||||
|
// 公司退款
|
||||||
|
return processCompanyRefund(refundRequest, ctx);
|
||||||
|
} else {
|
||||||
|
throw Exceptions.clierr("不支持的支付方式");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("退款处理异常,订单ID:{}", refundRequest.getOrderId(), e);
|
||||||
|
return R.failed(ExceptionMsg.CLI_ERR_MSG, "退款处理失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理微信退款
|
||||||
|
*/
|
||||||
|
private R<?> processWechatRefund(RefundRequestDto refundRequest, OrderInfoEntity orderInfo, PaymentContextResult ctx) {
|
||||||
|
if (refundRequest.getWechatRefundParams() == null) {
|
||||||
|
throw Exceptions.clierr("微信退款参数不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建微信退款请求
|
||||||
|
WechatPayRefundDto refundDto = new WechatPayRefundDto();
|
||||||
|
// refundDto.setOutTradeNo(orderInfo.getOutTradeNo());
|
||||||
|
// refundDto.setOutRefundNo(refundRequest.getWechatRefundParams().getOutRefundNo());
|
||||||
|
// refundDto.setReason(refundRequest.getReason());
|
||||||
|
// refundDto.setRefund(orderInfo.getSettleMoney().multiply(new BigDecimal("100")).longValue()); // 转换为分
|
||||||
|
// refundDto.setTotal(orderInfo.getSettleMoney().multiply(new BigDecimal("100")).longValue()); // 转换为分
|
||||||
|
// refundDto.setFundsAccount(refundRequest.getWechatRefundParams().getFundsAccount());
|
||||||
|
// refundDto.setNotifyUrl(refundRequest.getWechatRefundParams().getNotifyUrl());
|
||||||
|
|
||||||
|
// 调用微信退款接口
|
||||||
|
WechatPayRefundResponseDto response = wechatPayService.refund(refundDto);
|
||||||
|
|
||||||
|
// 更新订单状态为已退款
|
||||||
|
orderInfoService.lambdaUpdate()
|
||||||
|
.eq(OrderInfoEntity::getId, refundRequest.getOrderId())
|
||||||
|
.set(OrderInfoEntity::getPaymentStatus, PaymentStatus.YiTuiKuan)
|
||||||
|
.update();
|
||||||
|
|
||||||
|
log.info("微信退款成功,订单ID:{},退款单号:{}", refundRequest.getOrderId(), refundDto.getOutRefundNo());
|
||||||
|
return R.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理公司退款
|
||||||
|
*/
|
||||||
|
private R<?> processCompanyRefund(RefundRequestDto refundRequest, PaymentContextResult ctx) {
|
||||||
|
if (refundRequest.getRefundAmount() == null) {
|
||||||
|
throw Exceptions.clierr("公司退款金额不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refundRequest.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
throw Exceptions.clierr("退款金额必须大于0");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OrderExpenseItemsEntity> entityList = orderExpenseItemsService.list(Wrappers.<OrderExpenseItemsEntity>lambdaQuery()
|
||||||
|
.eq(OrderExpenseItemsEntity::getDeleted, Boolean.FALSE)
|
||||||
|
.eq(OrderExpenseItemsEntity::getOrderId, refundRequest.getOrderId()));
|
||||||
|
BigDecimal dbTotalAmount = BigDecimal.ZERO;
|
||||||
|
if (null == entityList || entityList.size() <= 0) {
|
||||||
|
throw Exceptions.clierr("结算清单不存在");
|
||||||
|
}
|
||||||
|
for (OrderExpenseItemsEntity it : entityList) {
|
||||||
|
BigDecimal dbSettle = it.getSettleMoney();
|
||||||
|
// 当dbSettle为null时,按0计算
|
||||||
|
if (dbSettle == null) {
|
||||||
|
dbSettle = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
// 累加数据库中的结算金额
|
||||||
|
dbTotalAmount = dbTotalAmount.add(dbSettle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refundRequest.getRefundAmount().compareTo(dbTotalAmount) > 0) {
|
||||||
|
throw Exceptions.clierr("退款金额不能超过订单已结算金额");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复公司账户余额
|
||||||
|
restoreCompanyBalance(ctx, refundRequest.getRefundAmount(), refundRequest.getOrderId(), refundRequest.getReason());
|
||||||
|
|
||||||
|
// 更新订单状态为已退款
|
||||||
|
orderInfoService.lambdaUpdate()
|
||||||
|
.eq(OrderInfoEntity::getId, refundRequest.getOrderId())
|
||||||
|
.set(OrderInfoEntity::getPaymentStatus, PaymentStatus.YiTuiKuan)
|
||||||
|
.update();
|
||||||
|
|
||||||
|
log.info("公司退款成功,订单ID:{},退款金额:{}", refundRequest.getOrderId(), refundRequest.getRefundAmount());
|
||||||
|
return R.success("退款成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复公司账户余额
|
||||||
|
*/
|
||||||
|
private void restoreCompanyBalance(PaymentContextResult ctx, BigDecimal refundAmount, Long orderId, String reason) {
|
||||||
|
if (ctx.getCompanyUserId() == null || ctx.getCompanyAccountId() == null) {
|
||||||
|
throw Exceptions.clierr("公司账户信息不完整,无法退款");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复公司账户余额
|
||||||
|
BigDecimal oldBalance = ctx.getCompanyBalance();
|
||||||
|
BigDecimal newBalance = oldBalance.add(refundAmount);
|
||||||
|
MoneyAccountEntity companyAccount = new MoneyAccountEntity()
|
||||||
|
.setId(ctx.getCompanyAccountId())
|
||||||
|
.setMoney(newBalance);
|
||||||
|
moneyAccountService.updateById(companyAccount);
|
||||||
|
|
||||||
|
// 记录资金明细(这里需要实现资金明细记录功能)
|
||||||
|
MoneyChangeDetailEntity changeDetail = new MoneyChangeDetailEntity()
|
||||||
|
.setCompanyId(ctx.getTransCompanyId())
|
||||||
|
.setOrderId(orderId)
|
||||||
|
.setMoneyAccountId(companyAccount.getId())
|
||||||
|
.setOldMoney(oldBalance)
|
||||||
|
.setDelta(refundAmount) // 扣减为负数
|
||||||
|
.setNewMoney(newBalance)
|
||||||
|
.setMoneyChangeCategory(MoneyChangeCategory.DingDanTuiKuan)
|
||||||
|
.setMemo(reason);
|
||||||
|
|
||||||
|
moneyChangeDetailService.save(changeDetail);
|
||||||
|
log.info("公司账户余额恢复成功,账户ID:{},恢复金额:{}", ctx.getCompanyAccountId(), refundAmount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从商户订单号中提取订单ID
|
* 从商户订单号中提取订单ID
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.njzscloud.supervisory.order.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款请求DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RefundRequestDto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "订单ID不能为空")
|
||||||
|
private Long orderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款金额(公司退款时使用,微信退款时忽略)
|
||||||
|
*/
|
||||||
|
private BigDecimal refundAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款原因
|
||||||
|
*/
|
||||||
|
@Size(max = 200, message = "退款原因不能超过200个字符")
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信支付退款相关参数(仅微信退款时使用)
|
||||||
|
*/
|
||||||
|
private WechatRefundParams wechatRefundParams;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class WechatRefundParams {
|
||||||
|
/**
|
||||||
|
* 商户退款单号
|
||||||
|
*/
|
||||||
|
@Size(max = 64, message = "商户退款单号不能超过64个字符")
|
||||||
|
private String outRefundNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款资金来源
|
||||||
|
*/
|
||||||
|
private String fundsAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款结果回调地址
|
||||||
|
*/
|
||||||
|
private String notifyUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,10 @@ public class PaymentContextResult {
|
||||||
|
|
||||||
private String wechatOpenid;
|
private String wechatOpenid;
|
||||||
|
|
||||||
|
private String paymentCategory;
|
||||||
|
|
||||||
|
private String outTradeNo;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,8 @@
|
||||||
a.id AS orderId,
|
a.id AS orderId,
|
||||||
a.trans_company_id AS transCompanyId,
|
a.trans_company_id AS transCompanyId,
|
||||||
a.sn,
|
a.sn,
|
||||||
|
a.payment_category,
|
||||||
|
a.out_trade_no,
|
||||||
bc.user_id AS companyUserId,
|
bc.user_id AS companyUserId,
|
||||||
ma1.id AS companyAccountId,
|
ma1.id AS companyAccountId,
|
||||||
ma1.money AS companyBalance,
|
ma1.money AS companyBalance,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue