最近在折腾微信支付,证书还是比较烦人的,所以有必要分享一些经验,减少你在开发微信支付时的踩坑。目前微信支付的API已经发展到V3版本,采用了流行的Restful风格。
今天来分享微信支付的难点——签名,虽然有很多好用的SDK但是如果你想深入了解微信支付还是需要了解一下的。
为了保证资金敏感数据的安全性,确保我们业务中的资金往来交易万无一失。目前微信支付第三方签发的权威的CA证书(Detailed explanation of WeChat payment in Java (1): API V3 version signature)中提供的私钥来进行签名。通过商户平台你可以设置并获取Detailed explanation of WeChat payment in Java (1): API V3 version signature。
切记在第一次设置的时候会提示下载,后面就不再提供下载了,具体参考说明。
设置后找到zip
压缩包解压,里面有很多文件,对于JAVA开发来说只需要关注apiclient_cert.p12
这个证书文件就行了,它包含了公私钥
,我们需要把它放在服务端并利用Java解析.p12
文件获取公钥私钥。
务必保证证书在服务器端的安全,它涉及到资金安全。
The next step is to parse the certificate. There are many ways to parse the certificate on the Internet. Here I use a more "formal" method to parse, using the JDK security packagejava.security.KeyStore
to resolve.
The WeChat payment API certificate uses the PKCS12
algorithm. We use KeyStore
to obtain the carrier of the public and private key pair KeyPair
and the certificate serial numberserialNumber
, I encapsulated the tool class:
import org.springframework.core.io.ClassPathResource;import java.security.KeyPair;import java.security.KeyStore;import java.security.PrivateKey;import java.security.PublicKey;import java.security.cert.X509Certificate;/** * KeyPairFactory * * @author dax * @since 13:41 **/public class KeyPairFactory { private KeyStore store; private final Object lock = new Object(); /** * 获取公私钥. * * @param keyPath the key path * @param keyAlias the key alias * @param keyPass password * @return the key pair */ public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) { ClassPathResource resource = new ClassPathResource(keyPath); char[] pem = keyPass.toCharArray(); try { synchronized (lock) { if (store == null) { synchronized (lock) { store = KeyStore.getInstance("PKCS12"); store.load(resource.getInputStream(), pem); } } } X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias); certificate.checkValidity(); // 证书的序列号 也有用 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: " + resource, e); } } }复制代码
If it looks familiar, you can see that it is a modified version of the public and private key extraction method used by JWT in the Spring Security tutorial of Fat Brother. You can compare the differences. place.
There are three parameters in this method, which must be explained here:
keyPath
API certificate apiclient_cert.p12
classpath
path, generally we will put it under the resources
path. Of course, you can modify the way to obtain the certificate input stream. keyAlias
The alias of the certificate is not available in this WeChat document. Brother Fat obtained it by DEBUG when loading the certificate and found that the value is fixed to Tenpay Certificate
. keyPass
Certificate password, this default is the merchant number, it also needs to be used in other configurations is mchid
, that is, you use Super Administrator A string of numbers in the personal profile when logging into the WeChat merchant platform. The signature of WeChat Pay V3 version is that we carry a specific signature in the HTTP request header when we call the specific WeChat Pay API. The encoding string is used by the WeChat payment server to verify the source of the request to ensure that the request is authentic and trustworthy.
The specific format of the signature string, no less than five lines in total, each line ends with a newline character \n
.
HTTP请求方法\n URL\n 请求时间戳\n 请求随机串\n 请求报文主体\n复制代码
POST
. https://api.mch.weixin.qq.com/v3/pay/transactions/app
, except the domain name part Get the URL participating in the signature. If there are query parameters in the request, '?' and the corresponding query string should be appended to the end of the URL. Here is /v3/pay/transactions/app
. System.currentTimeMillis() / 1000
to obtain it. 593BEC0C930BF1AFEB40B4A08C8FB242
. ""
; when the request method is# When ##POST or
PUT, please use
to actually send the JSON message of . For image upload API, please use the
JSON message corresponding to
meta.
string to be signed in the above format, and ## the signature result #Base64 encodingGet the signature value. The corresponding core Java code is: /**
* V3 SHA256withRSA 签名.
*
* @param method 请求方法 GET POST PUT DELETE 等
* @param canonicalUrl 例如 https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 ——> /v3/pay/transactions/app?version=1
* @param timestamp 当前时间戳 因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致
* @param nonceStr 随机字符串 要和TOKEN中的保持一致
* @param body 请求体 GET 为 "" POST 为JSON
* @param keyPair 商户API 证书解析的密钥对 实际使用的是其中的私钥
* @return the string
*/@SneakyThrowsString sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair) {
String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body)
.collect(Collectors.joining("\n", "", "\n"));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(keyPair.getPrivate());
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(sign.sign());
}复制代码
4. Use the signature
and place it in the Authorization# corresponding to the HTTP request. ##In the request header, the format is:
Authorization: WECHATPAY2-SHA256-RSA2048 {Token}复制代码
Token consists of the following five parts: ##Timestamp
Signature value
/** * 生成Token. * * @param mchId 商户号 * @param nonceStr 随机字符串 * @param timestamp 时间戳 * @param serialNo 证书序列号 * @param signature 签名 * @return the string */String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) { final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""; // 生成token return String.format(TOKEN_PATTERN, wechatPayProperties.getMchId(), nonceStr, timestamp, serialNo, signature); }复制代码
Will generate Token
is placed in the request header according to the above format to complete the use of the signature.
5. SummaryIn this article we have conducted a complete analysis of the difficult signatures and the use of signatures in the WeChat Pay V3 version, and also explained the analysis of API certificates. I believe it can help you. Solve some specific problems in payment development.
java basic tutorial
Related article introduction: How to implement the mini program payment function
The above is the detailed content of Detailed explanation of WeChat payment in Java (1): API V3 version signature. For more information, please follow other related articles on the PHP Chinese website!