微信代金券官方文档中有对接文档,但是根据官方文档很多地方不清晰,接下来给大家介绍一种简单的对接步骤。
官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/index.shtml
对接步骤:
准备工作:商户创建商品,激活商品等,提供证书等略过。
今天我们主要讲解java对接代金券发放接口和代金券查询接口https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_1.shtml
1、maven依赖
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.5</version>
</dependency>
@Service
public class WxTest {
private static final Logger log = Logger.getLogger(UserClubCardServiceImpl.class);
//商户id
@Value("${mch_id}")
private String mchId;
//证书密码
@Value("${serialno}")
private String mchSerialNo;
//访问微信的私钥文件地址 E:/file/key/wx_coupon/apiclient_key.pem
@Value("${private_key_file}")
private String merchantPrivateKeyFile;
//证书地址 E:/file/key/wx_coupon/wechatpay_XXXXXpem
@Value("${cert_file}")
private String certificateFile;
//改微信号的appid
@Value("${appid}")
private String appId;
/**
* 发放微信代金券
*/
public String sendCoupon(FavorCouponsCreateReq favorCouponsCreateReq) {
FileInputStream privateKeyInputStream = null;
FileInputStream certificateInputStream = null;
try {
privateKeyInputStream = new FileInputStream(new File(merchantPrivateKeyFile));
certificateInputStream = new FileInputStream(new File(certificateFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
log.error("read merchantPrivateKeyFile or certificateFile faild, errmsg:" + e.getMessage());
return null;
}
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKeyInputStream);
X509Certificate wechatpayCertificate = PemUtil.loadCertificate(certificateInputStream);
ArrayList<X509Certificate> wechatpayCertificates = new ArrayList<>();
wechatpayCertificates.add(wechatpayCertificate);
CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withWechatPay(wechatpayCertificates)
.build();
String sendCouponUrl = String.format("https://api.mch.weixin.qq.com/v3/marketing/favor/users/%s/coupons", favorCouponsCreateReq.getOpenId());
HashMap<String, String> paraMap = new HashMap<>();
paraMap.put("stock_id", favorCouponsCreateReq.getStock_id());
paraMap.put("out_request_no", favorCouponsCreateReq.getOut_request_no());
paraMap.put("appid", appId);
paraMap.put("stock_creator_mchid", mchId);
log.info("request url=" + sendCouponUrl);
log.info("request params=" + JSONObject.toJSONString(paraMap));
HttpPost httpPost = new HttpPost(sendCouponUrl);
StringEntity reqEntity = new StringEntity(JSONObject.toJSONString(paraMap),
ContentType.create("application/json", "utf-8"));
httpPost.setEntity(reqEntity);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-Type", "application/json");
CloseableHttpResponse response = null;
try {
log.info("微信发券接口地址:"+sendCouponUrl+",入参:"+JSONObject.toJSONString(httpPost));
response = httpClient.execute(httpPost);
log.info("微信发券接口返回结果code:"+response.getStatusLine().getStatusCode()+",内容:"+ EntityUtils.toString(response.getEntity()));
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String result = EntityUtils.toString(response.getEntity());
log.info("response=" + result);
return result;
} else {
String result = EntityUtils.toString(response.getEntity());
log.error("status_code=" + response.getStatusLine().getStatusCode() + ", response=" + result);
return result;
}
} catch (Exception e) {
log.error("excepiton:" + e.getMessage());
e.printStackTrace();
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
public String getCouponDetails(WxCouponsDetailsReq wxCouponsDetailsReq) throws Exception {
FileInputStream privateKeyInputStream = null;
FileInputStream certificateInputStream = null;
try {
privateKeyInputStream = new FileInputStream(new File(merchantPrivateKeyFile));
certificateInputStream = new FileInputStream(new File(certificateFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
log.error("read merchantPrivateKeyFile or certificateFile faild, errmsg:" + e.getMessage());
return null;
}
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKeyInputStream);
X509Certificate wechatpayCertificate = PemUtil.loadCertificate(certificateInputStream);
ArrayList<X509Certificate> wechatpayCertificates = new ArrayList<>();
wechatpayCertificates.add(wechatpayCertificate);
CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withWechatPay(wechatpayCertificates)
.build();
//get请求
String sendCouponUrl = String.format("https://api.mch.weixin.qq.com/v3/marketing/favor/users/%s/coupons/%s?appid="+appId,wxCouponsDetailsReq.getOpenid(),wxCouponsDetailsReq.getCoupon_id());
log.info("request url=" + sendCouponUrl);
log.info("request params=" + JSONObject.toJSONString(wxCouponsDetailsReq));
HttpGet httpGet = new HttpGet(sendCouponUrl);
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Content-Type", "application/json");
CloseableHttpResponse response = null;
try {
log.info("微信查询券详情接口地址:"+sendCouponUrl);
response = httpClient.execute(httpGet);
log.info("微信查询券详情返回结果code:"+response.getStatusLine().getStatusCode()+",内容:"+EntityUtils.toString(response.getEntity()));
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String result = EntityUtils.toString(response.getEntity());
log.info("response=" + result);
return result;
} else {
String result = EntityUtils.toString(response.getEntity());
log.error("status_code=" + response.getStatusLine().getStatusCode() + ", response=" + result);
return result;
}
} catch (Exception e) {
log.error("查询微信代金券详情异常:" + e.getMessage());
e.printStackTrace();
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
请求类
public class FavorCouponsCreateRequest {
/**
*
* 字段名:批次号
* 变量名:stock_id
* 是否必填:是
* 类型:string[1,20]
* 描述:
* 微信为每个批次分配的唯一id。
* 校验规则:必须为代金券(全场券或单品券)批次号,不支持立减与折扣。
* 示例值:9856000
*
*/
private String stock_id;
/**
*
* 字段名:商户单据号
* 变量名:out_request_no
* 是否必填:是
* 类型:string[1,128]
* 描述:
* 商户此次发放凭据号(格式:商户id+日期+流水号),可包含英文字母,数字,|,_,*,-等内容,不允许出现其他不合法符号,商户侧需保持唯一性。
* 示例值: 89560002019101000121
*
*/
private String out_request_no;
/**
*
* 字段名:公众账号ID
* 变量名:appid
* 是否必填:是
* 类型:string[1,128]
* 描述:
* 微信为发券方商户分配的公众账号ID,接口传入的所有appid应该为公众号的appid或者小程序的appid(在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。。
* 校验规则:
* 1、该appid需要与接口传入中的openid有对应关系;
* 2、该appid需要与调用接口的商户号(即请求头中的商户号)有绑定关系,若未绑定,可参考该指引完成绑定(商家商户号与AppID账号关联管理)
* 示例值:wx233544546545989
*
*/
private String appid;
/**
*
* 字段名:创建批次的商户号
* 变量名:stock_creator_mchid
* 是否必填:是
* 类型:string[1,20]
* 描述:
* 批次创建方商户号。
* 示例值:8956000
*
*/
private String stock_creator_mchid;
/**
*
* 字段名:指定面额发券,面额
* 变量名:coupon_value
* 是否必填:否
* 类型:uint64
* 描述:
* 指定面额发券场景,券面额,其他场景不需要填,单位:分。
* 校验规则:仅在发券时指定面额及门槛的场景才生效,常规发券场景请勿传入该信息。
* 示例值:100
*
*/
private Integer coupon_value;
/**
*
* 字段名:指定面额发券,券门槛
* 变量名:coupon_minimum
* 是否必填:是
* 类型:uint64
* 描述:
* 指定面额发券批次门槛,其他场景不需要,单位:分。
* 校验规则:仅在发券时指定面额及门槛的场景才生效,常规发券场景请勿传入该信息。
* 示例值:100
*
*/
private Integer coupon_minimum;
private String openid;}
public class WxCouponsDetailsReq {
private String coupon_id;
private String openid;
}