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');

 

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 Mini Program Pre-signing

Procedure: As described in the Step 2.2 of the Sequence Diagram, when the user agrees to the withholding services in the merchant’s mini program, the merchant should request the Mini Program - Pre-signing API to obtain the pre-signing ID (session_id) for futher pulling up the signing of mini program.

Code example-JAVA

1
2//Obtaining Signing Session ID API for mini-program signing
3public void miniProgramSignTest() throws IOException {
4	String miniprogramSignBody = ""
5	"{
6	"expired_time": "2021-11-20T13:29:35+08:00",
7	"openid": "of8YZ6A_ySrPYzjX7joXo_0000",
8	"out_contract_code": "20220614out_contract_code",
9	"plan_id": 10001,
10	"sp_appid": "wx7bc9000000000000",
11	"sub_mchid": "90325355",
12	"success_notify_url": "https://yoursite.com",
13	"user_display_name": "mike"
14}
15""
16";
17HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/miniprogram-pre-entrust-sign");
18httpPost.addHeader("Accept", "application/json");
19httpPost.addHeader("Content-type", "application/json; charset=utf-8");
20httpPost.setEntity(new StringEntity(miniprogramSignBody));
21CloseableHttpResponse response = httpClient.execute(httpPost);
22//Process the response
23}
24

Code example-Go

