|
|
@@ -0,0 +1,247 @@
|
|
|
+package com.zsElectric.boot.core.pay;
|
|
|
+
|
|
|
+import com.google.gson.Gson;
|
|
|
+import com.google.gson.JsonObject;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.auth.*;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
|
|
+import com.zsElectric.boot.common.util.StringUtils;
|
|
|
+import jakarta.annotation.PostConstruct;
|
|
|
+import jakarta.servlet.http.HttpServletRequest;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.http.client.methods.CloseableHttpResponse;
|
|
|
+import org.apache.http.client.methods.HttpGet;
|
|
|
+import org.apache.http.client.methods.HttpPost;
|
|
|
+import org.apache.http.client.utils.URIBuilder;
|
|
|
+import org.apache.http.entity.StringEntity;
|
|
|
+import org.apache.http.impl.client.CloseableHttpClient;
|
|
|
+import org.apache.http.util.EntityUtils;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.io.BufferedReader;
|
|
|
+import java.io.FileInputStream;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.PrivateKey;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+public class WechatPayV3Utils {
|
|
|
+
|
|
|
+ private CloseableHttpClient httpClient;
|
|
|
+ private Verifier verifier;
|
|
|
+ private PrivateKey merchantPrivateKey;
|
|
|
+ private Gson gson = new Gson();
|
|
|
+
|
|
|
+
|
|
|
+ // 初始化证书和客户端
|
|
|
+ @PostConstruct
|
|
|
+ public void init() {
|
|
|
+ try {
|
|
|
+ // 加载商户私钥
|
|
|
+ setMerchantPrivateKey();
|
|
|
+ // 初始化验证器
|
|
|
+ setVerifier();
|
|
|
+ // 初始化HTTP客户端
|
|
|
+ setHttpClient();
|
|
|
+ log.info("微信支付初始化成功");
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("微信支付初始化失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载商户私钥
|
|
|
+ */
|
|
|
+ private void setMerchantPrivateKey() {
|
|
|
+ try {
|
|
|
+ // 如果WECHAT_MCH_PRIVATE_KEY是文件路径
|
|
|
+ if (StringUtils.isNotEmpty(WechatConstants.WECHAT_MCH_PRIVATE_KEY)) {
|
|
|
+ // 如果是文件路径,从文件加载
|
|
|
+ if (WechatConstants.WECHAT_MCH_PRIVATE_KEY.endsWith(".pem")) {
|
|
|
+ InputStream inputStream = new FileInputStream(WechatConstants.WECHAT_MCH_PRIVATE_KEY);
|
|
|
+ this.merchantPrivateKey = PemUtil.loadPrivateKey(inputStream);
|
|
|
+ } else {
|
|
|
+ // 如果是私钥内容字符串,直接从字符串加载
|
|
|
+ this.merchantPrivateKey = PemUtil.loadPrivateKey(WechatConstants.WECHAT_MCH_PRIVATE_KEY);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.warn("微信商户私钥未配置");
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("加载商户私钥失败", e);
|
|
|
+ throw new RuntimeException("加载商户私钥失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取微信证书
|
|
|
+ *
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ private void setVerifier() throws Exception {
|
|
|
+ if (merchantPrivateKey == null) {
|
|
|
+ setMerchantPrivateKey();
|
|
|
+ }
|
|
|
+ // 获取证书管理器实例
|
|
|
+ CertificatesManager certificatesManager = CertificatesManager.getInstance();
|
|
|
+ // 向证书管理器增加需要自动更新平台证书的商户信息
|
|
|
+ certificatesManager.putMerchant(WechatConstants.WECHAT_MCH_ID,
|
|
|
+ new WechatPay2Credentials(WechatConstants.WECHAT_MCH_ID, new PrivateKeySigner(WechatConstants.WECHAT_MCH_SERIAL_NUM, merchantPrivateKey)),
|
|
|
+ WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(StandardCharsets.UTF_8));
|
|
|
+ // ... 若有多个商户号,可继续调用putMerchant添加商户信息
|
|
|
+
|
|
|
+ // 从证书管理器中获取verifier
|
|
|
+ verifier = certificatesManager.getVerifier(WechatConstants.WECHAT_MCH_ID);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建请求客户端
|
|
|
+ *
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ private void setHttpClient() throws Exception {
|
|
|
+ if (verifier == null) {
|
|
|
+ setVerifier();
|
|
|
+ }
|
|
|
+ WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
|
|
+ .withMerchant(WechatConstants.WECHAT_MCH_ID, WechatConstants.WECHAT_MCH_SERIAL_NUM, merchantPrivateKey)
|
|
|
+ .withValidator(response -> true);
|
|
|
+ // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
|
|
|
+
|
|
|
+ httpClient = builder.build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送POST请求
|
|
|
+ *
|
|
|
+ * @param url 请求地址
|
|
|
+ * @param params json参数
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public JsonObject sendPost(String url, JsonObject params) {
|
|
|
+ try {
|
|
|
+ if (httpClient == null) {
|
|
|
+ setHttpClient();
|
|
|
+ }
|
|
|
+ HttpPost httpPost = new HttpPost(url);
|
|
|
+ httpPost.addHeader("Accept", "application/json");
|
|
|
+ httpPost.addHeader("Content-type", "application/json; charset=utf-8");
|
|
|
+ httpPost.setEntity(new StringEntity(gson.toJson(params), StandardCharsets.UTF_8));
|
|
|
+ CloseableHttpResponse response = httpClient.execute(httpPost);
|
|
|
+ String bodyAsString = EntityUtils.toString(response.getEntity());
|
|
|
+ log.info("微信返回的内容:" + bodyAsString);
|
|
|
+ if (StringUtils.isEmpty(bodyAsString)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return gson.fromJson(bodyAsString, JsonObject.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("微信支付V3请求失败");
|
|
|
+ e.printStackTrace();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送get请求
|
|
|
+ *
|
|
|
+ * @param url 请求地址 参数直接在地址上拼接
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public JsonObject sendGet(String url) {
|
|
|
+ try {
|
|
|
+ if (httpClient == null) {
|
|
|
+ setHttpClient();
|
|
|
+ }
|
|
|
+ URIBuilder uriBuilder = new URIBuilder(url);
|
|
|
+ HttpGet httpGet = new HttpGet(uriBuilder.build());
|
|
|
+ httpGet.addHeader("Accept", "application/json");
|
|
|
+ CloseableHttpResponse response = httpClient.execute(httpGet);
|
|
|
+ String bodyAsString = EntityUtils.toString(response.getEntity());
|
|
|
+ log.info("微信返回的内容:" + bodyAsString);
|
|
|
+ if (StringUtils.isEmpty(bodyAsString)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return gson.fromJson(bodyAsString, JsonObject.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("微信支付V3请求失败");
|
|
|
+ e.printStackTrace();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 回调通知验签与解密
|
|
|
+ *
|
|
|
+ * @param request
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public JsonObject getCallbackData(HttpServletRequest request) {
|
|
|
+ try {
|
|
|
+ if (verifier == null) {
|
|
|
+ setVerifier();
|
|
|
+ }
|
|
|
+ String wechatPaySerial = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SERIAL);
|
|
|
+ String nonce = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_NONCE);
|
|
|
+ String timestamp = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP);
|
|
|
+ String signature = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE);
|
|
|
+ String body;
|
|
|
+ BufferedReader reader = request.getReader();
|
|
|
+ String line ;
|
|
|
+ StringBuilder inputString = new StringBuilder();
|
|
|
+ while ((line = reader.readLine()) != null) {
|
|
|
+ inputString.append(line);
|
|
|
+ }
|
|
|
+ body = inputString.toString();
|
|
|
+ log.info("body数据:"+body);
|
|
|
+ reader.close();
|
|
|
+ // 构建request,传入必要参数
|
|
|
+ NotificationRequest res = new NotificationRequest.Builder().withSerialNumber(wechatPaySerial)
|
|
|
+ .withNonce(nonce)
|
|
|
+ .withTimestamp(timestamp)
|
|
|
+ .withSignature(signature)
|
|
|
+ .withBody(body)
|
|
|
+ .build();
|
|
|
+ NotificationHandler handler = new NotificationHandler(verifier, WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(StandardCharsets.UTF_8));
|
|
|
+ // 验签和解析请求体
|
|
|
+ Notification notification = handler.parse(res);
|
|
|
+ log.info("回调通知数据:" + notification.toString());
|
|
|
+ // 解析开数据
|
|
|
+ String decryptData = notification.getDecryptData();
|
|
|
+ log.info("回调解析数据:" + decryptData);
|
|
|
+ if (StringUtils.isEmpty(decryptData)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return gson.fromJson(decryptData, JsonObject.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("微信支付V3回调失败");
|
|
|
+ e.printStackTrace();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信支付v3签名 RSA签名
|
|
|
+ *
|
|
|
+ * @param message 要签名的字符串
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public String signRSA(String message) {
|
|
|
+ try {
|
|
|
+ if (merchantPrivateKey == null) {
|
|
|
+ setMerchantPrivateKey();
|
|
|
+ }
|
|
|
+ Signer signer = new PrivateKeySigner(WechatConstants.WECHAT_MCH_SERIAL_NUM, merchantPrivateKey);
|
|
|
+ Signer.SignatureResult signature = signer.sign(message.getBytes(StandardCharsets.UTF_8));
|
|
|
+ return signature.getSign();
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|