Login expired. Please log in again.

Feedback

0/300

Feedback

Submitted successfully

ok

Feedback

Network exception, please try again later

ok

Signature Generation

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.

HTTP request method\n
URL\ n
Request timestamp\n
Request random string\n
Request 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).

GET

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.

/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.

 $ date +%s 
 1554208460

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.

$ hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random 
 593BEC0C930BF1AFEB40B4A08C8FB242

Step 5: Obtain the request body in the request.

• When the request method is GET, the message body is blank.

• When the request method is POST or PUT, use the actual JSON message sent.

• For an image upload API, use the JSON message corresponding to meta.

For a certificate download API, the request body is an empty string.

Step 6: According to the aforementioned rules, the constructed request signature string is:

GET\n 
 /v3/global/certificates\n 
 1554208460\n 
 593BEC0C930BF1AFEB40B4A08C8FB242\n 
 \n

3. Calculating Signature Value

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.

$ echo -n -e \
 "GET\n/v3/global/certificates\n1554208460\n593BEC0C930BF1AFEB40B4A08C8FB242\n\n" \
 | openssl dgst -sha256 -sign apiclient_key.pem \
 | openssl base64 -A
 uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==

4. Setting the HTTP Header

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.

Authorization: authentication type and signature information

Authorization includes:

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)

The Merchant API Certificate serial number, serial_no, is used for Declaring the Certificate Used

• Request random string nonce_str

• Timestamp timestamp

• Signature value signature

Notice
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.)

Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900009191",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==",timestamp="1554208460",serial_no="1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C"

Finally, we can create an HTTP request that includes a signature.

$ curl https://apihk.mch.weixin.qq.com/v3/global/certificates -H 'Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900009191",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9BxGw5dKjK+VdEUz2FeIoC+D5sB/LN+nGzX3hfZg6r5wT1pl2ZobmIc6p0ldN7J6yDgUzbX8Uk3sD4a4eZVPTBvqNDoUqcYMlZ9uuDdCvNv4TM3c1WzsXUrExwVkI1XO5jCNbgDJ25nkT/c1gIFvqoogl7MdSFGc4W4xZsqCItnqbypR3RuGIlR9h9vlRsy7zJR9PBI83X8alLDIfR1ukt1P7tMnmogZ0cuDY8cZsd8ZlCgLadmvej58SLsIkVxFJ8XyUgx9FmutKSYTmYtWBZ0+tNvfGmbXU7cob8H/4nLBiCwIUFluw==",timestamp="1554208460",serial_no="1DDE55AD98ED71D6EDD4A4A16996DE7B47773A8C"'

5. Demo Code

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.

import okhttp3.HttpUrl;
package com.wechat.v3;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.junit.Test;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;

public class SignTest {
protected static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
protected static final SecureRandom RANDOM = new SecureRandom();
protected static final PrivateKey privateKey = null; //need to be initialized
protected static final String certificateSerialNumber = null; //need to be initialized
protected static final String merchantId = null; //need to be initialized

@Test
public void signTest() throws IOException {
HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/certificates");
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-type", "application/json; charset=utf-8");
System.out.println(getToken(httpGet,""));
}

public String getToken(HttpRequestBase request, String body) throws IOException {
String nonceStr = generateNonceStr();
long timestamp = generateTimestamp();

String message = buildMessage(nonceStr, timestamp, request, body);
// log.debug("authorization message=[{}]", message);
String signature = sign(message.getBytes(StandardCharsets.UTF_8));

String token = "mchid=\"" + merchantId + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + certificateSerialNumber + "\","
+ "signature=\"" + signature + "\"";
// log.debug("authorization token=[{}]", token);

return token;
}

public String sign(byte[] message) {
try {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
} catch (SignatureException e) {
throw new RuntimeException("签名计算失败", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("无效的私钥", e);
}
}

protected String buildMessage(String nonce, long timestamp, HttpRequestBase request, String body) throws IOException {
URI uri = request.getURI();
String canonicalUrl = uri.getRawPath();
if (uri.getQuery() != null) {
canonicalUrl += "?" + uri.getRawQuery();
}

return request.getRequestLine().getMethod() + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonce + "\n"
+ body + "\n";
}

protected long generateTimestamp() {
return System.currentTimeMillis() / 1000;
}

protected String generateNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}

}
<? php
require_once('vendor/autoload.php');

use WeChatPay\ Crypto\ Rsa;

class SignTest {
	public static
	function timestamp(): int {
		return time();
	}

	public static
	function nonce(int $size = 32): string {
		if ($size < 1) {
			throw new InvalidArgumentException('Size must be a positive integer.');
		}

		return implode('', array_map(static
			function(string $c): string {
				return '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' [ord($c) % 62];
			}, str_split(random_bytes($size))));
	}

	public static
	function request(string $method, string $uri, string $timestamp, string $nonce, string $body = ''): string {
		return static::joinedByLineFeed($method, $uri, $timestamp, $nonce, $body);
	}

	public static
	function joinedByLineFeed(...$pieces): string {
		return implode("\n", array_merge($pieces, ['']));
	}

