Development Guidelines

Update Time:2025.03.24

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 JavaPHPGO are provided, encapsulating the basic functions such as signature generation, signature verification, encryption/decryption of sensitive information, and media document upload

JAVA

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

1implementation 'com.github.wechatpay-apiv3:wechatpay-apache-httpclient:0.4.7'
2

Maven:Add the following dependencies to the pom.xml document

1<dependency>
2<groupId>com.github.wechatpay-apiv3</groupId>
3<artifactId>wechatpay-apache-httpclient</artifactId>
4<version>0.4.7</version>
5</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.

1
2    private CloseableHttpClient httpClient;
3    private CertificatesManager certificatesManager;
4    private Verifier verifier;
5
6    @Before
7    //initialize verifier and build httpClient
8    public void setup() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException {
9
10        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
11        certificatesManager = CertificatesManager.getInstance();
12        certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
13                        new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
14                apiV3Key.getBytes(StandardCharsets.UTF_8));
15        verifier = certificatesManager.getVerifier(merchantId);
16        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
17                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
18                .withValidator(new WechatPay2Validator(verifier));
19        httpClient = builder.build();
20    }
21        

2.1.4. General Functions

Encrypting Sensitive Information

1
2    @Test
3    public void encryptedTest() throws IllegalBlockSizeException {
4        String text = "helloWorld";
5        String transformation = "RSA/ECB/PKCS1Padding";
6        String encryptedText = RsaCryptoUtil.encrypt(text, verifier.getValidCertificate(), transformation);
7        System.out.println(encryptedText);
8    }
9  

Decrypting Sensitive Information

1
2    @Test
3    public void decryptedTest() throws BadPaddingException {
4        String encryptedText = OBTgun4jiN13n3nmSg8v0MNDMy3mbs380yq4GrgO8vwCqXnvrWxwo3jUCNY2UY7MIOtv/SD8ke64MxcSB0hn5EzKv1LOLprI3NvvmNLS4C3SBulxpZG62RYp1+8FgwAI4M//icXvjtZWVH4KVDg==";
5        String transformation = "RSA/ECB/PKCS1Padding";
6        String decryptedText = RsaCryptoUtil.decrypt(encryptedText, PemUtil.loadPrivateKey(privateKey), transformation);
7        assert("helloWorld".equals(decryptedText));
8    }

Golang

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:

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.

1
2import (
3   "context"
4   "crypto/rand"
5   "crypto/rsa"
6   "crypto/x509"
7   "encoding/base64"
8   "fmt"
9   "github.com/wechatpay-apiv3/wechatpay-go/core"
10   "github.com/wechatpay-apiv3/wechatpay-go/core/option"
11   "github.com/wechatpay-apiv3/wechatpay-go/utils"
12   "log"
13   "net/http"
14)
15
16var (
17   mchID                      = ""                  // merchant id
18   mchCertificateSerialNumber = "" // merchant certificate serial number
19   mchAPIv3Key                = ""                  // merchant api v3 key
20   header                     = make(http.Header)
21   ctx                        = context.Background()
22   cert                       *x509.Certificate
23   mchPrivateKey              *rsa.PrivateKey
24   client                     *core.Client
25   err                        error
26)
27
28func setup() {
29   //Load platform certificate
30   cert, err = utils.LoadCertificateWithPath("Path/To/Platform/Cert")
31   if err != nil {
32      log.Fatal(err)
33   }
34   // Load private key
35   mchPrivateKey, err = utils.LoadPrivateKeyWithPath("Path/To/Private/Key")
36   if err != nil {
37      log.Fatal("load merchant private key error")
38   }
39   // Initialize client which is capable to update platform certificate periodically
40   opts := []core.ClientOption{
41      option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
42   }
43   client, err = core.NewClient(ctx, opts...)
44   if err != nil {
45      log.Fatalf("new wechat pay client err:%s", err)
46   }
47}
48        

2.2.4. General Functions

Encrypting Sensitive Information

1
2func Encryption(t *testing.T) {
3	var testRSACryptoUtilMchCertificateStr = `-----BEGIN CERTIFICATE-----
4	-----END CERTIFICATE-----`
5	testRSACryptoUtilCertificate, _ = LoadCertificate(testRSACryptoUtilMchCertificateStr)
6   const message = "hello world"
7   
8   // Encrypt the certificate by OAEP padding
9   ciphertext, _ := EncryptOAEPWithCertificate(message, testRSACryptoUtilCertificate)
10   
11  // Encrypt the certificate by PKCS1 padding
12   ciphertext, _ := EncryptPKCS1v15WithCertificate(message, testRSACryptoUtilCertificate)
13}
14  

Decrypting Sensitive Information

1
2func TestDecryption(t *testing.T) {
3	var testRSACryptoUtilPrivateKeyStr = `-----BEGIN PRIVATE KEY-----
4-----END PRIVATE KEY-----`
5	testRSACryptoUtilPrivateKey, _ = LoadPrivateKey(testingKey(testRSACryptoUtilPrivateKeyStr))
6	const ciphertext = ""
7
8	// Decrypt the private key by PKCS1 padding
9	decryptMessage, _ := DecryptPKCS1v15(ciphertext, testRSACryptoUtilPrivateKey)
10
11	// Decrypt the private key by OAEP padding
12	decryptMessage, _ := DecryptOAEP(ciphertext, testRSACryptoUtilPrivateKey)
13}

