2. 开发准备
2.1. 搭建和配置开发环境
为了帮助开发者调用开放接口,我们提供了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();
}
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]);
/*
Package core 微信支付api v3 go http-client 基础库,你可以使用它来创建一个client,并向微信支付发送http请求
只需要你在初始化客户端的时候,传递credential以及validator
credential用来生成http header中的authorization信息
validator则用来校验回包是否被篡改
如果http请求返回的err为nil,一般response.Body 都不为空,你可以尝试对其进行序列化
请注意及时关闭response.Body
注意:使用微信支付apiv3 go库需要引入相关的包,该示例代码必须引入的包名有以下信息
"context"
"crypto/x509"
"fmt"
"io/ioutil"
"log"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/utils"
*/
func SetUp() (opt []option.ClientOption, err error) {
//商户号
mchID := ""
//商户证书序列号
mchCertSerialNumber := ""
//商户私钥文件路径
privateKeyPath := ""
//平台证书文件路径
wechatCertificatePath := ""
// 加载商户私钥
privateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
log.Printf("load private err:%s", err.Error())
return nil, err
}
// 加载微信支付平台证书
wechatPayCertificate, err := utils.LoadCertificateWithPath(wechatCertificatePath)
if err != nil {
log.Printf("load certificate err:%s",err)
return nil, err
}
//设置header头中authorization信息
opts := []option.ClientOption{
option.WithMerchant(mchID, mchCertSerialNumber, privateKey), // 设置商户相关配置
option.WithWechatPay([]*x509.Certificate{wechatPayCertificate}), // 设置微信支付平台证书,用于校验回包信息用
}
return opts, nil
}
3、基于接口的示例代码,替换请求参数后可发起测试
说明:
• 上面的开发库为微信支付官方开发库,其它没有审核或者控制下的第三方工具和库,微信支付不保证它们的安全性和可靠性
通过包管理工具引入SDK后,可根据下面每个接口的示例代码替换相关参数后进行快速测试
• 开发者如果想详细了解签名生成、签名验证、敏感信息加/解密、媒体文件上传等常用方法的具体代码实现,可阅读下面的详细说明:
1.签名生成
2.签名验证
3.敏感信息加解密
4.merchantPrivateKey(私钥)
5.wechatpayCertificates(平台证书)
6.APIV3Key(V3 key)
• 如想更详细的了解我们的接口规则,可查看我们的接口规则指引文档
3.2. API接入(含示例代码)
文档展示了如何使用微信支付服务端 SDK 快速接入代金券产品,完成与微信支付对接的部分。
注意:
- 文档中的代码示例是用来阐述 API 基本使用方法,代码中的示例参数需替换成商户自己账号及请求参数才能跑通。
- 以下接入步骤仅提供参考,请商户结合自身业务需求进行评估、修改。
3.2.1. 【服务端】创建代金券批次
步骤说明:通过创建代金券批次接口,可创建代金券的类型包含预充值和免充值两种类型。
- 预充值代金券适用于第三方出资策划的活动,例如:满100减10. 指订单金额100元,用户实付90元,商户实收100元。
- 免充值适用于商户策划的活动,例如:满100减10。 指订单金额100元,用户实付90元(用户领券后,在支付中直接核销10元),商户实收90元。
示例代码
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\":\"xx98568865\","
+ "\"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();
}
}
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/marketing/favor/coupon-stocks', //请求URL
[
// JSON请求体
'json' => [
"stock_name" => "GD测批次4",
"comment" => "验证活动",
"belong_merchant" => "xx98568865",
"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",
],
'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;
}
func CreateCoupon() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/coupon-stocks"
//设置请求信息,此处也可以使用结构体来进行请求
mapInfo := map[string]interface{}{
"stock_name": "微信支付代金券批次",
"comment": "零售批次",
"belong_merchant": "xx98568865",
"available_begin_time": "2015-05-20T13:29:35.120+08:00",
"available_end_time": "2015-05-20T13:29:35.120+08:00",
" stock_use_rule": map[string]interface{}{
"max_coupons": 100,
"max_amount": 5000,
"max_amount_by_day": 400,
"max_coupons_per_user": 3,
"natural_person_limit": false,
"prevent_api_abuse": false,
},
"pattern_info": map[string]interface{}{
"description": "微信支付营销代金券",
"merchant_logo": "https://qpic.cn/xxx",
"merchant_name": "微信支付",
"background_color": "COLOR020",
"coupon_image": "https://qpic.cn/xxx",
},
"coupon_use_rule": map[string]interface{}{
"fixed_normal_coupon": map[string]interface{}{
"coupon_amount": 50,
"transaction_minimum": 100,
},
"goods_tag": []map[string]interface{}{
"123321",
"123322",
},
"trade_type": []map[string]interface{}{
"OTHER",
"APPPAY",
},
"combine_use": false,
"available_items": []map[string]interface{}{
"123321",
"123322",
},
"available_merchants": []map[string]interface{}{
"9856000",
"9856001",
},
},
"no_cash": false,
"stock_type": "NORMAL",
"out_request_no": "89560002019101000121",
}
// 发起请求
response, err := client.Post(ctx, URL, mapInfo)
if err != nil{
log.Printf("client post err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• out_request_no:商户创建批次凭据号(格式:商户id+日期+流水号),可包含英文字母,数字,|,_,*,-等内容,不允许出现其他不合法符号,商户侧需保持商户单据号全局唯一
• available_begin_time:批次开始时间
1. 开始时间不可早于当前时间
2. 不能创建365天后开始的批次
3. 批次可用时间范围最长为90天
• stock_name:批次名称·
校验规则:
1. 批次名称最多9个中文汉字
2. 批次名称最多20个字母
3. 批次名称中不能包含不当内容和特殊字符 _ , ; |
更多参数、响应详情及错误码请参见创建代金券批次接口文档
3.2.2. 【服务端】激活代金券批次
步骤说明:制券成功后,通过调用激活接口激活代金券批次。
- 说明:如果是预充值代金券,激活时从商户账户余额中锁定本批次的营销资金。
示例代码
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();
}
}
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/start', //请求URL
[
// JSON请求体
'json' => [
"stock_creator_mchid" => "8956000",
],
'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;
}
func ActiveCouponActiveCoupon() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/start"
//设置请求信息,此处也可以使用结构体来进行请求
mapInfo := map[string]interface{}{
"stock_creator_mchid": "8956000",
}
// 发起请求
response, err := client.Post(ctx, URL, mapInfo)
if err != nil{
log.Printf("client post err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见激活代金券批次接口文档
3.2.3. 【服务端】发放代金券批次
步骤说明:商户平台/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();
}
}
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/marketing/favor/users/o4GgauInH_RCEdvrrNGrntXDu6D4/coupons', //请求URL
[
// JSON请求体
'json' => [
"stock_id" => "9856000",
"out_request_no" => "89560002019101000121",
"appid" => "wx233544546545989",
"stock_creator_mchid" => "8956000",
],
'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;
}
func SendCoupon() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/users/o4GgauInH_RCEdvrrNGrntXDu6D4/coupons"
//设置请求信息,此处也可以使用结构体来进行请求
mapInfo := map[string]interface{}{
"stock_id": "9856000",
"out_request_no": "89560002019101000121",
"appid": "wx233544546545989",
"stock_creator_mchid": "8956000",
}
// 发起请求
response, err := client.Post(ctx, URL, mapInfo)
if err != nil{
log.Printf("client post err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• out_request_no:商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
• openid:openid是微信用户在appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户。openid获取方式请参考以下文档小程序获取openid、公众号获取openid、APP获取openid
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见发放代金券批次接口文档
3.2.4. 【服务端】暂停代金券批次
步骤说明:通过此接口可暂停指定代金券批次。暂停后,该代金券批次暂停发放,用户无法通过任何渠道再领取该批次的券。暂停接口的前提是批次处于激活状态。
示例代码
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();
}
}
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/pause', //请求URL
[
// JSON请求体
'json' => [
"stock_creator_mchid" => "8956000",
],
'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;
}
func PauseCoupon() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/pause"
//设置请求信息,此处也可以使用结构体来进行请求
mapInfo := map[string]interface{}{
"stock_creator_mchid": "8956000",
}
// 发起请求
response, err := client.Post(ctx, URL, mapInfo)
if err != nil{
log.Printf("client post err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id
更多参数、响应详情及错误码请参见暂停代金券批次接口文档
3.2.5. 【服务端】重启代金券批次
步骤说明:通过此接口可重启指定代金券批次。重启后,该代金券批次可以再次发放。重启接口的前提是批次处于暂停状态。
示例代码
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();
}
}
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/restart', //请求URL
[
// JSON请求体
'json' => [
"stock_creator_mchid" => "8956000",
],
'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;
}
func RestartCoupon() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856000/restart"
//设置请求信息,此处也可以使用结构体来进行请求
mapInfo := map[string]interface{}{
"stock_creator_mchid": "8956000",
}
// 发起请求
response, err := client.Post(ctx, URL, mapInfo)
if err != nil{
log.Printf("client post err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见重启代金券批次接口文档
3.2.6. 【服务端】条件查询批次列表
步骤说明:通过此接口可查询多个批次的信息,包括批次的配置信息以及批次概况数据。
示例代码
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();
}
}
try {
$resp = $client->request(
'GET',
'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', //请求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;
}
func GetStocksList() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "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"
// 发起请求
response, err := client.Get(ctx, URL)
if err != nil{
log.Printf("client get err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• status:批次状态
更多参数、响应详情及错误码请参见条件查询批次列表接口文档
3.2.7. 【服务端】查询批次详情
步骤说明:通过此接口可查询批次信息,包括批次的配置信息以及批次概况数据。
示例代码
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();
}
}
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856888?stock_creator_mchid=123456', //请求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;
}
func GetStocksInfo() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9856888?stock_creator_mchid=123456"
// 发起请求
response, err := client.Get(ctx, URL)
if err != nil{
log.Printf("client get err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见查询批次详情接口文档
3.2.8. 【服务端】查询代金券详情
步骤说明:通过此接口可查询代金券信息,包括代金券的基础信息、状态。如代金券已核销,会包括代金券核销的订单信息(订单号、单品信息等)。
示例代码
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();
}
}
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/marketing/favor/users/o4GgauInH_RCEdvrrNGrntXDu6D4/coupons/985688?appid=wx233544546545989', //请求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;
}
func GetConponInfo() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/users/o4GgauInH_RCEdvrrNGrntXDu6D4/coupons/985688?appid=wx233544546545989"
// 发起请求
response, err := client.Get(ctx, URL)
if err != nil{
log.Printf("client get err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
更多参数、响应详情及错误码请参见查询代金券详情接口文档
3.2.9. 【服务端】查询代金券可用商户
步骤说明:通过调用此接口可查询批次的可用商户号,判断券是否在某商户号可用,来决定是否展示。
示例代码
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();
}
}
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/merchants?offset=10&limit=10&stock_creator_mchid=1900001111', //请求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;
}
func GetConponMch() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/merchants?offset=10&limit=10&stock_creator_mchid=1900001111"
// 发起请求
response, err := client.Get(ctx, URL)
if err != nil{
log.Printf("client get err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
更多参数、响应详情及错误码请参见查询代金券可用商户接口文档
3.2.10. 【服务端】查询代金券可用单品
步骤说明:通过此接口可查询批次的可用商品编码,判断券是否可用于某些商品,来决定是否展示。
示例代码
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();
}
}
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/items?offset=10&limit=10&stock_creator_mchid=9865000', //请求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;
}
func GetConponItem() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/items?offset=10&limit=10&stock_creator_mchid=9865000"
// 发起请求
response, err := client.Get(ctx, URL)
if err != nil{
log.Printf("client get err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id
更多参数、响应详情及错误码请参见查询代金券可用单品接口文档
3.2.11. 【服务端】根据商户号查用户的券
步骤说明:可通过该接口查询用户在某商户号可用的全部券,可用于商户的小程序/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();
}
}
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/marketing/favor/users/o4GgauInH_RCEdvrrNGrntXDu6D4/coupons?appid=wx233544546545989', //请求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;
}
func GetUserConpon() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/users/2323dfsdf342342/coupons?appid=wx233544546545989"
// 发起请求
response, err := client.Get(ctx, URL)
if err != nil{
log.Printf("client get err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_creator_mchid:批次创建方商户号
• stock_id:微信为每个代金券批次分配的唯一id。
• openid:openid是微信用户在appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户。openid获取方式请参考以下文档小程序获取openid、公众号获取openid、APP获取openid
• available_mchid:可用商户号
更多参数、响应详情及错误码请参见根据商户号查用户的券接口文档
3.2.12. 【服务端】下载批次核销明细
步骤说明:可获取到某批次的核销明细数据,包括订单号、单品信息、银行流水号等,用于对账/数据分析。
示例代码
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();
}
}
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/use-flow', //请求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;
}
func UseBill() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/use-flow"
// 发起请求
response, err := client.Get(ctx, URL)
if err != nil{
log.Printf("client get err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_id:微信为每个代金券批次分配的唯一id
更多参数、响应详情及错误码请参见下载批次核销明细接口文档
3.2.13. 【服务端】下载批次退款明细
步骤说明:可获取到某批次的退款明细数据,包括订单号、单品信息、银行流水号等,用于对账/数据分析。
示例代码
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();
}
}
try {
$resp = $client->request(
'GET',
'https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/refund-flow', //请求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;
}
func RefundBill() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/stocks/9865000/refund-flow"
// 发起请求
response, err := client.Get(ctx, URL)
if err != nil{
log.Printf("client get err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• stock_id:微信为每个代金券批次分配的唯一id
更多参数、响应详情及错误码请参见下载批次退款明细接口文档
3.2.14. 【服务端】设置消息通知地址
步骤说明:用于设置接收营销事件通知的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();
}
}
try {
$resp = $client->request(
'POST',
'https://api.mch.weixin.qq.com/v3/marketing/favor/callbacks', //请求URL
[
// JSON请求体
'json' => [
"mchid" => "9856888",
"notify_url" => "https://pay.weixin.qq.com",
],
'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;
}
func SetCouponCallback() {
// 初始化客户端
ctx := context.TODO()
opts, err := SetUp()
if err != nil {
return
}
client, err := core.NewClient(ctx, opts...,)
if err != nil{
log.Printf("init client err:%s",err)
return
}
//设置请求地址
URL := "https://api.mch.weixin.qq.com/v3/marketing/favor/callbacks"
//设置请求信息,此处也可以使用结构体来进行请求
mapInfo := map[string]interface{}{
"mchid": "9856888",
"notify_url": "https://pay.weixin.qq.com",
}
// 发起请求
response, err := client.Post(ctx, URL, mapInfo)
if err != nil{
log.Printf("client post err:%s",err)
return
}
// 校验回包内容是否有逻辑错误
err = core.CheckResponse(response)
if err != nil{
log.Printf("check response err:%s",err)
return
}
// 读取回包信息
body, err := ioutil.ReadAll(response.Body)
if err != nil{
log.Printf("read response body err:%s",err)
return
}
fmt.Println(string(body))
}
}
重要入参说明:
• notify_url:必须为https协议。如果链接无法访问,商户将无法接收到微信通知。 通知url必须为直接可访问的url,不能携带参数。示例: “https://pay.weixin.qq.com”
更多参数、响应详情及错误码请参见设置消息通知地址接口文档
3.2.15.【服务端】核销事件回调通知
步骤说明:用户使用券后,微信会把相关核销券信息发送给商户,商户需要接收处理,并按照文档规范返回应答。出于安全的考虑,我们对核销券信息数据进行了加密,商户需要先对通知数据进行解密,才能得到核销券信息数据。
注意:
- 核销券信息通知是以POST 方法访问商户设置的通知url,通知的数据以JSON 格式通过请求主体(BODY)传输。通知的数据包括了加密的支付结果详情
- 加密不能保证通知请求来自微信。微信会对发送给商户的通知进行签名,并将签名值放在通知的HTTP头Wechatpay-Signature。商户应当验证签名,以确认请求来自微信,而不是其他的第三方。签名验证的算法请参考
《微信支付API v3签名验证》。
- 支付通知http应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx
- 商户成功接收到回调通知后应返回成功的http应答码为200或204
- 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱
- 对后台通知交互时,如果微信收到应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。(通知频率为1min1次,总计9次)
更多参数、响应详情及错误码请参见 核销事件回调通知支付通知API接口文档
4. 常见问题
Q:创建代金券接口报错:“你配置的信息需要开通特殊权限”
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. 不可使用的时间参数不可以传递
Q:创建代金券报错“批次预算等于批次面额乘以发券数”
A:请按以下步骤进行排查
1. max_amount需要等于coupon_amount(面额) * max_coupons(发放总上限)
2. 核对最终请求数据,并查看是否存在遗漏数据,建议使用postman进行调试
Q:创建代金券报错“活动未开始或已结束”
A:活动时间不能大于90天,请检查available_begin_time、available_end_time参数设置是否符合要求。
Q:激活代金券API,如果大于1min或者更久还报错“系统繁忙”?
A:请检查单个可用商户号下正在运营的活动是否控制在500个以内,若超过500个活动可能导致新活动激活失败的情况。
Q:设置消息通知地址报错“系统繁忙”
A:请按以下步骤进行排查
1. 请检查notify_url设置是否正确,notify_url必须为https
2. notify_url地址为一个可访问的地址
3. notify_url不能携带参数。示例:“http://pay.weixin.qq.com/wxpay/pay.action”