开发指引

更新时间:2024.11.27

1. 接口规则

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

2. 开发准备

2.1. 搭建和配置开发环境

开发者应当依据自身的编程语言来构建并配置相应的开发环境。

2.2. 业务开发配置

2.2.1. 注册App

App接入微信支付,需要先将商户App在微信开放平台进行注册,登记App开发参数以生成AppID。具体操作步骤如下:

1.登录微信开放平台,进入【管理中心 → 移动应用 → 创建移动应用】;


2.完成基本信息的录入,商户需要在本步骤提交App对应的下载地址,应用官网,应用水印,icon等业务信息;


3.完成平台信息的录入,商户需要在本步骤提交App在Android/iOS/鸿蒙端对应的开发参数,包括Android端应用的包名,应用签名,iOS端应用的Bundle ID, Universal Links,鸿蒙端应用的Bundle ID 和 Identifier等;




注意

Android应用包名和签名的相关说明,参考下方【2.2.3. Android开发要点说明】。

4.以上信息全部提交完成后,即完成App的注册,商户可在【管理中心 → 移动应用】中,选择具体的应用查看其AppID及已获得的接口能力;


5.获取到App的AppID后,需要将该AppID与商户的收款mch_id进行绑定,商户可登录商户平台后前往【产品中心 -> AppID账号管理】界面中进行AppID的绑定及管理,界面如图所示:


2.2.2. iOS开发要点说明

  • iOS系统OpenSDK升级指引

由于苹果公司在iOS13系统回收了查询 App bundleID 的能力,导致微信无法保证授权凭证能正确返回给AppID对应的应用。为此,微信支付强烈要求所有商户尽快升级到OpenSDK1.8.6,并让用户及时更新App,否则安全风险将一直存在。

 

详细OpenSDK升级指引请参见:OpenSDK升级指引

注意

OpenSDK升级后请一定按照文档要求完成验证工作,确保OpenSDK升级成功。

  • 开发配置:

以下项目开发环境以Xcode6.0,运行环境为IOS7.0为例,说明其开发中需要的操作

一、项目设置AppID

商户在微信开放平台申请开发App应用后,微信开放平台会生成App的唯一标识AppID。在Xcode中打开项目,设置项目属性中的URL Schemes为您的AppID。如图标红位置所示

二、注册AppID

商户App工程中引入微信lib库和头文件,调用API前,需要先向微信注册您的AppID,代码如下:

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

注意

OpenSDK前端拉起支付及SDK回调的相关说明,请参考OpenSDK调起支付说明

2.2.3. Android开发要点说明

1、后台设置

商户在微信开放平台申请开发应用后,微信开放平台会生成App的唯一标识AppID。由于需要保证支付安全,需要在开放平台绑定商户应用包名和应用签名,设置好后才能正常发起支付。设置界面在【开放平台】中的栏目【管理中心 修改应用 修改开发信息】里面,如图红框内所示。

 

应用包名:是在App项目配置文件AndroidManifest.xml中声明的package值,例如上图中的package="demo.wxpay.tenpay.com"。

 

应用签名:根据项目的应用包名和编译使用的keystore,可由签名工具生成一个32位的md5串,在调试的手机上安装签名工具后,运行可生成应用签名串,如图8.9所示,绿色串即应用签名。

 

点击下载签名生成工具

2、注册AppID

商户App工程中引入微信JAR包,调用API前,需要先向微信注册您的AppID,代码如下:

1final IWXAPI msgApi = WXAPIFactory.createWXAPI(context, null);
2// 将该app注册到微信
3msgApi.registerApp("wxd930ea5d5a258f4f");

注意

OpenSDK前端拉起支付及SDK回调的相关说明,请参考OpenSDK调起支付说明

2.2.4. 鸿蒙开发要点说明

1、配置鸿蒙应用信息

前往微信开放平台,在「管理中心 - 移动应用 - 详情 - 开发配置」中点击「编辑」进入修改开发信息的页面,继续在「平台信息」板块中填入鸿蒙应用所需的 Bundle ID 和 identifier 信息,然后提交审核,等待审核通过即可。

关于「鸿蒙应用」中的 Bundle ID、Identifier、以及应用下载地址的提供的说明如下:

 