1
2//Mini program signing API
3func miniProgramSigning() {
4	signBody := `{
5"expired_time": "2021-11-20T13:29:35+08:00",
6"openid": "of8YZ6A_ySrPYzjX7joXo_0000",
7"out_contract_code": "20220614out_contract_code",
8"plan_id": 10001,
9"sp_appid": "wx7bc9000000000000",
10"sub_mchid": "90325355",
11"success_notify_url": "https://yoursite.com",
12"user_display_name": "mike"
13}`
14	
15	result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/miniprogram-pre-entrust-sign", signBody)
16	if err != nil {
17		// Process error
18	}
19	log.Printf("%s", result.Response.Body)             

Code example-PHP

1
2//Mini Program Signing API
3  public function miniProgramSigning($instance)
4  {
5    try {
6      $resp = $instance
7        ->chain('v3/global/papay/contracts/miniprogram-pre-entrust-sign')
8        ->post([
9          'json' => [
10            "sp_appid" => "wx7bc9000000000000",
11			"sub_mchid" => "90325355",
12            "openid" => "of8YZ6A_ySrPYzjX7joXo_0000",
13            "out_contract_code" => "20220614out_contract_code",
14            "plan_id" => 10001,
15            "success_notify_url" => "https://www.yoursite.com",
16            "user_display_name" => "mike",
17			"expired_time" => "2021-11-20T13:29:35+08:00",
18          ]
19        ]);
20
21      echo $resp->getStatusCode(), PHP_EOL;
22      echo $resp->getBody(), PHP_EOL;
23    } catch (\Exception $e) {
24      // Exception handling
25    }
26  }

3.2.2 Mini Program Pull-up Signing

Procedure:

  1. Parse the Body json string returned by the 3.1 Mini Program Pre-signing interface, get the session_id therein. The return example is as follows: Pull up the signing mini program with the session_id obtained in the previous step

  2. Pull up the signing mini program with the session_id obtained in the previous step

Code example:

1				
2 var session_id = "201710180325670965"; // obtained from miniprogram-pre-entrust-sign
3// start signing process
4wx.navigateToMiniProgram({
5	appId: wxbd687630cd02ce1d,
6	path: 'pages/Oversea/walletSelect?sessionId=' + session_id,
7	extraData: {},
8	success(res) {
9		// Jumped to the signing mini program successfully
10	},
11	fail(res) {
12		// Failed to jump to the signing mini program 
13	}
14})
15
16// After signing process, user will return back to merchant's miniprogram
17App({
18	onShow(res)  {
19		if  (res.scene  ===  1038)  {  //Scenario value 1038: return from the opened min program
20			const { appId, extraData } = res.referrerInfo
21			if  (appId  ==  'wxbd687630cd02ce1d')  {  // appId is wxbd687630cd02ce1d: jump back from the signing min program
22				if  (typeof  extraData  ==  'undefined') {
23					// TODO
24					// The client min program is not sure of the signing result and needs to request the merchant-side background to confirm the signing result
25					return;
26				}
27				if (extraData.return_code  ==  'SUCCESS') {
28					// TODO
29					// The client min program signs successfully and needs to request the merchant-side background to confirm the signing result
30					var  contract_id  =  extraData.contract_id
31					return;
32				} 
33				else  {
34					// TODO
35					// Signing failed
36					return;
37				}
38			}
39		}
40	}
41})
42    			

3.2.3 Notification of Signing Results

Procedure: After the user has signed the contract, WeChat Pay will push the notification of signing results to the success_notify_url introduced while signing the contract, and the merchant needs to feed back accordingly after receiving the notification.

Example of signing notification

1
2 // 签约成功回调 http body 数据示例
3{
4    "id":"EV-2018022511223320873",
5    "create_time":"2022-06-14T14:01:35+08:00",
6    "resource_type":"encrypt-resource",
7    "event_type":"PAPAY.SIGN",
8	"summary": "签约成功",
9    "resource" : {
10        "algorithm":"AEAD_AES_256_GCM",
11        "ciphertext": "...",
12        "nonce": "dAvnRJWFOfdL",
13        "associated_data": "papay"
14    }
15}
16// 对 resource.ciphertext 解密后得到的数据示例如下:
17{
18   "sp_mchid":"10000091",
19   "sub_mchid":"10000097",
20   "out_contract_code":"100001256",
21   "plan_id":123,
22   "contract_id":"Wx15463511252015071056489715",
23   "sp_appid":"wxcbda96de0b165486",
24   "openid":"ouFhd5X9s9WteC3eWRjXV3lea123",
25   "operate_time":"2015-09-01T10:00:00+08:00"
26}
27
28// 解约成功回调 http body 数据示例
29{
30   "id":"2646bee0-aef8-5804-9752-ef1a56170fdf",
31   "create_time":"2022-06-14T16:31:53+08:00",
32   "resource_type":"encrypt-resource",
33   "event_type":"PAPAY.TERMINATE",
34   "summary":"解约成功",
35   "resource":{
36      "original_type":"papay",
37      "algorithm":"AEAD_AES_256_GCM",
38      "ciphertext": "...",
39      "associated_data":"papay",
40      "nonce":"SExJ2Xxx7sr1"
41   }
42}
43// 对 resource.ciphertext 解密后得到的数据示例如下:
44{
45	"sp_mchid": "10000091",
46	"sub_mchid": "10000097",
47	"out_contract_code": "100001256",
48	"plan_id": 123,
49	"contract_id": "Wx15463511252015071056489715",
50	"sp_appid": "wxcbda96de0b165486",
51	"openid": "ouFhd5X9s9WteC3eWRjXV3lea123",
52	"contract_termination_mode": "USER",
53	"operate_time": "2015-10-01T10:00:00+08:00"
54}
55

When the merchant receives the callback notification from WeChat Pay, it should update the locally stored user signing status and return the following http body to WeChat Pay after the update is completed, and the http status should be set to 200.

1
2{
3	"code": "SUCCESS",
4	"message": "OK"
5}
6    			

Notice

  • 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 details about the encrypted payment result.

  • 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 the WeChat Pay API V3 Signature Verification.

  • 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

  • The merchant should return a successful http response code of 200 or 204 after successfully receiving the callback notification

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

  • When interacting with the background notification, if the response received by WeChat from the merchant is nonconforming 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.4 Query Signing Results

Procedure: An exception is found with the merchant background, network, or server, and the merchant system does not receive the notification of signing results, the merchant may verify the signing status through the Query Signing Results API. WeChat Pay allows query the signing status by the merchant contract number (out_contract_code) or by the WeChat contract number (contract_id) returned by WeChat Pay.

Query the signing status by contract_id

Code example-JAVA

1
2//Querying Signing Status (By contract_id) API
3    public void querySignStatusTest() throws IOException {
4        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/202203242337333903387184301572?sub_mchid=10000097&sp_appid=wxcbda96de0b165486&sub_appid=wxcbda96de0b165484");
5        httpGet.addHeader("Accept", "application/json");
6        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
7        CloseableHttpResponse response = httpClient.execute(httpGet);
8        //Process the response
9    }
10

Code example-Go

1
2//Querying signing status API
3func querySigningStatus() {
4	result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/202203242337333903387184301572?sub_mchid=10000097&sp_appid=wxcbda96de0b165486&sub_appid=wxcbda96de0b165484")
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 Signing Status (By contract_id) API
3  public function querySignStatus($instance)
4  {
5    try {
6      $resp = $instance
7        ->v3->global->papay->contracts->_contract_id_
8        ->get([
9          'query' => [
10            'sp_appid' => 'wxcbda96de0000006',
11            'sub_appid' => 'wxcbda96de0000004',
12            'sub_mchid' => '110000000'
13          ],
14          'contract_id' => '202203242337333903387184301572'
15        ]);
16
17      echo $resp->getStatusCode(), PHP_EOL;
18      echo $resp->getBody(), PHP_EOL;
19    } catch (\Exception $e) {
20      // Exception handling
21    }
22  }

 

Query the signing status by out_contract_code

Code example-JAVA

1
2    //Querying Signing Status (By out_contract_code)
3    public void querySignStatusByContractCodeTest() throws IOException {
4        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/out-contract-code/100001261?sp_appid=wxcbda96de0b165486&sub_appid=wxcbda96de0b165484&plan_id=123");
5        httpGet.addHeader("Accept", "application/json");
6        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
7        CloseableHttpResponse response = httpClient.execute(httpGet);
8        //Process the response
9    }
10

Code example-Go

1
2//Querying signing status by out_contract_code API
3func querySigningStatusByContractCode() {
4	result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/out-contract-code/100001261?sp_appid=wxcbda96de0b165486&sub_appid=wxcbda96de0b165484&plan_id=123")
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 Signing Status (By out_contract_code) API
3  public function querySignStatusByContractCode($instance)
4  {
5    try {
6      $resp = $instance
7        ->v3->global->papay->contracts->outContractCode->_out_contract_code_
8        ->get([
9          'query' => [
10            'sp_appid' => 'wxcbda96de0000006',
11            'sub_appid' => 'wxcbda96de0000004',
12            'sub_mchid' => '110000000'
13          ],
14          '_out_contract_code_' => '100005698'
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:

contract_id: the unique contract number returned fro the WeChat side, such as: 202203242337333903387184301572

Out_contract_code: the contract number generated by the merchant side, such as: 100001261

sp_appid: appid bound to the service provider’s Official Account

sub_appid: mini program appid of the sub-merchant who initiates the signing

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

3.2.5 Deduction

Procedure: After signing the contract, the merchant can initiate a deduction using the corresponding contract ID

Code example-JAVA

1
2//Applying for Termination API
3public void deductionTest() throws IOException {
4		String deductionBody = """
5{
6	"sp_appid": "wxcbda96de0b165486",
7	"sub_mchid": "10000097",
8	"sub_appid": "wxcbda96de0b165484",
9	"description": "PAPAuto-debit支付测试",
10	"attach": "支付测试",
11	"notify_url": "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php",
12	"out_trade_no": "1217752501201407033233368018",
13	"goods_tag": "WXG",
14	"merchant_category_code": "1011",
15	"contract_id": "Wx15463511252015071056489715",
16	"amount": {
17		"total": 10000,
18		"currency": "HKD"
19	},
20	"scene_info": {
21		"device_ip": "59.37.125.32",
22		"device_id": "013467007045764"
23	}
24}
25""";
26        HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/papay/transactions");
27        httpPost.addHeader("Accept", "application/json");
28        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
29        httpPost.setEntity(new StringEntity(deductionBody));
30        CloseableHttpResponse response = httpClient.execute(httpPost);
31        //Process the response
32}
33

Code example-Go

1
2//Deduction API
3func deduction() {
4	body := `
5{
6	"sp_appid": "wxcbda96de0b165486",
7	"sub_mchid": "10000097",
8	"sub_appid": "wxcbda96de0b165484",
9	"description": "PAPAuto-debit支付测试",
10	"attach": "支付测试",
11	"notify_url": "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php",
12	"out_trade_no": "1217752501201407033233368018",
13	"goods_tag": "WXG",
14	"merchant_category_code": "1011",
15	"contract_id": "Wx15463511252015071056489715",
16	"amount": {
17		"total": 10000,
18		"currency": "HKD"
19	},
20	"scene_info": {
21		"device_ip": "59.37.125.32",
22		"device_id": "013467007045764"
23	}
24}
25`
26	result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/transactions", body)
27	if err != nil {
28		// Process error
29	}
30	log.Printf("%s", result.Response.Body)
31}               

Code example-PHP

1
2//Deduction API
3  public function deduction($instance)
4  {
5    try {
6      $resp = $instance
7        ->v3->global->papay->transactions
8        ->post([
9          'json' => [
10            "sp_appid" => "wxcbda96de0b165486",
11            "sub_mchid" => "10000097",
12            "sub_appid" => "wxcbda96de0b165484",
13            "description" => "PAPAuto-debit支付测试",
14            "attach" => "支付测试",
15            "notify_url" => "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php",
16            "out_trade_no" => "1217752501201407033233368018",
17            "goods_tag" => "WXG",
18            "merchant_category_code" => "1011",
19            "contract_id" => "Wx15463511252015071056489715",
20            "amount" => array(
21              "total" => 10000,
22              "currency" => "HKD"
23            ),
24            "scene_info" => array(
25              "device_ip" => "59.37.125.32",
26              "device_id" => "013467007045764"
27            )
28          ]
29        ]);
30      echo $resp->getStatusCode(), PHP_EOL;
31      echo $resp->getBody(), PHP_EOL;
32    } catch (\Exception $e) {
33      // Exception handling
34    }
35  }

Critical parameters:

contract_id: the user’s contract number, WeChat Pay verifies the contract relationship between the user and the merchant according to this field. NO_AUTH indicating that the contract does not exist will be returned if no contract has been signed or the user has terminated the contract.

3.2.6 Notification of Deduction Results

Procedure: WeChat Pay will send a asynchronous notification to the merchant, indicating the successful deduction, if any, through the notify_url parameter in the deduction request.

Notice

  • 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 details about the encrypted payment result.

  • 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 the WeChat Pay API V3 Signature Verification.

  • 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

  • The merchant should return a successful http response code of 200 or 204 after successfully receiving the callback notification

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

  • When interacting with the background notification, if the response received by WeChat from the merchant is nonconforming 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))

1
2// 扣款结果通知 http body 数据示例
3{
4    "id":"EV-2018022511223320873",
5    "create_time":"20180225112233",
6    "resource_type":"encrypt-resource",
7    "event_type":"TRANSACTION.SUCCESS",
8    "resource" : {
9        "algorithm":"AEAD_AES_256_GCM",
10        "ciphertext": "...",
11        "nonce": "...",
12        "associated_data": ""
13    }
14}
15// 对 resource.ciphertext 解密后得到的数据示例如下:
16{
17	"sp_mchid": "10000100",
18	"sp_appid": "wx2421b1c4370ec43b",
19	"sub_mchid": "20000100",
20	"out_trade_no": "20150806125346",
21	"transaction_id": "1008450740201411110005820873",
22	"attach": "支付测试",
23	"trade_type": "AUTH",
24	"bank_type": "CCB_DEBIT",
25	"success_time": "2018-06-08T10:34:56+08:00",
26	"trade_state": "SUCCESS",
27	"trade_state_desc": "支付成功",
28	"merchant_category_code": "1011",
29	"contract_id": "Wx15463511252015071056489715",
30	"payer": {
31		"sp_openid": "oUpF8uN95-Ptaags6E_roPHg7AG0"
32	},
33	"amount": {
34		"total": 528800,
35		"currency": "HKD",
36		"payer_total": 518799,
37		"payer_currency": "CNY",
38		"exchange_rate": {
39			"type": "SETTLEMENT_RATE",
40			"rate": 8000000
41		}
42	},
43	"promotion_detail": [{
44		"promotion_id": "109519",
45		"name": "单品惠-6",
46		"scope": "SINGLE",
47		"type": "DISCOUNT",
48		"amount": 1,
49		"currency": "HKD",
50		"activity_id": "931386",
51		"wechatpay_contribute_amount": 1,
52		"merchant_contribute_amount": 0,
53		"other_contribute_amount": 0,
54		"goods_detail": [{
55			"goods_id": "iphone6s_16G",
56			"goods_remark": "商品备注",
57			"quantity": 1,
58			"price": 528800
59		}]
60	}]
61}
62    			

3.2.7 Order Query API

Procedure: An exception is found with the merchant background, network, or server, and the merchant system does not receive the payment notification, the merchant may verify the order payment status through the Query Order interface.

Code example-JAVA

1
2	//Query transaction details by transaction_id   
3	public void queryOrderByIdTest() throws IOException {
4        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/4015463511252015071056489715?sub_mchid=10000097");
5        httpGet.addHeader("Accept", "application/json");
6        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
7        CloseableHttpResponse response = httpClient.execute(httpGet);
8        //Process the response
9    }
10
11	//Query transaction details by out_trade_no
12	public void queryOrderByNoTest() throws IOException {
13        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/out_trade_no/1217752501201407033233368018?sub_mchid=10000097");
14        httpGet.addHeader("Accept", "application/json");
15        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
16        CloseableHttpResponse response = httpClient.execute(httpGet);
17        //Process the response
18    }
19

Code example-Go

1
2//Query transaction details by transaction_id 
3func queryOrderById() {
4	result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/4015463511252015071056489715?sub_mchid=10000097")
5	if err != nil {
6		// Process error
7	}
8	log.Printf("%s", result.Response.Body)
9}
10
11
12//Query transaction details by out_trade_no
13func queryOrderByNo() {
14	result, err := client.Get(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/out_trade_no/1217752501201407033233368018?sub_mchid=10000097")
15	if err != nil {
16		// Process error
17	}
18	log.Printf("%s", result.Response.Body)
19}

Code example-PHP

1
2// Query transaction details by transaction_id 
3public function queryOrderById($instance)
4{
5    try {
6        $resp = $instance
7            ->v3->global->papay->transactions->_transaction_id_
8            ->get([
9                    'query' => [
10                		'sub_mchid'    => '10000097',
11                    ],
12                    'transaction_id' => '4015463511252015071056489715'
13                ]
14            );
15        echo $resp->getStatusCode(), PHP_EOL;
16        echo $resp->getBody(), PHP_EOL;
17    } catch (Exception $e) {
18        // Exception handling
19	}
20}
21
22// Query transaction details by out_trade_no
23public function queryOrderByNo($instance)
24{
25    try {
26        $resp = $instance
27            ->v3->global->papay->transactions->outTradeNo->_out_trade_no_
28            ->get([
29                    'query' => [
30                		'sub_mchid'    => '10000097',
31                    ],
32                    'out_trade_no' => '1217752501201407033233368018'
33                ]
34            );
35
36        echo $resp->getStatusCode(), PHP_EOL;
37        echo $resp->getBody(), PHP_EOL;
38    } catch (Exception $e) {
39        // Exception handling
40	}
41}

Critical parameters:

out_trade_no: order deduction ID at the merchant side

transaction_id:WeChat order ID

3.2.8 [Server] Close Order

Procedure: If payment for the merchant’s order fails and a new order ID needs to be generated to initiate payment again, the order closing interface needs to be called for the original order ID to avoid repeated payment; after an order is placed in the system, the user's payment times out and the system will exit without accepting the order, the order closing interface needs to be called to prevent the user from continuing operation.

Code example-JAVA

1
2//Applying for close order
3public void closeOrderTest() throws IOException {
4		String closeOrderBody = """
5{
6  "sub_mchid": "20000100"
7}
8""";
9        HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/out-trade-no/1217752501201407033233368018/reverse");
10        httpPost.addHeader("Accept", "application/json");
11        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
12        httpPost.setEntity(new StringEntity(closeOrderBody ));
13        CloseableHttpResponse response = httpClient.execute(httpPost);
14        //Process the response
15}
16

Code example-Go

1
2//Deduction API
3func closeOrder() {
4	body := `
5{
6  "sub_mchid": "20000100"
7}
8`
9	result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/transactions/out-trade-no/1217752501201407033233368018/reverse", body)
10	if err != nil {
11		// Process error
12	}
13	log.Printf("%s", result.Response.Body)
14}

Code example-PHP

1
2  // Call close order API
3  public function closeOrder($instance)
4  {
5    try {
6      $resp = $instance
7		->v3->global->papay->transactions->outTradeNo->_out_trade_no_->reverse
8        ->post([
9          'json' => [
10            'sub_mchid'    => '20000100'
11          ],
12          'out_trade_no' => '1217752501201407033233368018'
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: order deduction ID at the merchant side

3.2.9 [Server] Termination

Procedure: If the user intends to terminate the contract at the merchant side, the merchant needs to call the Termination API to terminate the contact.

Code example-JAVA

1
2//Applying for Termination API    
3public void deductionTest() throws IOException {
4		String terminateBody = """
5{
6"sp_appid": "wxcbda96de0b165486",
7"sub_mchid": "10000097",
8"sub_appid": "wxcbda96de0b165484", 
9"termination_note": "Cause of termination" 
10}
11""";
12        HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/100005698/terminate");
13        httpPost.addHeader("Accept", "application/json");
14        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
15        httpPost.setEntity(new StringEntity(terminateBody));
16        CloseableHttpResponse response = httpClient.execute(httpPost);
17        //Process the response
18    }
19

Code example-Go

1
2//Applying for Termination API
3func terminate() {
4	body := `
5{
6"sp_appid": "wxcbda96de0b165486",
7"sub_mchid": "10000097",
8"sub_appid": "wxcbda96de0b165484", 
9"termination_note": "Cause of termination" 
10}
11`
12	result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/papay/contracts/100005698/terminate", body)
13	if err != nil {
14		// Process error
15	}
16	log.Printf("%s", result.Response.Body)
17}

Code example-PHP

1
2  //Applying for Termination API
3  public function termination($instance)
4  {
5    try {
6      $resp = $instance
7        ->v3->global->papay->contracts->_contract_id_->terminate
8        ->post([
9          'json' => [
10            'sp_appid' => 'wxcbda96de0b165486',
11			'sub_mchid' => '10000097',
12			'sub_appid' => 'wxcbda96de0b165484',
13			'termination_note' => 'Cause of termination'
14          ],
15          '_contract_id_' => '100005698'
16        ]);
17      echo $resp->getStatusCode(), PHP_EOL;
18      echo $resp->getBody(), PHP_EOL;
19    } catch (\Exception $e) {
20      // Exception handling
21    }
22  }

Critical parameters:

contract_id: Deduction agreement ID at the WeChat side

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.