1. Introduction

Recently, in the process of dealing with wechat pay, certificates are still quite annoying, so it is necessary to share some experience to reduce your development of wechat pay when stepping on the pit. At present, wechat Pay API has been developed to V3 version, using the popular Restful style.

Today to share wechat pay difficult – signature, although there are a lot of good SDK but if you want to in-depth understanding of wechat pay still need to understand.

2. The API certificate

In order to ensure the security of money sensitive data, to ensure that the financial transactions in our business is safe. At present, wechat Pay is signed by the private key provided in the authoritative CA certificate (API certificate) issued by the third party. Through merchant platform you can set up and obtain API certificate.

Remember in the first setting when will prompt to download, later will not provide download, specific reference instructions.

For JAVA development, we only need to pay attention to the apiclient_cert.p12 certificate file, which contains the public and private keys, we need to put it in the server and use JAVA parsing. P12 file to obtain the public and private keys.

Make sure the certificate is secure on the server side as it relates to financial security.

Parsing API Certificates

Next is the resolution of the certificate, the resolution of the certificate has many methods on the Internet, here I use the more “formal” method to resolve, using the JDK security package java.security.keystore to resolve.

Wechat pay API certificate uses PKCS12 algorithm. We obtain the carrier KeyPair of the public and private KeyPair and the certificate serialNumber serialNumber through KeyStore. I encapsulate 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
 * @since13:41 * * /
public class KeyPairFactory {

    private KeyStore store;

    private final Object lock = new Object();

    /** * Obtain the public and private keys@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();
            // The certificate serial number is also useful
            String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
            // Public key of the certificate
            PublicKey publicKey = certificate.getPublicKey();
            // The private key of the certificate
            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); }}}Copy the code

JWT = JWT = JWT = JWT = JWT = JWT

This method takes three parameters that must be specified here:

  • keyPathAPI certificateapiclient_cert.p12theclasspathThe path, we usually put inresourcesOf course, you can modify the way you get the certificate input stream.
  • keyAliasThe alias of the certificate is not available in the wechat document. The value is fixed to be obtained by the DEBUG when loading the certificateTenpay Certificate
  • keyPassThe certificate password, which is the merchant number by default, is also used in other configurationsmchidThat’s what you useSuper administratorA string of numbers in your profile for logging in to the wechat merchant platform.

3. Sign the V3

When we call the specific API of wechat Pay, we carry a specific code string in the HTTP request header for the wechat Pay server to verify the source of the request and ensure that the request is authentic.

The signature format

The format of the signature string must be five lines, each line ending with a newline character \n.

HTTP request method \n URL\n request timestamp \n request random string \n Request message body \nCopy the code
  • HTTP request methodsThe request method required by the wechat payment API you call, such as APP payment, isPOST.
  • URLFor example, the APP payment document ishttps://api.mch.weixin.qq.com/v3/pay/transactions/appExcluding the domain name part, the participating URL is obtained. If there are query parameters in the request, the URL should have a ‘? ‘and the corresponding query string. Here for/v3/pay/transactions/app.
  • Request timestampServer system timestamp to ensure that the server time is correct and utilizedSystem.currentTimeMillis() / 1000Get.
  • Request random stringFind a utility class that generates similar593BEC0C930BF1AFEB40B4A08C8FB242The string will do.
  • Request message bodyIf it isGETRequest direct toNull character""; When the request method isPOSTorPUT, please useTrue to sendtheJSONMessage. Picture upload API, please usemetaThe correspondingJSONMessage.

To generate the signature

Then we use merchant private key to sign SHA256 with RSA for the string to be signed according to the above format, and Base64 encoding for the signature result to get the signature value. The corresponding core Java code is:

/** * V3 SHA256withRSA signature **@paramMethod Request methods GET, POST, PUT, and DELETE *@paramCanonicalUrl such as https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 - > / v3 / pay/the transactions/app? * version = 1@paramTimestamp the current timestamp must be consistent with the TOKEN * because it needs to be configured into the TOKEN@paramThe nonceStr random string must be consistent with the TOKEN *@paramBody Request body GET is "" POST is JSON *@paramKeyPair the keyPair that the merchant API certificate parses actually uses the private key * in it@return the string
 */
@SneakyThrows
String 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());
}
Copy the code

4. Use a signature

After the signature is generated, it forms a Token together with some parameters and is placed in the Authorization request header of the corresponding HTTP request in the following format:

Authorization: WECHATPAY2-SHA256-RSA2048 {Token}
Copy the code

Token consists of the following five parts:

  • The merchant id McHid of the merchant initiating the request (including the directly connected merchant, service provider or channel provider)

  • Merchant API certificate serial number serial_NO, used to declare the certificate used

  • Request a random string of nonce_str

  • Timestamp timestamp

  • The signature value signature

Token generation core code:

/** * generate Token. **@paramMcHId Merchant number *@paramNonceStr Random string *@paramTimestamp Indicates the time *@paramSerialNo Certificate serial number *@paramSignature 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 is generated
    return String.format(TOKEN_PATTERN,
            wechatPayProperties.getMchId(),
            nonceStr, timestamp, serialNo, signature);
}
Copy the code

To complete the use of the signature, place the generated Token in the request header in the format described above.

5. To summarize

In this paper, we have made a complete analysis of the difficult signature and the use of signature of wechat Payment V3 version, and also explained the analysis of API certificate, which I believe can help you solve some specific problems in the payment development. Later, I will explain the verification of signature when I have time, and pay attention to: code farmer Xiao Pangge timely access to a series of knowledge.

Follow our public id: Felordcn for more information

Personal blog: https://felord.cn