为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付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.敏感信息加解密
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
文档展示了如何使用微信支付服务端 SDK 快速接入代金券产品,完成与微信支付对接的部分。
注意:
步骤说明:通过创建代金券批次接口,可创建代金券的类型包含预充值和免充值两种类型。
示例代码
public void CreateCoupon() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/marketing/favor/coupon-stocks");
// 请求body参数
String reqdata = "{"
+ "\"stock_name\":\"GD测批次4\","
+ "\"comment\":\"验证活动\","
+ "\"belong_merchant\":\"xx98568865xxxx\","
+ "\"available_begin_time\":\"2020-02-13T18:00:00.120+08:00\","
+ "\"available_end_time\":\"2020-02-20T23:59:59.120+08:00\","
+ "\"stock_use_rule\": {"
+ "\"max_coupons\":10,"
+ "\"max_amount\":100,"
+ "\"max_coupons_per_user\":10,"
+ "\"natural_person_limit\":false,"
+ "\"prevent_api_abuse\":false"
+ "},"
+ "\"coupon_use_rule\": {"
+ "\"fixed_normal_coupon\": {"
+ "\"coupon_amount\":10,"
+ "\"transaction_minimum\":10"
+ "},"
+ "\"available_merchants\": ["
+ "\"0\":\"209784532\","
+ "\"1\":\"221003827\""
+ "]"
+ "},"
+ "\"no_cash\":false,"
+ "\"stock_type\":\"NORMAL\","
+ "\"out_request_no\":\"207662xxxxxx\""
+ "}";
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();
}
}
重要入参说明:
• out_trade_no:商户创建批次凭据号(格式:商户id+日期+流水号),可包含英文字母,数字,|,_,*,-等内容,不允许出现其他不合法符号,商户侧需保持商户单据号全局唯一
• available_begin_time:批次开始时间
1. 开始时间不可早于当前时间
2. 不能创建365天后开始的批次
3. 批次可用时间范围最长为90天
• stock_name:批次名称·
校验规则:
1. 批次名称最多9个中文汉字
2. 批次名称最多20个字母
3. 批次名称中不能包含不当内容和特殊字符 _ , ; |
更多参数、响应详情及错误码请参见创建代金券批次接口文档
步骤说明:制券成功后,通过调用激活接口激活代金券批次。
示例代码
public void ActiveCoupon() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/start");
// 请求body参数
String reqdata = "{"
+ "\"stock_creator_mchid\":\"8956000\""
+ "}";
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();
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见激活代金券批次接口文档
步骤说明:商户平台/API完成制券后,可使用发放代金券接口发券。通过调用此接口可发放指定批次给指定用户,发券场景可以是小程序、H5、APP等。
注意:
示例代码
public void SendCoupon() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/marketing/favor/users/o4GgauInH_RCEdvrrNGrntXDu6D4/coupons");
// 请求body参数
String reqdata = "{"
+ "\"stock_id\":\"9856000\","
+ "\"out_request_no\":\"89560002019101000121\","
+ "\"appid\":\"wx233544546545989\","
+ "\"stock_creator_mchid\":\"8956000\""
+ "}";
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();
}
}
重要入参说明:
• out_trade_no:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
• openid:openid是微信用户在appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户。openid获取方式请参考以下文档小程序获取openid、公众号获取openid、APP获取openid
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见发放代金券批次接口文档
步骤说明:通过此接口可暂停指定代金券批次。暂停后,该代金券批次暂停发放,用户无法通过任何渠道再领取该批次的券。暂停接口的前提是批次处于激活状态。
示例代码
public void PauseCoupon() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/pause");
// 请求body参数
String reqdata = "{"
+ "\"stock_creator_mchid\":\"8956000\""
+ "}";
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();
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id
更多参数、响应详情及错误码请参见暂停代金券批次接口文档
步骤说明:通过此接口可重启指定代金券批次。重启后,该代金券批次可以再次发放。重启接口的前提是批次处于暂停状态。
示例代码
public void RestartCoupon() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/restart");
// 请求body参数
String reqdata = "{"
+ "\"stock_creator_mchid\":\"8956000\""
+ "}";
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();
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见重启代金券批次接口文档
步骤说明:通过此接口可查询多个批次的信息,包括批次的配置信息以及批次概况数据。
示例代码
public void GetStocksList() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks?offset=1&limit=10&stock_creator_mchid=9856888&create_start_time%3D2015-05-20T13%3A29%3A35.120%2B08%3A00%26create_end_time%3D2015-05-20T13%3A29%3A35.120%2B08%3A00&status=paused");
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();
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• status:批次状态
更多参数、响应详情及错误码请参见条件查询批次列表接口文档
步骤说明:通过此接口可查询批次信息,包括批次的配置信息以及批次概况数据。
示例代码
public void GetStocksInfo() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856888?stock_creator_mchid=123456");
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();
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见查询批次详情接口文档
步骤说明:通过此接口可查询代金券信息,包括代金券的基础信息、状态。如代金券已核销,会包括代金券核销的订单信息(订单号、单品信息等)。
示例代码
public void GetConponInfo() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/marketing/favor/users/o4GgauInH_RCEdvrrNGrntXDu6D4/coupons/985688?appid=wx233544546545989");
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:openid是微信用户在appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户。openid获取方式请参考以下文档小程序获取openid、公众号获取openid、APP获取openid
• coupon_id:微信为代金券唯一分配的id。
更多参数、响应详情及错误码请参见查询代金券详情接口文档
步骤说明:通过调用此接口可查询批次的可用商户号,判断券是否在某商户号可用,来决定是否展示。
示例代码
public void GetConponMch() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/merchants?offset=10&limit=10&stock_creator_mchid=1900001111");
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();
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见查询代金券可用商户接口文档
步骤说明:通过此接口可查询批次的可用商品编码,判断券是否可用于某些商品,来决定是否展示。
示例代码
public void GetConponItem() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/items?offset=10&limit=10&stock_creator_mchid=9865000");
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();
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id
更多参数、响应详情及错误码请参见查询代金券可用单品接口文档
步骤说明:可通过该接口查询用户在某商户号可用的全部券,可用于商户的小程序/H5中,用户"我的代金券"或"提交订单页"展示优惠信息。无法查询到微信支付立减金。本接口查不到用户的微信支付立减金(又称“全平台通用券”),即在所有商户都可以使用的券,例如:摇摇乐红包;当按可用商户号查询时,无法查询用户已经核销的券
示例代码
public void GetUserConpon() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/marketing/favor/users/o4GgauInH_RCEdvrrNGrntXDu6D4/coupons?appid=wx233544546545989");
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();
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
• openid:openid是微信用户在appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户。openid获取方式请参考以下文档小程序获取openid、公众号获取openid、APP获取openid
• available_mchid:可用商户号
更多参数、响应详情及错误码请参见根据商户号查用户的券接口文档
步骤说明:可获取到某批次的核销明细数据,包括订单号、单品信息、银行流水号等,用于对账/数据分析。
示例代码
public void UseBill() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/use-flow");
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();
}
}
重要入参说明:
• stock_id:微信为每个代金券批次分配的唯一id
更多参数、响应详情及错误码请参见下载批次核销明细接口文档
步骤说明:可获取到某批次的退款明细数据,包括订单号、单品信息、银行流水号等,用于对账/数据分析。
示例代码
public void RefundBill() throws Exception{
//请求URL
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/refund-flow");
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();
}
}
重要入参说明:
• stock_id:微信为每个代金券批次分配的唯一id
更多参数、响应详情及错误码请参见下载批次退款明细接口文档
步骤说明:用于设置接收营销事件通知的URL,可接收营销相关的事件通知,包括核销、发放、退款等。
示例代码
public void SetCouponCallback() throws Exception{
//请求URL
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/marketing/favor/callbacks");
// 请求body参数
String reqdata = "{"
+ "\"mchid\":\"9856888\","
+ "\"notify_url\":\"https://pay.weixin.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();
}
}
重要入参说明:
• notify_url:必须为https协议。如果链接无法访问,商户将无法接收到微信通知。 通知url必须为直接可访问的url,不能携带参数。示例: “https://pay.weixin.qq.com”
更多参数、响应详情及错误码请参见设置消息通知地址接口文档
步骤说明:用户使用券后,微信会把相关核销券信息发送给商户,商户需要接收处理,并按照文档规范返回应答。出于安全的考虑,我们对核销券信息数据进行了加密,商户需要先对通知数据进行解密,才能得到核销券信息数据。
注意:
更多参数、响应详情及错误码请参见 核销事件回调通知支付通知API接口文档
A:请按以下步骤进行排查
1. stock_name:最多可填写9个字
2. max_coupons_per_user:单天发放个数上限不能为0
3. coupon_amount:10<=coupon_amount<=100000
4. available_time_after_receive:可用时间:相对时间,按分钟设置,是否1min<=分钟范围<=1440min
5. transaction_minimum校验规则:
a、使用门槛-券面额>=0.01(门槛要大于面额)
b、0.1元<=门槛<=100000
6. stock_type:目前只支持NORMAL
7. out_request_no:校验规则:不可以重复
8. 开始时间结束时间控制在90天内
9. 不可使用的时间参数不可以传递
A:请按以下步骤进行排查
1. max_amount需要等于coupon_amount(面额) * max_coupons(发放总上限)
2. 核对最终请求数据,并查看是否存在遗漏数据,建议使用postman进行调试
A:活动时间不能大于90天,请检查available_begin_time、available_end_time参数设置是否符合要求。
A:请检查单个可用商户号下正在运营的活动是否控制在500个以内,若超过500个活动可能导致新活动激活失败的情况。
A:请按以下步骤进行排查
1. 请检查notify_url设置是否正确,notify_url必须为https
2. notify_url地址为一个可访问的地址
3. notify_url不能携带参数。示例:“http://pay.weixin.qq.com/wxpay/pay.action”