微信支付之现金红包

相信微信红包大家已经非常熟悉了,今天带领大家使用代码实现微信红包功能。

1.1 发送流程

在实现发送红包之前,我们有必要了解一下发送流程。
微信红包接口调用流程:
① 后台API调用:待进入联调过程时与开发进行详细沟通;
② 告知服务器:告知服务器接收微信红包的用户openID,告知服务器该用户获得的金额;
③ 从商务号扣款:服务器获取信息后从对应的商务号扣取对应的金额;
④ 调用失败:因不符合发送规则,商务号余额不足等原因造成调用失败,反馈至调用方;
⑥ 发送成功:以微信红包公众账号发送对应红包至对应用户;
如下图所示:
流程图

1.2 接口调用请求

请求url: https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack。
是否需要证书: 是(证书及使用说明详见商户证书)。
请求方式: POST

1.3 网络封装RedPackConn方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

package org.wechat.redpack.conn;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
/**
* 请注意,该类中的方法依赖以下几个jar
* httpclient4.3.4.jar
* httpcore-4.3.2.jar
* commons-logging-1.1.3.jar
* @author Andy
*
*/
public class RedPackConn {
private static final String SENDREDPACK="https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack"; //发送现金红包
private static final String GETHBINFO="https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo"; //获取红包信息 (现金和裂变都是该接口)
private static final String SENDGROUPREDPACK="https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack"; //发送裂变红包
public static final String KEYSTORETYPE="PKCS12"; //密匙类型


/**
* 获取安全连接的方法,使用该方法进行连接
* @param certPath 证书的路径
* @param macid 商户号id
* @return 返回安全连接 (该安全连接添加了商户号,证书后的安全连接,
* 在微信API中制定需要证书的地方则通过该方法进行执行)
*/
private static CloseableHttpClient getSecureConn(String certPath,String macid){
char[] array = macid.toCharArray();
FileInputStream instream = null;
CloseableHttpClient httpClient = null;
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance(KEYSTORETYPE);
instream = new FileInputStream(new File(certPath));
keyStore.load(instream, array);
}catch(Exception ex){
ex.printStackTrace();
} finally {
try{
instream.close();
}catch(Exception ex){
ex.printStackTrace();
}
}
try{
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,array).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext,new String[] { "TLSv1" }, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
}catch(Exception ex){
ex.printStackTrace();
}
return httpClient;
}

/**
* 执行安全连接
* @param data 请求数据的参数
* @param path
* @param charter
* @param certPath
* @param macid
* @return
* @throws IOException
* @throws ClientProtocolException
*/
public static String executeConn(String data,String path,String certPath,String macid) throws ClientProtocolException, IOException{
String result="";
CloseableHttpClient httpclient = getSecureConn(certPath,macid);
try{
result = execute(httpclient,path,data);
}catch(Exception ex){
ex.printStackTrace();
}
return result;
}

