订单退款
parent
89b1679992
commit
c6f6c8c6a5
|
|
@ -15,6 +15,7 @@ public enum MoneyChangeCategory implements DictStr {
|
|||
PinTaiDaKuan("PinTaiDaKuan", "平台打款"),
|
||||
DingDanTiaoZhang("DingDanTiaoZhang", "订单调账"),
|
||||
DingDanKouKuan("DingDanKouKuan", "订单扣款"),
|
||||
DingDanTuiKuan("DingDanTuiKuan", "订单退款"),
|
||||
;
|
||||
private final String val;
|
||||
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.PaymentParam;
|
||||
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.OrderInfoService;
|
||||
import com.njzscloud.supervisory.order.service.WechatPayService;
|
||||
|
|
@ -37,6 +38,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付相关接口
|
||||
|
|
@ -83,8 +85,8 @@ public class PaymentController {
|
|||
|
||||
// 校验支付方式
|
||||
if (paymentParam.getPaymentCategory() == null ||
|
||||
!("wx".equals(paymentParam.getPaymentCategory()) ||
|
||||
"company".equals(paymentParam.getPaymentCategory()))) {
|
||||
!(PaymentWay.WX.getVal().equals(paymentParam.getPaymentCategory()) ||
|
||||
PaymentWay.COMPANY.getVal().equals(paymentParam.getPaymentCategory()))) {
|
||||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 paymentCategory;
|
||||
|
||||
private String outTradeNo;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -206,6 +206,8 @@
|
|||
a.id AS orderId,
|
||||
a.trans_company_id AS transCompanyId,
|
||||
a.sn,
|
||||
a.payment_category,
|
||||
a.out_trade_no,
|
||||
bc.user_id AS companyUserId,
|
||||
ma1.id AS companyAccountId,
|
||||
ma1.money AS companyBalance,
|
||||
|
|
|
|||
Loading…
Reference in New Issue