From 94278307c7bb6fb9e47650e73691f04a54513854 Mon Sep 17 00:00:00 2001
From: KKSU <15274802129@163.com>
Date: Wed, 17 Jan 2024 14:31:23 +0800
Subject: [PATCH] fapiao
---
src/main/java/cc/mrbird/febs/pay/controller/XcxPayController.java | 3
src/main/java/cc/mrbird/febs/pay/model/FPCertificates.java | 13 ++
src/main/java/cc/mrbird/febs/pay/service/WxFaPiaoService.java | 3
src/main/java/cc/mrbird/febs/pay/model/FPCertificateVo.java | 10 +
src/main/java/cc/mrbird/febs/pay/model/FPEncryptCertificate.java | 11 +
src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java | 280 ++++++++++++++-------------------------------
6 files changed, 127 insertions(+), 193 deletions(-)
diff --git a/src/main/java/cc/mrbird/febs/pay/controller/XcxPayController.java b/src/main/java/cc/mrbird/febs/pay/controller/XcxPayController.java
index 8ec360a..5486a11 100644
--- a/src/main/java/cc/mrbird/febs/pay/controller/XcxPayController.java
+++ b/src/main/java/cc/mrbird/febs/pay/controller/XcxPayController.java
@@ -42,6 +42,7 @@
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
+import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -253,7 +254,7 @@
*/
@Transactional(rollbackFor = Exception.class)
@RequestMapping(value = "/fapiaoCallBack")
- public Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ public Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, ParseException {
return wxFaPiaoService.fapiaoCallBack(request,requestBody);
}
diff --git a/src/main/java/cc/mrbird/febs/pay/model/FPCertificateVo.java b/src/main/java/cc/mrbird/febs/pay/model/FPCertificateVo.java
new file mode 100644
index 0000000..2490f68
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/pay/model/FPCertificateVo.java
@@ -0,0 +1,10 @@
+package cc.mrbird.febs.pay.model;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FPCertificateVo {
+ private List<FPCertificates> data;
+}
diff --git a/src/main/java/cc/mrbird/febs/pay/model/FPCertificates.java b/src/main/java/cc/mrbird/febs/pay/model/FPCertificates.java
new file mode 100644
index 0000000..07d8e95
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/pay/model/FPCertificates.java
@@ -0,0 +1,13 @@
+package cc.mrbird.febs.pay.model;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class FPCertificates {
+ private String serial_no;
+ private String effective_time;
+ private String expire_time;
+ private FPEncryptCertificate encrypt_certificate;
+}
diff --git a/src/main/java/cc/mrbird/febs/pay/model/FPEncryptCertificate.java b/src/main/java/cc/mrbird/febs/pay/model/FPEncryptCertificate.java
new file mode 100644
index 0000000..fe4005a
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/pay/model/FPEncryptCertificate.java
@@ -0,0 +1,11 @@
+package cc.mrbird.febs.pay.model;
+
+import lombok.Data;
+
+@Data
+public class FPEncryptCertificate {
+ private String algorithm;
+ private String nonce;
+ private String associated_data;
+ private String ciphertext;
+}
diff --git a/src/main/java/cc/mrbird/febs/pay/service/WxFaPiaoService.java b/src/main/java/cc/mrbird/febs/pay/service/WxFaPiaoService.java
index f2a1d16..586286c 100644
--- a/src/main/java/cc/mrbird/febs/pay/service/WxFaPiaoService.java
+++ b/src/main/java/cc/mrbird/febs/pay/service/WxFaPiaoService.java
@@ -10,6 +10,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
+import java.text.ParseException;
import java.util.Map;
public interface WxFaPiaoService {
@@ -22,5 +23,5 @@
String sendPost(String url, String params, String token);
- Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException;
+ Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, ParseException;
}
diff --git a/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java b/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java
index f35b58f..db77555 100644
--- a/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java
@@ -5,6 +5,9 @@
import cc.mrbird.febs.common.utils.SpringContextHolder;
import cc.mrbird.febs.mall.entity.MallOrderInfo;
import cc.mrbird.febs.mall.mapper.MallOrderInfoMapper;
+import cc.mrbird.febs.pay.model.FPCertificateVo;
+import cc.mrbird.febs.pay.model.FPCertificates;
+import cc.mrbird.febs.pay.model.FPEncryptCertificate;
import cc.mrbird.febs.pay.model.HeaderDto;
import cc.mrbird.febs.pay.service.WxFaPiaoService;
import cc.mrbird.febs.pay.util.RandomStringGenerator;
@@ -50,8 +53,10 @@
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Base64;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -210,7 +215,7 @@
}
@Override
- public Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ public Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException, ParseException {
Map<String,Object> map = new HashMap<>();
String signature = request.getHeader("Wechatpay-Signature");
String timestamp = request.getHeader("Wechatpay-Timestamp");
@@ -224,20 +229,9 @@
log.info("头信息---平台证书序列号:" + serial);
log.info("获取到的body信息:" + body);
//验签
- boolean signCheck = verifySign(request, body);
-// boolean signCheck = signCheck(timestamp, nonce, requestBody, signature);
+ boolean signCheck = signCheck(timestamp, nonce, requestBody, signature);
log.info("验签结果:" + signCheck);
if (signCheck) {
-// //解密参数
-// Resource resource = com.alibaba.fastjson.JSONObject.parseObject(com.alibaba.fastjson.JSONObject.toJSONString(requestBody.get("resource")), Resource.class);
-// AesUtil aesUtil = new AesUtil(CommonParameters.apiV3Key.getBytes("utf-8"));
-// String string = aesUtil.decryptToString(resource.getAssociated_data().getBytes("utf-8"), resource.getNonce().getBytes("utf-8"), resource.getCiphertext());
-// ComplaintInfo complaintInfo = JSONObject.parseObject(string, ComplaintInfo.class);
-// //获取投诉详情
-// ComplaintDetail complaintDetail = CommonUtils.GetComplaintsInfo(complaintInfo.getComplaint_id());
-// data.put("code", "SUCCESS");
-// data.put("message", "成功");
-// return data;
try {
//解析请求体
// Resource resource = com.alibaba.fastjson.JSONObject.parseObject(com.alibaba.fastjson.JSONObject.toJSONString(requestBody.get("resource")), Resource.class);
@@ -287,198 +281,102 @@
return map;
}
-// /**
-// * 验证签名
-// *
-// * @param timestamp 微信平台传入的时间戳
-// * @param nonce 微信平台传入的随机字符串
-// * @param requestBody 微信平台传入的消息体
-// * @param signature 微信平台传入的签名
-// * @return
-// * @throws NoSuchAlgorithmException
-// * @throws SignatureException
-// * @throws IOException
-// * @throws InvalidKeyException
-// */
-// public static boolean signCheck(String timestamp, String nonce, Map<String, Object> requestBody, String signature) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
-// //构造验签名串
-// String signatureStr = timestamp + "\n" + nonce + "\n" + com.alibaba.fastjson.JSONObject.toJSONString(requestBody) + "\n";
-// // 加载SHA256withRSA签名器
-// Signature signer = Signature.getInstance("SHA256withRSA");
-// // 用微信平台公钥对签名器进行初始化(调上一节中的获取平台证书方法)
-// signer.initVerify(getCertificates());
-// // 把我们构造的验签名串更新到签名器中
-// signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
-// // 把请求头中微信服务器返回的签名用Base64解码 并使用签名器进行验证
-// boolean result = signer.verify(Base64Utils.decodeFromString(signature));
-// return result;
-// }
+ /**
+ * 验证签名
+ *
+ * @param timestamp 微信平台传入的时间戳
+ * @param nonce 微信平台传入的随机字符串
+ * @param requestBody 微信平台传入的消息体
+ * @param signature 微信平台传入的签名
+ * @return
+ * @throws NoSuchAlgorithmException
+ * @throws SignatureException
+ * @throws IOException
+ * @throws InvalidKeyException
+ */
+ public boolean signCheck(String timestamp, String nonce, Map<String, Object> requestBody, String signature) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException, ParseException {
+ //构造验签名串
+ String signatureStr = timestamp + "\n" + nonce + "\n" + com.alibaba.fastjson.JSONObject.toJSONString(requestBody) + "\n";
+ // 加载SHA256withRSA签名器
+ Signature signer = Signature.getInstance("SHA256withRSA");
+ // 用微信平台公钥对签名器进行初始化(调上一节中的获取平台证书方法)
+ signer.initVerify(getCertificates());
+ // 把我们构造的验签名串更新到签名器中
+ signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
+ // 把请求头中微信服务器返回的签名用Base64解码 并使用签名器进行验证
+ boolean result = signer.verify(Base64Utils.decodeFromString(signature));
+ return result;
+ }
/**
* 获取平台证书
*/
-// public static X509Certificate getCertificates() throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException {
-// SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
-// CloseableHttpClient httpClient = CommonUtils.httpClient();
-// //请求URL
-// HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/certificates");
-// httpGet.setHeader("Accept", "application/json");
-// //生成签名
-// httpGet.setHeader("Authorization ", SignUtils.getSign("GET", HttpUrl.parse("https://api.mch.weixin.qq.com/v3/certificates"), ""));
-// httpGet.setHeader("User-Agent", "https://zh.wikipedia.org/wiki/User_agent");
-// //完成签名并执行请求
-// CloseableHttpResponse response = httpClient.execute(httpGet);
-// X509Certificate x509Certificate = null;
-// try {
-// int statusCode = response.getStatusLine().getStatusCode();
-// if (statusCode == 200) { //处理成功
-//// System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
-// CertificateVo certificateVo = JSONObject.parseObject(EntityUtils.toString(response.getEntity()), CertificateVo.class);
-// for (Certificates certificates : certificateVo.getData()) {
-// if (format.parse(certificates.getEffective_time()).before(new Date()) && format.parse(certificates.getExpire_time()).after(new Date())) {
-// EncryptCertificate encrypt_certificate = certificates.getEncrypt_certificate();
-// //解密
-// AesUtil aesUtil = new AesUtil(CommonParameters.apiV3Key.getBytes("utf-8"));
-// String pulicKey = aesUtil.decryptToString(encrypt_certificate.getAssociated_data().getBytes("utf-8"), encrypt_certificate.getNonce().getBytes("utf-8"), encrypt_certificate.getCiphertext());
-//
-// //获取平台证书
-// final CertificateFactory cf = CertificateFactory.getInstance("X509");
-//
-// ByteArrayInputStream inputStream = new ByteArrayInputStream(pulicKey.getBytes(StandardCharsets.UTF_8));
-//
-// x509Certificate = (X509Certificate) cf.generateCertificate(inputStream);
-// }
-// }
-// return x509Certificate;
-// } else if (statusCode == 204) { //处理成功,无返回Body
-// System.out.println("success");
-// return x509Certificate;
-// } else {
-// System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
-// return x509Certificate;
-// }
-// } catch (GeneralSecurityException | ParseException e) {
-// e.printStackTrace();
-// return null;
-// } finally {
-// response.close();
-// CommonUtils.after(httpClient);
-// }
-// }
+ public X509Certificate getCertificates() throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException, ParseException {
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ CloseableHttpClient httpClient = HttpClients.createDefault();
- /**
- * 功能描述: 验证签名
- * 注意:使用微信支付平台公钥验签
- * Wechatpay-Signature 微信返签名
- * Wechatpay-Serial 微信平台证书序列号
- *
- * @return java.lang.String
- * @author 影子
- */
- @SneakyThrows
- public boolean verifySign(HttpServletRequest request,String body) {
- boolean verify = false;
+ PrivateKey privateKey = this.getPrivateKeyV3();
+ String baseUrl = "https://api.mch.weixin.qq.com";
+ String canonicalUrl = "/v3/certificates";
+ String postStr = null;
try {
- String wechatPaySignature = request.getHeader("Wechatpay-Signature");
- String wechatPayTimestamp = request.getHeader("Wechatpay-Timestamp");
- String wechatPayNonce = request.getHeader("Wechatpay-Nonce");
- String wechatPaySerial = request.getHeader("Wechatpay-Serial");
- //组装签名串
- String signStr = Stream.of(wechatPayTimestamp, wechatPayNonce, body)
- .collect(Collectors.joining("\n", "", "\n"));
- //获取平台证书
- AutoUpdateCertificatesVerifier verifier = getVerifier(wechatPaySerial);
- //获取失败 验证失败
- if (verifier != null) {
- Signature signature = Signature.getInstance("SHA256withRSA");
- signature.initVerify(verifier.getValidCertificate());
- //放入签名串
- signature.update(signStr.getBytes(StandardCharsets.UTF_8));
- verify = signature.verify(Base64.getDecoder().decode(wechatPaySignature.getBytes()));
- }
- } catch (InvalidKeyException e) {
- e.printStackTrace();
+ postStr = this.createAuthorization(
+ "GET",
+ baseUrl+canonicalUrl,
+ "",
+ privateKey
+
+ );
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
- return verify;
- }
-
- /**
- * 保存微信平台证书
- */
- private static final ConcurrentHashMap<String, AutoUpdateCertificatesVerifier> verifierMap = new ConcurrentHashMap<>();
-
- /**
- * 功能描述:获取平台证书,自动更新
- * 注意:这个方法内置了平台证书的获取和返回值解密
- */
- public AutoUpdateCertificatesVerifier getVerifier(String mchSerialNo) {
- AutoUpdateCertificatesVerifier verifier = null;
- if (verifierMap.isEmpty() || !verifierMap.containsKey(mchSerialNo)) {
- verifierMap.clear();
- try {
- //传入证书
- PrivateKey privateKey = getPrivateKeyV3();
- //刷新
- PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
- WechatPay2Credentials credentials = new WechatPay2Credentials(xcxProperties.getWecharpayMchid(), signer);
- verifier = new AutoUpdateCertificatesVerifier(credentials
- , xcxProperties.getWecharpaySecretV3().getBytes("utf-8"));
- verifierMap.put(verifier.getValidCertificate().getSerialNumber()+"", verifier);
-
-// AutoUpdateCertificatesVerifier verifierNew = new AutoUpdateCertificatesVerifier(
-// new WechatPay2Credentials(
-// xcxProperties.getWecharpayMchid(),
-// new PrivateKeySigner(
-// mchSerialNo,
-// privateKey)),
-// xcxProperties.getWecharpaySecretV3().getBytes("utf-8"));
-// new WechatPay2Validator(verifierNew).;
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- } else {
- verifier = verifierMap.get(mchSerialNo);
- }
- return verifier;
- }
-
-
- /**
- * 获取公私钥.通过证书
- */
- private KeyStore store;
- private final Object lock = new Object();
- public KeyPair createPKCS12(String keyAlias, String keyPass) {
-// ClassPathResource resource = new ClassPathResource(xcxProperties.getCertLocalPath());
- ClassPathResource resource = new ClassPathResource("wxP12/apiclient_cert.p12");
-// File file = new File("src/main/resources/wxP12/apiclient_cert.p12");
- char[] pem = keyPass.toCharArray();
+ //请求URL
+ HttpGet httpGet = new HttpGet(baseUrl+canonicalUrl);
+ httpGet.setHeader("Accept", "application/json");
+ //生成签名
+ httpGet.setHeader("Authorization ", "WECHATPAY2-SHA256-RSA2048"+postStr);
+ httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36");
+ //完成签名并执行请求
+ CloseableHttpResponse response = httpClient.execute(httpGet);
+ X509Certificate x509Certificate = null;
try {
- synchronized (lock) {
- if (store == null) {
- synchronized (lock) {
- store = KeyStore.getInstance("PKCS12");
- store.load(resource.getInputStream(), pem);
-// store.load(new FileInputStream(file), pem);
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode == 200) { //处理成功
+// System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
+ FPCertificateVo certificateVo = com.alibaba.fastjson.JSONObject.parseObject(EntityUtils.toString(response.getEntity()), FPCertificateVo.class);
+ for (FPCertificates certificates : certificateVo.getData()) {
+ if (format.parse(certificates.getEffective_time()).before(new Date())
+ && format.parse(certificates.getExpire_time()).after(new Date())) {
+ FPEncryptCertificate encrypt_certificate = certificates.getEncrypt_certificate();
+ //解密
+ AesUtil aesUtil = new AesUtil(xcxProperties.getWecharpaySecretV3().getBytes("utf-8"));
+ String pulicKey = aesUtil.decryptToString(
+ encrypt_certificate.getAssociated_data().getBytes("utf-8"),
+ encrypt_certificate.getNonce().getBytes("utf-8"),
+ encrypt_certificate.getCiphertext());
+ //获取平台证书
+ final CertificateFactory cf = CertificateFactory.getInstance("X509");
+
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(pulicKey.getBytes(StandardCharsets.UTF_8));
+
+ x509Certificate = (X509Certificate) cf.generateCertificate(inputStream);
}
}
+ return x509Certificate;
+ } else if (statusCode == 204) { //处理成功,无返回Body
+ System.out.println("success");
+ return x509Certificate;
+ } else {
+ System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
+ return x509Certificate;
}
- X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias);
- certificate.checkValidity();
- // 证书的序列号 也有用 50F37206347BCC9E6AC9860DAACE52AC035F7C24
- String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
- // 证书的 公钥
- PublicKey publicKey = certificate.getPublicKey();
- // 证书的私钥
- PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem);
- return new KeyPair(publicKey, storeKey);
- } catch (Exception e) {
- throw new IllegalStateException("Cannot load keys from store: " , e);
+ } catch (GeneralSecurityException | ParseException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ response.close();
+ httpClient.close();
}
}
--
Gitblit v1.9.1