	public static
	function sign(string $message, $privateKey): string {
		if (!openssl_sign($message, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
			throw new UnexpectedValueException('Signing the input $message failed, please checking your $privateKey whether or nor correct.');
		}

		return base64_encode($signature);
	}

	public static
	function authorization(string $mchid, $privateKey, string $serial, string $method, string $uri, string $body = ''): string {
		$nonce = static::nonce();
		$timestamp = static::timestamp();
		$signature = static::sign(static::request($method, $uri, $timestamp, $nonce, $body), $privateKey);
		return sprintf(
			'WECHATPAY2-SHA256-RSA2048 mchid="%s",serial_no="%s",timestamp="%s",nonce_str="%s",signature="%s"',
			$mchid, $serial, $timestamp, $nonce, $signature
		);
	}
}

$merchantPrivateKeyFilePath = '/path/to/your/private/key/file';
$merchantId = '10010000';
$method = 'GET';
$uri = 'v3/global/certificates';
$merchantCertificateSerial = '329E9A85CDAAFAD289AA69AE71369CBF8A1290A2';
$merchantPrivateKeyFileContent = file_get_contents($merchantPrivateKeyFilePath);
$merchantPrivateKey = Rsa::from($merchantPrivateKeyFileContent, Rsa::KEY_TYPE_PRIVATE);

echo SignTest::authorization('', $merchantPrivateKey, $merchantCertificateSerial, $method, $uri);
package sign

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"github.com/wechatpay-apiv3/wechatpay-go/utils"
	"log"
	"testing"
	"time"
)

const (
	NonceSymbols              = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	NonceLength               = 32
	SignatureMessageFormat    = "%s\n%s\n%d\n%s\n%s\n"
	HeaderAuthorizationFormat = "%s mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""
)

var (
	signTestMchid            = ""                               // merchant id
	signTestCertSerialNumber = "" // merchant certificate serial number
	signTestPrivateKey       *rsa.PrivateKey
)

func GenerateNonce() (string, error) {
	bytes := make([]byte, NonceLength)
	_, err := rand.Read(bytes)
	if err != nil {
		return "", err
	}
	symbolsByteLength := byte(len(NonceSymbols))
	for i, b := range bytes {
		bytes[i] = NonceSymbols[b%symbolsByteLength]
	}
	return string(bytes), nil
}

// LoadPrivateKey 通过私钥的文本内容加载私钥
func LoadPrivateKey(privateKeyStr string) (privateKey *rsa.PrivateKey, err error) {
	block, _ := pem.Decode([]byte(privateKeyStr))
	if block == nil {
		return nil, fmt.Errorf("decode private key err")
	}
	if block.Type != "PRIVATE KEY" {
		return nil, fmt.Errorf("the kind of PEM should be PRVATE KEY")
	}
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("parse private key err:%s", err.Error())
	}
	privateKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		return nil, fmt.Errorf("%s is not rsa private key", privateKeyStr)
	}
	return privateKey, nil
}

func SignSHA256WithRSA(source string, privateKey *rsa.PrivateKey) (signature string, err error) {
	if privateKey == nil {
		return "", fmt.Errorf("private key should not be nil")
	}
	h := crypto.Hash.New(crypto.SHA256)
	_, err = h.Write([]byte(source))
	if err != nil {
		return "", nil
	}
	hashed := h.Sum(nil)
	signatureByte, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(signatureByte), nil
}

func getAuthorizationType() string {
	return "WECHATPAY2-SHA256-RSA2048"
}

func GenerateAuthorizationHeader(mchid, certificateSerialNo, method, canonicalURL, body string, privateKey *rsa.PrivateKey) (string, error) {
	nonce, err := GenerateNonce()
	if err != nil {
		return "", err
	}
	timestamp := time.Now().Unix()
	message := fmt.Sprintf(SignatureMessageFormat, method, canonicalURL, timestamp, nonce, body)
	signatureResult, err := SignSHA256WithRSA(message, privateKey)
	if err != nil {
		return "", err
	}
	authorization := fmt.Sprintf(
		HeaderAuthorizationFormat, getAuthorizationType(),
		mchid, nonce, timestamp, certificateSerialNo, signatureResult,
	)
	return authorization, nil
}

func TestGenerateAuthorizationHeader(t *testing.T) {
	// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
	signTestPrivateKey, err = utils.LoadPrivateKeyWithPath("/Path/to/your/private_key.pem")
	if err != nil {
		log.Fatal("load merchant private key error")
	}
	authHeader, err := GenerateAuthorizationHeader(signTestMchid, signTestCertSerialNumber, "GET", "/v3/global/certificates", "", signTestPrivateKey)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("The authorization header is: %s", authHeader)
}
Notice
If your request returns a signature error 401 Unauthorized, please refer to FAQs on Signatures
    Page Navigation

About  WeChat  Pay

Powered By Tencent & Tenpay Copyright©

2005-2024 Tenpay All Rights Reserved.

Contact Us
Wechat Pay Global

WeChat Pay Global

置顶