Home  >  Article  >  Java  >  Detailed explanation of WeChat payment in Java (1): API V3 version signature

Detailed explanation of WeChat payment in Java (1): API V3 version signature

coldplay.xixi
coldplay.xixiforward
2020-10-27 17:20:212423browse

java基础教程栏目介绍Java中的微信支付,实现API V3版本签名。

Detailed explanation of WeChat payment in Java (1): API V3 version signature

1、 前言

最近在折腾微信支付,证书还是比较烦人的,所以有必要分享一些经验,减少你在开发微信支付时的踩坑。目前微信支付的API已经发展到V3版本,采用了流行的Restful风格。

Detailed explanation of WeChat payment in Java (1): API V3 version signature

今天来分享微信支付的难点——签名,虽然有很多好用的SDK但是如果你想深入了解微信支付还是需要了解一下的。

2. Detailed explanation of WeChat payment in Java (1): API V3 version signature

为了保证资金敏感数据的安全性,确保我们业务中的资金往来交易万无一失。目前微信支付第三方签发的权威的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

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文件获取公钥私钥。

务必保证证书在服务器端的安全,它涉及到资金安全

Parsing the API certificate

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.

3. V3 signature

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.

Signature format

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复制代码
  • HTTP request method The request method required by the WeChat payment API you call, for example, APP payment is POST.
  • URL For example, the APP payment document is 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.
  • Request timestamp Server system timestamp, make sure the server time is correct and use System.currentTimeMillis() / 1000 to obtain it.
  • Request a random string Just find a tool to generate a string similar to 593BEC0C930BF1AFEB40B4A08C8FB242.
  • Request message body If it is GET the request is directly null character""; 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.
Generate signature

Then we use the merchant’s private key to perform SHA256 with RSA signature on the

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

After the signature is generated, it will be combined with some parameters to form a

Token

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:

The merchant who initiated the request (including directly connected merchants , service provider or channel provider) merchant number
    mchid
  • Merchant API certificate serial number
  • serial_no
  • , used to declare the certificate used

    Request random string
  • nonce_str
  • ##Timestamp

    timestamp
  • Signature value

    signature
  • Token
  • Generated core code:
/**
 * 生成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.

Related free learning recommendations:

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!

Statement:
This article is reproduced at:juejin.im. If there is any infringement, please contact admin@php.cn delete