Login expired. Please log in again.

Feedback

0/300

Feedback

Submitted successfully

ok

Feedback

Network exception, please try again later

ok

开发指引

1. 接口规则

为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考“APIv3接口规则

2. 开发环境搭建

为了帮助开发者调用开放接口,我们提供了JavaPHPGO语言版本的开发库,封装了签名生成、签名验证、敏感信息加/解密、媒体文件上传等基础功能


3. 业务时序图

3.1. 业务时序图

3.2. API接入示例

文档展示了如何使用微信支付服务端 SDK 快速接入扫码支付产品,完成与微信支付对接的部分。

注意

• 文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及请求参数才能跑通。

• 以下接入步骤仅提供参考,请商户结合自身业务需求进行评估、修改。

3.2.1 下单

步骤说明:通过本接口提交微信支付JSAPI支付订单,获取预支付交易单prepay_id。

代码示例


    @Test
    //Call Unified Order API
    public void unifiedOrderTest() throws IOException {
      String unifiedOrderBody = String.join("\n" ,
                		"{" ,
                        "'sp_appid': 'wx2421b1c4370ec43b'," ,
                        "'sp_mchid': '10000100'," ,
                        "'sub_mchid': '20000100'," ,
                        "'sub_appid': 'wx352671b037b437ec'," ,
                        "'out_trade_no': '20150806125346'," ,
                        "'merchant_category_code': '1011'," ,
                        "'payer': {" ,
                        "'sub_openid': 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'" ,
                        "}," ,
                        "'notify_url': 'https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php'," ,
                        "'trade_type': 'JSAPI'," ,
                        "'amount': {" ,
                        "'total': 10000," ,
                        "'currency': 'HKD'" ,
                        "}," ,
                        "'attach': 'Payment test'," ,
                        "'description': 'Miniprogramm Pay test'," ,
                        "'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/jsapi");
        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",
	"sub_appid": "wx352671b037b437ec",
	"out_trade_no": "20150806125346",
	"merchant_category_code": "1011",
	"payer": {
		"sub_openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
	},
	"notify_url": "https://wxpay.wxutil.com/pub_v2/pay/notify.v2.php",
	"trade_type": "JSAPI",
	"amount": {
		"total": 10000,
		"currency": "HKD"
	},
	"attach": "Payment test",
	"description": "Miniprogramm Pay test",
	"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/jsapi", 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/jsap')
            ->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'   => 'JSAPI',
                'amount'       => [
                    'total'    => 1,
                    'currency' => 'USD'
                ],	
				'payer'			=> [
					'sub_openid' => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o'
				],
                '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,
									}

重要参数:

out_trade_no: 商户系统内部订单号

notify_url: 接受异步支付结果回调通知的地址,通知url必须为外网可访问的url,不能携带参数。请使用https协议链接

openid: 用户在商户appid对应下的唯一标识,需要传appid才有返回 ,openid如何获取,可详见获取openid

sub_openid: 用户在子商户sub_appid下用户唯一标识,sp_openid和sub_openid可以选传其中之一,如果选择传sub_openid,则必须传sub_appid。下单前需要调用【网页授权】接口获取到用户的openid,

其他重要参数请前往JSAPI支付下单API文档页面参考。

3.2.2 微信内H5调起支付

步骤说明:获取到预签约单号后,机构需要将预签约单号和支付签名(paySign)返回给微信内H5页面,然后从H5页面中执行JS拉起微信支付收银台让用户确认订单信息及支付。

注意

• WeixinJSBridge内置对象在其他浏览器中无效。

• 列表中参数名区分大小,大小写错误签名验证会失败。

paySign生成规则

参与签名的字段及格式:

   公众号id
   时间戳
   随机字符串
   订单详情扩展字符串

待签名串举例:

   wx8888888888888888
   1414561699
   5K8264ILTKch16CQ2502SI8ZNMTM67VS
   prepay_id=123456789

计算签名值:

绝大多数编程语言提供的签名函数支持对签名数据 进行签名。强烈建议商户调用该类函数,使用商户私钥对待签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值。


function onBridgeReady(){
   WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {
           "appId":"wx2421b1c4370ec43b",     //公众号名称,由商户传入     
           "timeStamp":"1395712654",         //时间戳,自1970年以来的秒数     
           "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串     
           "package":"prepay_id=u802345jgfjsdfgsdg888",     
           "signType":"RSA",         //微信签名方式:     
           "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
       },
       function(res){     
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
       }
   ); 
}
if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
   }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
   }
}else{
   onBridgeReady();
}

重要参数:

paySign:构造的支付签名

package:下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*

其他重要参数请前往微信内H5调起支付文档页面参考。

3.2.3 查询订单

步骤说明:商户在用户下单一定时间后,需主动进行查单以确认订单状态。

代码示例


    @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,
									}

重要参数:

id: 微信支付订单号

out_trade_no:商户内部订单号

可以用上述任一订单号进行查询。其他重要参数请前往查询订单API文档页面参考。

3.2.4 申请退款

步骤说明:支付完成支付状态为SUCCESS后,可以调用该接口提交退款请求。

代码示例


    @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,
									}

重要参数:

out_trade_no: 商户内部订单号

transaction_id: 微信订单号

out_refund_no: 商户内部退款单号,应与订单号一一对应

notify_url: 退款结果接收地址

其他重要参数请前往申请退款API文档页面参考。

3.2.5 查询单笔退款

步骤说明:提交退款申请后,通过调用该接口查询退款状态。

代码示例


    @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,
									}

重要参数:

out_trade_no: 商户内部订单号

refund_id:微信支付退款单号

其他重要参数请前往查询单笔退款API文档页面参考。

3.2.6 查询所有退款

步骤说明:提交退款申请后,通过调用该接口查询一笔订单下对应的全部退款信息。

代码示例


    @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,
									}

重要参数:

out_trade_no: 商户内部订单号

transaction_id: 微信订单号

offset: 分页起始位置

count: 单页返回的记录数,最大20条记录.

其他重要参数请前往查询所有退款API文档页面参考。

3.2.7 下载对账单

步骤说明:商户每天可以通过调用该接口可以下载历史交易账单,通过对帐单核对后可以校对更正支付信息。

代码示例


    @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,
									}

重要参数:

date: 账单日期, 格式:20180103

其他重要参数请前往下载对账单API文档页面参考。

3.2.8 关闭订单

步骤说明:下单超过5分钟后,可以调用该接口对下单未支付的订单进行关单操作,以便商户更换订单号重新下单发起支付或商户系统退出不再受理后避免用户继续支付。

代码示例


    @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,
									}

重要参数:

out_trade_no: 商户内部订单号

transaction_id: 微信支付订单号

可以用上述任一订单号进行查询。其他重要参数请前往关闭订单API文档页面参考。

3.2.9 下载平台证书

步骤说明:在调用其他接口之前,需要调用该接口下载平台证书用于对返回消息进行验签和返回消息中的加密字段进行解密。

代码示例


    @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,
									}

重要参数:无

其他重要参数请前往下载平台证书API文档页面参考。

3.2.10 支付结果通知

步骤说明:用户完成支付后,微信支付会向下单时传入的notify_url推送支付结果通知,商户需要在接收到通知后返回对应的信息。

注意

• 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

• 如果在所有通知频率(4小时)后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。


特别提醒:机构系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄露导致出现“假通知”,造成资金损失。

3.2.10.1 通知规则

用户支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理后返回应答成功。只有支付成功才会通知。

对后台通知交互时,如果微信收到应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)

3.2.10.2 通知报文

支付结果通知是以“POST”方法访问商户设置的通知url,通知的数据以“JSON”格式通过请求主体(BODY)传输。通知的数据包括了加密的支付结果详情。

下面详细描述证书解密的流程

