Login expired. Please log in again.

Feedback

0/300

Feedback

Submitted successfully

ok

Feedback

Network exception, please try again later

ok

Development Guidelines

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

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 H5 payment through this interface, obtain the mweb_url to call payment.

Code example


    @Test
    //Call Unified Order API
    public void unifiedOrderTest() throws IOException {
      String unifiedOrderBody = String.join("\n" ,
                		"{" ,
                        "'sp_appid': 'wx2421b1c4370ec43b'," ,
                        "'sp_mchid': '10000100'," ,
                        "'sub_mchid': '20000100'," ,
                        "'out_trade_no': '20150806125346'," ,
                        "'merchant_category_code': '1011'," ,
                        "'notify_url': 'https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php'," ,
                        "'trade_type': 'MWEB'," ,
                        "'amount': {" ,
                        "'total': 10000," ,
                        "'currency': 'HKD'" ,
                        "}," ,
                        "'attach': 'Payment test'," ,
                        "'description': 'In-APP Pay test'," ,
                        "'goods_tag': '," ,
                        "'detail': {" ,
                        "'cost_price': 10000," ,
                        "'receipt_id': '1234'," ,
                        "'goods_detail': [{" ,
                        "'goods_id': 'iphone6s_16G'," ,
                        "'wxpay_goods_id': '1001'," ,
                        "'goods_name': 'iPhone6s 16G'," ,
                        "'quantity': 1," ,
                        "'price': 528800" ,
                        "}]" ,
                        "}," ,
                        "'scene_info': {" ,
                        "'payer_client_ip': '14.23.150.211'," ,
                        "'device_ip': '59.37.125.32'," ,
                        "'device_id': '013467007045764'," ,
                        "'operator_id': 'P001'," ,
                        "'store_info': {" ,
                        "'id': 'SZTX001'" ,
                        "}" ,
                        "}" ,
                        "}").replace("'","\"");
        HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/transactions/mweb");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(unifiedOrderBody));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        //Process the response
    }

//Call unified order API
func unifiedOrder() {
   orderRequestBody := `{
	"sp_appid": "wx2421b1c4370ec43b",
	"sp_mchid": "10000100",
	"sub_mchid": "20000100",
	"out_trade_no": "20150806125346",
	"merchant_category_code": "1011",
	"notify_url": "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php",
	"trade_type": "MWEB",
	"amount": {
		"total": 10000,
		"currency": "HKD"
	},
	"attach": "Payment test",
	"description": "In-APP Pay test",
	"goods_tag": "",
	"detail": {
		"cost_price": 10000,
		"receipt_id": "1234",
		"goods_detail": [{
			"goods_id": "iphone6s_16G",
			"wxpay_goods_id": "1001",
			"goods_name": "iPhone6s 16G",
			"quantity": 1,
			"price": 528800
		}]
	},
	"scene_info": {
		"payer_client_ip": "14.23.150.211",
		"device_ip": "59.37.125.32",
		"device_id": "013467007045764",
		"operator_id": "P001",
		"store_info": {
			"id": "SZTX001"
		}
	}
}`
   result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/transactions/mweb", orderRequestBody)
   if err != nil {
      // Process error
   }
   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
}

// Call Unified Order API
public function unifiedOrder($instance){
    try {
        $resp = $instance
            ->chain('v3/global/transactions/mweb')
            ->post(['json' => [
                'sp_appid'     => 'wxdace645e0bc2c424',
                'sp_mchid'     => '10000100',
                'sub_mchid'    => '20000100',
                'out_trade_no' => 'YX201710140020Z',
                'merchant_category_code' => '4111',
                'notify_url'   => 'https://weixin.qq.com/',
                'trade_type'   => 'MWEB',
                'amount'       => [
                    'total'    => 1,
                    'currency' => 'USD'
                ],	
				'scene_info'   => [
					'payer_client_ip' => '14.23.150.211'
				],
                'attach'  => 'mini-program payment test',
                'description'  => 'Image形象店-深圳腾大-QQ公仔',
                'goods_tag'  => '001'
            ]]);

        echo $resp->getStatusCode(), PHP_EOL;
        echo $resp->getBody(), PHP_EOL;
    } catch (Exception $e) {
        // EXception handling
        echo $e->getMessage(), PHP_EOL;
        if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
            $r = $e->getResponse();
            echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
            echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
        }
        echo $e->getTraceAsString(), PHP_EOL;
    }
}

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