PHP

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.

1
2require_once('vendor/autoload.php');
3
4use WeChatPay\Builder;
5use WeChatPay\Crypto\Rsa;
6use WeChatPay\Util\PemUtil;
7
8$merchantId = '190000****';
9$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
10$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
11$merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';
12$platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem';
13$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
14$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
15
16// init a API V3 instance
17$instance = Builder::factory([
18    'mchid'      => $merchantId,
19    'serial'     => $merchantCertificateSerial,
20    'privateKey' => $merchantPrivateKeyInstance,
21    'certs'      => [
22        $platformCertificateSerial => $platformPublicKeyInstance,
23    ],
24]); 
25        

2.3.4. General Functions

Encrypting Sensitive Information

1
2    $encryptor = static function(string $msg) use ($platformPublicKeyInstance): string {
3        return Rsa::encrypt($msg, $platformPublicKeyInstance, OPENSSL_PKCS1_PADDING);
4    };
5	$encryptedMsg =  $encryptor('HelloWorld');
6  

Decrypting Sensitive Information

1
2    $decryptor = static function(string $msg) use ($merchantPrivateKeyInstance): string {
3        return Rsa::decrypt($msg, $merchantPrivateKeyInstance, OPENSSL_PKCS1_PADDING);
4    };
5	$decryptedMsg =  $decryptor('ciphertext');

 

2.4. Key Points of iOS Development

Key Points of iOS Development

I. OpenSDK Upgrade Guidelines for iOS

Due to Apple's recall of the ability to query the App bundleID in iOS13, there is no guarantee that the authorization credentials will be correctly returned by WeChat to the App corresponding to the AppID. For this reason, WeChat Pay strongly requests all merchants to upgrade to OpenSDK 1.8.6 as soon as possible and cause users to update the APP in time, otherwise security risks will always exist. Thanks for your cooperation!

For the detailed OpenSDK Upgrade Guidelines, please refer to OpenSDK Upgrade Guidelines

Notice

  • After OpenSDK is upgraded, be sure to complete the verification according to the document requirements, and ensure that the OpenSDK has been upgraded successfully.

II. Development Configuration:

The Xcode 6.0 and IOS 7.0 are taken as the examples for project development environment and operating environment respectively, to introduce the operation necessary for development

  1. Set Project APPID

After the merchant applies for APP development at WeChat Open Platform, WeChat Open Platform will generate APP's unique identification, i.e., APPID. Open the project in Xcode and set the URL Schemes in the project properties as your APPID. Indicated as the red position in the figure

  1. Register APPID

The merchant APP project introduces the WeChat lib library and header file. Before calling API, register your APPID with WeChat first. The code is as follows:

1   [WXApi registerApp:@"wxd930ea5d5a258f4f" withDescription:@"demo 2.0"];
2

Notice

For instructions related to OpenSDK front-end pull-up payments and SDK callbacks

Key Points of Andriod Development

I. Background Settings

After the merchant applies for application development at WeChat Open Platform, WeChat Open Platform will generate APP's unique identification, i.e., APPID. To ensure payment security, bind the merchant application package name and application signature in the open platform, and set them up to initiate payment normally. Enter the [Management Center / Modify Application / Modify Development Information] in the [Open Platform] to access the setting screen, as shown in the red box in the figure.

Application package name is the package value declared in the APP project configuration file AndroidManifest.xml, such as package="demo.wxpay.tenpay.com” in the above figure.

Application signature is the keystore used according to the application package name and compilation of the project,consisting of a 32-bit md5 string generated by the signature tool. The application signature string can be

generated by the signature tool after installing the signature tool on the debugged mobile phone, as shown in Figure 8.9, which is in green.Download Address of Signature Tool

II. Register APPID

The merchant APP project introduces the WeChat JAR package. Before calling API, register your APPID with WeChat first. The code is as follows:

1final IWXAPI msgApi = WXAPIFactory.createWXAPI(context, null);
2
3// Resgiter this app with WeChat
4
5msgApi.registerApp("wxd930ea5d5a258f4f");
6

Notice

For instructions related to OpenSDK front-end pull-up payments and SDK callbacks

 

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 Place an order

Procedure: Submit a pre-order request for In-APP payment through this interface, obtain the prepay_id to call payment.

Code example-JAVA