注意事项:

  • 对于 App 已上架 Android 或iOS 应用市场的应用,千万别选择【未上架任何应用市场】,此选项意思是 Android、iOS 和鸿蒙都尚未上架应用市场,而未上架应用市场的应用使用微信能力则有如下的限制,因此开发者需谨慎操作

  • 因此,对于 App 已上架 Android 或iOS 应用市场的应用,需选择【已上架至少一个应用市场】,但需填写 App 备案号

  • 即,对于 Android 和 iOS 已上架的应用,在下面的配置中一定要选择【是】

 

2、下载鸿蒙开发的 IDE

前往鸿蒙官网下载 DevEco Studio NEXT

3、配置 sdk 依赖

在 DevEco Studio NEXT中打开你的项目,以demo 工程为示例:

修改项目中的oh-package.json5文件,在dependencies中加入微信 opensdk 的依赖项:

1{
2  "name": "demo",
3  "version": "1.0.0",
4  "description": "Please describe the basic information.",
5  "main": "",
6  "author": "",
7  "license": "",
8   "dependencies": {
9    "@tencent/wechat_open_sdk": "1.0.0"
10  }
11}

注意

OpenSDK前端拉起支付及SDK回调的相关说明,请参考OpenSDK调起支付说明

3. 快速接入

3.1. 业务流程图

重点步骤说明:

步骤3: 用户下单发起支付,商户可通过微信支付App下单API创建支付订单。

商户调用App下单API后,分正常返回和异常返回情况:

  • 正常返回:返回prepay_id,商户可根据返回的prepay_id来生成调用OpenSDK的签名以执行下一步。

  • 异常返回:返回HTTP code或错误码,商户可根据HTTP code列表错误码说明来排查原因并执行下一步操作。

步骤8: 商户通过App调起支付OpenSDK调起微信支付,发起支付请求,有关OpenSDK调起支付的详细说明,请参考iOS开发要点说明的说明

步骤15-19: 用户支付成功后,商户可通过以下两种方式获取订单状态。

我们通过以下接口将用户确认订单信息回调通知给商户系统:

方法一: 支付结果通知。用户支付成功后,微信支付会将支付成功的结果以回调通知的形式同步给商户,商户的回调地址需要在调用App下单API时传入notify_url参数。

方法二: 当因网络抖动或本身notify_url存在问题等原因,导致无法接收到回调通知时,商户也可主动调用查询订单API 查询订单API来获取订单状态。

3.2. API接入(含示例代码)

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

注意

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

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

3.2.1. 【服务端】App下单

步骤说明:用户在商户App内完成商户选择后进入支付页面,商户需要通过后端请求该App下单API来获取预支付ID。

JAVA代码示例:

1public void CreateOrder() throws Exception{
2  //请求URL
3  HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");
4  // 请求body参数
5  String reqdata = "{"
6          + "\"time_expire\":\"2018-06-08T10:34:56+08:00\","
7          + "\"amount\": {"
8          + "\"total\":100,"
9          + "\"currency\":\"CNY\""
10          + "},"
11          + "\"mchid\":\"1230000109\","
12          + "\"description\":\"Image形象店-深圳腾大-QQ公仔\","
13          + "\"notify_url\":\"https://www.weixin.qq.com/wxpay/pay.php\","
14          + "\"out_trade_no\":\"1217752501201407033233368018\","
15          + "\"goods_tag\":\"WXG\","
16          + "\"appid\":\"wxd678efh567hg6787\","
17          + "\"attach\":\"自定义数据说明\","
18          + "\"detail\": {"
19          + "\"invoice_id\":\"wx123\","
20          + "\"goods_detail\": ["
21          + "{"
22          + "\"goods_name\":\"iPhoneX 256G\","
23          + "\"wechatpay_goods_id\":\"1001\","
24          + "\"quantity\":1,"
25          + "\"merchant_goods_id\":\"商品编码\","
26          + "\"unit_price\":828800"
27          + "},"
28          + "{"
29          + "\"goods_name\":\"iPhoneX 256G\","
30          + "\"wechatpay_goods_id\":\"1001\","
31          + "\"quantity\":1,"
32          + "\"merchant_goods_id\":\"商品编码\","
33          + "\"unit_price\":828800"
34          + "}"
35          + "],"
36          + "\"cost_price\":608800"
37          + "},"
38          + "\"scene_info\": {"
39          + "\"store_info\": {"
40          + "\"address\":\"广东省深圳市南山区科技中一道10000号\","
41          + "\"area_code\":\"440305\","
42          + "\"name\":\"腾讯大厦分店\","
43          + "\"id\":\"0001\""
44          + "},"
45          + "\"device_id\":\"013467007045764\","
46          + "\"payer_client_ip\":\"14.23.150.211\""
47          + "}"
48          + "}";
49  StringEntity entity = new StringEntity(reqdata,"utf-8");
50  entity.setContentType("application/json");
51  httpPost.setEntity(entity);
52  httpPost.setHeader("Accept", "application/json");
53  //完成签名并执行请求
54  CloseableHttpResponse response = httpClient.execute(httpPost);
55  try {
56      int statusCode = response.getStatusLine().getStatusCode();
57      if (statusCode == 200) { //处理成功
58          System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
59      } else if (statusCode == 204) { //处理成功,无返回Body
60          System.out.println("success");
61      } else {
62          System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
63          throw new IOException("request failed");
64      }
65  } finally {
66      response.close();
67  }
68}