Critical parameters:

out_trade_no: internal order ID in merchant’s system

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

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

3.2.2 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:


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

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

// Call query order API
public function queryOrder($instance)
{
    try {
        $resp = $instance
            ->v3->global->transactions->outTradeNo->_out_trade_no_
            ->get([
                    'query' => [
                'sp_mchid'     => '1900000000',
                'sub_mchid'    => '100012321',
                    ],
                    'out_trade_no' => 'YX0001'
                ]
            );

        echo $resp->getStatusCode(), PHP_EOL;
        echo $resp->getBody(), PHP_EOL;
    } catch (Exception $e) {
        // Exception handling
	}
}

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

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.3 Refund

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

Code example


    @Test
    //Call Refund API
    public void refundTest() throws IOException {
		String refundBody = String.join("\n" ,
                		"{" ,
                        "'sp_appid': 'wx2421b1c4370ec43b', " ,
                        "'sp_mchid': '10000100'," ,
                        "'sub_mchid': '20000100'," ,
                        "'transaction_id': '1008450740201411110005820873'," ,
                        "'out_refund_no': 'R20150806125346'," ,
                        "    'amount' : {" ,
                        "        'refund': 5," ,
                        "        'total':10," ,
                        "         'currency':'HKD'" ,
                        "    }," ,
                        "    'reason': 'The item has been sold out.'," ,
                        "    'source': 'REFUND_SOURCE_UNSETTLED_FUNDS'" ,
                        "}").replace("'","\"");
        HttpPost httpPost = new HttpPost("https://apihk.mch.weixin.qq.com/v3/global/refunds");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        httpPost.setEntity(new StringEntity(refundBody));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        //Process the response 
    }

func refund() {
   refundRequestBody := `{
    "sp_appid": "wx2421b1c4370ec43b",
    "sp_mchid": "10000100",
    "sub_mchid": "20000100",
    "transaction_id": "1008450740201411110005820873",
    "out_refund_no": "R20150806125346",
    "amount" : {
        "refund": 50,
        "total":100,
         "currency":"HKD"
    },
    "reason": "The item has been sold out",
    "source": "REFUND_SOURCE_UNSETTLED_FUNDS"
}`
   result, err := client.Post(ctx, "https://apihk.mch.weixin.qq.com/v3/global/refunds", refundRequestBody)
   if err != nil {
      // Process error
   }
   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
}

// Call Refund API
public function refundOrder($instance){
    try {
        $resp = $instance
            ->chain('v3/global/refunds')
            ->post(['json' => [
                'sp_appid'     => 'wxdace12300000000',
                'sp_mchid'     => '1900000000',
                'sub_mchid'    => '100012321',
                'out_trade_no' => 'YX0001',
                "out_refund_no"=> "REFUNDYX0001",
                'amount'       => [
                    'refund'   => 1,
                    'total'    => 1,
                    'currency' => 'USD'
                ]
            ]]);
        echo $resp->getStatusCode(), PHP_EOL;
        echo $resp->getBody(), PHP_EOL;
    } catch (Exception $e) {
        // Exception handling
    }
}

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

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.4 Query one refund

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

Code example


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

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

// Call Query Refund API
public function queryRefund($instance){
    try {
        $resp = $instance
            ->v3->global->refunds->outRefundNo->_out_refund_no_
            ->get([
                'query' => [
                    'sp_mchid' => '1900000000',
                    'sub_mchid'=> '100012321'
                ],
                'out_refund_no' => 'REFUNDYX0001'
            ]);

        echo $resp->getStatusCode(), PHP_EOL;
        echo $resp->getBody(), PHP_EOL;
    } catch (Exception $e) {
        // Exception handling
    }
}

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

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.5 Query all refunds

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

Code example


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

//Query all refund API
func queryAllRefund() {
   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")
   if err != nil {
      // Process error
   }
   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
}

// Call Query All Refund API
public function queryAllRefund($instance){
    try {
        $resp = $instance
            ->v3->global->refunds
            ->get([
                'query' => [
                    'out_trade_no' => 'YX001',
                    'count' => 10,
                    'offset' => 0,
                    'sp_mchid' => '1900000000',
                    'sub_mchid'=> '100012321'
                ]
            ]);

        echo $resp->getStatusCode(), PHP_EOL;
        echo $resp->getBody(), PHP_EOL;
    } catch (Exception $e) {
        // Exception handling
    }
}

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

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


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

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