1
2 @Test
3    //Call Unified Order API
4    public void unifiedOrderTest() throws IOException {
5      String unifiedOrderBody = String.join("\n" ,
6                		"{" ,
7                        "'sp_appid': 'wx2421b1c4370ec43b'," ,
8                        "'sp_mchid': '10000100'," ,
9                        "'sub_mchid': '20000100'," ,
10                        "'sub_appid': 'wx352671b037b437ec'," ,
11                        "'out_trade_no': '20150806125346'," ,
12                        "'merchant_category_code': '1011'," ,
13                        "'payer': {" ,
14                        "'sp_openid': 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'" ,
15                        "}," ,
16                        "'notify_url': 'https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php'," ,
17                        "'trade_type': 'APP'," ,
18                        "'amount': {" ,
19                        "'total': 10000," ,
20                        "'currency': 'HKD'" ,
21                        "}," ,
22                        "'attach': 'Payment test'," ,
23                        "'description': 'In-APP Pay test'," ,
24                        "'goods_tag': '," ,
25                        "'detail': {" ,
26                        "'cost_price': 10000," ,
27                        "'receipt_id': '1234'," ,
28                        "'goods_detail': [{" ,
29                        "'goods_id': 'iphone6s_16G'," ,
30                        "'wxpay_goods_id': '1001'," ,
31                        "'goods_name': 'iPhone6s 16G'," ,
32                        "'quantity': 1," ,
33                        "'price': 528800" ,
34                        "}]" ,
35                        "}," ,
36                        "'scene_info': {" ,
37                        "'payer_client_ip': '14.23.150.211'," ,
38                        "'device_ip': '59.37.125.32'," ,
39                        "'device_id': '013467007045764'," ,
40                        "'operator_id': 'P001'," ,
41                        "'store_info': {" ,
42                        "'id': 'SZTX001'" ,
43                        "}" ,
44                        "}" ,
45                        "}").replace("'","\"");
46        HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/transactions/app");
47        httpPost.addHeader("Accept", "application/json");
48        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
49        httpPost.setEntity(new StringEntity(unifiedOrderBody));
50        CloseableHttpResponse response = httpClient.execute(httpPost);
51        //Process the response
52    }
53

Code example-Go

1//Call unified order API
2func unifiedOrder() {
3   orderRequestBody := `{
4	"sp_appid": "wx2421b1c4370ec43b",
5	"sp_mchid": "10000100",
6	"sub_mchid": "20000100",
7	"sub_appid": "wx352671b037b437ec",
8	"out_trade_no": "20150806125346",
9	"merchant_category_code": "1011",
10	"payer": {
11		"sp_openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
12	},
13	"notify_url": "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php",
14	"trade_type": "APP",
15	"amount": {
16		"total": 10000,
17		"currency": "HKD"
18	},
19	"attach": "Payment test",
20	"description": "In-APP Pay test",
21	"goods_tag": "",
22	"detail": {
23		"cost_price": 10000,
24		"receipt_id": "1234",
25		"goods_detail": [{
26			"goods_id": "iphone6s_16G",
27			"wxpay_goods_id": "1001",
28			"goods_name": "iPhone6s 16G",
29			"quantity": 1,
30			"price": 528800
31		}]
32	},
33	"scene_info": {
34		"payer_client_ip": "14.23.150.211",
35		"device_ip": "59.37.125.32",
36		"device_id": "013467007045764",
37		"operator_id": "P001",
38		"store_info": {
39			"id": "SZTX001"
40		}
41	}
42}`
43   result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/transactions/app", orderRequestBody)
44   if err != nil {
45      // Process error
46   }
47   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
48}
49

Code example-PHP

1
2// Call Unified Order API
3public function unifiedOrder($instance){
4    try {
5        $resp = $instance
6            ->chain('v3/global/transactions/app')
7            ->post(['json' => [
8                'sp_appid'     => 'wxdace645e0bc2c424',
9                'sp_mchid'     => '10000100',
10                'sub_mchid'    => '20000100',
11				'sub_appid'		=> 'wx352671b037b437ec',
12                'out_trade_no' => 'YX201710140020Z',
13                'merchant_category_code' => '4111',
14                'notify_url'   => 'https://weixin.qq.com/',
15                'trade_type'   => 'APP',
16                'amount'       => [
17                    'total'    => 1,
18                    'currency' => 'USD'
19                ],	
20				'payer'			=> [
21					'sub_openid' => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'
22				],
23                'attach'  => 'mini-program payment test',
24                'description'  => 'Image形象店-深圳腾大-QQ公仔',
25                'goods_tag'  => '001'
26            ]]);
27
28        echo $resp->getStatusCode(), PHP_EOL;
29        echo $resp->getBody(), PHP_EOL;
30    } catch (Exception $e) {
31        // EXception handling
32        echo $e->getMessage(), PHP_EOL;
33        if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
34            $r = $e->getResponse();
35            echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
36            echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
37        }
38        echo $e->getTraceAsString(), PHP_EOL;
39    }
40}
41

Critical parameters:

notify_url: For the address used to receive the asynchronous payment result callback notification, the notification URL must be a URL that is accessible by the external network and cannot carry any parameters. Please use an HTTPS protocol link

out_trade_no: internal order ID in merchant’s system

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.2 APP Pull-up Payment

Procedure: Get the necessary parameter prepay_id to initiate payment through the APP payment order interface, and use the SDK provided by WeChat Pay to pull up APP payment according to the rules in the interface definition.

The APP Pull-up Payment parameters must be calculated according to the signature rules:

Construct Signature String

1   There are four lines in the signature string, and each line is a parameter. Each line ends with \n (Newline character; the ASCII code value is 0x0A), and even the last line shall ends with \n.If the parameter itself ends with \n, an additional \n is also required
2

Fields and formats involved in the signature:

1   APPID
2   Timestamp
3   Random string
4   Prepayment transaction session
5

Data Example:

1   wx8888888888888888
2   1414561699
3   5K8264ILTKch16CQ2502SI8ZNMTM67VS
4   WX1217752501201407033233368018
5

