微信公众平台普通红包发放

业务需求:在后台管理系统为微信用户发放红包,弄了好几天总算搞定了,需要注意的是获取签名部分,流程大体如下:

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
201
202
203
204
205
206
207
208
209
210
//红包业务方法
public int sendRedpack(HttpServletRequest request,UserAverage userAverage) throws Exception {
WechatInfo wechat = getWechatInfo(request);
WechatInfo wechatInfo = wechatInfoService.queryWechatinfo(wechat.getAppid());
String sendName =wechatInfo.getWxname();
//微信证书路径
String certPath =this.getClass().getResource("/").getPath()+"xxxxx/apiclient_cert.p12";
certPath = URLDecoder.decode(certPath,"UTF-8");//证书路径解码
logger.info("证书路径:"+certPath);
//商户id
String wxappid = wechatInfo.getAppid();
// 微信支付分配的商户号
String partner = wechatInfo.getPartnerid();
//API密钥
String paternerKey =wechatInfo.getPartnerkey();
/**
* 发送普通红包
*/
boolean isSend = sendredpack(wechatInfo,request, "100", "1", "恭喜您获得竞赛奖金!",
"竞赛答题活动", "竞赛奖金", userAverage.getOpenid(),
partner, wxappid, sendName, paternerKey, certPath);
if(!isSend){
return 0;
}
return 1;
}
// 主封装方法
// * @param request 获取IP
// * @param total_amount 付款现金(单位分)
// * @param total_num 红包发放总人数
// * @param wishing 红包祝福语
// * @param act_name 活动名称
// * @param remark 备注
// * @param reOpenid 用户openid
// * @param partner 商户号
// * @param wxappid 公众账号appid
// * @param sendName 商户名称
// * @param paternerKey 商户签名key
// * @param certPath 证书路径
// * @return
// */
public static boolean sendredpack(WechatInfo wechatInfo,HttpServletRequest request,String total_amount,String total_num,String wishing,String act_name,String remark,String reOpenid,String partner,String wxappid,String sendName,String paternerKey,String certPath) throws Exception{
// 商户订单号
String mchBillno = RedPackUtil.createBillNo(wechatInfo);
String ip ="xxx.xx.xxx.xx";//服务器IP
Map<String, String> params = new HashMap<String, String>();
// 随机字符串
params.put("nonce_str", RedPackUtil.createNonce_str());
// 商户订单号
params.put("mch_billno", mchBillno);
// 商户号
params.put("mch_id", partner);
// 公众账号ID
params.put("wxappid", wxappid);
// 商户名称
params.put("send_name", sendName);
// 用户OPENID
params.put("re_openid", reOpenid);
// 付款现金(单位分)
params.put("total_amount", total_amount);
// 红包发放总人数
params.put("total_num", total_num);
// 红包祝福语
params.put("wishing", wishing);
// 终端IP
params.put("client_ip", ip);
// 活动名称
params.put("act_name", act_name);
// 备注
params.put("remark", remark);
//创建签名
String sign = RedPackUtil.redSignal(params, paternerKey);
logger.info("md5签名:"+sign);
params.put("sign", sign);
String xmlResult = RedPackUtil.sendRedPack(params, certPath, partner);//执行发送红包
logger.info("发送红包返回结果:"+xmlResult.toString());
Map<String, String> result = XMLUtil.xml2Map(xmlResult);
logger.info("红包参数:"+result.toString());
//此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
String return_code = result.get("return_code");
//业务结果
String result_code = result.get("result_code");
if (StringUtils.isNotBlank(return_code) && "SUCCESS".equals(return_code)) {
logger.info("通信成功return_code:"+return_code);
if (StringUtils.isNotBlank(result_code) && "SUCCESS".equals(result_code)) {
logger.info("发送成功result_code:"+result_code);
return true;
}else{
logger.info("发送失败result_code:"+result_code);
}
}else{
logger.info("通信失败return_code:"+return_code);
}
return false;
}
/**
* 生成商户订单号
* @param mch_id 商户号
* @param userId 该用户的userID
* @return
*/
public static String createBillNo(WechatInfo wechatInfo){
//组成: mch_id+yyyymmdd+10位一天内不能重复的数字
//10位一天内不能重复的数字实现方法如下:
//因为每个用户绑定了userId,他们的userId不同,加上随机生成的(10-length(userId))可保证这10位数字不一样
Date dt=new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyymmdd");
String nowTime= df.format(dt);
int length = 10 ;
return wechatInfo.getPartnerid() + nowTime + getRandomNum(length);
}
/**
* 生成红包参数随机数nonce_str
* @return
*/
public static String createNonce_str(){
return System.currentTimeMillis()/1000+getRandomNum(5);
}
/**
* 生成特定位数的随机数字
* @param length
* @return
*/
public static String getRandomNum(int length) {
String val = "";
Random random = new Random();
for (int i = 0; i < length; i++) {
val += String.valueOf(random.nextInt(10));
}
return val;
}
/**
* @Title: redSignal
* @Description: 发送红包签名生成
* @param @param params
* @param @return 设定文件
* @return String 返回类型
* @throws
*/
public static String redSignal(Map<String, String> params,String paternerKey) {
SortedMap<String, String> packageParams = new TreeMap<String, String>();
for (Map.Entry<String, String> m : params.entrySet()) {
packageParams.put(m.getKey(), m.getValue().toString());
}
StringBuffer sb = new StringBuffer();
Set<?> es = packageParams.entrySet();
Iterator<?> it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (!StringUtils.isEmpty(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + paternerKey);
System.out.println("未加密签名:"+sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(),"UTF-8").toUpperCase();
return sign;
}
private static String sendRedPackUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
/**
* 发送红包
* @param params 请求参数
* @param certPath 证书文件目录
* @param partner 证书密码
* @return {String}
* @throws Exception
*/
public static String sendRedPack(Map<String, String> params, String certPath, String partner) throws Exception {
return doSend(sendRedPackUrl, XMLUtil.map2Xmlstring(params), certPath, partner);
}
public static String doSend(String url, String data , String certPath , String partner) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File(certPath));//P12文件目录
try {
keyStore.load(instream, partner.toCharArray());//这里写密码..默认是你的MCHID
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, partner.toCharArray())//这里也是写密码的
.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
HttpPost httpost = new HttpPost(url); // 设置响应头信息
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
String jsonStr = toStringInfo(response.getEntity(),"UTF-8");
EntityUtils.consume(entity);
return jsonStr;
} finally {
response.close();
}
} finally {
httpclient.close();
}
}