PHP代码示例:

1try {
2  $resp = $client->request(
3      'POST',
4      'https://api.mch.weixin.qq.com/v3/pay/transactions/app', //请求URL
5      [
6          // JSON请求体
7          'json' => [
8              "time_expire" => "2018-06-08T10:34:56+08:00", 
9              "amount" => [
10                  "total" => 100, 
11                  "currency" => "CNY", 
12              ],
13              "mchid" => "1230000109", 
14              "description" => "Image形象店-深圳腾大-QQ公仔", 
15              "notify_url" => "https://www.weixin.qq.com/wxpay/pay.php", 
16              "out_trade_no" => "1217752501201407033233368018", 
17              "goods_tag" => "WXG", 
18              "appid" => "wxd678efh567hg6787", 
19              "attach" => "自定义数据说明", 
20              "detail" => [
21                  "invoice_id" => "wx123", 
22                  "goods_detail" => [
23                      [
24                          "goods_name" => "iPhoneX 256G", 
25                          "wechatpay_goods_id" => "1001", 
26                          "quantity" => 1, 
27                          "merchant_goods_id" => "商品编码", 
28                          "unit_price" => 828800, 
29                      ],
30                      [
31                          "goods_name" => "iPhoneX 256G", 
32                          "wechatpay_goods_id" => "1001", 
33                          "quantity" => 1, 
34                          "merchant_goods_id" => "商品编码", 
35                          "unit_price" => 828800, 
36                      ],
37                  ],
38                  "cost_price" => 608800, 
39              ],
40              "scene_info" => [
41                  "store_info" => [
42                      "address" => "广东省深圳市南山区科技中一道10000号", 
43                      "area_code" => "440305", 
44                      "name" => "腾讯大厦分店", 
45                      "id" => "0001", 
46                  ],
47                  "device_id" => "013467007045764", 
48                  "payer_client_ip" => "14.23.150.211", 
49              ]
50          ],
51          'headers' => [ 'Accept' => 'application/json' ]
52      ]
53  );
54  $statusCode = $resp->getStatusCode();
55  if ($statusCode == 200) { //处理成功
56      echo "success,return body = " . $resp->getBody()->getContents()."\n";
57  } else if ($statusCode == 204) { //处理成功,无返回Body
58      echo "success";
59  }
60} catch (RequestException $e) {
61  // 进行错误处理
62  echo $e->getMessage()."\n";
63  if ($e->hasResponse()) {
64      echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
65  }
66  return;
67}

重要入参说明:

  • out_trade_no:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一

  • description:商品描述

  • notify_url:支付回调通知URL,该地址必须为直接可访问的URL,不允许携带查询串

  • total:订单总金额,单位为分

更多参数、响应详情及错误码请参见App下单API接口文档。

3.2.2. 【客户端】OpenSDK调起支付

步骤说明: 通过App下单API成功获取 预支付交易会话标识(prepay_id) 后,需要通过OpenSDK来调起微信支付收银台

重要入参说明:

  • package: 取固定值Sign=WXPay

  • signType: 该接口V3版本仅支持RSA

  • paySign: 签名

paySign生成规则、响应详情及错误码请参见App调起支付接口文档。

iOS SDK调用说明

 

一、拉起支付

商户服务器生成支付订单,先调用App下单API生成预付单,获取到prepay_id后将参数再次签名传输给App发起支付。以下是调起微信支付的关键代码:

1PayReq *request = [[[PayReq alloc] init] autorelease];
2request.partnerId = @"10000100";
3request.prepayId= @"1101000000140415649af9fc314aa427";
4request.package = @"Sign=WXPay";
5request.nonceStr= @"a462b76e7436e98e0ed6e13c64b4fd1c";
6request.timeStamp= @"1397527777";
7request.sign= @"582282D72DD2B03AD892830965F428CB16E7A256";
8[WXApi sendReq:request]

注意

该sign生成字段名列表见调起支付API

二、SDK结果回调

按照微信SDK Sample,在类实现onResp函数,支付完成后,微信App会返回到商户App并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码。如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意一定不能以客户端返回作为用户支付的结果,应以服务器端接收的支付通知或查询API返回的结果为准。

代码示例如下:

1-(void)onResp:(BaseResp*)resp{
2if ([respisKindOfClass:[PayRespclass]]){
3    PayResp*response=(PayResp*)resp;
4    switch(response.errCode){
5                caseWXSuccess: //服务器端查询支付通知或查询API返回的结果再提示成功
6                          NSlog(@"支付成功");
7                break;
8                default:
9                          NSlog(@"支付失败,retcode=%d",resp.errCode);
10                break;
11      }
12  }
13}

回调中errCode值列表:

名称

描述

解决方案

-2

用户取消

无需处理。发生场景:用户不支付了,点击取消,返回App。

0

成功

展示成功页面

-1

错误

可能的原因:签名错误、未注册AppID、项目设置AppID不正确、注册的AppID与设置的不匹配、其他异常等。

Android SDK调用说明

 

一、SDK拉起支付

商户服务器生成支付订单,先调用App下单API生成预付单,获取到prepay_id后将参数再次签名传输给App发起支付。以下是调起微信支付的关键代码:

1IWXAPI api;
2  PayReq request = new PayReq();
3  request.appId = "wxd930ea5d5a258f4f";
4  request.partnerId = "1900000109";
5  request.prepayId= "1101000000140415649af9fc314aa427",;
6  request.packageValue = "Sign=WXPay";
7  request.nonceStr= "1101000000140429eb40476f8896f4c9";
8  request.timeStamp= "1398746574";
9  request.sign= "7FFECB600D7157C5AA49810D2D8F28BC2811827B";
10  api.sendReq(request);

注意

该sign生成字段名列表见调起支付API

二、支付结果回调

参照微信SDK Sample,在商户包名.wxapi包路径中实现WXPayEntryActivity类(包名或类名不一致会造成无法回调),在WXPayEntryActivity类中实现onResp函数,支付完成后,微信App会返回到商户App并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码。 如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意一定不能以客户端返回作为用户支付的结果,应以服务器端的接收的支付通知或查询API返回的结果为准。

代码示例如下:

1public void onResp(BaseRespresp){
2    if(resp.getType()==ConstantsAPI.COMMAND_PAY_BY_WX){
3    Log.d(TAG,"onPayFinish,errCode="+resp.errCode);
4    AlertDialog.Builderbuilder=newAlertDialog.Builder(this);
5    builder.setTitle(R.string.app_tip);
6    }
7}

回调中errCode值列表:

名称

描述

解决方案

0

成功

展示成功页面

-1

错误

可能的原因:签名错误、未注册AppID、项目设置AppID不正确、注册的AppID与设置的不匹配、其他异常等。

-2

用户取消

无需处理。发生场景:用户不支付了,点击取消,返回App。

鸿蒙 SDK 调用说明

 

一、SDK拉起支付

商户服务器生成支付订单,先调用App下单API生成预付单,获取到prepay_id后将参数再次签名传输给App发起支付。以下是调起微信支付的关键代码:

1import * as wxopensdk from '@wechat/open_sdk';
2
3// WXApi 是第三方app和微信通信的openApi接口,其实例通过WXAPIFactory获取,需要传入应用申请到的AppID
4export const WXApi = wxopensdk.WXAPIFactory.createWXAPI(APP_ID)
5
6// WXApiEventHandler为微信数据的回调
7class WXApiEventHandlerImpl implements wxopensdk.WXApiEventHandler {
8  private onReqCallbacks: Map<OnWXReq, OnWXReq> = new Map
9  private onRespCallbacks: Map<OnWXResp, OnWXResp> = new Map
10
11  registerOnWXReqCallback(on: OnWXReq) {
12    this.onReqCallbacks.set(on, on)
13  }
14  unregisterOnWXReqCallback(on: OnWXReq) {
15    this.onReqCallbacks.delete(on)
16  }
17
18  registerOnWXRespCallback(on: OnWXResp) {
19    this.onRespCallbacks.set(on, on)
20  }
21  unregisterOnWXRespCallback(on: OnWXResp) {
22    this.onRespCallbacks.delete(on)
23  }
24
25  onReq(req: wxopensdk.BaseReq): void {
26    Log.i(kTag, "onReq:%s", JSON.stringify(req))
27    this.onReqCallbacks.forEach((on) => {
28      on(req)
29    })
30  }
31
32  onResp(resp: wxopensdk.BaseResp): void {
33    Log.i(kTag, "onResp:%s", JSON.stringify(resp))
34    this.onRespCallbacks.forEach((on) => {
35      on(resp)
36    })
37  }
38}
39export const WXEventHandler = new WXApiEventHandlerImpl
40
41//调用支付
42let req = new wxopensdk.PayReq
43req.appId = 'wxd930ea5d5a258f4f'//微信开放平台审核通过的移动应用AppID
44req.partnerId = '1900000109'//微信支付商户号
45req.prepayId = '1101000000140415649af9fc314aa427'//调用微信支付下单接口返回的支付交易会话ID
46req.packageValue = 'Sign=WXPay'//填写固定值Sign=WXPay
47req.nonceStr = '1101000000140429eb40476f8896f4c9'//随机字符串,不长于32位
48req.timeStamp = '1398746574'//时间戳
49req.sign = '7FFECB600D7157C5AA49810D2D8F28BC2811827B'//签名值
50// 向微信发送登录请求:
51//   context为ohos内置类,app间跳转需依赖该类,开发者可在Component中获取
52//   finished为跳转微信的结果:true表示跳转成功;false表示跳转失败,可能是因为微信未安装
53let finished = await this.wxApi.sendReq(context: common.UIAbilityContext, req)
54
55
56// 在EntryAbility中响应来自微信的回调
57export default class EntryAbility extends UIAbility {
58  onCreate(want: Want, _launchParam: AbilityConstant.LaunchParam): void {
59    this.handleWeChatCallIfNeed(want)
60  }
61  
62  onNewWant(want: Want, _launchParam: AbilityConstant.LaunchParam): void {
63    this.handleWeChatCallIfNeed(want)
64  }
65
66  private handleWeChatCallIfNeed(want: Want) {
67    WXApi.handleWant(want, WXEventHandler)
68  }
69}

注意

该sign生成字段名列表见调起支付API

二、支付结果回调

支付完成后,微信App会返回到商户App并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码。如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意一定不能以客户端返回作为用户支付的结果,应以服务器端接收的支付通知或查询API返回的结果为准。

示例代码:

1onResp(resp: wxopensdk.BaseResp): void {
2Log.i(kTag, "onResp:%s", JSON.stringify(resp))
3 this.onRespCallbacks.forEach((on) => {
4 on(resp)
5 })
6 }

回调中errCode值列表:

描述

解决方案

0

成功

展示成功页面

-1

错误

可能的原因:签名错误、未注册AppID、项目设置AppID不正确、注册的AppID与设置的不匹配、其他异常等

-2

用户取消

无需处理。发生场景:用户不支付了,点击取消,返回App

3.2.3.【服务端】接收支付结果通知

步骤说明: 当用户完成支付,微信会把相关支付结果将通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答。

注意

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

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

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

  • 商户成功接收到回调通知后应返回成功的HTTP应答码为200或204。

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

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

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

更多参数、响应详情及错误码请参见 支付结果通知 接口文档。

3.2.4. 【服务端】查询订单

步骤说明: 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知时,商户可通过查询订单接口核实订单支付状态。

注意

查询订单可通过微信支付订单号商户订单号两种方式查询,两种查询方式返回结果相同

需要调用查询接口的情况:

  • 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知。

  • 调用支付接口后,返回系统错误或未知交易状态情况。

  • 调用付款码支付API,返回USERPAYING的状态。

  • 调用关单或撤销接口API之前,需确认支付状态。

 

示例代码(通过微信订单号查询):

JAVA示例代码:

1public void QueryOrder() throws Exception {
2   
3  //请求URL
4  URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/id/4200000745202011093730578574");
5  uriBuilder.setParameter("mchid", mchId);
6 
7  //完成签名并执行请求
8  HttpGet httpGet = new HttpGet(uriBuilder.build());
9  httpGet.addHeader("Accept", "application/json");
10  CloseableHttpResponse response = httpClient.execute(httpGet);
11   
12  try {
13      int statusCode = response.getStatusLine().getStatusCode();
14      if (statusCode == 200) {
15          System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
16      } else if (statusCode == 204) {
17          System.out.println("success");
18      } else {
19          System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
20          throw new IOException("request failed");
21      }
22  } finally {
23      response.close();
24  }
25}

PHP代码示例:

1try {
2    $resp = $client->request(
3        'GET',
4        'https://api.mch.weixin.qq.com/v3/pay/transactions/id/1217752501201407033233368018?mchid=1230000109', //请求URL
5        [
6            'headers' => [ 'Accept' => 'application/json']
7        ]
8    );
9    $statusCode = $resp->getStatusCode();
10    if ($statusCode == 200) { //处理成功
11        echo "success,return body = " . $resp->getBody()->getContents()."\n";
12    } else if ($statusCode == 204) { //处理成功,无返回Body
13        echo "success";
14    }
15} catch (RequestException $e) {
16    // 进行错误处理
17    echo $e->getMessage()."\n";
18    if ($e->hasResponse()) {
19        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
20    }
21    return;
22}

更多参数、响应详情及错误码请参见 微信支付订单号/商户订单号接口文档。

3.2.5. 【服务端】关闭订单

步骤说明: 当商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口

注意

  • 关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败。

  • 已支付成功的订单不能关闭。

JAVA代码示例:

1public void CloseOrder() throws Exception {
2     
3    //请求URL
4    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/sdkphp12345678920201028112429/close");
5    //请求body参数
6    String reqdata ="{\"mchid\": \""+mchId+"\"}";
7     
8    StringEntity entity = new StringEntity(reqdata,"utf-8");
9    entity.setContentType("application/json");
10    httpPost.setEntity(entity);
11    httpPost.setHeader("Accept", "application/json");
12   
13  //完成签名并执行请求
14    CloseableHttpResponse response = httpClient.execute(httpPost);
15    try {
16        int statusCode = response.getStatusLine().getStatusCode();
17        if (statusCode == 200) {
18            System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
19        } else if (statusCode == 204) {
20            System.out.println("success");
21        } else {
22            System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
23            throw new IOException("request failed");
24        }
25    } finally {
26        response.close();
27    }
28  }

PHP代码示例:

1try {
2    $resp = $client->request(
3        'POST',
4        'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close', //请求URL
5        [
6            // JSON请求体
7            'json' => [
8                "mchid " => "1230000109",
9            ],
10            'headers' => [ 'Accept' => 'application/json' ]
11        ]
12    );
13    $statusCode = $resp->getStatusCode();
14    if ($statusCode == 200) { //处理成功
15        echo "success,return body = " . $resp->getBody()->getContents()."\n";
16    } else if ($statusCode == 204) { //处理成功,无返回Body
17        echo "success";
18    }
19  } catch (RequestException $e) {
20    // 进行错误处理
21    echo $e->getMessage()."\n";
22    if ($e->hasResponse()) {
23        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
24    }
25    return;
26  }

更多参数、响应详情及错误码请参见 关闭订单接口文档。

3.2.6. 【服务端】申请交易账单

步骤说明: 微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址。

JAVA代码示例:

1public void TradeBill() throws Exception {
2   
3  //请求URL
4  URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/bill/tradebill");
5  uriBuilder.setParameter("bill_date", "2020-11-09");
6  uriBuilder.setParameter("bill_type", "ALL");
7 
8  //完成签名并执行请求
9  HttpGet httpGet = new HttpGet(uriBuilder.build());
10  httpGet.addHeader("Accept", "application/json");
11  CloseableHttpResponse response = httpClient.execute(httpGet);
12 
13  try {
14      int statusCode = response.getStatusLine().getStatusCode();
15      if (statusCode == 200) {
16          System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
17      } else if (statusCode == 204) {
18          System.out.println("success");
19      } else {
20          System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
21          throw new IOException("request failed");
22      }
23  } finally {
24      response.close();
25  }
26}

PHP代码示例:

1try {
2    $resp = $client->request(
3        'GET',
4        'https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=2019-06-11&bill_type=ALL', //请求URL
5        [
6            'headers' => [ 'Accept' => 'application/json']
7        ]
8    );
9    $statusCode = $resp->getStatusCode();
10    if ($statusCode == 200) { //处理成功
11        echo "success,return body = " . $resp->getBody()->getContents()."\n";
12    } else if ($statusCode == 204) { //处理成功,无返回Body
13        echo "success";
14    }
15} catch (RequestException $e) {
16    // 进行错误处理
17    echo $e->getMessage()."\n";
18    if ($e->hasResponse()) {
19        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
20    }
21    return;
22}

更多参数、响应详情及错误码请参见 申请交易账单支付通知API接口文档

3.2.7. 【服务端】下载账单

步骤说明: 通过申请交易账单接口获取到账单下载地址(download_url)后,再通过该接口获取到对应的账单文件,文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况。

注意

  • 账单文件的下载地址的有效时间为30s。

  • 强烈建议商户将实际账单文件的哈希值和之前从接口获取到的哈希值进行比对,以确认数据的完整性。

  • 该接口响应的信息请求头中不包含微信接口响应的签名值,因此需要跳过验签的流程。

  • 微信在次日9点启动生成前一天的对账单,建议商户10点后再获取。

JAVA代码示例:

1public void DownloadUrl(String download_url) throws Exception{
2  PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
3  //初始化httpClient
4  //该接口无需进行签名验证、通过withValidator((response) -> true)实现
5  httpClient =  WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey).withValidator((response) -> true).build();
6  //请求URL
7  //账单文件的下载地址的有效时间为30s
8  URIBuilder uriBuilder = new URIBuilder(download_url);
9  HttpGet httpGet = new HttpGet(uriBuilder.build());
10  httpGet.addHeader("Accept", "application/json");
11  //执行请求
12  CloseableHttpResponse response = httpClient.execute(httpGet);
13  try {
14      int statusCode = response.getStatusLine().getStatusCode();
15      if (statusCode == 200) {
16          System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
17      } else if (statusCode == 204) {
18          System.out.println("success");
19      } else {
20          System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
21          throw new IOException("request failed");
22      }
23  } finally {
24      response.close();
25  }
26}

PHP代码示例:

1try {
2    $resp = $client->request(
3        'GET',
4        'https://api.mch.weixin.qq.com/v3/billdownload/file?token=xx', //请求URL
5        [
6            'headers' => [ 'Accept' => 'application/json']
7        ]
8    );
9    $statusCode = $resp->getStatusCode();
10    if ($statusCode == 200) { //处理成功
11        echo "success,return body = " . $resp->getBody()->getContents()."\n";
12    } else if ($statusCode == 204) { //处理成功,无返回Body
13        echo "success";
14    }
15} catch (RequestException $e) {
16    // 进行错误处理
17    echo $e->getMessage()."\n";
18    if ($e->hasResponse()) {
19        echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
20    }
21    return;
22}

更多参数、响应详情及错误码请参见 下载账单支付通知API接口文档。

4. 常见问题

Q:微信App支付,前端调起的时候返回errcode = -1该如何排查?

A:请按以下几点进行排查:

1.查看App下单参数返回是否正常,是否有正确的在调用SDK前获取了正确的prepay_id;

2.查看调用SDK签名是否正确,请注意以下几点:

  • 参与签名的参数名大小写一定要与文档中保持一致;

  • App下单返回的签名和调用SDK使用的签名不是同一个,调用SDK需要单独根据SDK参数生成签名。

3.检查客户端调用sendReq(PayReq)对象赋值的正确性(必要时让商户提供数据),若通过异步获取到后台数据,比如data对象是 通过异步请求得到的对象:request.AppID = data.AppID; 实际AppID属性值为空;

4.检查对应的开发配置,包括iOS的AppID配置,Android的包名 \及包签名设置。

Q:App调用“唤起支付API”返回:商户支付下单ID非法

A:请确认唤起支付参数字段名是否与文档的一致。

Q:服务商模式下,调用App下单接口,返回“特殊子商户未授权的产品权限”

A:App支付需要进行单独的授权开通才可使用,请前往服务商平台子商户管理中找到对应的子商户授权服务商App支付权限。

Q:调用App下单接口,返回“sub_appid与sub_mch_id不匹配”

A:在调用App下单接口前,需保证子商户号与子商户App的AppID存在绑定关系,请服务商前往服务商平台的子商户管理页面中操作绑定。