Calculate Signature Value:

The signature functions provided by most programming languages support signature data for signing. 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.
Here we use command lines to demonstrate how to generate a signature.

1$ echo -n -e \
2"wx8888888888888888\n1414561699\n5K8264ILTKCH16CQ2502SI8ZNMTM67VS\nWX1217752501201407033233368018\n" \
3  | openssl dgst -sha256 -sign apiclient_key.pem \
4  | openssl base64 -A
5  uOVRnA4qG/MNnYzdQxJanN+zU+lTgIcnU9Bx7cob8H/4nLBiCwIUFluw==
6

Code example-IOS

1PayReq *request = [[[PayReq alloc] init] autorelease];
2request.partnerId = @"10000100";
3request.prepayId= @"1101000000140415649af9fc314aa427";
4request.package = @"Sign=WXPay";
5request.nonceStr= @"a462b76e7436e98e0ed6e13c64b4fd1c";
6request.timeStamp= @"1397527777";
7request.sign= @"oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==";
8[WXApi sendReq:request];
9

Code example-Android

1IWXAPI api;
2PayReq request = new PayReq();
3request.appId = "wxd930ea5d5a258f4f";
4request.partnerId = "1900000109";
5request.prepayId= "1101000000140415649af9fc314aa427",;
6request.packageValue = "Sign=WXPay";
7request.nonceStr= "1101000000140429eb40476f8896f4c9";
8request.timeStamp= "1398746574";
9request.sign= "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYDSs2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==";
10api.sendReq(request);

Critical parameters:

paySign: constructed payment signature

package:Specify as the static value “Sign=WXPay”

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.3 Query

Procedure: The merchant is required to take the initiative to check the order status after placing an order for a certain period of time.

Code example-JAVA

1
2    @Test
3    //Call query order API
4    public void queryOrderTest() throws IOException {
5        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/transactions/id/" +
6                "4200123456789000?sub_mchid=100012321&sp_mchid=1900000000");
7        httpGet.addHeader("Accept", "application/json");
8        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
9        CloseableHttpResponse response = httpClient.execute(httpGet);
10        //Process the response 
11    }
12

Code example-Go

1//Call query order API
2func queryOrder() {
3   result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/transactions/id/4200000000000000?sub_mchid=100012321&sp_mchid=1900000000")
4   if err != nil {
5      // Process error
6   }
7   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
8}
9

Code example-PHP

1
2// Call query order API
3public function queryOrder($instance)
4{
5    try {
6        $resp = $instance
7            ->v3->global->transactions->outTradeNo->_out_trade_no_
8            ->get([
9                    'query' => [
10                'sp_mchid'     => '1900000000',
11                'sub_mchid'    => '100012321',
12                    ],
13                    'out_trade_no' => 'YX0001'
14                ]
15            );
16
17        echo $resp->getStatusCode(), PHP_EOL;
18        echo $resp->getBody(), PHP_EOL;
19    } catch (Exception $e) {
20        // Exception handling
21	}
22}

Critical parameters:

id: WeChat payment order ID

out_trade_no: internal order ID in merchant’s system

Query with any order ID above. Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.4 Refund

Procedure: After the payment is completed and the payment status is SUCCESS, call this interface to submit a refund request.

Code example-JAVA

1
2    @Test
3    //Call Refund API
4    public void refundTest() throws IOException {
5		String refundBody = String.join("\n" ,
6                		"{" ,
7                        "'sp_appid': 'wx2421b1c4370ec43b', " ,
8                        "'sp_mchid': '10000100'," ,
9                        "'sub_mchid': '20000100'," ,
10                        "'transaction_id': '1008450740201411110005820873'," ,
11                        "'out_refund_no': 'R20150806125346'," ,
12                        "    'amount' : {" ,
13                        "        'refund': 5," ,
14                        "        'total':10," ,
15                        "         'currency':'HKD'" ,
16                        "    }," ,
17                        "    'reason': 'The item has been sold out.'," ,
18                        "    'source': 'REFUND_SOURCE_UNSETTLED_FUNDS'" ,
19                        "}").replace("'","\"");
20        HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/refunds");
21        httpPost.addHeader("Accept", "application/json");
22        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
23        httpPost.setEntity(new StringEntity(refundBody));
24        CloseableHttpResponse response = httpClient.execute(httpPost);
25        //Process the response 
26    }
27

Code example-Go

1
2func refund() {
3   refundRequestBody := `{
4    "sp_appid": "wx2421b1c4370ec43b",
5    "sp_mchid": "10000100",
6    "sub_mchid": "20000100",
7    "transaction_id": "1008450740201411110005820873",
8    "out_refund_no": "R20150806125346",
9    "amount" : {
10        "refund": 50,
11        "total":100,
12         "currency":"HKD"
13    },
14    "reason": "The item has been sold out",
15    "source": "REFUND_SOURCE_UNSETTLED_FUNDS"
16}`
17   result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/refunds", refundRequestBody)
18   if err != nil {
19      // Process error
20   }
21   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
22}

Code example-PHP

