为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付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.敏感信息加解密
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
重点步骤说明:
步骤3 用户发起投诉后,微信支付通过投诉通知回调API通知商户投诉单需处理。
步骤6 商户接收投诉后,可通过回复用户API接口向微信支付提交投诉处理回复。
步骤9 与用户协商一致后,商户通过反馈处理完成API反馈投诉单处理完成。
步骤14 用户如果不接受投诉处理结果,将再次发起投诉,微信支付会再次通知商户需处理投诉。
步骤15 商户在没有接收到微信投诉通知的情况下可主动调用查询投诉单详情API查询投诉单处理结果。
文档展示了如何使用微信支付服务端 SDK 快速接入消费者投诉产品,完成与微信支付对接的部分。
注意:
步骤说明:商户可通过调用此接口,查询指定时间段的所有用户投诉信息,以分页输出查询结果。
示例代码:
public void GetComplaintsList() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2?limit=5&offset=10&begin_date=2019-01-01&end_date=2019-01-01&complainted_mchid=190000XXXX");
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();
}
}
重要入参说明:
• begin_date:开始日期,投诉发生的开始日期,格式为yyyy-MM-dd。注意,查询日期跨度不超过30天
• end_date:结束日期,投诉发生的结束日期,格式为yyyy-MM-dd。注意,查询日期跨度不超过30天
注意:
更多参数、响应详情及错误码请参见查询投诉单列表接口文档
步骤说明:商户可通过调用此接口,查询指定投诉单的用户投诉详情,包含投诉内容、投诉关联订单、投诉人联系方式等信息,方便商户处理投诉。
示例代码:
public void GetComplaintsInfo() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/200201820200101080076610000");
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();
}
}
重要入参说明:
• complaint_id:投诉单对应的投诉单号,在投诉通知回调中会返回这个参数
注意:
更多参数、响应详情及错误码请参见查询投诉单详情接口文档
步骤说明:商户可通过调用此接口,查询指定投诉的用户商户协商历史,以分页输出查询结果,方便商户根据处理历史来制定后续处理方案。
示例代码:
public void GetComplaintsHis() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/200201820200101080076610000/negotiation-historys?limit=50&offset=10");
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();
}
}
重要入参说明:
• complaint_id:投诉单对应的投诉单号,在投诉通知回调中会返回这个参数
注意:
更多参数、响应详情及错误码请参见查询投诉协商历史接口文档
步骤说明:商户创建投诉通知回调URL 后,当有新的投诉事件发生、投诉状态发生变化时,商户会收到通知回调。商户需要接收处理,并按文档规范返回应答。
注意:
注意:
更多参数、响应详情及错误码请参见投诉通知回调接口文档
步骤说明:商户通过调用此接口创建投诉通知回调URL,当用户产生新投诉且投诉状态已变更时,微信支付会通过回调URL通知商户。
示例代码:
public void CreateComplaintsNotify() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications");
// 请求body参数
String reqdata = "{"
+ "\"url\":\"https://www.xxx.com/notify\""
+ "}";
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();
}
}
重要入参说明:
• url:通知地址,通知地址,仅支持https。
注意:
更多参数、响应详情及错误码请参见创建投诉通知回调地址接口文档
步骤说明:商户通过调用此接口查询投诉通知的回调URL。
示例代码:
public void GetComplaintsNotify() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications");
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();
}
}
注意:
更多参数、响应详情及错误码请参见查询投诉通知回调地址接口文档
步骤说明:商户通过调用此接口创建投诉通知回调URL,当用户产生新投诉且投诉状态已变更时,微信支付会通过回 调URL通知商户。
示例代码:
public void UpdateComplaintsNotify() throws Exception{
//请求URL
HttpPut httpPut = new HttpPut("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications");
// 请求body参数
String reqdata = "{"
+ "\"url\":\"https://www.123.com/notify\""
+ "}";
StringEntity entity = new StringEntity(reqdata,"utf-8");
entity.setContentType("application/json");
httpPut.setEntity(entity);
httpPut.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPut);
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();
}
}
重要入参说明:
• url:通知地址,通知地址,仅支持https。
注意:
更多参数、响应详情及错误码请参见更新投诉通知回调地址接口文档
步骤说明:商户通过调用此接口查询投诉通知的回调URL。
示例代码:
public void DelComplaintsNotify() throws Exception{
//请求URL
HttpDelete httpDel = new HttpDelete("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications");
httpDel.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpDel);
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();
}
}
注意:
更多参数、响应详情及错误码请参见删除投诉通知回调地址接口文档
步骤说明:商户可调用此接口回复用户。其中上传图片凭证需首先调用商户上传反馈图片接口,得到图片id,再将id填入请求。
示例代码:
public void ReplyInfo() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/200201820200101080076610000/response");
// 请求body参数
String reqdata = "{"
+ "\"complainted_mchid\":190001XXX,"
+ "\"response_content\":\"已与用户沟通解决\","
+ "\"response_images\": ["
+ "\"0\":\"8692450418652C00E4E623AF421E00B5.png\""
+ "]"
+ "}";
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();
}
}
重要入参说明:
• complaint_id:投诉单对应的投诉单号
• complainted_mchid:被诉商户号,投诉单对应的被诉商户号
• response_content:回复内容,具体的投诉处理方案,限制200个字符以内。
注意:
更多参数、响应详情及错误码请参见提交回复接口文档
步骤说明:商户可通过调用此接口,反馈投诉单已处理完成。
示例代码:
public void CompleteComplaints() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/200201820200101080076610000/complete");
// 请求body参数
String reqdata = "{"
+ "\"complainted_mchid\":190001XXX"
+ "}";
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();
}
}
重要入参说明:
• complaint_id:投诉单对应的投诉单号,在投诉通知回调中会返回这个参数
• complainted_mchid:被诉商户号,投诉单对应的被诉商户号
注意:
更多参数、响应详情及错误码请参见查询投诉单详情接口文档
步骤说明:商户上传反馈图片的接口。 将媒体图片进行二进制转换,得到的媒体图片二进制内容,在请求body中上传此二进制内容。 媒体图片只支持jpg、png、bmp格式,文件大小不能超过2M。
示例代码:
String filePath = "/your/home/hellokitty.png";
URI uri = new URI("https://api.mch.weixin.qq.com/v3/merchant-service/images/upload");
File file = new File(filePath);
try (FileInputStream ins1 = new FileInputStream(file)) {
String sha256 = DigestUtils.sha256Hex(ins1);
try (InputStream ins2 = new FileInputStream(file)) {
HttpPost request = new WechatPayUploadHttpPost.Builder(uri)
.withImage(file.getName(), sha256, ins2)
.build();
CloseableHttpResponse response1 = httpClient.execute(request);
}
}
重要入参说明:
• file:图片文件,将媒体图片进行二进制转换,得到的媒体图片二进制内容,在请求body中上传此二进制内容。媒体图片只支持jpg、png、bmp格式,文件大小不能超过2M。
注意:
更多参数、响应详情及错误码请参见商户上传反馈图片接口文档
A:商户反馈API每分钟60次,查询投诉单列表 API频率是每分钟200次
A:是实时的,会直接展示给用户
A:微信存在重复通知的情况,关于重复通知的客户投诉,通知id是一致的
A:请按照以下几点进行排查:
1. 请检查回调url是否能正常公网访问,不能是中文域名,不能是http,必须是https
2. 请注意回调url不能携带参数
3. 请检查是否开启了防火墙,如果开启了防火墙,请添加微信支付回调IP(微信支付回调通知出口IP列表)
4. 请检查设置通知url的商户号与投诉订单的商户号是否为同一个
5. APIv3密钥没有设置,设置路径:【微信商户平台—>账户设置—>API安全—>设置APIv3密钥】