支付wiki: https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml
支付api: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/index.shtml
开发工具包(SDK)下载: https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay6_0.shtml#part-1
JSAPI支付是指商户通过调用微信支付提供的JSAPI接口,在支付场景中调起微信支付模块完成收款。
JSAPI支付适用于线下场所、公众号场景和PC网站场景。
商户已有H5商城网站,用户通过消息或扫描二维码在微信内打开网页时,可以调用微信支付完成下单购买的流程。具体操作流程如下:
步骤一 如图2.1,商户下发图文消息或者通过自定义菜单吸引用户点击进入商户网页。
步骤二 如图2.2,进入商户网页,用户选择购买,完成选购流程。
步骤三 如图2.3,调起微信支付控件,用户开始输入支付密码。
步骤四 如图2.4,密码验证通过,支付成功。商户后台得到支付成功的通知。
步骤五 如图2.5,返回商户页面,显示购买成功。该页面由商户自定义。
步骤六 如图2.6,微信支付公众号下发支付凭证。
商户/服务商在接入前首先要判断自己公司注册区域适用的接入模式,微信支付目前提供两种接入方式:直连模式和服务商模式
信息、资金流:微信支付—>直连商户
直连模式,商户自行申请入驻微信支付,无需服务商协助。(商户平台申请)成为直连商户
—— 信息流 —— 资金流
服务商模式,商户申请成为微信支付服务商,服务商自身无法作为一个直连商户直接发起交易,其发起交易必须传入相关特约商户商户号的参数信息。( 服务商平台 申请)成为服务商
请结合自身实际情况来选择接入模式。
服务商模式相关说明详见: 服务商模式 介绍
商户自行申请入驻微信支付,无服务商协助。( 商户平台 申请)成为直连商户
由于微信支付的产品体系全部搭载于微信的社交体系之上,所以直连商户或服务商接入微信支付之前,都需要有一个微信社交载体,该载体对应的ID即为APPID。
对于直连商户,该社交载体可以是公众号( 什么是公众号 ),小程序( 什么是小程序 )或APP。
如申请社交载体为公众号,请前往 公众平台 申请
如申请社交载体为小程序,请前往 小程序平台 申请
如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台 申请
商户可根据实际的业务需求来选择申请不同的社交载体。
各类社交载体一旦申请成功后,可以登录对应平台查看账号信息以获取对应的appid。
申请mchid和APPID的操作互不影响,可以并行操作,申请地址如下: 商户号申请平台
申请成功后,会向服务商填写的联系邮箱下发通知邮件,内容包含申请成功的mchid及其登录账号密码,请妥善保存。
注意:一个mchid只能对应一个结算币种,若需要使用多个币种收款,需要申请对应数量的mchid。
APPID和mchid全部申请完毕后,需要建立两者之间的绑定关系。
直连模式下,APPID与mchid之间的关系为多对多,即一个APPID下可以绑定多个mchid,而一个mchid也可以绑定多个APPID。
API v3密钥主要用于平台证书解密、回调信息解密,具体使用方式可参见接口规则文档中 证书和回调报文解密 章节。
请根据以下步骤配置API key:
商户API证书具体使用说明可参见接口规则文档中私钥和证书章节
商户可登录微信商户平台,在【账户中心】->【API安全】目录下载证书
以下为具体下载步骤:
步骤1 在【商户平台】-“复制证书串”环节,点击“复制证书串”按钮后;
步骤2 在【证书工具】-“复制请求串”环节,点击“下一步”按钮进入“粘贴证书串”环节;
步骤3 在【证书工具】-“粘贴证书串”环节,点击“粘贴”按钮后;
步骤4 点击“下一步”按钮,进入【证书工具】-“生成证书”环节
1)商户最后请求拉起微信支付收银台的页面地址我们称之为“支付授权目录”,例如:https://www.weixin.com/pay.php的支付授权目录为:https://www.weixin.com/。
2)商户实际的支付授权目录必须和在微信支付商户平台设置的一致,否则会报错“当前页面的URL未注册:”
登录【微信支付商户平台—>产品中心—>开发配置】,设置后一般5分钟内生效。
1)如果支付授权目录设置为顶级域名(例如:https://www.weixin.com/ ),那么只校验顶级域名,不校验后缀;
2)如果支付授权目录设置为多级目录,就会进行全匹配,例如设置支付授权目录为https://www.weixin.com/abc/123/,则实际请求页面目录不能为https://www.weixin.com/abc/,也不能为https://www.weixin.com/abc/123/pay/,必须为https://www.weixin.com/abc/123/
开发JSAPI支付时,在JSAPI下单接口中要求必传用户openid,而获取openid则需要您在微信公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。具体界面如图所示:
开通流程:在入驻时选择线下场所,公众号场景,PC网站场景的商户系统默认开通此功能,其他商户如有需要,可以在入驻后前往商户平台-产品中心-JSAPI支付-申请开通
为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考“ APIv3接口规则 ”
备注:当前接口用于微信国内钱包
为了帮助开发者调用开放接口,我们提供了JAVA、PHP、GO三种语言版本的开发库,封装了签名生成、签名验证、敏感信息加/解密、媒体文件上传等基础功能(更多语言版本的开发库将在近期陆续提供)
测试步骤:
1、根据自身开发语言,选择对应的开发库并构建项目,具体配置请参考下面链接的详细说明:
• wechatpay-java(推荐)wechatpay-apache-httpclient,适用于Java开发者。
• wechatpay-php(推荐)、wechatpay-guzzle-middleware,适用于PHP开发者
注:当前开发指引接口PHP示例代码采用wechatpay-guzzle-middleware版本
• wechatpay-go,适用于Go开发者
更多资源可前往微信支付开发者社区搜索查看
2、创建加载商户私钥、加载平台证书、初始化httpClient的通用方法
use GuzzleHttp\Exception\RequestException;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use GuzzleHttp\HandlerStack;
// 商户相关配置,
$merchantId = '1000100'; // 商户号
$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号
$merchantPrivateKey = PemUtil::loadPrivateKey('./path/to/mch/private/key.pem'); // 商户私钥文件路径
// 微信支付平台配置
$wechatpayCertificate = PemUtil::loadCertificate('./path/to/wechatpay/cert.pem'); // 微信支付平台证书文件路径
// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
$client = new GuzzleHttp\Client(['handler' => $stack]);
3、基于接口的示例代码,替换请求参数后可发起测试
说明:
• 上面的开发库为微信支付官方开发库,其它没有审核或者控制下的第三方工具和库,微信支付不保证它们的安全性和可靠性
通过包管理工具引入SDK后,可根据下面每个接口的示例代码替换相关参数后进行快速测试
• 开发者如果想详细了解签名生成、签名验证、敏感信息加/解密、媒体文件上传等常用方法的具体代码实现,可阅读下面的详细说明:
1.签名生成
2.签名验证
3.敏感信息加解密
4.merchantPrivateKey(私钥)
5.wechatpayCertificates(平台证书)
6.APIV3Key(V3 key)
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
• 支付授权目录说明:
a、商户最后请求拉起微信支付收银台的页面地址我们称之为“支付目录”,例如:https://www.weixin.com/pay.php。
b、商户实际的支付目录必须和在微信支付商户平台设置的一致,否则会报错“当前页面的URL未注册:”
• 支付授权目录设置说明:
登录微信支付【商户平台->产品中心->开发配置】,设置后一般5分钟内生效。
图1支付目录配置
• 支付授权目录校验规则说明:
a、如果支付授权目录设置为顶级域名(例如:https://www.weixin.com/ ),那么只校验顶级域名,不校验后缀;
b、如果支付授权目录设置为多级目录,就会进行全匹配,例如设置支付授权目录为https://www.weixin.com/abc/123/,则实际请求页面目录不能为https://www.weixin.com/abc/,也不能为https://www.weixin.com/abc/123/pay/,必须为https://www.weixin.com/abc/123/
• 授权域名说明:开发JSAPI支付时,在JSAPI下单接口中要求必传用户openid,而获取openid则需要您在公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。具体配置页如图2所示
• 授权域名设置说明:登录【微信公众平台->公众号设置->功能设置】
重点步骤说明:
步骤3 用户下单发起支付,商户可通过JSAPI下单创建支付订单。
步骤8 商户可在微信浏览器内通过JSAPI调起支付API调起微信支付,发起支付请求。
步骤15 用户支付成功后,商户可接收到微信支付支付结果通知支付结果通知API。
步骤20 商户在没有接收到微信支付结果通知的情况下需要主动调用查询订单API查询支付结果。
文档展示了如何使用微信支付服务端 SDK 快速接入JSAPI支付产品,完成与微信支付对接的部分。
注意:
文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及请求参数才能跑通。
以下接入步骤仅提供参考,请商户结合自身业务需求进行评估、修改。
步骤说明:用户通过商户下发的模板消息或扫描二维码在微信内进入商户网页,当用户选择相关商户购买时,商户系统先调用该接口在微信支付服务后台生成预支付交易单。
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi', //请求URL
[
// JSON请求体
'json' => [
"time_expire" => "2018-06-08T10:34:56+08:00",
"amount" => [
"total" => 100,
"currency" => "CNY",
],
"mchid" => "1230000109",
"description" => "Image形象店-深圳腾大-QQ公仔",
"notify_url" => "https://www.weixin.qq.com/wxpay/pay.php",
"payer" => [
"openid" => "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o",
],
"out_trade_no" => "1217752501201407033233368018",
"goods_tag" => "WXG",
"appid" => "wxd678efh567hg6787",
"attach" => "自定义数据说明",
"detail" => [
"invoice_id" => "wx123",
"goods_detail" => [
[
"goods_name" => "iPhoneX 256G",
"wechatpay_goods_id" => "1001",
"quantity" => 1,
"merchant_goods_id" => "商品编码",
"unit_price" => 828800,
],
[
"goods_name" => "iPhoneX 256G",
"wechatpay_goods_id" => "1001",
"quantity" => 1,
"merchant_goods_id" => "商品编码",
"unit_price" => 828800,
],
],
"cost_price" => 608800,
],
"scene_info" => [
"store_info" => [
"address" => "广东省深圳市南山区科技中一道10000号",
"area_code" => "440305",
"name" => "腾讯大厦分店",
"id" => "0001",
],
"device_id" => "013467007045764",
"payer_client_ip" => "14.23.150.211",
]
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
echo "success,return body = " . $resp->getBody()->getContents()."\n";
} else if ($statusCode == 204) { //处理成功,无返回Body
echo "success";
}
} catch (RequestException $e) {
// 进行错误处理
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
重要入参说明:
• out_trade_no:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
• description:商品描述
• notify_url:支付回调通知URL,该地址必须为直接可访问的URL,不允许携带查询串
• total:订单总金额,单位为分
• openid:openid是微信用户在appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户。openid获取方式请参考以下文档小程序获取openid、公众号获取openid、APP获取openid
更多参数、响应详情及错误码请参见JSAPI下单接口文档
步骤说明:通过JSAPI下单API成功获取预支付交易会话标识(prepay_id)后,需要通过JSAPI调起支付API来调起微信支付收银台
注意:
• WeixinJSBridge内置对象在其他浏览器中无效
• 此API需要将请求参数进行签名(参与签名的参数为:appId、timeStamp、nonceStr、package,参数区分大小写)
示例代码:
function onBridgeReady(){
WeixinJSBridge.invoke('getBrandWCPayRequest', {
"appId": "wx2421b1c4370ecxxx", //公众号ID,由商户传入 "timeStamp": "1395712654", //时间戳,自1970年以来的秒数 "nonceStr": "e61463f8efa94090b1f366cccfbbb444", //随机串 "package": "prepay_id=wx21201855730335ac86f8c43d1889123400",
"signType": "RSA", //微信签名方式: "paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg=="//微信签名
},
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);
} elseif (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
重要入参说明:
• package:JSAPI下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
• signType:该接口V3版本仅支持RSA
• paySign:签名
paySign生成规则、响应详情及错误码请参见 JSAPI调起支付接口文档
步骤说明:当用户完成支付,微信会把相关支付结果将通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
注意:
支付结果通知是以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)
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序支付通知API接口文档
步骤说明:当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知时,商户可通过查询订单接口核实订单支付状态
示例代码(通过微信订单号查询):
ry {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/pay/transactions/id/1217752501201407033233368018?mchid=1230000109', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
echo "success,return body = " . $resp->getBody()->getContents()."\n";
} else if ($statusCode == 204) { //处理成功,无返回Body
echo "success";
}
} catch (RequestException $e) {
// 进行错误处理
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
查询订单可通过微信支付订单号或商户订单号两种方式查询,两种查询方式返回结果相同
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序查询订单API接口文档
步骤说明:当商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close', //请求URL
[
// JSON请求体
'json' => [
"mchid " => "1230000109",
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
echo "success,return body = " . $resp->getBody()->getContents()."\n";
} else if ($statusCode == 204) { //处理成功,无返回Body
echo "success";
}
} catch (RequestException $e) {
// 进行错误处理
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
A:请按以下步骤进行排查:
1. 建议检查一下公众号的功能。比如是不是在订阅号/未认证的公众号里面尝试调用认证服务号的功能
2. 确认APPID是否认证过期或者APPID填写错误
3. 请尝试使用snsapi_userinfo的授权登录方式
A:请按以下步骤进行排查:
1. 请检查你的下单接口是否指定了支付用户的身份,该功能需单独开通指定身份支付权限方可使用
2. 请确认你使用的商户号是否有jsapi支付的权限,可登录商户平台-产品中心查看
A:请检查下单接口中使用的商户号是否在商户平台配置了对应的支付目录,可参考“设置支付目录”章节说明
A:请按以下步骤进行排查:
1. 检查下单接口传的appid与获取openid接口的appid是否同一个(需一致)
2. 检查appid对应的公众号后台,是否配置的授权域名和获取openid的域名一致。授权域名配置路径:【公众平台-> 设置-> 公众号设置-> 功能设置–> 网页授权域名】
A:JSAPI支付只能从微信浏览器内发起支付请求
模块名称 |
功能列表 |
描述 |
JSAPI支付 |
JSAPI下单 |
通过本接口提交微信支付JSAPI支付订单。 |
查询订单 |
通过此接口查询订单状态。 |
|
关闭订单 |
通过此接口关闭待支付订单。 |
|
JSAPI调起支付 |
通过使用微信浏览器里面H5网页中调用微信JSSDK执行JS调起支付。 |
|
支付结果通知 |
微信支付通过支付通知接口将用户支付成功消息通知给商户。 |
|
申请退款 |
商户可以通过该接口将支付金额退还给买家。 |
|
查询单笔退款 |
提交退款申请后,通过调用该接口查询退款状态。 |
|
退款结果通知 |
微信支付通过退款通知接口将用户退款成功消息通知给商户。 |
|
申请交易账单 |
商户可以通过该接口获取交易账单文件的下载地址。 |
|
申请资金账单 |
商户可以通过该接口获取资金账单文件的下载地址。 |
|
下载账单 |
通过申请交易/资金账单获取到download_url在该接口获取到对应的账单。 |
APP支付是指商户通过在移动端应用APP中集成开放SDK调起微信支付模块来完成支付。目前微信支付支持手机系统有:IOS(苹果)、Android(安卓)和WP(Windows Phone)
APP支付适用于在移动端APP中集成微信支付功能的场景。商户APP调用微信提供的SDK调用微信支付模块,商户APP会跳转到微信中完成支付,支付完后跳回到商户APP内,最后展示支付结果。具体操作流程如下:
步骤一 用户进入商户APP,选择商品下单、确认购买,进入支付环节。商户服务后台生成支付订单,签名后将数据传输到APP端。以微信提供的DEMO为例,见图1.1。
步骤二 用户点击后发起支付操作,进入到微信界面,调起微信支付,出现确认支付界面,见图1.2。
步骤三 用户确认收款方和金额,点击立即支付后出现输入密码界面,可选择零钱或银行卡支付见图1.3。
步骤四 输入正确密码后,支付完成,用户端微信出现支付详情页面。见图1.4。
步骤五 回跳到商户APP中,商户APP根据支付结果个性化展示订单处理结果。见图1.5。
商户/服务商在接入前首先要判断自己公司注册区域适用的接入模式,微信支付目前提供两种接入方式:直连模式和服务商模式。
信息、资金流:微信支付—>直连商户
直连模式,商户自行申请入驻微信支付,无需服务商协助。(商户平台申请)成为直连商户
服务商模式,商户申请成为微信支付服务商,服务商自身无法作为一个直连商户直接发起交易,其发起交易必须传入相关特约商户商户号的参数信息。(服务商平台申请)成为服务商
请结合自身实际情况来选择接入模式。
服务商模式相关说明详见:服务商模式介绍
商户自行申请入驻微信支付,无服务商协助。(商户平台申请)成为直连商户
由于微信支付的产品体系全部搭载于微信的社交体系之上,所以直连商户或服务商接入微信支付之前,都需要有一个微信社交载体,该载体对应的ID即为APPID。
对于直连商户,该社交载体可以是公众号(什么是公众号),小程序(什么是小程序)或APP。
如申请社交载体为公众号,请前往 公众平台申请
如申请社交载体为小程序,请前往 小程序平台 申请
如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台申请
商户可根据实际的业务需求来选择申请不同的社交载体。
各类社交载体一旦申请成功后,可以登录对应平台查看账号信息以获取对应的appid。
申请mchid和APPID的操作互不影响,可以并行操作,申请地址如下: 商户号申请平台
申请成功后,会向服务商填写的联系邮箱下发通知邮件,内容包含申请成功的mchid及其登录账号密码,请妥善保存。
注意:一个mchid只能对应一个结算币种,若需要使用多个币种收款,需要申请对应数量的mchid。
APPID和mchid全部申请完毕后,需要建立两者之间的绑定关系。
直连模式下,APPID与mchid之间的关系为多对多,即一个APPID下可以绑定多个mchid,而一个mchid也可以绑定多个APPID。
API v3密钥主要用于平台证书解密、回调信息解密,具体使用方式可参见接口规则文档中证书和回调报文解密章节。
请根据以下步骤配置API key:
商户API证书具体使用说明可参见接口规则文档中私钥和证书章节
商户可登录微信商户平台,在【账户中心】->【API安全】目录下载证书
以下为具体下载步骤:
步骤1 在【商户平台】-“复制证书串”环节,点击“复制证书串”按钮后;
步骤2 在【证书工具】-“复制请求串”环节,点击“下一步”按钮进入“粘贴证书串”环节;
步骤3 在【证书工具】-“粘贴证书串”环节,点击“粘贴”按钮后;
步骤4 点击“下一步”按钮,进入【证书工具】-“生成证书”环节
APP商户申请时,如选择提供截图,请参见《微信支付APP商户截图标准》提供APP首页截图、APP尾页截图、APP应用内截图和APP支付页截图。
列表首位+默认勾选+标准logo+推荐标签+标语
素材下载:《微信支付logo、APP支付素材下载》
开通流程:在入驻时选择APP场景的商户系统默认开通此功能,其他商户如有需要,可以在入驻后前往商户平台-产品中心-APP支付-申请开通
为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考“APIv3接口规则”
备注:当前接口用于微信国内钱包
为了帮助开发者调用开放接口,我们提供了JAVA、PHP、GO三种语言版本的开发库,封装了签名生成、签名验证、敏感信息加/解密、媒体文件上传等基础功能(更多语言版本的开发库将在近期陆续提供)
测试步骤:
1、根据自身开发语言,选择对应的开发库并构建项目,具体配置请参考下面链接的详细说明:
• wechatpay-java(推荐)wechatpay-apache-httpclient,适用于Java开发者。
• wechatpay-php(推荐)、wechatpay-guzzle-middleware,适用于PHP开发者
注:当前开发指引接口PHP示例代码采用wechatpay-guzzle-middleware版本
• wechatpay-go,适用于Go开发者
更多资源可前往微信支付开发者社区搜索查看
2、创建加载商户私钥、加载平台证书、初始化httpClient的通用方法
use GuzzleHttp\Exception\RequestException;
use WechatPay\GuzzleMiddleware\WechatPayMiddleware;
use WechatPay\GuzzleMiddleware\Util\PemUtil;
use GuzzleHttp\HandlerStack;
// 商户相关配置,
$merchantId = '1000100'; // 商户号
$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号
$merchantPrivateKey = PemUtil::loadPrivateKey('./path/to/mch/private/key.pem'); // 商户私钥文件路径
// 微信支付平台配置
$wechatpayCertificate = PemUtil::loadCertificate('./path/to/wechatpay/cert.pem'); // 微信支付平台证书文件路径
// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
$client = new GuzzleHttp\Client(['handler' => $stack]);
、基于接口的示例代码,替换请求参数后可发起测试
说明:
• 上面的开发库为微信支付官方开发库,其它没有审核或者控制下的第三方工具和库,微信支付不保证它们的安全性和可靠性
通过包管理工具引入SDK后,可根据下面每个接口的示例代码替换相关参数后进行快速测试
• 开发者如果想详细了解签名生成、签名验证、敏感信息加/解密、媒体文件上传等常用方法的具体代码实现,可阅读下面的详细说明:
1.签名生成
2.签名验证
3.敏感信息加解密
4.merchantPrivateKey(私钥)
5.wechatpayCertificates(平台证书)
6.APIV3Key(V3 key)
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
APP接入微信支付,需要先将商户APP在微信开放平台进行注册,登记APP开发参数以生成APPID。具体操作步骤如下:
一、登录微信开放平台,进入【管理中心 → 移动应用 → 创建移动应用】;
二、完成基本信息的录入,商户需要在本步骤提交APP对应的下载地址,应用官网,应用水印,icon等业务信息;
三、完成平台信息的录入,商户需要在本步骤提交APP在Android及iOS端对应的开发参数,包括Android端应用的包名,应用签名,iOS端应用的bundle ID, universal link等;
注意:
Android应用包名和签名的相关说明,参考“Andriod开发要点说明”。
四、以上信息全部提交完成后,即完成APP的注册,商户可在【管理中心 → 移动应用】中,选择具体的应用查看其APPID及已获得的接口能力;
五、获取到APP的APPID后,需要将该APPID与商户的收款mch_id进行绑定,商户可登录商户平台后前往【产品中心 -> AppID账号管理】界面中进行AppID的绑定及管理,界面如图所示:
重点步骤说明:
步骤3:用户下单发起支付,商户可通过微信支付APP下单API创建支付订单。
商户调用APP下单API后,分正常返回和异常返回情况:
正常返回:返回prepay_id,商户可根据返回的prepay_id来生成调用OpenSDK的签名以执行下一步。
异常返回:返回http code或错误码,商户可根据http code列表 或错误码说明来排查原因并执行下一步操作
步骤8: 商户通过APP调起支付OpenSDK调起微信支付,发起支付请求,有关OpenSDK调起支付的详细说明,请参考2.2.2部分的说明
步骤15-19: 用户支付成功后,商户可通过以下两种方式获取订单状态
我们通过以下接口将用户确认订单信息回调通知给商户系统:
方法一:支付结果通知。用户支付成功后,微信支付会将支付成功的结果以回调通知的形式同步给商户,商户的回调地址需要在调用APP下单API时传入notify_url参数。
方法二:当因网络抖动或本身notify_url存在问题等原因,导致无法接收到回调通知时,商户也可主动调用查询订单API 查询订单API来获取订单状态
本文档展示了如何使用微信支付服务端 SDK 快速接入APP支付产品,完成与微信支付对接的部分。
注意:
文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及参数才能跑通。
以下接入步骤仅提供参考,请商户结合自身业务需求进行评估、修改。
步骤说明:用户在商户APP内完成商户选择后进入支付页面,商户需要通过后端请求该APP下单API来获取预支付ID。
示例代码
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/app', //请求URL
[
// JSON请求体
'json' => [
"time_expire" => "2018-06-08T10:34:56+08:00",
"amount" => [
"total" => 100,
"currency" => "CNY",
],
"mchid" => "1230000109",
"description" => "Image形象店-深圳腾大-QQ公仔",
"notify_url" => "https://www.weixin.qq.com/wxpay/pay.php",
"out_trade_no" => "1217752501201407033233368018",
"goods_tag" => "WXG",
"appid" => "wxd678efh567hg6787",
"attach" => "自定义数据说明",
"detail" => [
"invoice_id" => "wx123",
"goods_detail" => [
[
"goods_name" => "iPhoneX 256G",
"wechatpay_goods_id" => "1001",
"quantity" => 1,
"merchant_goods_id" => "商品编码",
"unit_price" => 828800,
],
[
"goods_name" => "iPhoneX 256G",
"wechatpay_goods_id" => "1001",
"quantity" => 1,
"merchant_goods_id" => "商品编码",
"unit_price" => 828800,
],
],
"cost_price" => 608800,
],
"scene_info" => [
"store_info" => [
"address" => "广东省深圳市南山区科技中一道10000号",
"area_code" => "440305",
"name" => "腾讯大厦分店",
"id" => "0001",
],
"device_id" => "013467007045764",
"payer_client_ip" => "14.23.150.211",
]
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
echo "success,return body = " . $resp->getBody()->getContents()."\n";
} else if ($statusCode == 204) { //处理成功,无返回Body
echo "success";
}
} catch (RequestException $e) {
// 进行错误处理
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
重要参数说明:
• out_trade_no:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
• description:商品描述
• notify_url:支付回调通知URL,该地址必须为直接可访问的URL,不允许携带查询串
• total:订单总金额,单位为分
更多参数、响应详情及错误码请参见APP下单API接口文档
步骤说明:通过APP下单API成功获取预支付交易会话标识(prepay_id)后,需要通过OpenSDK来调起微信支付收银台
该步骤请使用开放平台的官方OpenSDK:
IOS OpenSDK下载地址
Android OpenSDK下载地址
SDK的调用需要携带签名(参与签名的参数为:appid、partnerid、prepayid、package、noncestr、timestamp,参数区分大小写)
重要入参说明:
package:取固定值Sign=WXPay
signType:该接口V3版本仅支持RSA
paySign:签名
paySign生成规则、响应详情及错误码请参见APP调起支付接口文档
二、支付结果回调
参照微信SDK Sample,在net.sourceforge.simcpux.wxapi包路径中实现WXPayEntryActivity类(包名或类名不一致会造成无法回调),在WXPayEntryActivity类中实现onResp函数,支付完成后,微信APP会返回到商户APP并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码, 如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意一定不能以客户端返回作为用户支付的结果,应以服务器端的接收的支付通知或查询API返回的结果为准。代码示例如下:
publicvoidonResp(BaseRespresp){
if(resp.getType()==ConstantsAPI.COMMAND_PAY_BY_WX){
Log.d(TAG,"onPayFinish,errCode="+resp.errCode);
AlertDialog.Builderbuilder=newAlertDialog.Builder(this);
builder.setTitle(R.string.app_tip);
}
}
回调中errCode值列表:
名称 |
描述 |
解决方案 |
0 |
成功 |
展示成功页面 |
-1 |
错误 |
可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。 |
-2 |
用户取消 |
无需处理。发生场景:用户不支付了,点击取消,返回APP。 |
步骤说明:当用户完成支付,微信会把相关支付结果将通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
注意:
支付结果通知是以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)
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序支付通知API接口文档
步骤说明:当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知时,商户可通过查询订单接口核实订单支付状态
示例代码(通过微信订单号查询):
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/pay/transactions/id/1217752501201407033233368018?mchid=1230000109', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
echo "success,return body = " . $resp->getBody()->getContents()."\n";
} else if ($statusCode == 204) { //处理成功,无返回Body
echo "success";
}
} catch (RequestException $e) {
// 进行错误处理
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
查询订单可通过微信支付订单号或商户订单号两种方式查询,两种查询方式返回结果相同
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序查询订单API接口文档
步骤说明:当商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口
示例代码:
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close', //请求URL
[
// JSON请求体
'json' => [
"mchid " => "1230000109",
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功
echo "success,return body = " . $resp->getBody()->getContents()."\n";
} else if ($statusCode == 204) { //处理成功,无返回Body
echo "success";
}
} catch (RequestException $e) {
// 进行错误处理
echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo "failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
A:1. 查看APP下单参数返回是否正常,是否有正确的在调用SDK前获取了正确的prepay_id;
2.查看调用SDK签名是否正确,请注意以下几点:
a) 参与签名的参数名大小写一定要与文档中保持一致;
b) APP下单返回的签名和调用SDK使用的签名不是同一个,调用SDK需要单独根据SDK参数生成签名;
3. 检查客户端调用sendReq(PayReq)对象赋值的正确性(必要时让商户提供数据),若通过异步获取到后台数据,比如data对象是通过异步请求得到的对象:request.appId = data.appid; 实际appid属性值为空;
4. 检查对应的开发配置,包括iOS的appid配置,Android的包名及包签名设置。
A:请确认唤起支付参数字段名是否与文档的一致。
A:APP支付需要进行单独的授权开通才可使用,请前往服务商平台子商户管理中找到对应的子商户授权服务商APP支付权限。
A:在调用APP下单接口前,需保证子商户号与子商户APP的appid存在绑定关系,请服务商前往服务商平台的子商户管理页面中操作绑定。
模块名称 |
功能列表 |
描述 |
APP支付 |
APP下单 |
通过本接口提交微信支付APP支付订单。 |
查询订单 |
通过此接口查询订单状态。 |
|
关闭订单 |
通过此接口关闭待支付订单。 |
|
APP调起支付 |
通过合单下单接口获取到发起支付的必要参数prepay_id,可以按照接口定义中的规则,使用微信支付提供的SDK调起APP支付。 |
|
支付结果通知 |
微信支付通过支付通知接口将用户支付成功消息通知给商户。 |
|
申请退款 |
商户可以通过该接口将支付金额退还给买家。 |
|
查询单笔退款 |
提交退款申请后,通过调用该接口查询退款状态 。 |
|
退款结果通知 |
微信支付通过退款通知接口将用户退款成功消息通知给商户。 |
|
申请交易账单 |
商户可以通过该接口获取交易账单文件的下载地址。 |
|
申请资金账单 |
商户可以通过该接口获取资金账单文件的下载地址。 |
|
下载账单 |
通过申请交易/资金账单获取到download_url在该接口获取到对应的账单。 |
H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付。
说明:要求商户已有H5商城网站,并且已经过ICP备案,即可申请接入。
提醒:H5支付不建议在APP端使用,如需要在APP中使用微信支付,请接APP支付,文档详见《APP支付文档》
H5支付主要用于触屏版的手机浏览器请求微信支付的场景,方便从外部浏览器唤起微信支付。
用户侧使用H5支付具体操作流程如下:
步骤一 用户从非微信浏览器的站点导航进入商户H5网页,用户挑选需购买商品,选择微信支付发起购买流程。
步骤二 进入微信客户端确认交易,输入支付密码。
步骤三 支付成功,用户收到支付凭证,同时商户后台收到支付成功的通知。
商户/服务商在接入前首先要判断自己公司注册区域适用的接入模式,微信支付目前提供两种接入方式:直连模式和服务商模式。
信息、资金流:微信支付—>直连商户
直连模式,商户自行申请入驻微信支付,无需服务商协助。(商户平台申请)成为直连商户
—— 信息流 —— 资金流
服务商模式,商户申请成为微信支付服务商,服务商自身无法作为一个直连商户直接发起交易,其发起交易必须传入相关特约商户商户号的参数信息。(服务商平台申请)成为服务商
请结合自身实际情况来选择接入模式。
服务商模式相关说明详见:服务商模式介绍
商户自行申请入驻微信支付,无服务商协助。(商户平台申请)成为直连商户
由于微信支付的产品体系全部搭载于微信的社交体系之上,所以直连商户或服务商接入微信支付之前,都需要有一个微信社交载体,该载体对应的ID即为APPID。
对于直连商户,该社交载体可以是公众号(什么是公众号),小程序(什么是小程序)或APP。
如申请社交载体为公众号,请前往 公众平台申请
如申请社交载体为小程序,请前往 小程序平台 申请
如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台申请
商户可根据实际的业务需求来选择申请不同的社交载体。
各类社交载体一旦申请成功后,可以登录对应平台查看账号信息以获取对应的appid。
申请mchid和APPID的操作互不影响,可以并行操作,申请地址如下: 商户号申请平台
申请成功后,会向服务商填写的联系邮箱下发通知邮件,内容包含申请成功的mchid及其登录账号密码,请妥善保存。
注意:一个mchid只能对应一个结算币种,若需要使用多个币种收款,需要申请对应数量的mchid。
APPID和mchid全部申请完毕后,需要建立两者之间的绑定关系。
直连模式下,APPID与mchid之间的关系为多对多,即一个APPID下可以绑定多个mchid,而一个mchid也可以绑定多个APPID。
API v3密钥主要用于平台证书解密、回调信息解密,具体使用方式可参见接口规则文档中证书和回调报文解密章节。
请根据以下步骤配置API key:
商户API证书具体使用说明可参见接口规则文档中私钥和证书章节
商户可登录微信商户平台,在【账户中心】->【API安全】目录下载证书
以下为具体下载步骤:
步骤1 在【商户平台】-“复制证书串”环节,点击“复制证书串”按钮后;
步骤2 在【证书工具】-“复制请求串”环节,点击“下一步”按钮进入“粘贴证书串”环节;
步骤3 在【证书工具】-“粘贴证书串”环节,点击“粘贴”按钮后;
步骤4 点击“下一步”按钮,进入【证书工具】-“生成证书”环节
1、H5支付域名
2、售卖产品/使用场景的描述
3、所售卖产品对应的官方网站域名或详情页网址
开通流程:入驻成为商户:在线提交营业执照、身份证、银行账户等基本信息,快速提交申请
申请规则详见:商户申请H5支付权限需要注意哪些规则?
为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考“APIv3接口规则”
备注:当前接口用于微信国内钱包
为了帮助开发者调用开放接口,我们提供了JAVA、PHP、GO三种语言版本的开发库,封装了签名生成、签名验证、敏感信息加/解密、媒体文件上传等基础功能(更多语言版本的开发库将在近期陆续提供)
测试步骤:
1、根据自身开发语言,选择对应的开发库并构建项目,具体配置请参考下面链接的详细说明:
• wechatpay-java(推荐)wechatpay-apache-httpclient,适用于Java开发者。
• wechatpay-php(推荐)、wechatpay-guzzle-middleware,适用于PHP开发者
注:当前开发指引接口PHP示例代码采用wechatpay-guzzle-middleware版本
• wechatpay-go,适用于Go开发者
更多资源可前往微信支付开发者社区搜索查看
2、创建加载商户私钥、加载平台证书、初始化httpClient的通用方法
JAVA
PHP
useGuzzleHttp\Exception\RequestException;
useWechatPay\GuzzleMiddleware\WechatPayMiddleware;
useWechatPay\GuzzleMiddleware\Util\PemUtil;
useGuzzleHttp\HandlerStack;
// 商户相关配置,
$merchantId = '1000100'; // 商户号
$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号
$merchantPrivateKey = PemUtil::loadPrivateKey('./path/to/mch/private/key.pem'); // 商户私钥文件路径// 微信支付平台配置
$wechatpayCertificate = PemUtil::loadCertificate('./path/to/wechatpay/cert.pem'); // 微信支付平台证书文件路径// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
$client = new GuzzleHttp\Client(['handler' => $stack]);
3、基于接口的示例代码,替换请求参数后可发起测试
说明:
• 上面的开发库为微信支付官方开发库,其它没有审核或者控制下的第三方工具和库,微信支付不保证它们的安全性和可靠性
通过包管理工具引入SDK后,可根据下面每个接口的示例代码替换相关参数后进行快速测试
• 开发者如果想详细了解签名生成、签名验证、敏感信息加/解密、媒体文件上传等常用方法的具体代码实现,可阅读下面的详细说明:
1.签名生成
2.签名验证
3.敏感信息加解密
4.merchantPrivateKey(私钥)
5.wechatpayCertificates(平台证书)
6.APIV3Key(V3 key)
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
● 前往微信支付商户平台—>产品中心—>H5支付—>申请开通
● 登录【微信支付商户平台—>产品中心—>开发配置—>H5支付】,设置后一般10分钟内生效。
注意:
域名必须通过ICP备案
域名填写格式不包含http://或https://
重点步骤说明:
步骤1 用户向商户系统后台请求下单,商户后台必须做好安全校验
当跨域请求不是简单请求时,浏览器会发起Options预检请求,此时商户后台需要支持Options请求且校验Origin头部,如果不在允许的白名单列表内,则返回403且不返回Access-Control-Allow-*
相关头部
针对GET/POST的跨域下单请求,商户后台需要校验Origin头部是否合法且用户Cookie是否完备(若用户未登陆则先引导登陆商户站点),否则返回403且不返回Access-Control-Allow-*
相关头部
步骤2 商户可通过H5下单API创建支付订单。
步骤3 用户通过微信外部的浏览器调起微信支付中间页,进行发起支付请求。
步骤5 用户支付成功后,商户可接收到微信支付支付结果通知支付通知API
步骤8 商户在没有接收到微信支付结果通知的情况下需要主动调用查询订单API查询支付结果。
注意:
商户需按照安全规范进行接入,若因未遵循规范接入而出现安全问题,财付通将根据《微信支付服务协议》条款处理
以上图示,仅为示例,只供参考。请商户自行确认是否实现了跨越访问白名单限制和用户登录态校验。
本文档展示了如何使用微信支付服务端 SDK 快速接入H5支付产品,完成与微信支付对接的部分。
注意:
文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及请求参数才能跑通。
以下接入步骤仅提供参考,请商户结合自身业务需求进行评估、修改。
步骤说明:用户使用微信外部的浏览器访问商户H5页面,当用户选择相关商品购买时,商户系统先调用该接口在微信支付服务后台生成预支付交易单。
示例代码
PHP
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/h5', //请求URL
[
// JSON请求体'json' => [
"time_expire" => "2018-06-08T10:34:56+08:00",
"amount" => [
"total" => 100,
"currency" => "CNY",
],
"mchid" => "1900006891",
"description" => "Image形象店-深圳腾大-QQ公仔",
"notify_url" => "https://www.weixin.qq.com/wxpay/pay.php",
"out_trade_no" => "1217752501201407033233368022",
"goods_tag" => "WXG",
"appid" => "wxdace645e0bc2c424",
"attach" => "自定义数据说明",
"scene_info" => [
"store_info" => [
"address" => "广东省深圳市南山区科技中一道10000号",
"area_code" => "440305",
"name" => "腾讯大厦分店",
"id" => "0001",
],
"device_id" => "013467007045764",
"payer_client_ip" => "14.23.150.211",
"h5_info"=>[
"type"=>"IOS"
],
]
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
重要参数说明:
• out_trade_no:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
• description:商品描述
• notify_url:支付回调通知URL,该地址必须为直接可访问的URL,不允许携带查询串
• total:订单总金额,单位为分
• scene_info:支付场景描述
更多参数、响应详情及错误码请参见H5下单API接口文档
步骤说明:通过H5下单API成功获取H5下单返回的支付中间页(h5_url)后,用户需要通过微信外部的浏览器调起微信支付收银台
注意:
h5_url为拉起微信支付收银台的中间页面,可通过访问该url来拉起微信客户端,完成支付,h5_url的有效期为5分钟
微信支付收银台中间页会进行H5权限的校验,安全性检查
正常流程用户支付完成后会返回至发起支付的页面,如需返回至指定页面,则可以在h5_url后拼接上redirect_url参数,来指定回调页面。您希望用户支付完成后跳转至https://www.wechatpay.com.cn,则拼接后的地址为h5_url= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096&redirect_url=https%3A%2F%2Fwww.wechatpay.com.cn
需对redirect_url进行urlencode处理
由于设置redirect_url后,回跳指定页面的操作可能发生在:
1、微信支付中间页调起微信收银台后超过5秒
2、用户点击“取消支付”或支付完成后点击“完成”按钮。因此无法保证页面回跳时,支付流程已结束,所以商户设置的redirect_url地址不能自动执行查单操作,应让用户去点击按钮触发查单操作,回跳页面展示效果可参考下图
步骤说明:当用户完成支付,微信会把相关支付结果将通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
注意:
支付结果通知是以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)
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序支付通知API接口文档
步骤说明:当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知时,商户可通过查询订单接口核实订单支付状态
示例代码(通过微信订单号查询):
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/pay/transactions/id/1217752501201407033233368018?mchid=1230000109', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
查询订单可通过微信支付订单号或商户订单号两种方式查询,两种查询方式返回结果相同
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序查询订单API接口文档
步骤说明:当商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口
示例代码:
PHP
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close', //请求URL
[
// JSON请求体'json' => [
"mchid " => "1230000109",
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟
已支付成功的订单不能关闭
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序接口文档
步骤说明:微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址
示例代码:
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=2019-06-11&bill_type=ALL', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序接口文档
步骤说明:通过申请交易账单接口获取到账单下载地址(download_url)后,再通过该接口获取到对应的账单文件,文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况
示例代码:
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/billdownload/file?token=xx', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
• 账单文件的下载地址的有效时间为30s
• 强烈建议商户将实际账单文件的哈希值和之前从接口获取到的哈希值进行比对,以确认数据的完整性
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序下载账单API接口文档
A:请按以下几点进行排查:
1. 当前调起H5支付的referer为空导致,一般是因为直接访问页面调起H5支付,请按正常流程进行页面跳转后发起支付,或自行抓包确认referer值是否为空
2. 如果是APP里调起H5支付,需要在webview中手动设置referer,如(Map extraHeaders = new HashMap();extraHeaders.put("Referer", "商户申请H5时提交的授权域名");//例如 https://pay.weixin.qq.com )
A:请按以下几点进行排查:
1,当前调起H5支付的域名(微信侧从referer中获取)与申请H5支付时提交的授权域名不一致,如需添加或修改授权域名,请登录商户号对应的【商户平台 -> 产品中心 -> 开发配置】自行配置
2,如果设置了回跳地址redirect_url,请确认设置的回跳地址的域名与申请H5支付时提交的授权域名是否一致
A:H5下单返回的H5_URL生成后,有效期为5分钟,如超时请重新生成H5_URL后再发起支付
A:H5支付不能直接在微信客户端内调起,请在外部浏览器调起
A:请按以下几点进行排查:
1,请确认同一个H5_URL只被一个微信号调起,如果不同微信号调起请重新下单生成新的H5_URL
2,如H5_URL有添加redirect_url,请确认参数拼接格式是否有误,是否有对redirect_url的值做urlencode,可参考以下例子格式:
https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096&redirect_url=https%3A%2F%2Fwww.wechatpay.com.cn
A:完成H5支付后需通过schame信息返回调起支付的浏览器,但由于部分浏览器隐藏了这个信息,在无法拿到schame信息的情况下,就会默认回到safari浏览器
模块名称 |
功能列表 |
描述 |
H5支付 |
H5下单 |
通过本接口提交微信支付H5支付订单。 |
查询订单 |
通过此接口查询订单状态。 |
|
关闭订单 |
通过此接口关闭待支付订单。 |
|
H5调起支付 |
调用微信支付的H5支付接口,微信后台系统返回链接参数h5_url,商户通过h5_url调起微信支付中间页。 |
|
支付结果通知 |
微信支付通过支付通知接口将用户支付成功消息通知给商户。 |
|
申请退款 |
商户可以通过该接口将支付金额退还给买家。 |
|
查询单笔退款 |
提交退款申请后,通过调用该接口查询退款状态 。 |
|
退款结果通知 |
微信支付通过退款通知接口将用户退款成功消息通知给商户。 |
|
申请交易账单 |
商户可以通过该接口获取交易账单文件的下载地址。 |
|
申请资金账单 |
商户可以通过该接口获取资金账单文件的下载地址。 |
|
下载账单 |
通过申请交易/资金账单获取到download_url在该接口获取到对应的账单。 |
Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。
Native支付适用于PC网站、实体店单品或订单、媒体广告支付等场景
用户扫描商户展示在各种场景的二维码进行支付,具体操作流程如下:
步骤一 商户根据微信支付的规则,为不同商品生成不同的二维码(如图3.1),展示在各种场景,用于用户扫描购买。
步骤二 用户使用微信“扫一扫”(如图3.2)扫描二维码后,获取商品支付信息,引导用户完成支付(如图3.3)。
步骤三 用户确认支付,输入支付密码(如图3.4)。
步骤四 支付完成后会提示用户支付成功(如图3.5),商户后台得到支付成功的通知,然后进行发货处理。
商户/服务商在接入前首先要判断自己公司注册区域适用的接入模式,微信支付目前提供两种接入方式:直连模式和服务商模式。
信息、资金流:微信支付—>直连商户
直连模式,商户自行申请入驻微信支付,无需服务商协助。(商户平台申请)成为直连商户
—— 信息流 —— 资金流
服务商模式,商户申请成为微信支付服务商,服务商自身无法作为一个直连商户直接发起交易,其发起交易必须传入相关特约商户商户号的参数信息。(服务商平台申请)成为服务商
请结合自身实际情况来选择接入模式。
服务商模式相关说明详见:服务商模式介绍
商户自行申请入驻微信支付,无服务商协助。(商户平台申请)成为直连商户
由于微信支付的产品体系全部搭载于微信的社交体系之上,所以直连商户或服务商接入微信支付之前,都需要有一个微信社交载体,该载体对应的ID即为APPID。
对于直连商户,该社交载体可以是公众号(什么是公众号),小程序(什么是小程序)或APP。
如申请社交载体为公众号,请前往 公众平台申请
如申请社交载体为小程序,请前往 小程序平台 申请
如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台申请
商户可根据实际的业务需求来选择申请不同的社交载体。
各类社交载体一旦申请成功后,可以登录对应平台查看账号信息以获取对应的appid。
申请mchid和APPID的操作互不影响,可以并行操作,申请地址如下: 商户号申请平台
申请成功后,会向服务商填写的联系邮箱下发通知邮件,内容包含申请成功的mchid及其登录账号密码,请妥善保存。
注意:一个mchid只能对应一个结算币种,若需要使用多个币种收款,需要申请对应数量的mchid。
APPID和mchid全部申请完毕后,需要建立两者之间的绑定关系。
直连模式下,APPID与mchid之间的关系为多对多,即一个APPID下可以绑定多个mchid,而一个mchid也可以绑定多个APPID。
API v3密钥主要用于平台证书解密、回调信息解密,具体使用方式可参见接口规则文档中证书和回调报文解密章节。
请根据以下步骤配置API key:
商户API证书具体使用说明可参见接口规则文档中私钥和证书章节
商户可登录微信商户平台,在【账户中心】->【API安全】目录下载证书
以下为具体下载步骤:
步骤1 在【商户平台】-“复制证书串”环节,点击“复制证书串”按钮后;
步骤2 在【证书工具】-“复制请求串”环节,点击“下一步”按钮进入“粘贴证书串”环节;
步骤3 在【证书工具】-“粘贴证书串”环节,点击“粘贴”按钮后;
步骤4 点击“下一步”按钮,进入【证书工具】-“生成证书”环节
示例1:单独显要区域,常驻微信支付
示例2:单独页签+页签首位+新logo+推荐标签+直接二维码
开通流程:在入驻时选择PC网站场景的商户系统默认开通此功能,其他商户如有需要,可以在入驻后前往商户平台-产品中心-Native支付-申请开通。
为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考“APIv3接口规则”
备注:当前接口用于微信国内钱包
为了帮助开发者调用开放接口,我们提供了JAVA、PHP、GO三种语言版本的开发库,封装了签名生成、签名验证、敏感信息加/解密、媒体文件上传等基础功能(更多语言版本的开发库将在近期陆续提供)
测试步骤:
1、根据自身开发语言,选择对应的开发库并构建项目,具体配置请参考下面链接的详细说明:
• wechatpay-java(推荐)wechatpay-apache-httpclient,适用于Java开发者。
• wechatpay-php(推荐)、wechatpay-guzzle-middleware,适用于PHP开发者
注:当前开发指引接口PHP示例代码采用wechatpay-guzzle-middleware版本
• wechatpay-go,适用于Go开发者
更多资源可前往微信支付开发者社区搜索查看
2、创建加载商户私钥、加载平台证书、初始化httpClient的通用方法
JAVA
PHP
useGuzzleHttp\Exception\RequestException;
useWechatPay\GuzzleMiddleware\WechatPayMiddleware;
useWechatPay\GuzzleMiddleware\Util\PemUtil;
useGuzzleHttp\HandlerStack;
// 商户相关配置,
$merchantId = '1000100'; // 商户号
$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号
$merchantPrivateKey = PemUtil::loadPrivateKey('./path/to/mch/private/key.pem'); // 商户私钥文件路径// 微信支付平台配置
$wechatpayCertificate = PemUtil::loadCertificate('./path/to/wechatpay/cert.pem'); // 微信支付平台证书文件路径// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
$client = new GuzzleHttp\Client(['handler' => $stack]);
3、基于接口的示例代码,替换请求参数后可发起测试
说明:
• 上面的开发库为微信支付官方开发库,其它没有审核或者控制下的第三方工具和库,微信支付不保证它们的安全性和可靠性
通过包管理工具引入SDK后,可根据下面每个接口的示例代码替换相关参数后进行快速测试
• 开发者如果想详细了解签名生成、签名验证、敏感信息加/解密、媒体文件上传等常用方法的具体代码实现,可阅读下面的详细说明:
1.签名生成
2.签名验证
3.敏感信息加解密
4.merchantPrivateKey(私钥)
5.wechatpayCertificates(平台证书)
6.APIV3Key(V3 key)
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
重点步骤说明:
步骤2用户确认支付后,商户调用微信支付Native下单API生成预支付交易以获取支付二维码链接code_url;
商户调用NativeNative下单API后,分正常返回和异常返回情况:
正常返回:返回code_url,商户可根据返回的code_url来生成调用OpenSDK的签名以执行下一步。
异常返回:返回http code或错误码,商户可根据http code列表 或错误码说明来排查原因并执行下一步操作
步骤4: 商户根据返回的code_url生成二维码供用户扫描,有关二维码的规则请参考3.2.2部分的说明
步骤9-11: 用户支付成功后,商户可通过以下两种方式获取订单状态
方法一:支付结果通知。用户支付成功后,微信支付会将支付成功的结果以回调通知的形式同步给商户,商户的回调地址需要在调用Native下单API时传入notify_url参数。
方法二:当因网络抖动或本身notify_url存在问题等原因,导致无法接收到回调通知时,商户也可主动调用查询订单API来获取订单状态
本章节展示了如何使用微信支付服务端 SDK 快速接入Native支付产品,完成与微信支付对接的部分。
注意:
文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及参数才能调试成功。
以下接入步骤仅提供参考,请商户结合实际业务需求进行评估、修改。
步骤说明:用户在商户PC网站内完成商品选择后进入支付页面,商户需要通过后端请求该Native下单API来获取支付二维码链接code_url。
示例代码
PHP
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/native', //请求URL
[
// JSON请求体'json' => [
"time_expire" => "2018-06-08T10:34:56+08:00",
"amount" => [
"total" => 100,
"currency" => "CNY",
],
"mchid" => "1230000109",
"description" => "Image形象店-深圳腾大-QQ公仔",
"notify_url" => "https://www.weixin.qq.com/wxpay/pay.php",
"out_trade_no" => "1217752501201407033233368018",
"goods_tag" => "WXG",
"appid" => "wxd678efh567hg6787",
"attach" => "自定义数据说明",
"detail" => [
"invoice_id" => "wx123",
"goods_detail" => [
[
"goods_name" => "iPhoneX 256G",
"wechatpay_goods_id" => "1001",
"quantity" => 1,
"merchant_goods_id" => "商品编码",
"unit_price" => 828800,
],
[
"goods_name" => "iPhoneX 256G",
"wechatpay_goods_id" => "1001",
"quantity" => 1,
"merchant_goods_id" => "商品编码",
"unit_price" => 828800,
],
],
"cost_price" => 608800,
],
"scene_info" => [
"store_info" => [
"address" => "广东省深圳市南山区科技中一道10000号",
"area_code" => "440305",
"name" => "腾讯大厦分店",
"id" => "0001",
],
"device_id" => "013467007045764",
"payer_client_ip" => "14.23.150.211",
]
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
重要参数说明:
• out_trade_no:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
• description:商品描述
• notify_url:支付回调通知URL,该地址必须为直接可访问的URL,不允许携带查询串
• total:订单总金额,单位为分
更多参数、响应详情及错误码请参见Native下单API接口文档
步骤说明:通过Native下单API成功获取支付二维码链接(code_url)后,需要在前端(PC网页或POS机具)生成二维码供用户扫描支付。
注意
code_url对应链接格式:weixin://weixin://pay.weixin.qq.com/bizpayurl/up?pr=NwY5Mz9&groupid=00。请商户调用第三方库将code_url生成二维码图片。该模式链接较短,生成的二维码打印到结账小票上的识别率较高。
例如,weixin://weixin://pay.weixin.qq.com/bizpayurl/up?pr=NwY5Mz9&groupid=00 生成二维码见下图
更多二维码的相关背景知识可参考
https://www.qrcode.com/zh/index.html
步骤说明:当用户完成支付,微信会把相关支付结果将通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
注意:
支付结果通知是以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)
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序支付通知API接口文档
步骤说明:当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知时,商户可通过查询订单接口核实订单支付状态
示例代码(通过微信订单号查询):
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/pay/transactions/id/1217752501201407033233368018?mchid=1230000109', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
查询订单可通过微信支付订单号或商户订单号两种方式查询,两种查询方式返回结果相同
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序查询订单API接口文档
步骤说明:当商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口
示例代码:
PHP
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close', //请求URL
[
// JSON请求体'json' => [
"mchid " => "1230000109",
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟
已支付成功的订单不能关闭
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序接口文档
步骤说明:微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址
示例代码:
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=2019-06-11&bill_type=ALL', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序接口文档
步骤说明:通过申请交易账单接口获取到账单下载地址(download_url)后,再通过该接口获取到对应的账单文件,文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况
示例代码:
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/billdownload/file?token=xx', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
• 账单文件的下载地址的有效时间为30s
• 强烈建议商户将实际账单文件的哈希值和之前从接口获取到的哈希值进行比对,以确认数据的完整性
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序下载账单API接口文档
一个二维码的有效期是根据Native下单接口返回的code_url决定,code_url的有效期是2小时。
A:微信支付已经不支持通过长按识别二维码的方式或通过相册识别二维码的方式完成支付。
模块名称 |
功能列表 |
描述 |
Native支付 |
Native下单 |
通过本接口提交微信支付Native支付订单。 |
查询订单 |
通过此接口查询订单状态。 |
|
关闭订单 |
通过此接口关闭待支付订单。 |
|
Native调起支付 |
商户后台系统先调用微信支付的Native支付接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。 |
|
支付结果通知 |
微信支付通过支付通知接口将用户支付成功消息通知给商户。 |
|
申请退款 |
商户可以通过该接口将支付金额退还给买家。 |
|
查询单笔退款 |
提交退款申请后,通过调用该接口查询退款状态 。 |
|
退款结果通知 |
微信支付通过退款通知接口将用户退款成功消息通知给商户。 |
|
申请交易账单 |
商户可以通过该接口获取交易账单文件的下载地址。 |
|
申请资金账单 |
商户可以通过该接口获取资金账单文件的下载地址。 |
|
下载账单 |
通过申请交易/资金账单获取到download_url在该接口获取到对应的账单。 |
商户已有微信小程序,用户通过好友分享或扫描二维码在微信内打开小程序时,可以调用微信支付完成下单购买的流程。
注意:小程序不能通过拉起H5页面做jsapi支付,小程序内只能使用小程序支付
步骤1:如图1,用户通过分享或扫描二维码进入商户小程序,用户选择购买,完成选购流程。
步骤2:如图3,调起微信支付控件,用户开始输入支付密码。
步骤3:如图4,密码验证通过,支付成功。商户后台得到支付成功的通知。
步骤4:如图5,返回商户小程序,显示购买成功。
步骤5:如图6,微信支付公众号下发支付凭证。
了解完微信支付不同模式的基础账号关系与使用后,针对第三方模式进行枚举说明,以小程序举例。
小程序的开发:第三方服务商帮商户开发小程序,有三种业务场景:
1、第三方自己申请账号,自己开发,生成指定内页给特约商户用,该模式简称中心化模式。
2、以特约商户身份申请小程序appid,第三方完成开发,该模式简称外包模式。
3、通过开放平台第三方开发者代特约商户进行小程序的开发,该模式简称第三方模式。
无论哪种模式开发的小程序,在使用面对用户的微信支付能力时,都以该小程序appid为主体id来调用微信开放平台提供的api。
以在某小程序中发起微信支付为例,分后台下单和前端js拉起收银台两部分。
其中,后台下单对应微信支付的三大类开放模式,前端js拉起收银台需通过该小程序的appid,且该appid参与后台下单。即,根据不同的开放模式和业务场景枚举,第三方开发小程序使用微信支付时,可分为9种(3*3)不同的组合模式,结合实际业务诉求选择对应的组合模式进行开发。
注意:以上3*3共计9种组合,均能成功接入微信支付。但红色连接线为二清模式,政策上不允许采用该模式。有需求的第三方开发者可通过银行渠道商模式接入。
商户/服务商在接入前首先要判断自己公司注册区域适用的接入模式,微信支付目前提供两种接入方式:直连模式和服务商模式。
信息、资金流:微信支付—>直连商户
直连模式,商户自行申请入驻微信支付,无需服务商协助。(商户平台申请)成为直连商户
—— 信息流 —— 资金流
服务商模式,商户申请成为微信支付服务商,服务商自身无法作为一个直连商户直接发起交易,其发起交易必须传入相关特约商户商户号的参数信息。(服务商平台申请)成为服务商
请结合自身实际情况来选择接入模式。
服务商模式相关说明详见:服务商模式介绍
商户自行申请入驻微信支付,无服务商协助。(商户平台申请)成为直连商户
由于微信支付的产品体系全部搭载于微信的社交体系之上,所以直连商户或服务商接入微信支付之前,都需要有一个微信社交载体,该载体对应的ID即为APPID。
对于直连商户,该社交载体可以是公众号(什么是公众号),小程序(什么是小程序)或APP。
如申请社交载体为公众号,请前往 公众平台申请
如申请社交载体为小程序,请前往 小程序平台 申请
如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台申请
商户可根据实际的业务需求来选择申请不同的社交载体。
各类社交载体一旦申请成功后,可以登录对应平台查看账号信息以获取对应的appid。
申请mchid和APPID的操作互不影响,可以并行操作,申请地址如下: 商户号申请平台
申请成功后,会向服务商填写的联系邮箱下发通知邮件,内容包含申请成功的mchid及其登录账号密码,请妥善保存。
注意:一个mchid只能对应一个结算币种,若需要使用多个币种收款,需要申请对应数量的mchid。
APPID和mchid全部申请完毕后,需要建立两者之间的绑定关系。
直连模式下,APPID与mchid之间的关系为多对多,即一个APPID下可以绑定多个mchid,而一个mchid也可以绑定多个APPID。
API v3密钥主要用于平台证书解密、回调信息解密,具体使用方式可参见接口规则文档中证书和回调报文解密章节。
请根据以下步骤配置API key:
商户API证书具体使用说明可参见接口规则文档中私钥和证书章节
商户可登录微信商户平台,在【账户中心】->【API安全】目录下载证书
以下为具体下载步骤:
步骤1 在【商户平台】-“复制证书串”环节,点击“复制证书串”按钮后;
步骤2 在【证书工具】-“复制请求串”环节,点击“下一步”按钮进入“粘贴证书串”环节;
步骤3 在【证书工具】-“粘贴证书串”环节,点击“粘贴”按钮后;
步骤4 点击“下一步”按钮,进入【证书工具】-“生成证书”环节
1、申请小程序开发者账号,进行微信认证,获取appid登录《微信公众平台》,注册一个小程序的开发者账号。小程序账号申请指引
2、小程序开通微信支付,即申请或复用微信支付商户号,申请完小程序后,登录小程序后台。点击左侧导航栏的微信支付,在页面中进行开通。
点击开通按钮后,有2种方式可以获取微信支付能力,新申请微信支付商户号或绑定一个已有的微信支付商户号,请根据你的业务需要和具体情况选择,只能二选一。
为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考“APIv3接口规则”
备注:当前接口用于微信国内钱包
为了帮助开发者调用开放接口,我们提供了JAVA、PHP、GO三种语言版本的开发库,封装了签名生成、签名验证、敏感信息加/解密、媒体文件上传等基础功能(更多语言版本的开发库将在近期陆续提供)
测试步骤:
1、根据自身开发语言,选择对应的开发库并构建项目,具体配置请参考下面链接的详细说明:
• wechatpay-java(推荐)wechatpay-apache-httpclient,适用于Java开发者。
• wechatpay-php(推荐)、wechatpay-guzzle-middleware,适用于PHP开发者
注:当前开发指引接口PHP示例代码采用wechatpay-guzzle-middleware版本
• wechatpay-go,适用于Go开发者
更多资源可前往微信支付开发者社区搜索查看
2、创建加载商户私钥、加载平台证书、初始化httpClient的通用方法
JAVA
PHP
useGuzzleHttp\Exception\RequestException;
useWechatPay\GuzzleMiddleware\WechatPayMiddleware;
useWechatPay\GuzzleMiddleware\Util\PemUtil;
useGuzzleHttp\HandlerStack;
// 商户相关配置,
$merchantId = '1000100'; // 商户号
$merchantSerialNumber = 'XXXXXXXXXX'; // 商户API证书序列号
$merchantPrivateKey = PemUtil::loadPrivateKey('./path/to/mch/private/key.pem'); // 商户私钥文件路径// 微信支付平台配置
$wechatpayCertificate = PemUtil::loadCertificate('./path/to/wechatpay/cert.pem'); // 微信支付平台证书文件路径// 构造一个WechatPayMiddleware
$wechatpayMiddleware = WechatPayMiddleware::builder()
->withMerchant($merchantId, $merchantSerialNumber, $merchantPrivateKey) // 传入商户相关配置
->withWechatPay([ $wechatpayCertificate ]) // 可传入多个微信支付平台证书,参数类型为array
->build();
// 将WechatPayMiddleware添加到Guzzle的HandlerStack中
$stack = GuzzleHttp\HandlerStack::create();
$stack->push($wechatpayMiddleware, 'wechatpay');
// 创建Guzzle HTTP Client时,将HandlerStack传入,接下来,正常使用Guzzle发起API请求,WechatPayMiddleware会自动地处理签名和验签
$client = new GuzzleHttp\Client(['handler' => $stack]);
3、基于接口的示例代码,替换请求参数后可发起测试
说明:
• 上面的开发库为微信支付官方开发库,其它没有审核或者控制下的第三方工具和库,微信支付不保证它们的安全性和可靠性
通过包管理工具引入SDK后,可根据下面每个接口的示例代码替换相关参数后进行快速测试
• 开发者如果想详细了解签名生成、签名验证、敏感信息加/解密、媒体文件上传等常用方法的具体代码实现,可阅读下面的详细说明:
1.签名生成
2.签名验证
3.敏感信息加解密
4.merchantPrivateKey(私钥)
5.wechatpayCertificates(平台证书)
6.APIV3Key(V3 key)
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
a、小程序开通微信支付,即申请或复用微信支付商户号,申请完小程序后,登录小程序后台。点击左侧导航栏的微信支付,在页面中进行开通。(开通申请要求小程序已发布上线)
b、点击开通按钮后,有2种方式可以获取微信支付能力,新申请微信支付商户号或绑定一个已有的微信支付商户号,请根据你的业务需要和具体情况选择,只能二选一。开通指引
a、小程序访问商户服务都是通过HTTPS,开发部署的时候需要HTTPS服务器
b、服务器域名配置
①、每个微信小程序需要事先设置通信域名,小程序只可以跟指定的域名进行网络通信。包括普通 HTTPS 请求(wx.request)、上传文件(wx.uploadFile)、下载文件(wx.downloadFile)和 WebSocket 通信(wx.connectSocket)
②、从基础库 2.4.0 开始,网络接口允许与局域网 IP 通信,但要注意 不允许与本机 IP 通信
注意:
域名只支持 https (wx.request、wx.uploadFile、wx.downloadFile) 和 wss (wx.connectSocket) 协议
域名不能使用 IP 地址(小程序的局域网 IP 除外)或 localhost
可以配置端口,如 https://myserver.com:8080,但是配置后只能向 https://myserver.com:8080/ 发起请求。如果向 https://myserver.com、https://myserver.com:9091 等 URL 请求则会失败
如果不配置端口。如 https://myserver.com,那么请求的 URL 中也不能包含端口,甚至是默认的 443 端口也不可以。如果向 https://myserver.com:443 请求则会失败
域名必须经过 ICP 备案
出于安全考虑,api.weixin.qq.com 不能被配置为服务器域名,相关API也不能在小程序内调用。 开发者应将 AppSecret 保存到后台服务器中,通过服务器使用 getAccessToken 接口获取 access_token,并调用相关 API
不支持配置父域名,使用子域名
可查阅小程序网络请求以了解更多信息
重点步骤说明:
步骤4:用户下单发起支付,商户可通过JSAPI下单创建支付订单。
步骤9:商户小程序内使用小程序调起支付API(wx.requestPayment)发起微信支付,详见小程序API文档
步骤16:用户支付成功后,商户可接收到微信支付支付结果通知支付通知API。
步骤21:商户在没有接收到微信支付结果通知的情况下需要主动调用查询订单API查询支付结果。
本章节展示了如何使用微信支付服务端 SDK 快速接入小程序支付产品,完成与微信支付对接的部分。
注意:
文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及请求参数才能跑通。
以下接入步骤仅提供参考,请商户结合自身业务需求进行评估、修改。
步骤说明:用户通过商户小程序进入商户网页,当用户选择相关商品购买时,商户系统先调用该接口在微信支付服务后台生成预支付交易单。
示例代码
PHP
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi', //请求URL
[
// JSON请求体'json' => [
"time_expire" => "2018-06-08T10:34:56+08:00",
"amount" => [
"total" => 100,
"currency" => "CNY",
],
"mchid" => "1230000109",
"description" => "Image形象店-深圳腾大-QQ公仔",
"notify_url" => "https://www.weixin.qq.com/wxpay/pay.php",
"payer" => [
"openid" => "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o",
],
"out_trade_no" => "1217752501201407033233368018",
"goods_tag" => "WXG",
"appid" => "wxd678efh567hg6787",
"attach" => "自定义数据说明",
"scene_info" => [
"store_info" => [
"address" => "广东省深圳市南山区科技中一道10000号",
"area_code" => "440305",
"name" => "腾讯大厦分店",
"id" => "0001",
],
"device_id" => "013467007045764",
"payer_client_ip" => "14.23.150.211",
]
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
重要参数说明:
• out_trade_no:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
• description:商品描述
• notify_url:支付回调通知URL,该地址必须为直接可访问的URL,不允许携带查询串
• total:订单总金额,单位为分
• openid:openid是微信用户在appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户。openid获取方式请参考以下文档 小程序获取openid、 公众号获取openid、 APP获取openid、
更多参数、响应详情及错误码请参见JSAPI下单接口文档
步骤说明:通过JSAPI下单成功获取预支付交易会话标识(prepay_id)后,需要通过小程序调起支付API来调起微信支付收银台
注意:
此API需要将请求参数进行签名(参与签名的参数为:appId、timeStamp、nonceStr、package,参数区分大小写)
appId必须为最后拉起收银台的小程序appid
示例代码:
wx.requestPayment(
{
"timeStamp": "1414561699",
"nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
"package": "prepay_id=wx201410272009395522657a690389285100",
"signType": "RSA",
"paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==",
"success":function(res){},
"fail":function(res){},
"complete":function(res){}
})
• package:小程序下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
• signType:该接口V3版本仅支持RSA
• paySign:签名
paySign生成规则、响应详情请参见小程序调起支付API接口文档
步骤说明:当用户完成支付,微信会把相关支付结果将通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
注意:
支付结果通知是以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)
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序支付通知API接口文档
步骤说明:当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知时,商户可通过查询订单接口核实订单支付状态
示例代码(通过微信订单号查询):
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/pay/transactions/id/1217752501201407033233368018?mchid=1230000109', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
查询订单可通过微信支付订单号或商户订单号两种方式查询,两种查询方式返回结果相同
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序查询订单API接口文档
步骤说明:当商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口
示例代码:
PHP
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close', //请求URL
[
// JSON请求体'json' => [
"mchid " => "1230000109",
],
'headers' => [ 'Accept' => 'application/json' ]
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟
已支付成功的订单不能关闭
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序接口文档
步骤说明:微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址
示例代码:
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/bill/tradebill?bill_date=2019-06-11&bill_type=ALL', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序接口文档
步骤说明:通过申请交易账单接口获取到账单下载地址(download_url)后,再通过该接口获取到对应的账单文件,文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况
示例代码:
PHP
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/billdownload/file?token=xx', //请求URL
[
'headers' => [ 'Accept' => 'application/json']
]
);
$statusCode = $resp->getStatusCode();
if ($statusCode == 200) { //处理成功echo"success,return body = " . $resp->getBody()->getContents()."\n";
} elseif ($statusCode == 204) { //处理成功,无返回Bodyecho"success";
}
} catch (RequestException $e) {
// 进行错误处理echo $e->getMessage()."\n";
if ($e->hasResponse()) {
echo"failed,resp code = " . $e->getResponse()->getStatusCode() . " return body = " . $e->getResponse()->getBody() . "\n";
}
return;
}
注意:
• 账单文件的下载地址的有效时间为30s
• 强烈建议商户将实际账单文件的哈希值和之前从接口获取到的哈希值进行比对,以确认数据的完整性
更多参数、响应详情及错误码请参见 JSAPI / APP / H5 / Native / 小程序下载账单API接口文档
A:目前在小程序内只支持小程序支付,不支持H5或JSAPI支付
A:请按以下几点进行排查:
1,请检查请求参数是否正确,如请求参数中的APPID是否与小程序对应
2,APPID对应的小程序是否有开通小程序支付功能,权限状态是否正常,可登录商户平台核实
3,APPID与商户号是否存在绑定关系,可登录商户平台核实
A:可通过以下两种方式开通:
1,绑定已有公众号的微信支付:耗时10分钟即可,只需原有公众号开通微信支付,小程序微信支付可以选择绑定原来的微信支付商户号,即可开通成功
2,新申请微信支付:耗时需要1-5个工作日,需要提交和申请小程序一样的资料进行审核,审核通过后才能开通成功
模块名称 |
功能列表 |
描述 |
小程序支付 |
JSAPI下单 |
通过本接口提交微信支付小程序支付订单。 |
查询订单 |
通过此接口查询订单状态。 |
|
关闭订单 |
通过此接口关闭待支付订单。 |
|
小程序调起支付 |
通过小程序下单接口获取到发起支付的必要参数prepay_id,可以按照接口定义中的规则,调起小程序支付。 |
|
支付结果通知 |
微信支付通过支付通知接口将用户支付成功消息通知给商户。 |
|
申请退款 |
商户可以通过该接口将支付金额退还给买家。 |
|
查询单笔退款 |
提交退款申请后,通过调用该接口查询退款状态 。 |
|
退款结果通知 |
微信支付通过退款通知接口将用户退款成功消息通知给商户。 |
|
申请交易账单 |
商户可以通过该接口获取交易账单文件的下载地址。 |
|
申请资金账单 |
商户可以通过该接口获取资金账单文件的下载地址。 |
|
下载账单 |
通过申请交易/资金账单获取到download_url在该接口获取到对应的账单。 |
SDK下载,链接: https://pan.baidu.com/s/1ggZuH306tEMPJlShui2F8A 提取码: g4th
支付流程如下:
用户登录app后,进入商品购买页面,前端请求app商品列表api接口,获取商品列表信息并展示
用户点击'购买'操作,发送给服务端一条当前商品信息,服务端保存该商品的相关订单数据,并返回给前端支付所需要的请求数据,前端调用支付sdk,发起支付请求
/**
* 支付下单
* @param array $params 订单信息
* @return array
*/
public static function Pay($params)
{
//获取商品内部订单号
$innerOno = self::genInnerOno(date("Y-m-d H:i:s"), $params['user_id'], $paymentType);
//生成订单
$order = new Order();
$order->order_id = $innerOno; // 内部订单号
$order->type = $params['type']; // 支付类型:1 微信jsapi支付, 2 微信H5支付,4 微信app支付,5 微信小程序支付,7 微信native支付
$order->product_name = $params['name']; // 商品名称
$order->number = $params['num']; // 购买商品个数
$order->price = $params['price']; // 商品单价, 1个商品价格
$order->total_price = $params['total_price']; // 总的价格
$order->pay_amount = $params['pay_amount']; // 应付总额: 实际应该支付的价格= 总金额 - 优惠金额
$order->count = $params['count']; // 购买后得到商品对应的产品数量
$order->status = 0; // 状态:0 待支付
$order->user_id = $params['user_id']; // 用户游戏ID
$order->expired_at = $params['expired_at']; // 过期时间
$order->created_by = $params['created_by']; // 创建者
$order->updated_by = $params['updated_by']; // 创建者
$result = $order->save();
if (!$result) {
$errors = '';
if ($order->hasErrors()) {
$tmp = $order->getErrors();
foreach ($tmp as $rows) {
foreach ($rows as $row) {
$errors .= $row . '
';
}
}
}
throw new \Exception($errors);
}
$orderId = $order->id;
$title = $model->product_name;
$title = replaceStr($title);
$price = intval($model->pay_amount * 100);
if (in_array($model->user_id, Yii::$app->params['pay_test_user_ids'])) {
$price = 1;
if ($model->trade_code == self::PAY_WAY_WECHAT_H5 || $model->trade_code == self::PAY_WAY_WECHAT_JSAPI || $model->trade_code == self::PAY_WAY_WECHAT_APPLET|| $model->trade_code == self::PAY_WAY_WECHAT_MIDAS) {
$price = 1;
}
}
//构建向微信支付发送的参数
$data = [
'inner_ono' => $model->deal_number,
'title' => $title,
'price' => $price,
'body' => $title,
];
$logDir = Yii::getAlias("@frontend/runtime/logs/wxpay");
try {
//引入上面下载的SDK中的付方法
$pay = new Pay();
if ($tradeType == self::PAY_WAY_WECHAT_APP) { //微信app支付
$data = $pay->appPay($data);
if (!$data) {
jsonFail();
}
$result['app_sign'] = $data;
} elseif ($tradeType == self::PAY_WAY_WECHAT_JSAPI || $tradeType == self::PAY_WAY_WECHAT_APPLET) { //微信jsapi支付,微信小程序支付
//获取用户微信open_id
$user = GameUserReg::find()->select(['wx'])->where(['id' => $model->game_user_id])->asArray()->one();
if (!$user) {
throw new \Exception('用户不存在');
}
if (!$user['wx']) {
throw new \Exception('open_id不能为空');
}
$data['open_id'] = $user['wx'];
$data = $pay->jsApiPay($data);
$result['jsapi_sign'] = $data;
} elseif ($tradeType == self::PAY_WAY_WECHAT_H5) { //微信H5支付
$data = $pay->h5Pay($data);
$result['mweb_url'] = $data;
} elseif ($tradeType == self::PAY_WAY_WECHAT_NATIVE) { //微信native支付
$data = $pay->nativePay($data);
$result['code_url'] = $data['code_url'];
}
$msg = "#{$model->created_by} 2nd. 统一下单 - end." . json_encode($data);
fileLogByDir($msg, $logDir);
} catch (\Exception $e) {
$msg = 'error: ' . $e->getMessage() . ' (file: ' . $e->getFile() . ' [' . $e->getLine() . '])';
fileLogByDir($msg, $logDir);
throw $e;
}
return jsonSuccess($result);
}
/**
* 生成本系统内部订单号, 在向第三方服务商发起支付时需要使用.
*
* 下单时的处理流程:
* 1. 属性 ono 不要赋值, 成功插入(保存)订单.
* 1. 支付前生成本系统内部订单号, 向第三方发起支付请求.
* 1. 接收到支付成功回调通知时, 将第三方订单号保存到本次付款的订单的 ono 字段上.
*
* 格式: 下单时间+支付方式+ 4位数字修正值. 如 "20150930140041+1+1234".
* 最大长度 32 个字符. 这也是 wx 可接受的订单号最大长度.
*
* @param int $createdAt 订单创建时间戳, 即 下单时间戳.
* @param int $createdBy 订单创建人 ID, 即 下单会员 ID.
* @return string
* @throws \Exception
*/
public static function genInnerOno($createdAt, $createdBy)
{
$createdAt = strtotime($createdAt);
if (1 > $createdAt || 1 > $createdBy) {
throw new \Exception('参数错误');
}
// 生成一个修正值, 一定程度上增加订单号的随机性.
$tmp = intval(substr($createdAt, -4)) + intval(substr($createdBy, -2));
if (4 < strlen($tmp)) {
$tmp = substr($tmp, -4);
}
$tmp = date('YmdHis', $createdAt) . $tmp . rand(100, 900);
return $tmp;
}
前端获取服务端返回的支付所需要的请求数据后,拼接支付请求参数,调用支付sdk,拉起微信发起支付请求
前端支付成功后,微信服务器会请求业务服务端的回调方法,对支付订单进行校验验签操作,根据验签结果,处理订单业务逻辑
php以yii2框架为参考
/**
* 支付回调
*/
public function actionNotify()
{
$input_data = file_get_contents('php://input');
//转换通知的JSON文本消息为PHP Array数组
$inBodyArray = json_decode($input_data, true);
//判断通知请求
$trans = Yii::$app->db->beginTransaction();
if ($inBodyArray['event_type'] == "TRANSACTION.SUCCESS") { //通知的类型,支付成功通知的类型为TRANSACTION.SUCCESS
try {
if ($inBodyArray['resource_type'] == "encrypt-resource") { //通知的资源数据类型
//获取resource通知资源数据(json格式)
$nonce = $inBodyArray['resource']['nonce'];
$aad = $inBodyArray['resource']['associated_data'];
$text = $inBodyArray['resource']['ciphertext'];
//商户平台设置的api v3 密码
$key = Yii::$app->params['wx']['api_v3_key'];
// 解密ciphertext文本消息
$inBodyResource = AesGcm::decrypt($text, $key, $nonce, $aad);
// 把解密后的文本转换为PHP Array数组
$inBodyResourceArray = ArrayHelper::toArray(json_decode($inBodyResource, true));
//判断交易状态
$trade_state = $inBodyResourceArray['trade_state'];
if ($trade_state != 'SUCCESS') {
throw new \Exception($inBodyResourceArray['trade_state_desc']);
}
// 处理业务逻辑
$ono = $inBodyResourceArray["transaction_id"];
$innerOno = $inBodyResourceArray['out_trade_no'];
// 订单状态
$status = UserOrder::STATUS_BUY;
//交易类型
$trade_type = $inBodyResourceArray['trade_type'];
if ($trade_type == 'JSAPI') {
$trade_code = UserOrder::PAY_WAY_WECHAT_JSAPI;
} elseif ($trade_type == 'NATIVE') {
$trade_code = UserOrder::PAY_WAY_WECHAT_NATIVE;
} elseif ($trade_type == 'APP') {
$trade_code = UserOrder::PAY_WAY_WECHAT_APP;
} elseif ($trade_type == 'MWEB') {
$trade_code = UserOrder::PAY_WAY_WECHAT_H5;
} else {
$trade_code = null;
}
// 变更订单属性
Order::changeAttribute(
['inner_ono' => $innerOno],
[
'status' => $status,
'paid_at' => time(),
'ono' => $ono,
'trade_code' => $trade_code,
]
);
// 变更订单商品状态
OrderWare::changeStatus($status, ['inner_ono' => $innerOno]);
$trans->commit();
$msg = '支付成功后回调: ' . $ono . '订单更新成功';
fileLogByDir($msg, $logDir);
return json_encode[
"code" => "SUCCESS",
"message" => ""
]);
}
} catch (\Exception $e) {
$msg = 'error: ' . $e->getMessage() . ' (file: ' . $e->getFile() . ' [' . $e->getLine() . '])';
fileLogByDir($msg, $logDir);
$trans->rollBack();
}
}
return json_encode([
"code" => "ERROR",
"message" => "失败"
]);
}
/**
* 操作成功
* @example1 jsonSuccess();
*/
function jsonSuccess($data = NULL, $message = '操作成功')
{
header("Content-Type:application/json");
$json = array();
$json['code'] = 0;
$json['data'] = $data;
$json['message'] = $message;
$json['request_time'] = now();
echo Json::encode($json);
exit();
}
/**
* 错误信息
* @example1 jsonFail();
* @example1 jsonFail("删除失败!");
* @example2 jsonFail($model->getErrors());
*/
function jsonFail($message = '请求失败', $code = 1, $data = null)
{
header("Content-Type:application/json");
$json = array();
$json['code'] = $code;
$json['data'] = $data;
$json['message'] = $message;
$json['request_time'] = now();
echo Json::encode($json);
exit();
}
/**
* 过滤掉特殊的字符
* @param $str
* @param string $replacement
* @return string|string[]|null
*/
function replaceStr($str, $replacement = '')
{
$regex = "/\/|\~|\,|\。|\!|\?|\“|\”|\【|\】|\『|\』|\:|\;|\《|\》|\’|\‘|\ |\·|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\+|\{|\}|\:|\<|\>|\?|\[|\]|\,|\.|\/|\;|\'|\`|\-|\=|\\\|\|/";
$result = preg_replace($regex, $replacement, $str);
return $result;
}
Yii2 支付宝相关配置:params-local.php
[
//应用ID,您的APPID。
'app_id' => "xxx",
//小程序应用ID
'applet_id' => 'xxx',
//小程序应用秘钥
'applet_secret' => 'xxx',
//小程序消息通知token
'token' => 'xxx',
//小程序消息通知 消息加密密钥
'encodingAESKey' => 'xxx',
//公众平台的appId
'app_id_public' => 'xxx',
'app_id_secret' => 'xxx',
//商户私钥,您的原始格式RSA私钥
'merchant_id' => "xxx",
//异步通知地址
'notify_url' => "http://xxx.com/wechat-notify",
// 正式环境支付网关
'gateway_url' => "", //https://api.mch.weixin.qq.com/pay/unifiedorder
//商户API证书序列号
'serial_number' => "xxx",
//同步跳转
'return_url' => "",
//编码格式
'charset' => "UTF-8",
//签名方式
'sign_type' => "MD5",
//KEY:商户支付密钥,设置地址:https://pay.weixin.qq.com/index.php/account/api_cert
'key' => "xxx",
//V3秘钥
'api_v3_key' => "xxx",
'pay_test' => 1, //是否支付测试
'is_screen_pay' => 1, //是否屏蔽支付
],
];