1
2// Call Refund API
3public function refundOrder($instance){
4    try {
5        $resp = $instance
6            ->chain('v3/global/refunds')
7            ->post(['json' => [
8                'sp_appid'     => 'wxdace12300000000',
9                'sp_mchid'     => '1900000000',
10                'sub_mchid'    => '100012321',
11                'out_trade_no' => 'YX0001',
12                "out_refund_no"=> "REFUNDYX0001",
13                'amount'       => [
14                    'refund'   => 1,
15                    'total'    => 1,
16                    'currency' => 'USD'
17                ]
18            ]]);
19        echo $resp->getStatusCode(), PHP_EOL;
20        echo $resp->getBody(), PHP_EOL;
21    } catch (Exception $e) {
22        // Exception handling
23    }
24}

Critical parameters:

out_trade_no: internal order ID in merchant’s system

transaction_id: WeChat order ID

out_refund_no: internal refund No. in merchant’s system, which should correspond to order ID

notify_url: address for receiving refund results

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.5 Query one refund

Procedure: After submitting a refund request, query the refund status by calling this interface.

Code example-JAVA

1
2    @Test
3    //Call Query Refund API
4    public void querySingleRefundTest() throws IOException {
5        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/refunds/out-refund-no/RYX001?sub_mchid=100012321&sp_mchid=1900000000");
6        httpGet.addHeader("Accept", "application/json");
7        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
8        CloseableHttpResponse response = httpClient.execute(httpGet);
9        //Process the response 
10    }
11

Code example-Go

1
2//Query single refund API
3func querySingleRefund() {
4   result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/refunds/out-refund-no/REFUNDYX001?sub_mchid=100012321&sp_mchid=1900000000")
5   if err != nil {
6      // Process error
7   }
8   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
9}

Code example-PHP

1
2// Call Query Refund API
3public function queryRefund($instance){
4    try {
5        $resp = $instance
6            ->v3->global->refunds->outRefundNo->_out_refund_no_
7            ->get([
8                'query' => [
9                    'sp_mchid' => '1900000000',
10                    'sub_mchid'=> '100012321'
11                ],
12                'out_refund_no' => 'REFUNDYX0001'
13            ]);
14
15        echo $resp->getStatusCode(), PHP_EOL;
16        echo $resp->getBody(), PHP_EOL;
17    } catch (Exception $e) {
18        // Exception handling
19    }
20}

Critical parameters:

out_trade_no: internal refund No. in merchant’s system

refund_id:WeChat payment refund order ID

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.6 Query all refunds

Procedure: After submitting a refund request, query all refund information corresponding to one payment order.

Code example-JAVA

1
2    @Test
3    //Call Query All Refund API
4    public void queryAllRefundTest() throws IOException {
5        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/refunds" +
6                "?out_trade_no=YX202111100020&count=10&offset=0&sp_mchid=1900000000&sub_mchid=100012321");
7        httpGet.addHeader("Accept", "application/json");
8        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
9        CloseableHttpResponse response = httpClient.execute(httpGet);
10        //Process the response 
11    }
12

Code example-Go

1
2//Query all refund API
3func queryAllRefund() {
4   result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/refunds/out-refund-no/out_trade_no=YX001&count=10&offset=0&sp_mchid=1900000000&sub_mchid=100012321")
5   if err != nil {
6      // Process error
7   }
8   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
9}

Code example-PHP

1
2// Call Query All Refund API
3public function queryAllRefund($instance){
4    try {
5        $resp = $instance
6            ->v3->global->refunds
7            ->get([
8                'query' => [
9                    'out_trade_no' => 'YX001',
10                    'count' => 10,
11                    'offset' => 0,
12                    'sp_mchid' => '1900000000',
13                    'sub_mchid'=> '100012321'
14                ]
15            ]);
16
17        echo $resp->getStatusCode(), PHP_EOL;
18        echo $resp->getBody(), PHP_EOL;
19    } catch (Exception $e) {
20        // Exception handling
21    }
22}

Critical parameters:

out_trade_no: Internal order ID in merchant’s system

transaction_id: WeChat order ID

offset: Start position of paging

count: Number of records returned in a single page, up to 20 records.

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.7 Download statement of account

Procedure: Merchants can download the historical transaction statements by calling this interface on a daily basis, and correct the payment information by reconciling the statements.

Code example-JAVA

1
2    @Test
3    //Downloading Reconciliation API
4    public void downloadReconTest() throws IOException {
5        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/statements?date=20220401&mchid=1900000000");
6        httpGet.addHeader("Accept", "application/json");
7        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
8        CloseableHttpResponse response = httpClient.execute(httpGet);
9        //Process the response 
10    }
11

Code example-Go

1
2//Download recon file API
3func downloadReconFile() {
4   result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/statements?date=20220401&mchid=1900000000")
5   if err != nil {
6      // Process error
7   }
8   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
9}

Code example-PHP

1
2//Downloading Reconciliation API
3public function downloadReconFile($instance){
4    try {
5        $resp = $instance
6            ->v3->global->statements
7            ->get([
8                'query' => [
9                    'date' => '20220401',
10                    'mchid' => '1900000000'
11                ]
12            ]);
13
14        echo $resp->getStatusCode(), PHP_EOL;
15        echo $resp->getBody(), PHP_EOL;
16    } catch (Exception $e) {
17        // Exception handling
18    }
19}

