|
|
@@ -10,6 +10,8 @@ import com.alibaba.fastjson2.JSONObject;
|
|
|
import com.kym.common.config.WxPayConfig;
|
|
|
import com.kym.common.constant.ResponseEnum;
|
|
|
import com.kym.common.exception.BusinessException;
|
|
|
+import com.kym.common.utils.CommUtil;
|
|
|
+import com.kym.common.utils.LambadaTools;
|
|
|
import com.kym.common.utils.OrderUtils;
|
|
|
import com.kym.entity.miniapp.Account;
|
|
|
import com.kym.entity.miniapp.PayLog;
|
|
|
@@ -45,6 +47,8 @@ import java.io.FileInputStream;
|
|
|
import java.io.IOException;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.time.LocalDateTime;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
|
|
|
/**
|
|
|
@@ -198,8 +202,6 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
return service.prepayWithRequestPayment(request);
|
|
|
}
|
|
|
|
|
|
- // TODO: 2023-09-11 判断退款金额需要几笔充值订单进行退款操作 这个放在外层做,这里只支持单笔订单退款
|
|
|
-
|
|
|
/**
|
|
|
* 微信支付订单号查询订单
|
|
|
*/
|
|
|
@@ -296,16 +298,10 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 创建退款申请
|
|
|
- *
|
|
|
- * @return
|
|
|
+ * 申请退款
|
|
|
*/
|
|
|
@Override
|
|
|
- public Refund wxRefund(JSONObject params) {
|
|
|
- // 查询用户是否有未完结的订单,如果有则提示等待订单完成后再申请,如果无则查询可用余额,将金额转至冻结金额
|
|
|
- // 生成订单号
|
|
|
- String outRefundNo = OrderUtils.getOrderNo();
|
|
|
-
|
|
|
+ public void applyWxRefund() {
|
|
|
var userId = StpUtil.getLoginIdAsLong();
|
|
|
var chargeOrder = chargeOrderService.getChargingOrderByUserId(userId);
|
|
|
if (chargeOrder != null) {
|
|
|
@@ -315,32 +311,97 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
if (account.getBalance() <= 0) {
|
|
|
throw new BusinessException("账户余额不足,无需退款");
|
|
|
}
|
|
|
+
|
|
|
// 将余额转移至冻结余额
|
|
|
accountService.lambdaUpdate().setSql(" frozen_amount = balance ,balance = 0").eq(Account::getUserId, userId);
|
|
|
|
|
|
+ // 余额作为退款金额
|
|
|
+ AtomicInteger refundAmount = new AtomicInteger(account.getBalance());
|
|
|
+ // 充值记录
|
|
|
+ var payLogs = payLogService.lambdaQuery().eq(PayLog::getUserId, userId).eq(PayLog::getTradeState, "SUCCESS").orderByDesc(PayLog::getSuccessTime).list();
|
|
|
+ // 最后一次的充值金额可以覆盖退款金额
|
|
|
+ if (!CommUtil.isEmptyOrNull(payLogs) && payLogs.get(0).getTotal() >= refundAmount.get()) {
|
|
|
+ // 退款日志
|
|
|
+ createRefundLog(payLogs.get(0));
|
|
|
+ } else if (!CommUtil.isEmptyOrNull(payLogs) && payLogs.get(0).getTotal() < refundAmount.get()) {
|
|
|
+ // 最后一次的充值金额不能覆盖退款金额,拆分成多笔退款
|
|
|
+ int amount = 0;
|
|
|
+ var refundPayLogs = new ArrayList<PayLog>();
|
|
|
+ for (PayLog payLog : payLogs) {
|
|
|
+ amount += payLog.getTotal();
|
|
|
+ refundPayLogs.add(payLog);
|
|
|
+ if (amount >= refundAmount.get()) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 需要退款的记录数量
|
|
|
+ var size = refundPayLogs.size();
|
|
|
+ refundPayLogs.forEach(LambadaTools.forEachWithIndex((item, index) -> {
|
|
|
+ // 如果不是最后一笔,金额全退,最后一笔退剩余的金额
|
|
|
+ if (index < size) {
|
|
|
+ createRefundLog(item);
|
|
|
+ refundAmount.addAndGet(-item.getTotal());
|
|
|
+ } else {
|
|
|
+ item.setTotal(refundAmount.get());
|
|
|
+ createRefundLog(item);
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ } else {
|
|
|
+ // 退款异常
|
|
|
+ LOGGER.error("退款异常:userId:{},余额:{},payLogs:{}", userId, refundAmount.get(), payLogs);
|
|
|
+ throw new BusinessException("退款异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建退款申请记录
|
|
|
+ *
|
|
|
+ * @param payLog
|
|
|
+ */
|
|
|
+ public void createRefundLog(PayLog payLog) {
|
|
|
+ // 退款日志
|
|
|
+ var refundLog = new RefundLog()
|
|
|
+ .setUserId(payLog.getUserId())
|
|
|
+ .setOutTradeNo(payLog.getOutTradeNo())
|
|
|
+ .setOutRefundNo(OrderUtils.getOrderNo())
|
|
|
+ .setTotal(payLog.getTotal())
|
|
|
+ .setRefund(payLog.getTotal());
|
|
|
+ refundLogService.save(refundLog);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理退款
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Refund wxRefund(long refundLogId) {
|
|
|
+ // 通过退款申请id获取退款申请记录
|
|
|
+ var refundLog = refundLogService.getById(refundLogId);
|
|
|
+
|
|
|
// 钱包流水
|
|
|
var walletDetail = new WalletDetail()
|
|
|
.setType(WalletDetail.TYPE_提现)
|
|
|
.setStatus(WalletDetail.STATUS_待确认)
|
|
|
- .setUserId(userId)
|
|
|
- .setAmount(params.getIntValue("amount"))
|
|
|
- .setOrderNo(outRefundNo);
|
|
|
+ .setUserId(refundLog.getUserId())
|
|
|
+ .setAmount(refundLog.getRefund())
|
|
|
+ .setOrderNo(refundLog.getOutRefundNo());
|
|
|
walletDetailService.save(walletDetail);
|
|
|
|
|
|
-
|
|
|
CreateRequest request = new CreateRequest();
|
|
|
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
// 原始第三方订单号
|
|
|
- request.setOutTradeNo(params.getString("outTradeNo"));
|
|
|
+ request.setOutTradeNo(refundLog.getOutTradeNo());
|
|
|
// 退款订单号
|
|
|
- request.setOutRefundNo(outRefundNo);
|
|
|
+ request.setOutRefundNo(refundLog.getOutRefundNo());
|
|
|
// 退款原因
|
|
|
request.setReason("用户申请退款");
|
|
|
// 回调通知地址
|
|
|
request.setNotifyUrl(conf.getRefundNotifyUrl());
|
|
|
var amount = new AmountReq();
|
|
|
// 退款金额
|
|
|
- amount.setRefund(params.getLong("amount"));
|
|
|
+ amount.setRefund((long) refundLog.getRefund());
|
|
|
request.setAmount(amount);
|
|
|
return refundService.create(request);
|
|
|
}
|
|
|
@@ -352,7 +413,6 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
*/
|
|
|
@Override
|
|
|
public Refund queryByOutRefundNo(String outRefundNo) {
|
|
|
-
|
|
|
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
|
|
|
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
request.setOutRefundNo(outRefundNo);
|
|
|
@@ -377,21 +437,11 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
DateTime dt = DateUtil.parse(refundNotification.getSuccessTime());
|
|
|
LocalDateTime successTime = LocalDateTimeUtil.of(dt);
|
|
|
|
|
|
- // 资金流水
|
|
|
- var walletDetail = walletDetailService.getWalletDetailByOrderNo(refundNotification.getOutTradeNo(), WalletDetail.TYPE_提现);
|
|
|
- walletDetail.setStatus(WalletDetail.STATUS_已确认) //已确认
|
|
|
- .setCurrency(refundNotification.getAmount().getCurrency())
|
|
|
- .setAmount(refundNotification.getAmount().getRefund().intValue())
|
|
|
- .setTransactionTime(successTime);
|
|
|
- walletDetailService.updateById(walletDetail);
|
|
|
-
|
|
|
- // 退款日志
|
|
|
- var refundLog = new RefundLog()
|
|
|
- .setUserId(walletDetail.getUserId())
|
|
|
+ //退款日志在申请时插入,接收通知时更新
|
|
|
+ var refundLog = refundLogService.lambdaQuery().eq(RefundLog::getOutRefundNo, refundNotification.getOutRefundNo()).one();
|
|
|
+ refundLog
|
|
|
.setRefundId(refundNotification.getRefundId())
|
|
|
.setTransactionId(refundNotification.getTransactionId())
|
|
|
- .setOutTradeNo(walletDetail.getOrderNo())
|
|
|
- .setOutRefundNo(refundNotification.getOutRefundNo())
|
|
|
.setChannel(refundNotification.getChannel().name())
|
|
|
.setUserReceivedAccount(refundNotification.getUserReceivedAccount())
|
|
|
.setSuccessTime(successTime)
|
|
|
@@ -401,14 +451,23 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
.setRefund(refundNotification.getAmount().getRefund().intValue())
|
|
|
.setCurrency(refundNotification.getAmount().getCurrency());
|
|
|
refundLog.setCreateTime(LocalDateTimeUtil.of(DateUtil.parse(refundNotification.getCreateTime())));
|
|
|
- refundLogService.save(refundLog);
|
|
|
+ refundLogService.updateById(refundLog);
|
|
|
+
|
|
|
if ("SUCCESS".equals(refundNotification.getRefundStatus().name())) {
|
|
|
- // 退款成功将冻结余额清零,新增退款钱包流水,更新退款流水
|
|
|
- accountService.lambdaUpdate().setSql("frozen_amount = 0").eq(Account::getUserId, walletDetail.getUserId());
|
|
|
+ // 冻结金额扣减此次退款金额
|
|
|
+ accountService.lambdaUpdate().setSql(" frozen_amount = (frozen_amount - %d)".formatted(refundNotification.getAmount().getRefund().intValue())).eq(Account::getUserId, refundLog.getUserId());
|
|
|
+ // 更新资金流水
|
|
|
+ var walletDetail = walletDetailService.getWalletDetailByOrderNo(refundNotification.getOutTradeNo(), WalletDetail.TYPE_提现);
|
|
|
+ walletDetail.setStatus(WalletDetail.STATUS_已确认) //已确认
|
|
|
+ .setCurrency(refundNotification.getAmount().getCurrency())
|
|
|
+ .setAmount(refundNotification.getAmount().getRefund().intValue())
|
|
|
+ .setTransactionTime(successTime);
|
|
|
+ walletDetailService.updateById(walletDetail);
|
|
|
+
|
|
|
LOGGER.info("微信退款回调{}:业务处理结束", notifyRes[2]);
|
|
|
} else {
|
|
|
// 退款失败
|
|
|
- LOGGER.error("微信退款失败,用户id:{},退款状态:{} \n 退款结果通知详情:{}", walletDetail.getUserId(), refundNotification.getRefundStatus().name(), refundNotification);
|
|
|
+ LOGGER.error("微信退款失败,用户id:{},退款状态:{} \n 退款结果通知详情:{}", refundLog.getUserId(), refundNotification.getRefundStatus().name(), refundNotification);
|
|
|
throw new BusinessException("处理异常");
|
|
|
}
|
|
|
} catch (ValidationException e) {
|