1、从商户平台上获取商户的密钥,记为“key”。

2、针对“algorithm”中描述的算法(目前为AEAD_AES_256_GCM),取得对应的参数“nonce”和“associated_data”。

3、使用“key”、“nonce”和“associated_data”对数据密文“ciphertext”进行解密(需要先对ciphertext做base64解码,然后再解密),得到证书内容

注意
AEAD_AES_256_GCM算法的接口细节,请参考rfc5116。微信支付使用的密钥key长度为32个字节,随机串nonce长度12个字节,associated_data长度小于16个字节并可能为空。
3.2.10.3 通知签名

加密不能保证通知请求来自微信。微信会对发送给商户的通知进行签名,并将签名值放在通知的HTTP头Wechatpay-Signature。商户应当验证签名,以确认请求来自微信,而不是其他的第三方。签名验证的算法请参考《微信支付API V3签名验证》。

3.2.10.4 回调示例

支付成功结果通知


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

商户对resource对象进行解密后,得到的资源对象示例


{
    "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.10.5 通知应答

支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx。

注意
当商户后台应答失败时,微信支付将记录下应答的报文,建议商户按照以下格式返回。

其他重要参数请前往支付结果通知API文档页面参考。

3.2.11 退款通知

步骤说明:退款状态改变后,微信会把相关退款结果发送给商户。

注意

• 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

• 如果在所有通知频率(4小时)后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。


特别提醒:机构系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄露导致出现“假通知”,造成资金损失。

3.2.11.1 通知规则

退款状态改变后,微信会把相关退款结果发送给商户。

对后台通知交互时,如果微信收到应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m)

3.2.11.2 通知报文

退款结果通知是以“POST”方法访问商户设置的通知url,通知的数据以“JSON”格式通过请求主体(BODY)传输。通知的数据包括了加密的退款结果详情。

下面详细描述证书解密的流程

1、从商户平台上获取商户的密钥,记为“key”。

2、针对“algorithm”中描述的算法(目前为AEAD_AES_256_GCM),取得对应的参数“nonce”和“associated_data”。

3、使用“key”、“nonce”和“associated_data”对数据密文“ciphertext”进行解密(需要先对ciphertext做base64解码,然后再解密),得到证书内容

注意
AEAD_AES_256_GCM算法的接口细节,请参考rfc5116。微信支付使用的密钥key长度为32个字节,随机串nonce长度12个字节,associated_data长度小于16个字节并可能为空。
3.2.11.3 通知签名

加密不能保证通知请求来自微信。微信会对发送给商户的通知进行签名,并将签名值放在通知的HTTP头Wechatpay-Signature。商户应当验证签名,以确认请求来自微信,而不是其他的第三方。签名验证的算法请参考《微信支付API V3签名验证》。

3.2.11.4 回调示例

退款通知


{
	"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": "..."
	}
}

商户对resource对象进行解密后,得到的资源对象示例


{
    "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.11.5 通知应答

退款通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx。

注意
当商户后台应答失败时,微信支付将记录下应答的报文,建议商户按照以下格式返回。

其他重要参数请前往退款通知API文档页面参考。

3.2.12 查询资金结算明细

步骤说明:商户在交易完结之后,可按结算日期查询已结算资金明细(sette_state为SETTLED),也可以查询未结算资金明细(sette_state为UNSETTLE)。

注意
该接口供跨境收单机构/直连商户使用,特别是,日本/澳门机构商户若开通香港钱包业务,需要对接该接口。

代码示例


    @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,
									}

重要参数:

settle_state: 资金结算状态,枚举值:

SETTLED:已结算

UNSETTLE:未结算

其他重要参数请前往查询资金结算明细API文档页面参考。

    页面导航

About  WeChat  Pay

Powered By Tencent & Tenpay Copyright©

2005-2024 Tenpay All Rights Reserved.

Contact Us
Wechat Pay Global

WeChat Pay Global

置顶