/**
* 返回普通连接
* @param data 请求时所带的参数
* @param path 请求的路径
* @return
*/
public static String executeConn(String data,String path){
String result = "";
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
result = execute(httpclient,path,data);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 执行网络请求
* @param httpclient 待执行的请求client,其中包括安全cleint
* @param path 请求路径
* @param data 请求数据
* @return
* @throws ClientProtocolException
* @throws IOException
*/
private static String execute(CloseableHttpClient httpclient,String path,String data) throws ClientProtocolException, IOException{
String result = "";
try {
HttpPost httpPost = new HttpPost(path);
StringEntity rentity = new StringEntity(data);
rentity.setContentEncoding("UTF-8");
rentity.setContentType("application/json;charset=utf8");
httpPost.setEntity(rentity);
CloseableHttpResponse response = httpclient.execute(httpPost);
StringBuffer buffer = new StringBuffer();
try {
HttpEntity entity = response.getEntity();
System.out.println(response.getStatusLine());
if (entity != null) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String text;
while ((text = bufferedReader.readLine())!= null) {
buffer.append(text);
}
}
result = buffer.toString();
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
return result;
}

/**
* 发送红包方法 (注意该方法如果请求参数中有中文,则会出现签名错误有待解决)
* @param data 请求对象的json数据集
* @param certPath 证书的路径,
* @param macid 商户号
* @return 请求结果后的返回信息
*/
public static String sendRedInfo(String data,String certPath,String macid){
String result="";
try {
result = executeConn(data,SENDREDPACK,certPath,macid);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}

/**
* 获取红包信息
* @param data 请求参数对象
* @param certPath 证书路径
* @param macid 商户编号
* @return 返回请求后的信息
*/
public static String getRedInfoList(String data,String certPath,String macid){
String result = "";
try {
result = executeConn(data,GETHBINFO,certPath,macid);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}

/**
* 发送裂变红包
* @param data 请求参数对象
* @param cretPath 证书的路径
* @param macid 商户编号
* @return
*/
public static String sendGroupRedBack(String data,String cretPath,String macid){
String result="";
try {
result = executeConn(data,SENDGROUPREDPACK,cretPath,macid);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}

这里使用到了httpcliet,封装了发送红包的请求层,注释已经很清楚,这里不在一一解释。仅仅这样是不够的,我们进行了进一步的封装。

1.4 发送工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package org.wechat.redpack.utils;

import org.apache.log4j.Logger;
import org.wechat.common.entity.results.WechatResult;
import org.wechat.common.utils.ConvertXMLUtils;
import org.wechat.common.utils.Signature;
import org.wechat.common.utils.XStreamFactory;
import org.wechat.redpack.conn.RedPackConn;
import org.wechat.redpack.request.BaseRedPackRequest;
import org.wechat.redpack.request.GethbinfoRequest;
import org.wechat.redpack.request.GroupredpackRequest;
import org.wechat.redpack.request.RedpackRequest;
import org.wechat.redpack.respose.GethbinfoRespose;
import org.wechat.redpack.respose.HbListItem;
import org.wechat.redpack.respose.SendredpackRespose;

import com.thoughtworks.xstream.XStream;

/**
* 发送红包的工具类
* @author Andy
*
*/
public class SendRedPackUtils {
public static final String SUCCESS="SUCCESS"; //成功标识
public static final String FAIL="FAIL"; //失败标识
private static Logger log = Logger.getLogger(SendRedPackUtils.class);

/**
* 发送红包方法
* @param request 发送红包的请求对象实体
* @param apikey 商户中的apikey
* @param certPath api证书路径
* @param macid 商户号
* @return
*/
private String sendRedUtils(BaseRedPackRequest request,String apikey,String certPath,String macid){
String result="";
String sign = Signature.getSignForObject(request,apikey);
request.setSign(sign);
XStream st = XStreamFactory.initSplitLine();
st.alias("xml",RedpackRequest.class);
String data = st.toXML(request);
result =RedPackConn.sendRedInfo(data, certPath, macid);
log.info("sendRedUtils:["+result+"]");
return result;
}

/**
* 发送红包(简化结果集后的方法)
* @param request 发送红包的请求对象
* @param apikey apikey
* @param certPath 正式路径
* @param macId 商户号
* @return result.isSusccess==true 则表示为发送成功,否则为发送失败, result.msg 为提示信息,result.obj为 request对象
*/
public WechatResult sendRed(RedpackRequest request,String apikey,String certPath,String macId){
String data = sendRedUtils(request,apikey,certPath,macId);
WechatResult result = convertResult(data,request);
return result;
}

/**
* 将返回值封装成WechatResult
* (应用于现金红包和裂变红包)
* @param respose 请求成功后返回的xml格式的数据包
* @param request 请求对象 (方便追踪)
* @return result.isSusccess==true 则表示为发送成功,否则为发送失败, result.msg 为提示信息,result.obj为 request对象
*/
private WechatResult convertResult(String data,BaseRedPackRequest request){
WechatResult result = new WechatResult();
SendredpackRespose respose = (SendredpackRespose) ConvertXMLUtils.getObjectFromXML(data, SendredpackRespose.class);
if(respose.getReturn_code().equalsIgnoreCase(SUCCESS)&&respose.getResult_code().equalsIgnoreCase(SUCCESS)){ //表示成功
result.setSuccess(true);
}
result.setMsg(respose.getReturn_msg());
result.setObj(request);
return result;
}

/**
* 发送裂变红包
* @param request 请求参数对象
* @param apiKey api密匙(注意此处是商户号中的密匙,并不是appkey)
* @param certPath 证书的路径
* @param macId 商户号
* @return result.isSusccess==true 则表示为发送成功,否则为发送失败, result.msg 为提示信息,result.obj为 request对象
*/
public WechatResult sendGroupRedPack(GroupredpackRequest request,String apiKey,String certPath,String macId){
String sign = Signature.getSignForObject(request,apiKey);
request.setSign(sign);
XStream xstream = XStreamFactory.initSplitLine();
xstream.alias("xml",GroupredpackRequest.class);
String data = xstream.toXML(request);
String resultData = RedPackConn.sendGroupRedBack(data, certPath, macId);
WechatResult result = convertResult(resultData,request);
return result;
}

/**
* 获取红包信息
* @param request 获取红包信息请求对象实体
* @param certPath 证书所在路径
* @param macId 商户号
* @return
*/
public WechatResult getHbInfo(GethbinfoRequest request,String certPath,String macId,String apiKey){
WechatResult result = new WechatResult();
String sign = Signature.getSignForObject(request,apiKey);
request.setSign(sign);
XStream st = XStreamFactory.initSplitLine();
st.alias("xml",GethbinfoRequest.class);
String data = st.toXML(request);
String strData =RedPackConn.getRedInfoList(data, certPath,macId);
System.out.println("data-->"+strData);
GethbinfoRespose respose = (GethbinfoRespose) ConvertXMLUtils.getObjectFromCollectionXML(strData,GethbinfoRespose.class,HbListItem.class);
System.out.println(respose.getAmount());
log.info(strData);
return result;
}
}

到这里,现金红包,裂变红包,获取红包信息,已经封装好,只需要调用SendRedPackUtils中的方法即可。

1.5 调用示例

这里是调用发送红包的示例方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void testHttpClientSendRedBack(){
RedpackRequest request = new RedpackRequest();
request.setAct_name(""); //名称
request.setClient_ip(""); //客户端ip
request.setMch_billno(""); //交易号
request.setMch_id(""); //商户号
request.setNonce_str(""); //随机字符串
request.setRe_openid(“”); //接收用户openid
request.setRemark(""); //备注
request.setSend_name(""); //用户名称
request.setTotal_amount(); //发送金额 分为单位
request.setTotal_num(); //红包数量
request.setWishing(""); //红包名称
request.setWxappid(""); //appid
SendRedPackUtils utils = new SendRedPackUtils();
WechatResult result = utils.sendRed(request,"apikey",“证书路径","商户id");
String jsonData = ConvertJsonUtils.toJsonString(result);
System.out.println(jsonData);
}

1.6 依赖库

由于在该项目中涉及到的接口比较多,有微信支付,微信现金红包,裂变红包,微信公众号相关接口,微信授权等,所以涉及到的jar比较多,我会一一为大家讲解。
commons-logging-1.1.3.jar
dom4j-1.6.1.jar
fastjson-1.1.15.jar
httpclient-4.3.4.jar
httpcore-4.3.2.jar
log4j.jar
xpp3-1.1.4c.jar
xstream-1.4.7.jar
百度网盘下载地址:
http://pan.baidu.com/s/1eSymCds

1.7 参考文献

微信支付官方文档
微信现金红包文档