为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付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的通用方法
@Before
public void setup() throws IOException {
// 加载商户私钥(privateKey:私钥字符串)
PrivateKey merchantPrivateKey = PemUtil
.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8"));
// 初始化httpClient
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier)).build();
}
@After
public void after() throws IOException {
httpClient.close();
}
3、基于接口的示例代码,替换请求参数后可发起测试
说明:
• 上面的开发库为微信支付官方开发库,其它没有审核或者控制下的第三方工具和库,微信支付不保证它们的安全性和可靠性
通过包管理工具引入SDK后,可根据下面每个接口的示例代码替换相关参数后进行快速测试
• 开发者如果想详细了解签名生成、签名验证、敏感信息加/解密、媒体文件上传等常用方法的具体代码实现,可阅读下面的详细说明:
1.签名生成
2.签名验证
3.敏感信息加解密
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
1、微信支付分的相关配置参数在商户入驻的过程中都已经配置完成(前往查看配置相关内容),例如授权结果回调url、service_notify_url、测试白名单、免确认订单模式的权限等。
2、如果发现配置信息有误,请主动联系微信支付分运营同学协助修改,或者点击右侧导航栏进入在线技术客服进行技术咨询。
重点步骤说明:
步骤1 用户在商户侧下单购买产品或服务,此时,我们需要先对用户的授权状态进行查询
查询用户授权状态的两种方法:
用户授权状态一共分三种:
UNAVAILABLE:用户未授权服务
AVAILABLE:用户已授权服务
UNBINDUSER:未绑定用户
当查询到用户授权状态为UNAVAILABLE、UNBINDUSER时,请前往步骤二:引导用户开启服务;
当查询到用户授权状态为AVAILABLE时,请前往步骤三:创建支付分订单;
步骤2 引导用户开启授权服务
开启授权有两步操作,第一步:调用后台接口进行预授权,第二步:调用前端方法跳转进入微信中让用户进行授权。
请求开启授权之后,我们需要确认开启授权是否成功,共有两种方法:
1、通过接口主动查询(未及时收到用户授权成功通知情况下且用户已回到商家授权页面时商户侧主动查询。)——查询与用户授权记录(openid)、查询与用户授权记录(授权协议号)
2、等待支付分的异步通知——开启/解除授权结果回调通知
若获取到用户的授权状态为AVAILABLE:用户已授权服务,则前往步骤三;
若获取到用户的授权状态为UNAVAILABLE-用户未授权服务、UNBINDUSER-未绑定用户,则商户可根据业务需要选择继续查询或者结束支付分流程,进入商户自己的业务流程。
步骤3 创建支付分订单
我们通过创建支付分订单API接口创建一笔待支付的支付分订单。
请求:
入参“need_user_confirm”,取值请选择 “false”;
入参“risk_fund:name”,取值请选择【先享模式】中的枚举值。
返回:
创建服务订单失败原因会在接口返回字段“message”中返回,主要失败原因有两种:
1、存在未完结订单
2、综合评估不通过,建议商户稍后重试或者使用其他渠道支付
步骤4 商户为用户提供服务,待服务结束后,商户调用完结订单接口完结当前订单。
调用完结支付分订单API接口后,微信支付分就会发起对用户的扣款,但是在用户扣款过程中可能会出现一些特殊情况,下面列举了几种特殊情况以及对应的处理方法,供大家参考:
请求:
• 情况一:扣款过程中,发现扣款金额有误(注意:此时需要订单为“待支付”状态)
• 情况二:扣款过程中,用户通过其它方式完成了支付,希望微信支付分停止继续扣款
处理方法:商户调用同步服务订单信息接口将订单支付成功状态同步给微信支付分,微信支付分将停止继续扣款的操作
步骤5 收到用户扣款成功通知,业务流程结束
通过支付成功回调通知API接口可以获取到用户扣款成功的通知,同时,商户也可以根据情况通过查询支付分订单API接口主动查询扣款情况。
如遇到网络、服务器等原因造成无法正常接收扣款成功通知,一般有两种解决方法:
1.主动查单,通过查询支付分订单API 接口主动查询扣款情况
本文档展示了如何使用微信支付服务端 SDK 快速接入微信支付分产品,完成与微信支付对接的部分。
注意:
步骤说明:创建支付分订单的前提是用户必须有授权关系,所以在免确认模式下,我们最先要做的就是确保用户是已授权状态。目前提供了使用openid和授权协议号两种条件进行查询的方式。下面以查询与用户授权记录(openid)举例接口调用方式。
示例代码
public void GetPermissionsByOpenid() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/payscore/permissions/openid/{openid}");
httpGet.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
重要入参说明:
• 查询条件参数openid或者授权协议号都是在接口url中传递的,请勿传到body中去。
更多参数、响应详情及错误码请参见查询与用户授权记录(openid)接口文档
步骤说明:创建支付分订单的前提是用户必须有授权关系,所以在免确认模式下,我们最先要做的就是确保用户是已授权状态。通过商户预授权接口获取跳转用户授权页必填参数“预授权token”。
示例代码:
public void permissions() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/permissions");
// 请求body参数
String reqdata = "{"
+ "\"service_id\":\"500001\","
+ "\"appid\":\"wxd678efh567hg6787\","
+ "\"authorization_code\":\"1234323JKHDFE1243252\","
+ "\"notify_url\":\"http://www.qq.com\""
+ "}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
重要入参说明:
• 完成用户授权需要两步操作,预授权接口仅是为获取关键参数“预授权token”,商户还需引导用户跳转到微信授权页进行授权操作。
更多参数、响应详情及错误码请参见 商户预授权API接口文档
步骤说明:通过商户预授权接口获取到跳转用户授权页必填参数“预授权token”后,即可通过前端方法跳转到微信客户端,让用户开启授权服务。前端跳转方法请根据用户实际使用场景(APP、小程序、微信内H5)来选择。
步骤说明:当用户授权或解约服务成功时,微信会把相关信息异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
注意:
更多参数、响应详情及错误码请参见开启/解除授权服务回调通知API接口文档
步骤说明:完成用户授权后,即可创建支付分订单,为用户提供服务了。
示例代码:
public void CreateServiceOrder() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/serviceorder");
// 请求body参数
String reqdata = "{"
+ "\"out_order_no\":\"1234323JKHDFE1243252\","
+ "\"appid\":\"wxd678efh567hg6787\","
+ "\"service_id\":\"500001\","
+ "\"service_introduction\":\"某某酒店\","
+ "\"post_payments\": ["
+ "{"
+ "\"name\":\"就餐费用服务费\","
+ "\"amount\":4000,"
+ "\"description\":\"就餐人均100元服务费:100/小时\","
+ "\"count\":1"
+ "}"
+ "],"
+ "\"post_discounts\": ["
+ "{"
+ "\"name\":\"满20减1元\","
+ "\"description\":\"不与其他优惠叠加\""
+ "}"
+ "],"
+ "\"time_range\": {"
+ "\"start_time\":\"20091225091010\","
+ "\"end_time\":\"20091225121010\""
+ "},"
+ "\"location\": {"
+ "\"start_location\":\"嗨客时尚主题展餐厅\","
+ "\"end_location\":\"嗨客时尚主题展餐厅\""
+ "},"
+ "\"risk_fund\": {"
+ "\"name\":\"ESTIMATE_ORDER_COST\","
+ "\"amount\":10000,"
+ "\"description\":\"就餐的预估费用\""
+ "},"
+ "\"attach\":\"Easdfowealsdkjfnlaksjdlfkwqoi&wl3l2sald\","
+ "\"notify_url\":\"https://api.test.com\","
+ "\"openid\":\"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o\","
+ "\"need_user_confirm\":true"
+ "}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
重要参数说明:
更多参数、响应详情及错误码请参见 创建支付分订单API接口文档
步骤说明:用户服务结束后,商户通过请求完结支付分订单接口,通过微信支付分进行用户扣款操作。
示例代码:
public void CompleteServiceOrder() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/complete");
// 请求body参数
String reqdata = "{"
+ "\"appid\":\"wxd678efh567hg6787\","
+ "\"service_id\":\"500001\","
+ "\"post_payments\": ["
+ "{"
+ "\"name\":\"就餐费用服务费\","
+ "\"amount\":4000,"
+ "\"description\":\"就餐人均100元服务费:100/小时\","
+ "\"count\":1"
+ "}"
+ "],"
+ "\"post_discounts\": ["
+ "{"
+ "\"name\":\"满20减1元\","
+ "\"description\":\"不与其他优惠叠加\","
+ "\"amount\":4000"
+ "}"
+ "],"
+ "\"total_amount\":3900,"
+ "\"time_range\": {"
+ "\"start_time\":\"20091225091010\","
+ "\"end_time\":\"20091225121010\""
+ "},"
+ "\"location\": {"
+ "\"end_location\":\"嗨客时尚主题展餐厅\""
+ "},"
+ "\"profit_sharing\":false"
+ "}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
更多参数、响应详情及错误码请参见 完结支付分订单API接口文档
步骤说明:在用户扣款成功前、完结订单后(即订单状态为“待支付”),如需修改订单支付金额,可通过此接口进行订单金额修改。修改成功后,微信支付将按照修改后的金额进行用户扣款。
示例代码:
public void ModifyServiceOrder() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/modify");
// 请求body参数
String reqdata = "{"
+ "\"appid\":\"wxd678efh567hg6787\","
+ "\"service_id\":\"500001\","
+ "\"post_payments\": ["
+ "{"
+ "\"name\":\"就餐费用服务费\","
+ "\"amount\":4000,"
+ "\"description\":\"就餐人均100元服务费:100/小时\","
+ "\"count\":1"
+ "}"
+ "],"
+ "\"post_discounts\": ["
+ "{"
+ "\"name\":\"满20减1元\","
+ "\"description\":\"不与其他优惠叠加\","
+ "\"amount\":100"
+ "}"
+ "],"
+ "\"total_amount\":2000,"
+ "\"reason\":\"用户投诉\""
+ "}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
更多参数、响应详情及错误码请参见修改订单金额API接口文档
步骤说明:订单为以下状态时可以取消订单:CREATED(已创单)、DOING(进行中)(包括商户完结支付分订单后,且支付分订单收款状态为待支付USER_PAYING)。
示例代码:
public void CancelServiceOrder() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/cancel");
// 请求body参数
String reqdata = "{"
+ "\"appid\":\"wxd678efh567hg6787\","
+ "\"service_id\":\"500001\","
+ "\"reason\":\"用户投诉\""
+ "}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
更多参数、响应详情及错误码请参见取消支付分订单API接口文档
步骤说明:由于一些原因,用户与商户达成线下支付或者其他支付方式支付的协议,商户可通过此接口告知微信支付该笔订单无需继续扣款,微信支付在接到此信息后将不再发起用户扣款。
示例代码:
public void SyncServiceOrder() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/sync");
// 请求body参数
String reqdata = "{"
+ "\"appid\":\"wxd678efh567hg6787\","
+ "\"service_id\":\"500001\","
+ "\"type\":\"Order_Paid\","
+ "\"detail\": {"
+ "\"paid_time\":\"20091225091210\""
+ "}"
+ "}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
更多参数、响应详情及错误码请参见 同步服务订单信息API接口文档
步骤说明:一般在创建订单后、订单完结成功后等关键流程中,商户可能有知晓订单状态的需求,此时即可通过该接口查询订单状态。
示例代码:
public void GetServiceOrder() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/payscore/serviceorder?service_id=500001&out_order_no=8416518464133&appid=wxd678efh567hg6787");
httpGet.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
更多参数、响应详情及错误码请参见 查询支付分订单API接口文档
步骤说明:微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址。
示例代码:
public void TradeBill() throws Exception {
//请求URL
URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/bill/tradebill");
uriBuilder.setParameter("bill_date", "2020-11-09");
uriBuilder.setParameter("bill_type", "ALL");
//完成签名并执行请求
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
更多参数、响应详情及错误码请参见 申请交易账单API接口文档
步骤说明:通过申请交易账单接口获取到账单下载地址(download_url)后,再通过该接口获取到对应的账单文件,文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况
示例代码:
public void DownloadUrl(String download_url) throws Exception{
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
//初始化httpClient
//该接口无需进行签名验证、通过withValidator((response) -> true)实现
httpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey).withValidator((response) -> true).build();
//请求URL
//账单文件的下载地址的有效时间为30s
URIBuilder uriBuilder = new URIBuilder(download_url);
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
//执行请求
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
} else if (statusCode == 204) {
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
}
注意:
更多参数、响应详情及错误码请参见 下载账单API接口文档
步骤说明:当用户完成支付,微信会把相关支付结果将通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
注意:
更多参数、响应详情及错误码请参见 支付成功回调通知API接口文档
步骤说明:如果用户或者商户有解除授权关系的需求,可通过该接口进行“解约”。我们提供了两种解约方式:使用签约协议号和使用用户openid进行解约,下面我们以使用用户openid解约为例,进行代码演示。
更多参数、响应详情及错误码请参见 解除用户授权关系API接口文档
微信支付根据用户不同的使用场景(APP、小程序、微信内H5)分别提供了对应跳转订单详情页的方法,请根据场景进行选择,详见跳转订单详情页API文档
A:检查测试微信是否开通白名单,提供服务id和微信号联系运营开通白名单
A:1、检查商户号和appid是否配置了通用化接口权限,可以联系微信侧运营确认和配置通用化接口权限
A:2、如果商户开通的是免确认订单权限,创建订单时need_user_confirm只能传false,如果商户开通的是含确认订单权限,创建订单时need_user_confirm只能传true
A:1、检查商户号开通的是哪种模式的权限,需确认模式只能使用先免模式,免确认模式只能使用先享模式
A:表示免确认流程创单用户被不对外风控拦截
A:请商户自行检查mch_id和appid是否有对应的绑定关系。绑定步骤参考:商家商户号与AppID账号关联管理