Merchants can follow the steps below to generate the requested signature. At the end of this section, we have prepared demo codes in a variety of commonly used programming languages for developers' reference.。
WeChat Pay API V3 requires merchants to sign the request. WeChat Pay will verify the signature after receiving the request. If signature verification fails, WeChat Pay API V3 will refuse to process the request and return 401 Unauthorized.
1. Preparation
A merchant needs to obtain a WeChat Pay merchant account, log in to the merchant platform through the super administrator account, and obtain the Merchant API Certificate . The compressed package of Merchant API Certificate contains the private key and merchant certificate necessary for signing.
2. Constructing a Signature String
Constructing a Signature String We hope that the merchant's technical developers will construct the signature string according to the conventions specified in the current document. WeChat Pay uses the same method to construct the signature string. The signature will not be verified if the merchant constructs the signature string incorrectly. The specific format of the signature string is explained below.
A signature string has five lines, and each line has a parameter. Each line ends with \n (Newline character; the ASCII code value is 0x0A), and even the last line shall end with \n. If the parameter itself ends with \n, an additional \n is also required.
1HTTP request method\n2URL\n3Request timestamp\n4Request random string\n5Request body\n
By calling the Obtaining WeChat Pay Platform Certificate API in the command line, we will provide developers with step by step instructions on how to request signatures. According to the API document, the URL for obtaining the merchant platform certificate is https://apihk.mch.weixin.qq.com/v3/global/certificates. The request method is GET with no query parameters.
Step 1: Obtain the HTTP request method (such as GET, POST, and PUT).
1GET
Step 2: Obtain the absolute URL of the request, and remove the domain to obtain the URL participating in the signature. If the request has query parameters, a '?' and the corresponding query string should be added to the end of the URL.
1/v3/global/certificates
Step 3: Obtain the current timestamp of the system when the request was initiated. Timestamps include the total number of seconds from January 1, 1970, 00: 00: 00, GMT (Beijing time, January 1, 1970, 08: 00: 00) to the present time. WeChat Pay will refuse to process requests initiated a long time ago. Merchants are requested to keep their system time accurate.
1$date+%s21554208460
Step 4: Generate a random string request, referring to Algorithm for Generating Random Numbers.Here, we use the command line to directly generate a random string request.
The signature functions provided by most programming languages support signing signature data. It is recommended that merchants call such functions, use the merchant's private key to sign the signature string with SHA256 with RSA, and perform Base64 encoding on the signature result to obtain the signature value.
WeChat Pay Merchant API V3 requires the request to pass the signature through the HTTP Authorization header. Authorization is composed of the authentication type and signature information.
Here we use command lines to demonstrate how to generate a signature.
1.Authentication type, currently WECHATPAY2-SHA256-RSA2048
2.Signature information
This is the merchant ID mchid of the merchant that initiated the request (including directly connected merchants, service providers, or channel merchants)
There is no requirement on the order for the five pieces of signature information above.
An example of the Authorization header is as follows: (note that the example may have line breaks due to typesetting. The actual data should be on one line.)
Developers can visit Development Tool to obtain the corresponding language library.Please refer to FAQs on how to load the private key in the program.
The sample code for calculating the signature is as follows.
JAVA
1importokhttp3.HttpUrl;2packagecom.wechat.v3;34importorg.apache.http.client.methods.HttpGet;5importorg.apache.http.client.methods.HttpRequestBase;6importorg.junit.Test;78importjava.io.IOException;9importjava.net.URI;10importjava.nio.charset.StandardCharsets;11importjava.security.*;12importjava.util.Base64;1314publicclassSignTest{15protectedstaticfinalStringSYMBOLS="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";16protectedstaticfinalSecureRandomRANDOM=newSecureRandom();17protectedstaticfinalPrivateKeyprivateKey=null;//need to be initialized18protectedstaticfinalStringcertificateSerialNumber=null;//need to be initialized19protectedstaticfinalStringmerchantId=null;//need to be initialized2021@Test22publicvoidsignTest()throwsIOException{23HttpGethttpGet=newHttpGet("https://apihk.mch.weixin.qq.com/v3/global/certificates");24httpGet.addHeader("Accept","application/json");25httpGet.addHeader("Content-type","application/json; charset=utf-8");26System.out.println(getToken(httpGet,""));27}2829publicStringgetToken(HttpRequestBaserequest,Stringbody)throwsIOException{30StringnonceStr=generateNonceStr();31longtimestamp=generateTimestamp();3233Stringmessage=buildMessage(nonceStr,timestamp,request,body);34// log.debug("authorization message=[{}]", message);35Stringsignature=sign(message.getBytes(StandardCharsets.UTF_8));3637Stringtoken="mchid=\""+merchantId+"\","38+"nonce_str=\""+nonceStr+"\","39+"timestamp=\""+timestamp+"\","40+"serial_no=\""+certificateSerialNumber+"\","41+"signature=\""+signature+"\"";42// log.debug("authorization token=[{}]", token);4344returntoken;45}4647publicStringsign(byte[]message){48try{49Signaturesign=Signature.getInstance("SHA256withRSA");50sign.initSign(privateKey);51sign.update(message);52returnBase64.getEncoder().encodeToString(sign.sign());53}catch(NoSuchAlgorithmExceptione){54thrownewRuntimeException("当前Java环境不支持SHA256withRSA",e);55}catch(SignatureExceptione){56thrownewRuntimeException("签名计算失败",e);57}catch(InvalidKeyExceptione){58thrownewRuntimeException("无效的私钥",e);59}60}6162protectedStringbuildMessage(Stringnonce,longtimestamp,HttpRequestBaserequest,Stringbody)throwsIOException{63URIuri=request.getURI();64StringcanonicalUrl=uri.getRawPath();65if(uri.getQuery()!=null){66canonicalUrl+="?"+uri.getRawQuery();67}6869returnrequest.getRequestLine().getMethod()+"\n"70+canonicalUrl+"\n"71+timestamp+"\n"72+nonce+"\n"73+body+"\n";74}7576protectedlonggenerateTimestamp(){77returnSystem.currentTimeMillis()/1000;78}7980protectedStringgenerateNonceStr(){81char[]nonceChars=newchar[32];82for(intindex=0;index<nonceChars.length;++index){83nonceChars[index]=SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));84}85returnnewString(nonceChars);86}87}
PHP
1<?php2require_once('vendor/autoload.php');34useWeChatPay\Crypto\Rsa;56classSignTest{7publicstatic8functiontimestamp():int{9returntime();10}1112publicstatic13functionnonce(int$size=32):string{14if($size<1){15thrownewInvalidArgumentException('Size must be a positive integer.');16}1718returnimplode('',array_map(static19function(string$c):string{20return'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'[ord($c)%62];21},str_split(random_bytes($size))));22}2324publicstatic25functionrequest(string$method,string$uri,string$timestamp,string$nonce,string$body=''):string{26returnstatic::joinedByLineFeed($method,$uri,$timestamp,$nonce,$body);27}2829publicstatic30functionjoinedByLineFeed(...$pieces):string{31returnimplode("\n",array_merge($pieces,['']));32}3334publicstatic35functionsign(string$message,$privateKey):string{36if(!openssl_sign($message,$signature,$privateKey,OPENSSL_ALGO_SHA256)){37thrownewUnexpectedValueException('Signing the input $message failed, please checking your $privateKey whether or nor correct.');38}3940returnbase64_encode($signature);41}4243publicstatic44functionauthorization(string$mchid,$privateKey,string$serial,string$method,string$uri,string$body=''):string{45$nonce=static::nonce();46$timestamp=static::timestamp();47$signature=static::sign(static::request($method,$uri,$timestamp,$nonce,$body),$privateKey);48returnsprintf(49'WECHATPAY2-SHA256-RSA2048 mchid="%s",serial_no="%s",timestamp="%s",nonce_str="%s",signature="%s"',50$mchid,$serial,$timestamp,$nonce,$signature51);52}53}5455$merchantPrivateKeyFilePath='/path/to/your/private/key/file';56$merchantId='10010000';57$method='GET';58$uri='v3/global/certificates';59$merchantCertificateSerial='329E9A85CDAAFAD289AA69AE71369CBF8A1290A2';60$merchantPrivateKeyFileContent=file_get_contents($merchantPrivateKeyFilePath);61$merchantPrivateKey=Rsa::from($merchantPrivateKeyFileContent,Rsa::KEY_TYPE_PRIVATE);6263echoSignTest::authorization('',$merchantPrivateKey,$merchantCertificateSerial,$method,$uri);
GO
1packagesign23import(4"crypto"5"crypto/rand"6"crypto/rsa"7"crypto/x509"8"encoding/base64"9"encoding/pem"10"fmt"11"github.com/wechatpay-apiv3/wechatpay-go/utils"12"log"13"testing"14"time"15)1617const(18NonceSymbols="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"19NonceLength=3220SignatureMessageFormat="%s\n%s\n%d\n%s\n%s\n"21HeaderAuthorizationFormat="%s mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""22)2324var(25signTestMchid=""// merchant id26signTestCertSerialNumber=""// merchant certificate serial number27signTestPrivateKey*rsa.PrivateKey28)2930funcGenerateNonce()(string,error){31bytes:=make([]byte,NonceLength)32_,err:=rand.Read(bytes)33iferr!=nil{34return"",err35}36symbolsByteLength:=byte(len(NonceSymbols))37fori,b:=rangebytes{38bytes[i]=NonceSymbols[b%symbolsByteLength]39}40returnstring(bytes),nil41}4243// LoadPrivateKey 通过私钥的文本内容加载私钥44funcLoadPrivateKey(privateKeyStrstring)(privateKey*rsa.PrivateKey,errerror){45block,_:=pem.Decode([]byte(privateKeyStr))46ifblock==nil{47returnnil,fmt.Errorf("decode private key err")48}49ifblock.Type!="PRIVATE KEY"{50returnnil,fmt.Errorf("the kind of PEM should be PRVATE KEY")51}52key,err:=x509.ParsePKCS8PrivateKey(block.Bytes)53iferr!=nil{54returnnil,fmt.Errorf("parse private key err:%s",err.Error())55}56privateKey,ok:=key.(*rsa.PrivateKey)57if!ok{58returnnil,fmt.Errorf("%s is not rsa private key",privateKeyStr)59}60returnprivateKey,nil61}6263funcSignSHA256WithRSA(sourcestring,privateKey*rsa.PrivateKey)(signaturestring,errerror){64ifprivateKey==nil{65return"",fmt.Errorf("private key should not be nil")66}67h:=crypto.Hash.New(crypto.SHA256)68_,err=h.Write([]byte(source))69iferr!=nil{70return"",nil71}72hashed:=h.Sum(nil)73signatureByte,err:=rsa.SignPKCS1v15(rand.Reader,privateKey,crypto.SHA256,hashed)74iferr!=nil{75return"",err76}77returnbase64.StdEncoding.EncodeToString(signatureByte),nil78}7980funcgetAuthorizationType()string{81return"WECHATPAY2-SHA256-RSA2048"82}8384funcGenerateAuthorizationHeader(mchid,certificateSerialNo,method,canonicalURL,bodystring,privateKey*rsa.PrivateKey)(string,error){85nonce,err:=GenerateNonce()86iferr!=nil{87return"",err88}89timestamp:=time.Now().Unix()90message:=fmt.Sprintf(SignatureMessageFormat,method,canonicalURL,timestamp,nonce,body)91signatureResult,err:=SignSHA256WithRSA(message,privateKey)92iferr!=nil{93return"",err94}95authorization:=fmt.Sprintf(96HeaderAuthorizationFormat,getAuthorizationType(),97mchid,nonce,timestamp,certificateSerialNo,signatureResult,98)99returnauthorization,nil100}101102funcTestGenerateAuthorizationHeader(t*testing.T){103// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名104signTestPrivateKey,err=utils.LoadPrivateKeyWithPath("/Path/to/your/private_key.pem")105iferr!=nil{106log.Fatal("load merchant private key error")107}108authHeader,err:=GenerateAuthorizationHeader(signTestMchid,signTestCertSerialNumber,"GET","/v3/global/certificates","",signTestPrivateKey)109iferr!=nil{110log.Fatal(err)111}112log.Printf("The authorization header is: %s",authHeader)113}
Notice
If your request returns a signature error 401 Unauthorized, please refer to FAQs on Signatures