Critical parameters:

date: Statement date, format: 20180103

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.8 Close and order

Procedure: After placing an order, call this interface to close the orders not paid, allowing the merchant replace the order ID to re-order and initiate payment or avoid repeated payment by the user after quiting merchant's system without accepting the order.

Code example-JAVA

1
2    @Test
3    //Call close order API
4    public void closeOrderTest() throws IOException {
5		String closeBody = String.join("\n" ,
6                		"{" ,
7                        "    'sp_mchid': '10000100'," ,
8                        "    'sub_mchid': '20000100'" ,
9                        "}").replace("'","\"");
10        HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/transactions/out-trade-no/YX001/close");
11        httpPost.addHeader("Accept", "application/json");
12        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
13        httpPost.setEntity(new StringEntity(closeBody));
14        CloseableHttpResponse response = httpClient.execute(httpPost);
15        System.out.println(response.getStatusLine().getStatusCode());
16    }
17

Code example-Go

1
2//Call close order API
3func closeOrder() {
4   closeOrderBody := `{
5    "sp_mchid": "10000100",
6    "sub_mchid": "20000100"
7   }`
8   result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/transactions/out-trade-no/YX001/close", closeOrderBody)
9   if err != nil {
10      // Process error
11   }
12   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
13}

Code example-PHP

1
2// Call close order API
3public function closeOrder($instance){
4    try {
5        $resp = $instance
6            ->v3->global->transactions->outTradeNo->_out_trade_no_->close
7            ->post([
8                'json' => [
9                'sp_mchid'     => '10000100',
10                'sub_mchid'    => '20000100'
11                ],
12                'out_trade_no' => 'YX0001'
13            ]);
14        echo $resp->getStatusCode(), PHP_EOL;
15        echo $resp->getBody(), PHP_EOL;
16    } catch (Exception $e) {
17        // Exception handling
18    }
19}

Critical parameters:

out_trade_no: Internal order ID in merchant’s system

transaction_id: WeChat order ID

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.9 Download platform certificate

Procedure: Before calling other interfaces, call this interface to download the platform certificate to verify the signature of the returned message and decrypt the encrypted fields therein.

Code example-JAVA

1
2    @Test
3    //Call certificate downloading API
4    public void certDownloadingTest() throws IOException {
5        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/certificates");
6        httpGet.addHeader("Accept", "application/json");
7        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
8
9        CloseableHttpResponse response = httpClient.execute(httpGet);
10        //Process the response 
11//Get the response body: EntityUtils.toString(response.getEntity());
12//Get the response status code: response.getStatusLine().getStatusCode();
13
14        //Instead of calling the API to download platform certificate,
15        //We also recommend use the certificateMenager to get the valid certificate
16        verifier.getValidCertificate();
17    }
18

Code example-Go

1
2//Calling download certificate API
3func downloadCert() {
4   result, err := client.Get((ctx, "https://apihk.mch.weixin.qq.com/v3/global/certificates"))
5   if err != nil {
6      if core.IsAPIError(err, "INVALID_REQUEST") {
7         //Process invalid request
8      }
9      // Process other error
10   }
11   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
12}

Code example-PHP

1
2// Call download certificate API
3public function downloadCert($instance){
4    $resp = $instance->chain('v3/global/certificates')->get(
5        ['debug' => true] // debug mode,https://docs.guzzlephp.org/en/stable/request-options.html#debug
6    );
7    echo $resp->getBody(), PHP_EOL;
8}

Critical parameters: None

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.10 Notification of payment results

Procedure: After the user makes the payment, WeChat Pay will push the payment result notification to the notify_url introduced while placing the order, and the merchant needs to feed back accordingly after receiving the notification.

Notes:

  • The same notification may be sent to the merchant system multiple times. The merchant system must be able to process duplicate notifications properly. It is recommended to first check the status of the corresponding business data when receiving a notification for processing, and determine whether the notification has been processed. If it has not been processed, the notification should be processed once again; if it has been processed, the success result should be returned. Before status checking and processing of the business data, a data lock should be used to implement concurrency control and avoid data confusion caused by function reentry.

  • If no callback is received from WeChat side after all notification frequencies (4 hours), the merchant shall call the Query Order interface to confirm the order status.

 

Special reminder: The merchant system must perform signature verification for the content of the notification of payment results, and verify whether the returned order amount is consistent with that of the merchant side, thus preventing any "false notifications" and even loss of funds caused by data leakage.

3.2.10.1 Notification Rules

After the payment is made, WeChat will send the related payment result and user information to the merchant. The merchant needs to receive and process the information, and return the response successfully. Only the successful payments will be notified.

When interacting with the background notification, if the response received by WeChat from the merchant is not success or timed out, WeChat thinks the notification fails. WeChat will periodically resend the notification according to the specified policy to maximize the success rate of the notification, but WeChat does not guarantee final success of the notification. ((Notification frequency is 15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - total 24h and 4m)) for repeated notification.

3.2.10.2 Notification Message

The payment result notification accesses the notification URL set by the merchant using the POST method, and the notification data is transmitted through the request body (BODY) in the JSON format. The data in the notification includes the encrypted payment result details.

The certificate decryption process is detailed as follows

