微信公众号红包支付接口的使用
本篇介绍微信商户平台中的红包发送接口
1.准备阶段
需要一个开通了微信支付的公众号,并且在产品中心中开通微信红包功能,并且设置好基本设置,注意IP地址一定要是调用接口的服务器的正确的外网地址,防刷等级和每个用户限量按自己的需要配置就可以了.
2.开发
在微信的开发文档中我们可以看到,红包的请求和微信返回的数据都是XML格式的,所以我们首先要写一个工具类能够将XML转化成我们能够读取的格式和把类转化成XML格式,在这里我使用的是xstream,也可以使用其他的工具,sxtream有一个BUG,会导致生成XML文件时,会把"-"变成"--",解决办法可以直接用replace替换成"-",另外需要注意编码格式,一定要全部都使用同一个格式,不然会导致调用接口时返回错误
格式转换工具类好了之后需要写HTTP请求的工具类,红包发送接口需要在请求中携带证书信息,首先我们从微信开发文档中下载证书,下载有4个格式的证书,JAVA的话使用.p12格式的文件,将文件放入一个目录下,如C:\\apiclient_cert.p12目录下,保存好目录路径.我们在文档中可以看见,红包支付需要的数据如下图
还有三个参数并不是必须的,我们选择忽略掉,其他参数如下
1).nonce_str:随即字符串,自己设置
2).sign:签名,这个需要设置好所有参数之后加密产生
3).商户订单号:组成为商户号加上日期再加上10位一天内不能重复的数字,后面10位数字可以用时间戳截取后10位数字完全可以使用
4).商户号:重要的数据,在微信商户平台中可以查看,妥善保管
5).appid:公众号ID
6).商户名称:自己设置
7).re_openid:接受红包的用户ID
8).IP:当前服务器外网IP地址
其他参数按需设置
参数设置好了之后保存在项目中,也可以保存在properties文件中
然后我们需要获取签名sign,跟微信其他签名一样,将所有需要使用的参数按参数名ASCII码从小到大排序,如果参数为空则不参加排序,注意区分大小写,排序好之后需要加上一个key值,这个key值是在微信商户平台中的账户中心中的API安全中设置,我们得到的字符串应如下所示
act_name=XXXX&client_ip=100.100.100.80&mch_billno=23233&mch_id=1394445702&nonce_str=3223&re_openid=3322321213&remark=xxxx&send_name=XXXXX&total_amount=100&total_num=1&wishing=XXXXX&wxappid=WXX32DD&key=qwertyuioplkjhgfdsazxcvbnmWW
注意一定要加上KEY值,然后使用MD5加密,注意此处的编码格式,微信提供了一个网站供我们测试签名http://mch.weixin.qq.com/wiki/tools/signverify/
HTTP请求工具我们使用httpclient,需要加入两个包,pom文件如下
我们的代
public static String sendRedpack(String url,RedpackVo vo) throws IOException, NoSuchAlgorithmException, IllegalArgumentException, IllegalAccessException{
String content = vo.toString(vo);
content = content + "key=" + Constants.KEY;
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(content.getBytes("UTF-8"));
byte[] digest = md.digest();
StringBuffer hexstr = new StringBuffer();
String shaHex = "";
for (int i = 0; i < digest.length; i++) {
shaHex = Integer.toHexString(digest[i] & 0xFF);
if (shaHex.length() < 2) {
hexstr.append(0);
}
hexstr.append(shaHex);
}
vo.setSign(hexstr.toString());
String params = MessageUtil.redpackToXml(vo).replace("__", "_");
String result = "";
FileInputStream instream = null;
CloseableHttpClient httpclient = null;
BufferedReader br = null;
try{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
instream = new FileInputStream(new File(Constants.getCertLocalPath()));
keyStore.load(instream, Constants.getCertPassword().toCharArray());
SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, Constants.getCertPassword().toCharArray()).build();
SSLConnectionSocketFactory sslcsf = new SSLConnectionSocketFactory(sslContext,new String[]{"TLSv1"},null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
httpclient = HttpClients.custom().setSSLSocketFactory(sslcsf).build();
HttpPost httpPost = new HttpPost(url);
StringEntity reqEntity = new StringEntity(params,"UTF-8");
httpPost.setEntity(reqEntity);
CloseableHttpResponse response = httpclient.execute(httpPost);
HttpEntity entity = response.getEntity();
if(entity != null){
br = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
String text;
while((text = br.readLine()) != null){
result+=text;
}
}
}catch(Exception e){
throw new RuntimeException();
}finally{
if(instream != null)
instream.close();
if(br != null)
br.close();
if(httpclient != null)
httpclient.close();
}
return result;
}
package com.pub.vo;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import com.framework.util.DateFormat;
import com.framework.util.PropertiesLoader;
public class RedpackVo {
private String act_name;
private String client_ip;
private String mch_billno;
private String mch_id;
private String nonce_str;
private String re_openid;
private String remark;
private String send_name;
private String sign;
private Integer total_amount;
private Integer total_num;
private String wishing;
private String wxappid;
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getMch_billno() {
return mch_billno;
}
public void setMch_billno(String mch_billno) {
this.mch_billno = mch_billno;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getWxappid() {
return wxappid;
}
public void setWxappid(String wxappid) {
this.wxappid = wxappid;
}
public String getSend_name() {
return send_name;
}
public void setSend_name(String send_name) {
this.send_name = send_name;
}
public String getRe_openid() {
return re_openid;
}
public void setRe_openid(String re_openid) {
this.re_openid = re_openid;
}
public Integer getTotal_amount() {
return total_amount;
}
public void setTotal_amount(Integer total_amount) {
this.total_amount = total_amount;
}
public Integer getTotal_num() {
return total_num;
}
public void setTotal_num(Integer total_num) {
this.total_num = total_num;
}
public String getWishing() {
return wishing;
}
public void setWishing(String wishing) {
this.wishing = wishing;
}
public String getClient_ip() {
return client_ip;
}
public void setClient_ip(String client_ip) {
this.client_ip = client_ip;
}
public String getAct_name() {
return act_name;
}
public void setAct_name(String act_name) {
this.act_name = act_name;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public RedpackVo(String openid,Integer amt){
Properties p = PropertiesLoader.loadProperty("/application.properties");
setAct_name(p.getProperty("ACT_NAME"));
setClient_ip(p.getProperty("CLIENT_IP"));
setMch_id(p.getProperty("MCH_ID"));
setNonce_str(p.getProperty("NONCE_STR"));
setRemark(p.getProperty("WISHING"));
setWxappid(p.getProperty("APP_ID"));
setWishing(p.getProperty("WISHING"));
setSend_name(p.getProperty("SEND_NAME"));
String time = String.valueOf(System.currentTimeMillis());
setTotal_num(1);
setRe_openid(openid);
setTotal_amount(amt);
setMch_billno(mch_id + DateFormat.getYmd() + time.substring(time.length()-10, time.length()));
}
public String toString(RedpackVo vo) throws IllegalArgumentException, IllegalAccessException{
Class<? extends RedpackVo> clazz = vo.getClass();
Field[] fields = clazz.getDeclaredFields();
List<String> names = new ArrayList<String>();
for (Field field : fields) {
if(field.get(vo) != null){
names.add(field.getName()+"="+field.get(vo)+"&");
}
}
StringBuffer sb = new StringBuffer();
String[] str = names.toArray(new String[names.size()]);
Arrays.sort(str);
for(int i =0; i<str.length ; i++){
sb.append(str[i]);
}
return sb.toString();
}
}
@SuppressWarnings("unchecked")
public static Map<String, String> parseXml(String connet) throws IOException, DocumentException {
Map<String, String> map = new HashMap<String, String>();
InputStream inputStream = new ByteArrayInputStream(connet.getBytes("UTF-8"));
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for (Element e : elementList)
map.put(e.getName(), e.getText());
inputStream.close();
inputStream = null;
return map;
}
到现在我们已经拥有了调用接口所需的一切条件,接下来我们写一个main方法测试一下
public static void main(String[] args) throws NoSuchAlgorithmException, IllegalArgumentException, IllegalAccessException, IOException {
String result = WxHttpHelper.sendRedpack(WechatHttplocal.sendredpack(), new RedpackVo("openid",1000));
}
没错,调用的方法就是这么简单,因为我们前面已经把工具类都写完了,注意这里的金额是以分为单位的,写1000代表是发10元红包,微信设置发送红包最少1元,所以填100以下微信会返回错误代码
如果前面一切配置正常,微信一般会返回如下数据
失败则返回
微信红包接口开发结束,如有问题可以留言
忘记说了,证书密码一般是公众号的商户号,只要你没有修改默认都是这个
Constants.getCertLocalPath()证书的地址
Constants.getCertPassword()证书的密码,默认商户号