//Downloading Reconciliation API
public function downloadReconFile($instance){
    try {
        $resp = $instance
            ->v3->global->statements
            ->get([
                'query' => [
                    'date' => '20220401',
                    'mchid' => '1900000000'
                ]
            ]);

        echo $resp->getStatusCode(), PHP_EOL;
        echo $resp->getBody(), PHP_EOL;
    } catch (Exception $e) {
        // Exception handling
    }
}

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

Critical parameters:

date: Statement date, format: 20180103

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

3.2.7 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


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

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

// Call close order API
public function closeOrder($instance){
    try {
        $resp = $instance
            ->v3->global->transactions->outTradeNo->_out_trade_no_->close
            ->post([
                'json' => [
                'sp_mchid'     => '10000100',
                'sub_mchid'    => '20000100'
                ],
                'out_trade_no' => 'YX0001'
            ]);
        echo $resp->getStatusCode(), PHP_EOL;
        echo $resp->getBody(), PHP_EOL;
    } catch (Exception $e) {
        // Exception handling
    }
}

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

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


    @Test
    //Call certificate downloading API
    public void certDownloadingTest() throws IOException {
        HttpGet httpGet = new HttpGet("https://apihk.mch.weixin.qq.com/v3/global/certificates");
        httpGet.addHeader("Accept", "application/json");
        httpGet.addHeader("Content-type", "application/json; charset=utf-8");

        CloseableHttpResponse response = httpClient.execute(httpGet);
        //Process the response 
//Get the response body: EntityUtils.toString(response.getEntity());
//Get the response status code: response.getStatusLine().getStatusCode();

        //Instead of calling the API to download platform certificate,
        //We also recommend use the certificateMenager to get the valid certificate
        verifier.getValidCertificate();
    }

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

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

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

Critical parameters: None

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

3.2.9 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.9.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.9.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.9.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.9.4 Callback Example

Notification of Successful Payments


{
    "id":"EV-2018022511223320873",
    "create_time":"20180225112233",
    "resource_type":"encrypt-resource",
    "event_type":"TRANSACTION.SUCCESS",
    "resource" : {
        "algorithm":"AEAD_AES_256_GCM",
        "ciphertext": "...",
        "nonce": "...",
        "associated_data": ""
    }
}

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


{
    "id": "1008450740201411110005820873",
    "sp_appid": "wx2421b1c4370ec43b",
    "sp_mchid": "10000100",
    "sub_mchid": "20000100",
    "out_trade_no": "20150806125346",
    "payer": {
      "sp_openid": "oUpF8uN95-Ptaags6E_roPHg7AG0"
    },
    "amount" : {
        "total": 528800,
        "currency": "HKD",
        "payer_total": 518799,
        "payer_currency": "CNY",
        "exchange_rate" : {
            "type": "SETTLEMENT_RATE",
            "rate": 8000000
        }
    },
    "trade_type": "MICROPAY",
    "trade_state": "SUCCESS",
    "trade_state_desc": "支付成功",
    "bank_type": "CCB_DEBIT",
    "attach": "支付测试",
    "success_time": "2018-06-08T10:34:56+08:00",
    "promotion_detail":[
        {
            "promotion_id":"109519",
            "name":"单品惠-6",
            "scope":"SINGLE",
            "type":"DISCOUNT",
            "amount":1,
            "currency":"HKD",
            "activity_id":"931386",
            "wechatpay_contribute_amount":1,
            "merchant_contribute_amount":0,
            "other_contribute_amount":0,
            "goods_detail":[
                {
                    "goods_id":"iphone6s_16G",
                    "goods_remark":"商品备注",
                    "quantity":1,
                    "price":528800
                }
            ]
        }
    ]
}
3.2.9.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.10 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.10.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.10.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.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 Refund Result


{
	"id": "EV-2018022511223320873",
	"create_time": "2018-06-08T10:34:56+08:00",
	"resource_type": "encrypt-resource",
	"event_type": "REFUND.SUCCESS",
	"summary": "退款成功",
	"resource": {
		"original_type": "refund",
		"algorithm": "AEAD_AES_256_GCM",
		"ciphertext": "...",
		"associated_data": "",
		"nonce": "..."
	}
}

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