1、Obtain the merchant's key from the merchant platform, and record it as “key”.

2、Get the corresponding parameters “nonce” and “associated_data” for the algorithm described in “algorithm” (currently AEAD_AES_256_GCM).

3、Decrypt “ciphertext” with “key”, “nonce” and “associated_data” (decode base64 for ciphertext first, and then decrypt it) to obtain the certificate content

Notes:

For the interface details of the AEAD_AES_256_GCM algorithm, please refer to rfc5116. The length of the key used in Wechat Pay is 32 bytes, the length of the random string nonce is 12 bytes, and the length of associated_data is less than 16 bytes and may be empty.

3.2.10.3 Notification Signature

Encryption does not guarantee that the notification requests come from WeChat. WeChat will sign the notification sent to the merchant and put the signature value in the HTTP header Wechatpay-Signature of the notification. Merchants should verify the signature to confirm that the request comes from WeChat, and not any other third parties. For the signature verification algorithm, please refer to WeChat Pay API V3 Signature Verification.

3.2.10.4 Callback Example

Notification of Successful Payments

1
2{
3    "id":"EV-2018022511223320873",
4    "create_time":"20180225112233",
5    "resource_type":"encrypt-resource",
6    "event_type":"TRANSACTION.SUCCESS",
7    "resource" : {
8        "algorithm":"AEAD_AES_256_GCM",
9        "ciphertext": "...",
10        "nonce": "...",
11        "associated_data": ""
12    }
13}
14

An example of the resource object obtained after the merchant decrypts the resource object

1
2{
3    "id": "1008450740201411110005820873",
4    "sp_appid": "wx2421b1c4370ec43b",
5    "sp_mchid": "10000100",
6    "sub_mchid": "20000100",
7    "out_trade_no": "20150806125346",
8    "payer": {
9      "sp_openid": "oUpF8uN95-Ptaags6E_roPHg7AG0"
10    },
11    "amount" : {
12        "total": 528800,
13        "currency": "HKD",
14        "payer_total": 518799,
15        "payer_currency": "CNY",
16        "exchange_rate" : {
17            "type": "SETTLEMENT_RATE",
18            "rate": 8000000
19        }
20    },
21    "trade_type": "MICROPAY",
22    "trade_state": "SUCCESS",
23    "trade_state_desc": "支付成功",
24    "bank_type": "CCB_DEBIT",
25    "attach": "支付测试",
26    "success_time": "2018-06-08T10:34:56+08:00",
27    "promotion_detail":[
28        {
29            "promotion_id":"109519",
30            "name":"单品惠-6",
31            "scope":"SINGLE",
32            "type":"DISCOUNT",
33            "amount":1,
34            "currency":"HKD",
35            "activity_id":"931386",
36            "wechatpay_contribute_amount":1,
37            "merchant_contribute_amount":0,
38            "other_contribute_amount":0,
39            "goods_detail":[
40                {
41                    "goods_id":"iphone6s_16G",
42                    "goods_remark":"商品备注",
43                    "quantity":1,
44                    "price":528800
45                }
46            ]
47        }
48    ]
49}
50
3.2.10.5 Notification Response

The http response code of the payment notification should be either 200 or 204 for normal receipt. In case of any callback processing error, the HTTP status code of the response should be 500 or 4xx.

Notes:

When the merchant background returns the failure response to Weixin Pay, Weixin Pay will record the response message. It is recommended that the merchant return the message in the following format.

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.11 Refund notification

Procedure: After the user makes the payment, WeChat Pay will push the payment result notification to the notify_url introduced while placing the order, and the merchant needs to feed back accordingly after receiving the notification.

Notes:

  • The same notification may be sent to the merchant system multiple times. The merchant system must be able to process duplicate notifications properly. It is recommended to first check the status of the corresponding business data when receiving a notification for processing, and determine whether the notification has been processed. If not, the notification should be processed; if it has been processed, the success result should be returned. Before status checking and processing of the business data, a data lock should be used to implement concurrency control and avoid data confusion caused by function reentry.

  • If no callback is received from WeChat side after all notification frequencies (4 hours), the merchant shall call the Query Order interface to confirm the order status.

 

Special reminder: The merchant system must perform signature verification for the content of the notification of payment results, and verify whether the returned order amount is consistent with that of the merchant side, thus preventing any "false notification" and even capital loss caused by data leakage.

3.2.11.1 Notification Rules

In case of any refund status changes, WeChat will send the related refund results to the merchant.

When interacting with the background notification, if the response received by WeChat from the merchant is not success or timed out, WeChat thinks the notification fails. WeChat will periodically resend the notification according to the specified policy to maximize the success rate of the notification, but WeChat does not guarantee final success of the notification. ((Notification frequency is 15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - total 24h and 4m))

3.2.11.2 Notification Message

The deduction result notification accesses the notification URL set by the merchant using the POST method, and the notification data is transmitted through the request body (BODY) in the JSON format. The data in the notification includes the details about the encrypted refund result.

The certificate decryption process is detailed as follows

1、Obtain the merchant's key from the merchant platform, and record it as “key”.

2、Get the corresponding parameters “nonce” and “associated_data” for the algorithm described in “algorithm” (currently AEAD_AES_256_GCM).

