|
|
@@ -0,0 +1,203 @@
|
|
|
+package com.kym.service.wechat.impl;
|
|
|
+
|
|
|
+import com.kym.common.config.WxPayConfig;
|
|
|
+import com.kym.common.utils.OrderUtils;
|
|
|
+import com.kym.entity.miniapp.PayLog;
|
|
|
+import com.kym.service.miniapp.PayLogService;
|
|
|
+import com.kym.service.miniapp.WalletDetailService;
|
|
|
+import com.kym.service.wechat.WxPayService;
|
|
|
+import com.wechat.pay.java.core.Config;
|
|
|
+import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
|
|
+import com.wechat.pay.java.core.exception.ValidationException;
|
|
|
+import com.wechat.pay.java.core.notification.NotificationConfig;
|
|
|
+import com.wechat.pay.java.core.notification.NotificationParser;
|
|
|
+import com.wechat.pay.java.core.notification.RequestParam;
|
|
|
+import com.wechat.pay.java.service.payments.jsapi.JsapiService;
|
|
|
+import com.wechat.pay.java.service.payments.jsapi.model.*;
|
|
|
+import com.wechat.pay.java.service.payments.model.Transaction;
|
|
|
+import jakarta.annotation.PostConstruct;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.http.HttpStatus;
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.io.BufferedReader;
|
|
|
+import java.io.IOException;
|
|
|
+import java.math.BigDecimal;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author skyline
|
|
|
+ * @description 微信支付
|
|
|
+ * @date 2023-08-10 14:03
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class WxPayServiceImpl implements WxPayService {
|
|
|
+
|
|
|
+ private static final Logger LOGGER = LoggerFactory.getLogger(WxPayServiceImpl.class);
|
|
|
+
|
|
|
+ public static JsapiService service;
|
|
|
+
|
|
|
+ public static Config config;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WxPayConfig conf;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WalletDetailService walletDetailService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private PayLogService payLogService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 商户订单号查询订单
|
|
|
+ */
|
|
|
+ public static Transaction queryOrderByOutTradeNo() {
|
|
|
+
|
|
|
+ QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
|
|
|
+ // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
+ // 调用接口
|
|
|
+ return service.queryOrderByOutTradeNo(request);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @PostConstruct
|
|
|
+ void init() {
|
|
|
+ config = new RSAAutoCertificateConfig.Builder()
|
|
|
+ .merchantId(conf.getMchid()) // 商户号
|
|
|
+ .privateKeyFromPath(conf.getKeyPath()) // 商户API私钥路径
|
|
|
+ .merchantSerialNumber(conf.getMchsn()) // 商户证书序列号
|
|
|
+ .apiV3Key(conf.getV3key()) // 商户APIV3密钥
|
|
|
+ .build();
|
|
|
+ service = new JsapiService.Builder().config(config).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * JSAPI支付下单
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public PrepayResponse prepay(BigDecimal rechargeAmount, String openid) {
|
|
|
+ // 生成订单号
|
|
|
+ String outTradeNo = OrderUtils.getOrderNo();
|
|
|
+ // request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
+ PrepayRequest request = new PrepayRequest();
|
|
|
+ Amount amount = new Amount();
|
|
|
+ amount.setTotal(rechargeAmount.multiply(new BigDecimal(100)).intValue());
|
|
|
+ request.setAmount(amount);
|
|
|
+ request.setAppid(conf.getAppid());
|
|
|
+ request.setMchid(conf.getMchid());
|
|
|
+ request.setDescription("K7充电生活充值");
|
|
|
+ request.setNotifyUrl(conf.getNotifyUrl());
|
|
|
+ request.setOutTradeNo(outTradeNo);
|
|
|
+ Payer payer = new Payer();
|
|
|
+ payer.setOpenid(openid);
|
|
|
+ request.setPayer(payer);
|
|
|
+ return service.prepay(request);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信支付订单号查询订单
|
|
|
+ */
|
|
|
+ public Transaction queryOrderById(String transactionId) {
|
|
|
+ QueryOrderByIdRequest request = new QueryOrderByIdRequest();
|
|
|
+ // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
+ request.setMchid(conf.getMchid());
|
|
|
+ request.setTransactionId(transactionId);
|
|
|
+ // 调用接口
|
|
|
+ return service.queryOrderById(request);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 关闭订单
|
|
|
+ */
|
|
|
+ public void closeOrder(String outTradeNo) {
|
|
|
+ CloseOrderRequest request = new CloseOrderRequest();
|
|
|
+ // 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
|
|
|
+ request.setMchid(conf.getMchid());
|
|
|
+ request.setOutTradeNo(outTradeNo);
|
|
|
+ // 调用接口
|
|
|
+ service.closeOrder(request);
|
|
|
+ }
|
|
|
+
|
|
|
+ public ResponseEntity.BodyBuilder wxNotify(HttpServletRequest request) throws IOException {
|
|
|
+ var signature = request.getHeader("Wechatpay-Signature");
|
|
|
+ var serial = request.getHeader("Wechatpay-Serial");
|
|
|
+ var nonce = request.getHeader("Wechatpay-Nonce");
|
|
|
+ var timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
|
+ var signatureType = request.getHeader("Wechatpay-Signature-Type");
|
|
|
+
|
|
|
+ // request中获取body
|
|
|
+ BufferedReader br = request.getReader();
|
|
|
+ String str;
|
|
|
+ var requestBody = new StringBuilder();
|
|
|
+ while ((str = br.readLine()) != null) {
|
|
|
+ requestBody.append(str);
|
|
|
+
|
|
|
+
|
|
|
+ // 构造 RequestParam
|
|
|
+ RequestParam requestParam = new RequestParam.Builder()
|
|
|
+ .serialNumber(serial)
|
|
|
+ .nonce(nonce) // 随机数
|
|
|
+ .signature(signature)
|
|
|
+ .timestamp(timestamp)
|
|
|
+ .body(requestBody.toString())
|
|
|
+ .build();
|
|
|
+
|
|
|
+
|
|
|
+ // 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
|
|
|
+ // 初始化 NotificationParser
|
|
|
+ NotificationParser parser = new NotificationParser((NotificationConfig) config);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 以支付通知回调为例,验签、解密并转换成 Transaction
|
|
|
+ Transaction transaction = parser.parse(requestParam, Transaction.class);
|
|
|
+ // 判断是否已经接收处理过通知
|
|
|
+ if (payLogService.getOne(payLogService.lambdaQuery().eq(PayLog::getOutTradeNo, transaction.getOutTradeNo())) != null) {
|
|
|
+ return ResponseEntity.status(HttpStatus.OK);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 资金流水
|
|
|
+ var walletDetail = walletDetailService.getWalletDetailByOrderNo(transaction.getOutTradeNo());
|
|
|
+ if (walletDetail != null) {
|
|
|
+ walletDetail.setType(1); // 充值
|
|
|
+ walletDetail.setStatus(1); //已确认
|
|
|
+ walletDetail.setCurrency(transaction.getAmount().getCurrency());
|
|
|
+ walletDetail.setAmount(transaction.getAmount().getTotal());
|
|
|
+
|
|
|
+ // 支付记录
|
|
|
+ var payLog = new PayLog();
|
|
|
+ payLog.setUserId(walletDetail.getUserId());
|
|
|
+ payLog.setOpenid(transaction.getPayer().getOpenid());
|
|
|
+ payLog.setBankType(transaction.getBankType());
|
|
|
+ payLog.setMchId(transaction.getMchid());
|
|
|
+ payLog.setOutTradeNo(transaction.getOutTradeNo());
|
|
|
+ payLog.setTransactionId(transaction.getTransactionId());
|
|
|
+ payLog.setSuccessTime(transaction.getSuccessTime());
|
|
|
+ payLog.setTradeType(transaction.getTradeType().name());
|
|
|
+ payLog.setTradeState(transaction.getTradeState().name());
|
|
|
+ payLog.setAttach(transaction.getAttach());
|
|
|
+ payLog.setTotal(transaction.getAmount().getTotal());
|
|
|
+ payLog.setCurrency(transaction.getAmount().getCurrency());
|
|
|
+ payLog.setPayerTotal(transaction.getAmount().getPayerTotal());
|
|
|
+ payLog.setPayerCurrency(transaction.getAmount().getPayerCurrency());
|
|
|
+ payLogService.save(payLog);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ LOGGER.error("微信支付通知处理异常,资金流水为空,回调信息:{}", transaction);
|
|
|
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (ValidationException e) {
|
|
|
+ // 签名验证失败,返回 401 UNAUTHORIZED 状态码
|
|
|
+ LOGGER.error("微信支付通知眼前失败", e);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理成功,返回 200 OK 状态码
|
|
|
+ return ResponseEntity.status(HttpStatus.OK);
|
|
|
+ }
|
|
|
+}
|