{
    "sp_mchid": "1900000100",
    "sub_mchid": "1900000109",
    "transaction_id": "1008450740201411110005820873",
    "out_trade_no": "20150806125346",
    "refund_id": "50200207182018070300011301001",
    "out_refund_no": "7752501201407033233368018",
    "refund_status": "SUCCESS",
    "success_time": "2018-06-08T10:34:56+08:00",
    "recv_account": "招商银行信用卡0403",
    "fund_source": "REFUND_SOURCE_UNSETTLED_FUNDS",
    "amount" : {
        "total": 528800,
        "currency": "HKD",
        "refund": 528800,
        "payer_total": 528800,
        "payer_refund": 528800,
        "payer_currency": "HKD",
        "exchange_rate" : {
            "type": "SETTLEMENT_RATE",
            "rate": 100000000
        }
    }
}
3.2.10.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.11 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


    @Test
    //Query Settlement API
    public void querySettlementTest() throws IOException {
        HttpGet httpGet = new HttpGet("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");
        httpGet.addHeader("Accept", "application/json");
        httpGet.addHeader("Content-type", "application/json; charset=utf-8");
        CloseableHttpResponse response = httpClient.execute(httpGet);
        //Process the response 
    }

//Query settlement API
func querySettlement() {
   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")
   if err != nil {
      // Process error
   }
   log.Printf("status=%d resp=%s", result.Response.StatusCode, result.Response.Body)
}

//Querying Settlement Info API
public function querySettlementInfo($instance){
    try {
        $resp = $instance
            ->v3->global->settle->settlements
            ->get(['query' => [
                'sub_mchid' => '100012321',
                'settle_state' => 'SETTLED',
                'settle_start_date' => '20221225',
                'settle_end_date' => '20221226',
                'offset' => '10',
                'limit' => '5'
            ]]);

        echo $resp->getStatusCode(), PHP_EOL;
        echo $resp->getBody(), PHP_EOL;
    } catch (\Exception $e) {
        // Exception handling
    }
}

									{
										"stock_id": "Python",
										"stock_creator_mchid": "123456",
										"limit": 10,
									}

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.

4. Guide for Obtaining User IP by H5 Payment

HH5 Payment requires merchants to upload the user’s real IP address "payer_client_ip" in the unified ordering API. The following guides for obtaining the user’s IP are provided to ensure that the user’s IP address obtained by WeChat is consistent with that obtained by the merchant.

Without agent

The way to obtain the IP is relatively simple when the front-end access layer of the merchant. Get 'REMOTE_ADDR' directly.

With agent

In the case of an agent, as the client needs to be replaced to access the server, when the request packet passes through the reverse agent, the IP header of the IP packet is modified in the agent server, and the source IP address of the packet header obtained by the back-end web server is the IP address of the agent server. Therefore, the backend server program cannot obtain the user's real IP.


When nginx has an agent:

Add the following to the configuration in nginx:

vi /usr/local/apache/conf/httpd.conf
Include conf/extra/httpd-remoteip.conf
vi /usr/local/apache/conf/extra/httpd-remoteip.conf
LoadModule remoteip_module modules/mod_remoteip.so
RemoteIPHeader X-Forwarded-For
RemoteIPinternalProxy 127.0.0.1

When Apache has an agent:

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Code example


string  GetClientIp(CgiInput *  poInput)  { 
	string  client_ip  =  ""; 
	string  strClientIPList; 
	GetHttpHeader("X-Forwarded-For",  strClientIPList);  
	if  (strClientIPList.empty())  { 
		GetHttpHeader("X-Real-IP",  strClientIPList); 
	}  
	if  (!strClientIPList.empty())  { 
		size_t  iPos  =  strClientIPList.find( "," ); 
		if ( iPos  !=  std::string::npos )  { 
			client_ip  =  strClientIPList.substr( iPos ); 
		} 
		else  { 
			client_ip  =  strClientIPList; 
		} 
	}  
	if  (client_ip.empty())  { 
		GetHttpHeader("PROXY_FORWARDED_FOR",  strClientIPList); 
		//Perform compatibility 
		if (strClientIPList.empty())  { 
			client_ip  =  getRemoteAddr(); 
		} 
		else  { 
			size_t  iPos  =  strClientIPList.find( "," ); 
			if ( iPos  !=  std::string::npos )  { 
				client_ip  =  strClientIPList.substr( iPos ); 
			} 
			else  { 
				client_ip  =  strClientIPList; 
			}  
		} 
	}  
	if (!MMPayCommFunc::IsIp(client_ip)) 
		client_ip  =  getRemoteAddr(); 
	return  client_ip; 
} 