3、Decrypt “ciphertext” with “key”, “nonce” and “associated_data” (perfomr base64 decoding for ciphertext first, and then decrypt it) to obtain the certificate content

Notice

For the interface details of the AEAD_AES_256_GCM algorithm, please refer to rfc5116. The length of the key used in Wechat Pay is 32 bytes, the length of the random string nonce is 12 bytes, and the length of associated_data is less than 16 bytes and may be empty.

3.2.11.3 Notification Signature

Encryption does not guarantee that the notification requests come from WeChat. WeChat will sign the notification sent to the merchant and put the signature value in the HTTP header Wechatpay-Signature of the notification. Merchants should verify the signature to confirm that the request comes from WeChat, and not any other third parties. For the signature verification algorithm, please refer to WeChat Pay API V3 Signature Verification.

3.2.11.4 Callback Example

Notification of Refund Result

1
2{
3	"id": "EV-2018022511223320873",
4	"create_time": "2018-06-08T10:34:56+08:00",
5	"resource_type": "encrypt-resource",
6	"event_type": "REFUND.SUCCESS",
7	"summary": "退款成功",
8	"resource": {
9		"original_type": "refund",
10		"algorithm": "AEAD_AES_256_GCM",
11		"ciphertext": "...",
12		"associated_data": "",
13		"nonce": "..."
14	}
15}
16

An example of the resource object obtained after the merchant decrypts the resource object

1
2{
3    "sp_mchid": "1900000100",
4    "sub_mchid": "1900000109",
5    "transaction_id": "1008450740201411110005820873",
6    "out_trade_no": "20150806125346",
7    "refund_id": "50200207182018070300011301001",
8    "out_refund_no": "7752501201407033233368018",
9    "refund_status": "SUCCESS",
10    "success_time": "2018-06-08T10:34:56+08:00",
11    "recv_account": "招商银行信用卡0403",
12    "fund_source": "REFUND_SOURCE_UNSETTLED_FUNDS",
13    "amount" : {
14        "total": 528800,
15        "currency": "HKD",
16        "refund": 528800,
17        "payer_total": 528800,
18        "payer_refund": 528800,
19        "payer_currency": "HKD",
20        "exchange_rate" : {
21            "type": "SETTLEMENT_RATE",
22            "rate": 100000000
23        }
24    }
25}
26
3.2.11.5 Notification Response

The http response code of the refund notification should be either 200 or 204 for normal receipt. In case of any callback processing error, the HTTP status code of the response should be 500 or 4xx.

Note:

When the merchant background returns the failure response to Weixin Pay, Weixin Pay will record the response message. It is recommended that the merchant return the message in the following format.

Please refer to the API document for ordering by scanning code for other critical parameters.

3.2.12 API for querying fund settlement details

Procedure: After transactions are completed, the merchant can query the settled fund details (sette_state is SETTLED) or the unsettled fund details (sette_state is UNSETTLE) by settlement date.

Note:

This API is designed for the cross-border acquiring institutions/direct merchants. In particular, if institutions/merchants in Japan and Macao have enabled Hong Kong Wallet services, they need to connect this API.

Code example-JAVA

1
2    @Test
3    //Query Settlement API
4    public void querySettlementTest() throws IOException {
5        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/settle/settlements" +
6                "?sub_mchid=100012321&settle_state=SETTLED&settle_start_date=20221225" +
7                "&settle_end_date=20221226&offset=10&limit=5");
8        httpGet.addHeader("Accept", "application/json");
9        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
10        CloseableHttpResponse response = httpClient.execute(httpGet);
11        //Process the response 
12    }
13

Code example-Go

1
2//Query settlement API
3func querySettlement() {
4   result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/settle/settlements?sub_mchid=100012321&settle_state=SETTLED&settle_start_date=20221225&settle_end_date=20221226&offset=10&limit=5")
5   if err != nil {
6      // Process error
7   }
8   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
9}

Code example-PHP

1
2//Querying Settlement Info API
3public function querySettlementInfo($instance){
4    try {
5        $resp = $instance
6            ->v3->global->settle->settlements
7            ->get(['query' => [
8                'sub_mchid' => '100012321',
9                'settle_state' => 'SETTLED',
10                'settle_start_date' => '20221225',
11                'settle_end_date' => '20221226',
12                'offset' => '10',
13                'limit' => '5'
14            ]]);
15
16        echo $resp->getStatusCode(), PHP_EOL;
17        echo $resp->getBody(), PHP_EOL;
18    } catch (\Exception $e) {
19        // Exception handling
20    }
21}

Critical parameters:

settle_state: Fund settlement status, enumeration value:

SETTLED:settled

UNSETTLE:not settled

Please refer to the API document for ordering by scanning code for other critical parameters.

About  WeChat  Pay

Powered By Tencent & Tenpay Copyright©

2005-2025 Tenpay All Rights Reserved.

Contact Us
Wechat Pay Global

WeChat Pay Global

Contact Us

Customer Service Tel

+86 571 95017

9:00-18:00 Monday-Friday GMT+8

Business Development

wxpayglobal@tencent.com

Developer Support

wepayTS@tencent.com

Wechat Pay Global

About Tenpay
Powered By Tencent & Tenpay Copyright© 2005-2025 Tenpay All Rights Reserved.