1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
| package com.qujie.mintwo.system.wechatpay;
import com.alibaba.fastjson.JSON; import com.qujie.mintwo.config.WechatConfig; import com.wechat.pay.contrib.apache.httpclient.Validator; import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; import com.wechat.pay.contrib.apache.httpclient.auth.*; import com.wechat.pay.contrib.apache.httpclient.util.AesUtil; import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import java.io.*; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Comparator; import java.util.List;
import static org.apache.http.HttpHeaders.ACCEPT; import static org.apache.http.HttpHeaders.CONTENT_TYPE; import static org.apache.http.HttpStatus.SC_OK; import static org.apache.http.entity.ContentType.APPLICATION_JSON;
/** * @author 夏天 * @version 1.0 * @date 2021/11/24 */ @Component public class WechatPayApiUtil { private final Logger logger = LoggerFactory.getLogger(WechatPayApiUtil.class); /** * 参数配置 */ @Resource private WechatConfig wechatConfig;
public CloseableHttpResponse v3Execution(HttpUriRequest request) throws IOException, GeneralSecurityException { request.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString()); request.addHeader(ACCEPT, APPLICATION_JSON.toString()); FileInputStream privateKey; try { privateKey = new FileInputStream("D:\\apiClient_key.pem"); }catch (FileNotFoundException e){ logger.error("微信支付接口调用所需的私钥文件不存在,路径【{}】",wechatConfig.getPrivateKeyPath()); throw new FileNotFoundException("微信支付接口调用所需的私钥文件不存在"); } PrivateKey merChantPrivateKey = PemUtil.loadPrivateKey(privateKey); List<X509Certificate> certs = new ArrayList<>(); try { FileInputStream certKey = new FileInputStream("D:\\api\\apiClient_cert.pem"); certs.add(PemUtil.loadCertificate(certKey)); }catch (FileNotFoundException e){ logger.error("微信支付接口调用所需的平台证书文件不存在,路径【{}】",wechatConfig.getCertKeyPath()); certs = downloadCertificate(); } AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier( new WechatPay2Credentials(wechatConfig.getMerchantId(), new PrivateKeySigner(wechatConfig.getSerialNumber(), merChantPrivateKey)), wechatConfig.getApiKey().getBytes(StandardCharsets.UTF_8));
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() .withMerchant(wechatConfig.getMerchantId(),wechatConfig.getSerialNumber(),merChantPrivateKey).withValidator(new WechatPay2Validator(verifier)); try (CloseableHttpClient client = builder.build()) { CloseableHttpResponse response = client.execute(request); int statusCode = response.getStatusLine().getStatusCode(); String body = EntityUtils.toString(response.getEntity()); if (SC_OK == statusCode) { logger.info("微信支付商家平台接口调用成功,接口响应信息:【{}】",body); if(validate(response,certs)){ logger.info("微信支付商家平台响应信息验证签名成功"); return response; }else { logger.error("微信支付商家平台响应信息验证签名失败"); throw new IOException("微信支付商家平台响应信息验证签名失败"); } }else { logger.error("微信支付商家平台接口调用失败,错误码【{}】,错误信息【{}】",statusCode,body); return response; } } } private boolean validate(CloseableHttpResponse response,List<X509Certificate> x509Certs) throws IOException { //从下载的证书中,获取对报文签名的私钥所对应的证书,并进行验签来验证证书准确 Verifier verifier = new CertificatesVerifier(x509Certs); Validator validator = new WechatPay2Validator(verifier); return validator.validate(response); } public List<X509Certificate> downloadCertificate() throws IOException, GeneralSecurityException { FileInputStream privateKey; try { privateKey = new FileInputStream("D:\\apiClient_key.pem"); }catch (FileNotFoundException e){ logger.error("微信支付接口调用所需的私钥文件路径【{}】不存在",wechatConfig.getPrivateKeyPath()); throw new FileNotFoundException("微信支付接口调用所需的私钥文件不存在"); } PrivateKey merChantPrivateKey = PemUtil.loadPrivateKey(privateKey); WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() .withMerchant(wechatConfig.getMerchantId(),wechatConfig.getSerialNumber(),merChantPrivateKey).withValidator(response -> true); HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/certificates"); httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString()); try (CloseableHttpClient client = builder.build()) { CloseableHttpResponse response = client.execute(httpGet); int statusCode = response.getStatusLine().getStatusCode(); String body = EntityUtils.toString(response.getEntity()); if (SC_OK != statusCode) { logger.error("微信支付商家平台接口调用失败,接口响应码:【{}】,接口响应信息:【{}】",statusCode,body); throw new IOException("微信支付商家平台接口调用失败"); } logger.info("微信支付平台证书获取成功,接口响应信息:【{}】",body); CertificateList certList = JSON.parseObject(body, CertificateList.class); List<X509Certificate> x509Certs = new ArrayList<>(); AesUtil aesUtil = new AesUtil(wechatConfig.getApiKey().getBytes(StandardCharsets.UTF_8)); CertificateItem certificateItem = certList.getCerts().stream().max(Comparator.comparing(CertificateItem::getExpireTime)).orElse(null); if(certificateItem == null){ logger.error("微信支付获取平台证书接口响应信息获取证书为空"); throw new IOException("微信支付获取平台证书接口响应信息获取证书为空"); } String plainCertificate = aesUtil.decryptToString(certificateItem.getEncryptCertificate().getAssociatedData().getBytes(StandardCharsets.UTF_8), certificateItem.getEncryptCertificate().getNonce().getBytes(StandardCharsets.UTF_8), certificateItem.getEncryptCertificate().getCiphertext()); ByteArrayInputStream inputStream = new ByteArrayInputStream(plainCertificate.getBytes(StandardCharsets.UTF_8)); X509Certificate x509Cert = PemUtil.loadCertificate(inputStream); x509Certs.add(x509Cert); //从下载的证书中,获取对报文签名的私钥所对应的证书,并进行验签来验证证书准确 boolean isCorrectCert = validate(response,x509Certs); logger.info(isCorrectCert ? "=== validate success ===" : "=== validate failed ==="); if(!isCorrectCert){ logger.error("微信支付平台证书响应结果验证失败"); throw new IOException("微信支付平台证书响应结果验证失败"); } File file = new File("D:\\api"); if(!file.exists()){ boolean result = file.mkdirs(); logger.info("目录【{}】不存在需要创建目录,创建结果:【{}】",file.getAbsolutePath(),result); } String outputAbsoluteFilename = file.getAbsolutePath() + File.separator + "apiClient_cert.pem"; try (BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(outputAbsoluteFilename), StandardCharsets.UTF_8))) { writer.write(plainCertificate); } logger.info("save cert file absolute path {} " , outputAbsoluteFilename); return x509Certs; } }
}
|