5. FAQs

Callback page

In the normal process, the user will return to the page where the payment is initiated after completing the payment. If the user needs to return to the specified page, you can splice the redirect_url parameter after MWEB_URL to specify the callback page.
For example, if you want to redirect users to https://www.wechatpay.com.cn after the payment is completed, you can do the following:


Suppose you get MWEB_URL= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096 through the unified order API,


the spliced address should be MWEB_URL= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096&redirect_url=https%3A%2F%2Fwww.wechatpay.com.cn.

Note:

1. Urlencode processing for redirect_url is required.

2. After the redirect_url is set, the user may return to the specified page when: 1. more than 5 seconds after the WeChat Pay middle page calls the WeChat cashier; 2. the user taps Cancel Payment or taps Finish after the payment is completed. There is no guarantee that the payment process has ended when the user returns to the specified page. Therefore, the redirect_url address set by the merchant cannot automatically perform the order check operation. The user needs to tap the button to trigger the order check operation. See the figure below for the display effect of returning to the specified page.


6. Other Common Errors

Problem Problem Description Solution
The network environment fails to pass the security verification. Please try again later. 1. The terminal IP (spbill_create_ip) of the unified order transmission on the merchant side is inconsistent with the terminal IP detected on WeChat when the user actually transfers the payment. This problem generally occurs when the merchant fails to transmit the correct terminal IP to spbill_create_ip while placing an order. Please refer toGuide for Obtaining Client IPfor details.

2. The network is changed when the unified order is placed and the payment is transferred. For example, the unified order is placed in the Wi-Fi network, after the order is successfully placed, the network is switched to 4G to transfer the payment. In such a case, the normal interception will be triggered and the user needs to re-initiate the payment process in the same network environment.
The merchant parameter format is incorrect. Please contact the merchant. 1. The current referrer for H5 payment is empty. This problem generally occurs when the user directly accesses the page to initiate H5 Payment. Please follow the normal process to initiate the payment after being redirected, or capture the packet to check whether the referrer value is empty

2. If H5 Payment is initiated in the app, you need to manually set the referrer in the webview, such as (Map extraHeaders = new HashMap();
extraHeaders.put("Referer", "the authorized domain submitted by the merchant when you apply for H5 Payment");//e.g. http://www.baidu.com ))

The merchant has unconfigured parameters. Please contact the merchant. 1. The domain currently calling H5 Payment (obtained from the referrer WeChat) is inconsistent with the authorized domain submitted when you apply for H5 Payment. If you need to add or modify the authorized domain, please log in to the WeChat Pay Merchant Platform and enter the Development Configuration menu, configure the H5 Payment Authorized Domain in the Payment Authorization Config.

2. If the redirect_url is set, check whether the domain of the redirect URL is the same as the authorized domain submitted when you apply for H5 Payment.
Payment request has expired. Please re-initiate payment. After the MWEB_URL returned by the unified order is generated, it will remain valid for 5 minutes. If it expires, regenerate the MWEB_URL and initiate the payment.
Open the order outside WeChat to make payment. H5 Payment cannot be called directly in WeChat. Instead, call it in a third-party browser.

iOS: Signature verification failed
Android: System is busy, please try again later
1. Make sure that the same MWEB_URL is only called by one WeChat account. If it is called by another WeChat account, please place an order again to generate a new MWEB_URL.

2. If redirect_url is added to MWEB_URL, check whether the parameter splicing format is wrong, whether urlencode is performed on the value of redirect_url. See the following example format:

https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f23161
9da20804912345&package=1037687096&redirect_url=
https%3A%2F%2Fwww.wechatpay.com.cn

    Webpage navigation

About  WeChat  Pay

Powered By Tencent & Tenpay Copyright©

2005-2024 Tenpay All Rights Reserved.

Contact Us
Wechat Pay Global

WeChat Pay Global

置顶