1. API Rules
In order to provide a simple, consistent and easy-to-use development experience to merchants while ensuring payment security, we have launched the latest WeChat Pay APIv3 interface. Please refer to “APIv3 Interface Rules” for the specific rules of this API version.
2. Development Environment Setup
To help developers call the open interface, the development libraries of Java、PHP、GO are provided, encapsulating the basic functions such as signature generation, signature verification, encryption/decryption of sensitive information, and media document upload
2.1.1. Conditions of Use
• WechatPay merchant account
• Java 1.8 or later
• Maven or Gradle;
• 0.4.6 or later version of the Java httpClient SDK
2.1.2. Installation
Gradle:Add the following dependencies to your build.gradle document
implementation 'com.github.wechatpay-apiv3:wechatpay-apache-httpclient:0.4.7'
Maven:Add the following dependencies to the pom.xml document
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</dependency>
2.1.3. Initialization
Initialize WechatPayHttpClientBuilder to create an httpClient, which will be used to send requests to call the WeChatPay interface. Initialize a verifier instance with CertificatesManager to verify the signature.
private CloseableHttpClient httpClient;
private CertificatesManager certificatesManager;
private Verifier verifier;
@Before
//initialize verifier and build httpClient
public void setup() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
certificatesManager = CertificatesManager.getInstance();
certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));
verifier = certificatesManager.getVerifier(merchantId);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
httpClient = builder.build();
}
2.1.4. General Functions
@Test
public void encryptedTest() throws IllegalBlockSizeException {
String text = "helloWorld";
String transformation = "RSA/ECB/PKCS1Padding";
String encryptedText = RsaCryptoUtil.encrypt(text, verifier.getValidCertificate(), transformation);
System.out.println(encryptedText);
}
@Test
public void decryptedTest() throws BadPaddingException {
String encryptedText = OBTgun4jiN13n3nmSg8v0MNDMy3mbs380yq4GrgO8vwCqXnvrWxwo3jUCNY2UY7MIOtv/SD8ke64MxcSB0hn5EzKv1LOLprI3NvvmNLS4C3SBulxpZG62RYp1+8FgwAI4M//icXvjtZWVH4KVDg==";
String transformation = "RSA/ECB/PKCS1Padding";
String decryptedText = RsaCryptoUtil.decrypt(encryptedText, PemUtil.loadPrivateKey(privateKey), transformation);
assert("helloWorld".equals(decryptedText));
}
2.2.1. Conditions of Use
• WechatPay merchant account
• GO SDK 0.2.13 or later version
2.2.2. Installation
1. Manage your projects with Go Modules
If Go Modules are not used for the dependency management of your projects, run in the project root directory:
go mod init
2. Without cloning the code in the repository, run directly in the project directory:
go get -u github.com/wechatpay-apiv3/wechatpay-go
to add a dependency, modify modand download SDK
2.2.3. Initialization
Initialize Client and load the merchant account, private key, API V3 key, merchant certificate and serial number to Client for sending an interface request later.
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"fmt"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/utils"
"log"
"net/http"
)
var (
mchID = "" // merchant id
mchCertificateSerialNumber = "" // merchant certificate serial number
mchAPIv3Key = "" // merchant api v3 key
header = make(http.Header)
ctx = context.Background()
cert *x509.Certificate
mchPrivateKey *rsa.PrivateKey
client *core.Client
err error
)
func setup() {
//Load platform certificate
cert, err = utils.LoadCertificateWithPath("Path/To/Platform/Cert")
if err != nil {
log.Fatal(err)
}
// Load private key
mchPrivateKey, err = utils.LoadPrivateKeyWithPath("Path/To/Private/Key")
if err != nil {
log.Fatal("load merchant private key error")
}
// Initialize client which is capable to update platform certificate periodically
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
}
client, err = core.NewClient(ctx, opts...)
if err != nil {
log.Fatalf("new wechat pay client err:%s", err)
}
}
2.2.4. General Functions
func Encryption(t *testing.T) {
var testRSACryptoUtilMchCertificateStr = `-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----`
testRSACryptoUtilCertificate, _ = LoadCertificate(testRSACryptoUtilMchCertificateStr)
const message = "hello world"
// Encrypt the certificate by OAEP padding
ciphertext, _ := EncryptOAEPWithCertificate(message, testRSACryptoUtilCertificate)
// Encrypt the certificate by PKCS1 padding
ciphertext, _ := EncryptPKCS1v15WithCertificate(message, testRSACryptoUtilCertificate)
}
func TestDecryption(t *testing.T) {
var testRSACryptoUtilPrivateKeyStr = `-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----`
testRSACryptoUtilPrivateKey, _ = LoadPrivateKey(testingKey(testRSACryptoUtilPrivateKeyStr))
const ciphertext = ""
// Decrypt the private key by PKCS1 padding
decryptMessage, _ := DecryptPKCS1v15(ciphertext, testRSACryptoUtilPrivateKey)
// Decrypt the private key by OAEP padding
decryptMessage, _ := DecryptOAEP(ciphertext, testRSACryptoUtilPrivateKey)
}
2.3.1. Conditions of Use
• Guzzle 7.0,PHP >= 7.2.5
• Guzzle 6.5,PHP >= 7.1.2
• PHP 8 is supported.
2.3.2. Installation
It is recommended to use the PHP package management tool Composer to install SDK:
composer require wechatpay/wechatpay
2.3.3. Initialization
Initialize Client and load the merchant account, private key, API V3 key, merchant certificate and serial number to Client in order to send an interface request later.
require_once('vendor/autoload.php');
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;
$merchantId = '190000****';
$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
$merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';
$platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem';
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
// init a API V3 instance
$instance = Builder::factory([
'mchid' => $merchantId,
'serial' => $merchantCertificateSerial,
'privateKey' => $merchantPrivateKeyInstance,
'certs' => [
$platformCertificateSerial => $platformPublicKeyInstance,
],
]);
2.3.4. General Functions
$encryptor = static function(string $msg) use ($platformPublicKeyInstance): string {
return Rsa::encrypt($msg, $platformPublicKeyInstance, OPENSSL_PKCS1_PADDING);
};
$encryptedMsg = $encryptor('HelloWorld');
$decryptor = static function(string $msg) use ($merchantPrivateKeyInstance): string {
return Rsa::decrypt($msg, $merchantPrivateKeyInstance, OPENSSL_PKCS1_PADDING);
};
$decryptedMsg = $decryptor('ciphertext');
3. Fast Access
3.1. Business Sequence Chart
3.2. Example of API access
The document shows how to use the WeChat Pay server SDK to In-App Payment by scanning code and interface with WeChat Pay.
Notice
• The code examples in the document illustrate the basic usage of API. Before running the code, the example parameters in the code need to be replaced with the merchant's own account and request parameters.
• The access steps below are for your information, and should be evaluated and modified according to the merchant’s own business demands.
3.2.1 User identity verification API
Procedure: This interface allows merchants to request WeChat Pay to verify the consistency between the user’s real name for user payment and the identity information involved in the parameter, and merchants can use the correct information for customs clearance based on the verification results.
Sensitive information fields: certificate_id and certificate_name are the sensitive information of users. Such information and the users' privacy should be encrypted and transmitted according to WeChat Pay APIv3 to avoid being intercepted by intermediaries.
Encryption method:
• Download the WeChat Pay Platform Certificate, obtain the serial number and public key of certificate
• Add the serial number of WeChat Pay Platform Certificate in the HTTP header, Wechatpay-Serial : ${Serial Number of WeChat Pay Platform Certificate}
• Use the public key of WeChat Pay Platform Certificate to encrypt the sensitive fields with RSA, and choose RSAES-PKCS1-v1_5 as the padding scheme
• Obtain the final content by performing the base64 encoding for ciphertext
Code example
//Id info verification API
public void idVerification() throws IOException {
String verifyIdInfoBody = """{
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"out_trade_no": "20150806125346",
"transaction_id": "1000320306201511078440737890",
"sub_order_no": "20150806125346",
"customs": "SHANGHAI_ZS",
"merchant_customs_no": "123456",
"certificate_type": "IDCARD",
"certificate_id": "HHp24MagiQ6i7c5yIow5cJP5x2cGsAA8hi5yIow5cJP5x", // sensitive field, encryption content
"certificate_name": "HHp24MagiQ6i7c5yIow/9ZTk5Zt5cJP5x2cGsAA8hif07fRdb2" //sensitive field,encryption content
}""";
HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/customs/verify-certificate");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.addHeader("Wechatpay-Serial", "0798ABFDCBXXXXXXXXXXXXXXXXXXXXXXX054A761");
httpPost.setEntity(new StringEntity(verifyIdInfoBody));
CloseableHttpResponse response = httpClient.execute(httpPost);
//Process the response
}
//Call id verification API
func idVerification() {
idVerificationBody := `{
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"out_trade_no": "20150806125346",
"transaction_id": "1000320306201511078440737890",
"sub_order_no": "20150806125346",
"customs": "SHANGHAI_ZS",
"merchant_customs_no": "123456",
"certificate_type": "IDCARD",
"certificate_id": "HHp24MagiQ6i7c5yIow5cJP5x2cGsAA8hi5yIow5cJP5x",
"certificate_name": "HHp24MagiQ6i7c5yIow/9ZTk5Zt5cJP5x2cGsAA8hif07fRdb2"
}`
header.Add("Wechatpay-Serial", "")
header.Add("Idempotency-Key", "20220518-001")
result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/customs/verify-certificate", header, nil, idVerificationBody, "application/json")
if err != nil {
// Process error
}
log.Printf("%s", result.Response.Body)
}
//Id info verification API
public function idInfoVerification($instance, $platformPublicKeyInstance, $platformCertificateSerial){
$encryptor = static function(string $msg) use ($platformPublicKeyInstance): string {
return Rsa::encrypt($msg, $platformPublicKeyInstance, OPENSSL_PKCS1_PADDING);
};
try {
$resp = $instance
->chain('/v3/global/customs/verify-certificate')
->post([
'json' => [
'appid' => 'wxdace12300000000',
'mchid' => '1900000000',
'out_trade_no' => 'YX001',
'transaction_id' => '420000000001500000089',
'customs' => 'SHANGHAI_ZS',
'merchant_customs_no' => '123456',
'sub_order_no' => '20150806125346',
'certificate_type' => 'IDCARD',
'certificate_id' => 'HHp24MagiQ6i7c5yIow5cJP5x2cGsAA8hi5yIow5cJP5x',
'certificate_name' => 'HHp24MagiQ6i7c5yIow/9ZTk5Zt5cJP5x2cGsAA8hif07fRdb2'
],
'headers' => [
'Wechatpay-Serial' => $platformCertificateSerial
]
]);
echo $resp->getStatusCode(), PHP_EOL;
echo $resp->getBody(), PHP_EOL;
} catch (\Exception $e) {
// Exception handling
}
}
{
"stock_id": "Python",
"stock_creator_mchid": "123456",
"limit": 10,
}
Please refer to the API document for ordering by scanning code for other critical parameters.
3.2.2 API for additional order information submission
Procedure: Merchants request WeChat Pay to push the customs declaration information of payment orders to the Customs through this interface. WeChat Pay will asynchronously push the payment order declaration information to the Customs after receiving the additional information of customs declaration
Code example
//Customs Declaration API
public void customsDeclaration() throws IOException {
String customsDeclarationBody= """{
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"out_trade_no": "20150806125346",
"transaction_id": "1000320306201511078440737890",
"customs": "SHANGHAI_ZS",
"merchant_customs_no": "123456",
"duty": 888,
"sub_order_no": "20150806125346",
"fee_type": "CNY",
"order_fee": 888,
"transport_fee": 888,
"product_fee": 888
}""";
HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/customs/orders");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(customsDeclarationBody));
CloseableHttpResponse response = httpClient.execute(httpPost);
//Process the response
}
//Call customs declaration API
func customsDeclaration() {
customsDeclarationBody := `
{
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"out_trade_no": "20150806125346",
"transaction_id": "1000320306201511078440737890",
"customs": "SHANGHAI_ZS",
"merchant_customs_no": "123456",
"duty": 888,
"sub_order_no": "20150806125346",
"fee_type": "CNY",
"order_fee": 888,
"transport_fee": 888,
"product_fee": 888
}
`
result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/customs/orders", customsDeclarationBody)
if err != nil {
// Process error
}
log.Printf("%s", result.Response.Body)
}
//Customs Declaration API
public function customsDeclaration($instance){
try {
$resp = $instance
->chain('/v3/global/customs/orders')
->post([
'json' => [
'appid' => 'wxd678efh567hg6787',
'mchid' => '1230000109',
'out_trade_no' => '20150806125346',
'transaction_id' => '1000320306201511078440737890',
'customs' => 'SHANGHAI_ZS',
'merchant_customs_no' => '123456',
'duty' => 888,
'sub_order_no' => '20150806125346',
'fee_type' => 'CNY',
'order_fee' => 888,
'transport_fee' => 888,
'product_fee' => 888
]
]);
echo $resp->getStatusCode(), PHP_EOL;
echo $resp->getBody(), PHP_EOL;
} catch (\Exception $e) {
// Exception handling
}
}
{
"stock_id": "Python",
"stock_creator_mchid": "123456",
"limit": 10,
}
Please refer to the API document for ordering by scanning code for other critical parameters.
3.2.3 API for modifying customs declaration information
Procedure: This interface may be called to modify the customs declaration information if the merchant has incorrectly uploaded some fields. This interface allows to modify the following 5 fields only: merchant_customs_no, duty, order_fee, transport_fee, and product_fee
Code example
//Modify Customs Declaration API
public void modifyCustomsDeclarartionTest() throws IOException {
String modifyCustomsDeclarationBody = """
{
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"out_trade_no": "20150806125346",
"transaction_id": "1000320306201511078440737890",
"sub_order_no": "20150806125346",
"customs": "SHANGHAI_ZS",
"merchant_customs_no": "123456",
"duty": 888,
"order_fee": 888,
"transport_fee": 888,
"product_fee": 888
}
""";
HttpPatch httpPatch = new HttpPatch("https://apihk.mch.weixin.qq.com/v3/global/customs/orders");
httpPatch.addHeader("Accept", "application/json");
httpPatch.addHeader("Content-type", "application/json; charset=utf-8");
httpPatch.setEntity(new StringEntity(modifyCustomsDeclarationBody));
CloseableHttpResponse response = httpClient.execute(httpPatch);
//Process the response
}
//Modify customs declaration API
func modifyCustomsDeclarartion() {
modifyCustomsDeclarartionBody := `
{
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"out_trade_no": "20150806125346",
"transaction_id": "1000320306201511078440737890",
"sub_order_no": "20150806125346",
"customs": "SHANGHAI_ZS",
"merchant_customs_no": "123456",
"duty": 888,
"order_fee": 888,
"transport_fee": 888,
"product_fee": 888
}
`
result, err := client.Patch(ctx, "https://apihk.mch.weixin.qq.com/v3/global/customs/orders", modifyCustomsDeclarartionBody)
if err != nil {
// Process error
}
log.Printf("%s", result.Response.Body)
}
//Modify Customs Declaration API
public function modifyCustomsDeclarartion($instance){
try {
$resp = $instance
->chain('/v3/global/customs/orders')
->patch([
'json' => [
'appid' => 'wxd678efh567hg6787',
'mchid' => '1230000109',
'out_trade_no' => '20150806125346',
'transaction_id' => '1000320306201511078440737890',
'customs' => 'SHANGHAI_ZS',
'merchant_customs_no' => '123456',
'duty' => 888,
'sub_order_no' => '20150806125346',
'order_fee' => 888,
'transport_fee' => 888,
'product_fee' => 888
]
]);
echo $resp->getStatusCode(), PHP_EOL;
echo $resp->getBody(), PHP_EOL;
} catch (\Exception $e) {
// Exception handling
}
}
{
"stock_id": "Python",
"stock_creator_mchid": "123456",
"limit": 10,
}
Please refer to the API document for ordering by scanning code for other critical parameters.
3.2.4 API for querying additional order information
Procedure: This interface allows merchants to query the additional customs declaration information submitted, as well as the status of customs declaration.
Code example
//Query Customs Declaration API
public void queryCustomsDeclarationTest() throws IOException {
HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/customs/orders?appid=wxd678efh567hg6787&mchid=mchid&order_type=transaction_id&order_no=1000320306201511078440737890&customs=SHANGHAI_ZS&offset=1&limit=20");
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-type", "application/json; charset=utf-8");
CloseableHttpResponse response = httpClient.execute(httpGet);
//Process the response
}
//Call query customs declaration API
func queryCustomsDeclaration() {
result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/customs/orders?appid=wxd678efh567hg6787&mchid=mchid&order_type=transaction_id&order_no=1000320306201511078440737890&customs=SHANGHAI_ZS&offset=1&limit=20")
if err != nil {
// Process error
}
log.Printf("%s", result.Response.Body)
}
//Query Customs Declaration API
public function queryCustomsDeclaration($instance){
try {
$resp = $instance
->chain('/v3/global/customs/orders')
->get([
'query' => [
'mchid' => '1900000000',
'appid' => 'wxd678efh567hg6787',
'order_type' => 'transaction_id',
'order_no' => '1000320306201511078440737890',
'customs' => 'SHANGHAI_ZS',
'offset' => 1,
'limit' => 20
]
]);
echo $resp->getStatusCode(), PHP_EOL;
echo $resp->getBody(), PHP_EOL;
} catch (\Exception $e) {
// Exception handling
}
}
{
"stock_id": "Python",
"stock_creator_mchid": "123456",
"limit": 10,
}
Please refer to the API document for ordering by scanning code for other critical parameters.
3.2.5 API for re-pushing additional order information
Procedure: When an order has been declared and the Customs requires the merchant to re-push the declaration information, this interface allows the merchant to re-send the declaration information to the Customs.
Code example
//Repush Customs Declaration API
public void repushCustomsDeclarartionTest() throws IOException {
String repushBody = """
{
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"out_trade_no": "20150806125346",
"transaction_id": "1000320306201511078440737890",
"sub_order_no": "20150806125346",
"sub_order_id": "1000320306201511078440737891",
"customs": "SHANGHAI_ZS",
"merchant_customs_no": "123456"
}
""";
HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/customs/redeclare");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(repushBody));
CloseableHttpResponse response = httpClient.execute(httpPost);
//Process the response
}
//Repush customs declaration API
func repushCustomsDeclarartion() {
repushBody := `
{
"appid": "wxd678efh567hg6787",
"mchid": "1230000109",
"out_trade_no": "20150806125346",
"transaction_id": "1000320306201511078440737890",
"sub_order_no": "20150806125346",
"sub_order_id": "1000320306201511078440737891",
"customs": "SHANGHAI_ZS",
"merchant_customs_no": "123456"
}
`
result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/customs/redeclare", repushBody)
if err != nil {
// Process error
}
log.Printf("%s", result.Response.Body)
}
//Repush Customs Declaration API
public function repushCustomsDeclarartion($instance){
try {
$resp = $instance
->chain('v3/global/customs/redeclare')
->post([
'json' => [
'mchid' => '1230000109',
'appid' => 'wxd678efh567hg6787',
'out_trade_no' => '20150806125346',
'transaction_id' => '1000320306201511078440737890',
'sub_order_no' => '20150806125346',
'sub_order_id' => '1000320306201511078440737891',
'customs' => 'SHANGHAI_ZS',
'merchant_customs_no' => '123456'
]
]);
echo $resp->getStatusCode(), PHP_EOL;
echo $resp->getBody(), PHP_EOL;
} catch (\Exception $e) {
// Exception handling
}
}
{
"stock_id": "Python",
"stock_creator_mchid": "123456",
"limit": 10,
}
Please refer to the API document for ordering by scanning code for other critical parameters.