|
@@ -13,10 +13,9 @@ import com.kym.common.exception.BusinessException;
|
|
|
import com.kym.common.utils.OrderUtils;
|
|
import com.kym.common.utils.OrderUtils;
|
|
|
import com.kym.entity.miniapp.Account;
|
|
import com.kym.entity.miniapp.Account;
|
|
|
import com.kym.entity.miniapp.PayLog;
|
|
import com.kym.entity.miniapp.PayLog;
|
|
|
|
|
+import com.kym.entity.miniapp.RefundLog;
|
|
|
import com.kym.entity.miniapp.WalletDetail;
|
|
import com.kym.entity.miniapp.WalletDetail;
|
|
|
-import com.kym.service.miniapp.AccountService;
|
|
|
|
|
-import com.kym.service.miniapp.PayLogService;
|
|
|
|
|
-import com.kym.service.miniapp.WalletDetailService;
|
|
|
|
|
|
|
+import com.kym.service.miniapp.*;
|
|
|
import com.kym.service.wechat.WxPayService;
|
|
import com.kym.service.wechat.WxPayService;
|
|
|
import com.wechat.pay.java.core.Config;
|
|
import com.wechat.pay.java.core.Config;
|
|
|
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
|
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
|
@@ -37,8 +36,6 @@ import lombok.SneakyThrows;
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.core.io.ClassPathResource;
|
|
import org.springframework.core.io.ClassPathResource;
|
|
|
-import org.springframework.http.HttpStatus;
|
|
|
|
|
-import org.springframework.http.ResponseEntity;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
@@ -74,11 +71,29 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
|
|
|
|
|
private final AccountService accountService;
|
|
private final AccountService accountService;
|
|
|
|
|
|
|
|
- public WxPayServiceImpl(WxPayConfig conf, WalletDetailService walletDetailService, PayLogService payLogService, AccountService accountService) {
|
|
|
|
|
|
|
+ private final ChargeOrderService chargeOrderService;
|
|
|
|
|
+
|
|
|
|
|
+ private final RefundLogService refundLogService;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ public WxPayServiceImpl(WxPayConfig conf, WalletDetailService walletDetailService, PayLogService payLogService, AccountService accountService, ChargeOrderService chargeOrderService, RefundLogService refundLogService) {
|
|
|
this.conf = conf;
|
|
this.conf = conf;
|
|
|
this.walletDetailService = walletDetailService;
|
|
this.walletDetailService = walletDetailService;
|
|
|
this.payLogService = payLogService;
|
|
this.payLogService = payLogService;
|
|
|
this.accountService = accountService;
|
|
this.accountService = accountService;
|
|
|
|
|
+ this.chargeOrderService = chargeOrderService;
|
|
|
|
|
+ this.refundLogService = refundLogService;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 商户订单号查询订单
|
|
|
|
|
+ */
|
|
|
|
|
+ public static Transaction queryOrderByOutTradeNo() {
|
|
|
|
|
+
|
|
|
|
|
+ QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
|
|
|
|
|
+ // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
|
|
+ // 调用接口
|
|
|
|
|
+ return jsapiService.queryOrderByOutTradeNo(request);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -106,19 +121,6 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
refundService = new RefundService.Builder().config(config).build();
|
|
refundService = new RefundService.Builder().config(config).build();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 商户订单号查询订单
|
|
|
|
|
- */
|
|
|
|
|
- public static Transaction queryOrderByOutTradeNo() {
|
|
|
|
|
-
|
|
|
|
|
- QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
|
|
|
|
|
- // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
|
|
- // 调用接口
|
|
|
|
|
- return jsapiService.queryOrderByOutTradeNo(request);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
@SneakyThrows
|
|
@SneakyThrows
|
|
|
Object[] handleWxNotify(HttpServletRequest request) {
|
|
Object[] handleWxNotify(HttpServletRequest request) {
|
|
|
var no = RandomUtil.randomInt(1000, 9999);
|
|
var no = RandomUtil.randomInt(1000, 9999);
|
|
@@ -156,8 +158,6 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
return new Object[]{requestParam, parser, no};
|
|
return new Object[]{requestParam, parser, no};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* JSAPI支付下单
|
|
* JSAPI支付下单
|
|
|
*/
|
|
*/
|
|
@@ -171,7 +171,7 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
var userId = StpUtil.getLoginIdAsLong();
|
|
var userId = StpUtil.getLoginIdAsLong();
|
|
|
// 生成订单号
|
|
// 生成订单号
|
|
|
String outTradeNo = OrderUtils.getOrderNo();
|
|
String outTradeNo = OrderUtils.getOrderNo();
|
|
|
- // 创建支付记录
|
|
|
|
|
|
|
+ // 创建钱包流水
|
|
|
var walletDetail = new WalletDetail()
|
|
var walletDetail = new WalletDetail()
|
|
|
.setType(WalletDetail.TYPE_充值)
|
|
.setType(WalletDetail.TYPE_充值)
|
|
|
.setStatus(WalletDetail.STATUS_待确认)
|
|
.setStatus(WalletDetail.STATUS_待确认)
|
|
@@ -198,6 +198,8 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
return service.prepayWithRequestPayment(request);
|
|
return service.prepayWithRequestPayment(request);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // TODO: 2023-09-11 判断退款金额需要几笔充值订单进行退款操作 这个放在外层做,这里只支持单笔订单退款
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 微信支付订单号查询订单
|
|
* 微信支付订单号查询订单
|
|
|
*/
|
|
*/
|
|
@@ -212,6 +214,8 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 关闭订单
|
|
* 关闭订单
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param outTradeNo
|
|
|
*/
|
|
*/
|
|
|
public void closeOrder(String outTradeNo) {
|
|
public void closeOrder(String outTradeNo) {
|
|
|
CloseOrderRequest request = new CloseOrderRequest();
|
|
CloseOrderRequest request = new CloseOrderRequest();
|
|
@@ -240,14 +244,14 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
LOGGER.info("微信支付回调{}:验签解密完毕,数据:\n{}", notifyRes[2], transaction);
|
|
LOGGER.info("微信支付回调{}:验签解密完毕,数据:\n{}", notifyRes[2], transaction);
|
|
|
// 判断是否已经接收处理过通知
|
|
// 判断是否已经接收处理过通知
|
|
|
if (payLogService.lambdaQuery().eq(PayLog::getOutTradeNo, transaction.getOutTradeNo()).one() != null) {
|
|
if (payLogService.lambdaQuery().eq(PayLog::getOutTradeNo, transaction.getOutTradeNo()).one() != null) {
|
|
|
- return ;
|
|
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DateTime dt = DateUtil.parse(transaction.getSuccessTime());
|
|
DateTime dt = DateUtil.parse(transaction.getSuccessTime());
|
|
|
LocalDateTime successTime = LocalDateTimeUtil.of(dt);
|
|
LocalDateTime successTime = LocalDateTimeUtil.of(dt);
|
|
|
|
|
|
|
|
- // 资金流水
|
|
|
|
|
- var walletDetail = walletDetailService.getWalletDetailByOrderNo(transaction.getOutTradeNo());
|
|
|
|
|
|
|
+ // 钱包流水
|
|
|
|
|
+ var walletDetail = walletDetailService.getWalletDetailByOrderNo(transaction.getOutTradeNo(), WalletDetail.TYPE_充值);
|
|
|
if (walletDetail != null) {
|
|
if (walletDetail != null) {
|
|
|
walletDetail.setStatus(WalletDetail.STATUS_已确认); //已确认
|
|
walletDetail.setStatus(WalletDetail.STATUS_已确认); //已确认
|
|
|
walletDetail.setCurrency(transaction.getAmount().getCurrency());
|
|
walletDetail.setCurrency(transaction.getAmount().getCurrency());
|
|
@@ -291,7 +295,6 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* 创建退款申请
|
|
* 创建退款申请
|
|
|
*
|
|
*
|
|
@@ -299,13 +302,38 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
*/
|
|
*/
|
|
|
@Override
|
|
@Override
|
|
|
public Refund wxRefund(JSONObject params) {
|
|
public Refund wxRefund(JSONObject params) {
|
|
|
|
|
+ // 查询用户是否有未完结的订单,如果有则提示等待订单完成后再申请,如果无则查询可用余额,将金额转至冻结金额
|
|
|
|
|
+ // 生成订单号
|
|
|
|
|
+ String outRefundNo = OrderUtils.getOrderNo();
|
|
|
|
|
+
|
|
|
|
|
+ var userId = StpUtil.getLoginIdAsLong();
|
|
|
|
|
+ var chargeOrder = chargeOrderService.getChargingOrderByUserId(userId);
|
|
|
|
|
+ if (chargeOrder != null) {
|
|
|
|
|
+ throw new BusinessException("存在未完结的订单,请等待所有订单完结之后重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ var account = accountService.getAccountByUserId(userId);
|
|
|
|
|
+ if (account.getBalance() <= 0) {
|
|
|
|
|
+ throw new BusinessException("账户余额不足,无需退款");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 将余额转移至冻结余额
|
|
|
|
|
+ accountService.lambdaUpdate().setSql(" frozen_amount = balance ,balance = 0").eq(Account::getUserId, userId);
|
|
|
|
|
+
|
|
|
|
|
+ // 钱包流水
|
|
|
|
|
+ var walletDetail = new WalletDetail()
|
|
|
|
|
+ .setType(WalletDetail.TYPE_提现)
|
|
|
|
|
+ .setStatus(WalletDetail.STATUS_待确认)
|
|
|
|
|
+ .setUserId(userId)
|
|
|
|
|
+ .setAmount(params.getIntValue("amount"))
|
|
|
|
|
+ .setOrderNo(outRefundNo);
|
|
|
|
|
+ walletDetailService.save(walletDetail);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
CreateRequest request = new CreateRequest();
|
|
CreateRequest request = new CreateRequest();
|
|
|
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
// 原始第三方订单号
|
|
// 原始第三方订单号
|
|
|
- // TODO: 2023-09-05 填入业务数据
|
|
|
|
|
- request.setOutTradeNo("");
|
|
|
|
|
|
|
+ request.setOutTradeNo(params.getString("outTradeNo"));
|
|
|
// 退款订单号
|
|
// 退款订单号
|
|
|
- request.setOutRefundNo("");
|
|
|
|
|
|
|
+ request.setOutRefundNo(outRefundNo);
|
|
|
// 退款原因
|
|
// 退款原因
|
|
|
request.setReason("用户申请退款");
|
|
request.setReason("用户申请退款");
|
|
|
// 回调通知地址
|
|
// 回调通知地址
|
|
@@ -332,34 +360,61 @@ public class WxPayServiceImpl implements WxPayService {
|
|
|
return refundService.queryByOutRefundNo(request);
|
|
return refundService.queryByOutRefundNo(request);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 微信退款结果通知
|
|
* 微信退款结果通知
|
|
|
*
|
|
*
|
|
|
* @param request
|
|
* @param request
|
|
|
- * @return
|
|
|
|
|
*/
|
|
*/
|
|
|
@SneakyThrows
|
|
@SneakyThrows
|
|
|
@Override
|
|
@Override
|
|
|
- public ResponseEntity.BodyBuilder wxRefundNotify(HttpServletRequest request) {
|
|
|
|
|
|
|
+ public void wxRefundNotify(HttpServletRequest request) {
|
|
|
var notifyRes = handleWxNotify(request);
|
|
var notifyRes = handleWxNotify(request);
|
|
|
try {
|
|
try {
|
|
|
// 以支付通知回调为例,验签、解密并转换成 RefundNotification
|
|
// 以支付通知回调为例,验签、解密并转换成 RefundNotification
|
|
|
RefundNotification refundNotification = ((NotificationParser) notifyRes[1]).parse((RequestParam) notifyRes[0], RefundNotification.class);
|
|
RefundNotification refundNotification = ((NotificationParser) notifyRes[1]).parse((RequestParam) notifyRes[0], RefundNotification.class);
|
|
|
- // TODO: 2023-09-05 处理业务逻辑
|
|
|
|
|
|
|
+ LOGGER.info("微信退款回调{}:验签解密完毕,数据:\n{}", notifyRes[2], refundNotification);
|
|
|
|
|
+ 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())
|
|
|
|
|
+ .setRefundId(refundNotification.getRefundId())
|
|
|
|
|
+ .setTransactionId(refundNotification.getTransactionId())
|
|
|
|
|
+ .setOutTradeNo(walletDetail.getOrderNo())
|
|
|
|
|
+ .setOutRefundNo(refundNotification.getOutRefundNo())
|
|
|
|
|
+ .setChannel(refundNotification.getChannel().name())
|
|
|
|
|
+ .setUserReceivedAccount(refundNotification.getUserReceivedAccount())
|
|
|
|
|
+ .setSuccessTime(successTime)
|
|
|
|
|
+ .setStatus(refundNotification.getRefundStatus().name())
|
|
|
|
|
+ .setFundsAccount(refundNotification.getFundsAccount().name())
|
|
|
|
|
+ .setTotal(refundNotification.getAmount().getTotal().intValue())
|
|
|
|
|
+ .setRefund(refundNotification.getAmount().getRefund().intValue())
|
|
|
|
|
+ .setCurrency(refundNotification.getAmount().getCurrency());
|
|
|
|
|
+ refundLog.setCreateTime(LocalDateTimeUtil.of(DateUtil.parse(refundNotification.getCreateTime())));
|
|
|
|
|
+ refundLogService.save(refundLog);
|
|
|
|
|
+ if ("SUCCESS".equals(refundNotification.getRefundStatus().name())) {
|
|
|
|
|
+ // 退款成功将冻结余额清零,新增退款钱包流水,更新退款流水
|
|
|
|
|
+ accountService.lambdaUpdate().setSql("frozen_amount = 0").eq(Account::getUserId, walletDetail.getUserId());
|
|
|
|
|
+ LOGGER.info("微信退款回调{}:业务处理结束", notifyRes[2]);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 退款失败
|
|
|
|
|
+ LOGGER.error("微信退款失败,用户id:{},退款状态:{} \n 退款结果通知详情:{}", walletDetail.getUserId(), refundNotification.getRefundStatus().name(), refundNotification);
|
|
|
|
|
+ throw new BusinessException("处理异常");
|
|
|
|
|
+ }
|
|
|
} catch (ValidationException e) {
|
|
} catch (ValidationException e) {
|
|
|
// 签名验证失败,返回 401 UNAUTHORIZED 状态码
|
|
// 签名验证失败,返回 401 UNAUTHORIZED 状态码
|
|
|
- LOGGER.error("sign verification failed", e);
|
|
|
|
|
- return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 如果处理失败,应返回 4xx/5xx 的状态码,例如 500 INTERNAL_SERVER_ERROR
|
|
|
|
|
- if (false) {
|
|
|
|
|
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
|
|
|
|
|
+ LOGGER.error("微信退款通知验签失败", e);
|
|
|
|
|
+ throw new BusinessException("验签失败");
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // 处理成功,返回 200 OK 状态码
|
|
|
|
|
- return ResponseEntity.status(HttpStatus.OK);
|
|
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|