package cn.obp_code.common.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
import javax.net.ssl.SSLContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
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.protocol.HTTP;
import org.apache.http.util.Args;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* 微信企业活动发红包工具类
* 包含md5,获取随机字符串,签名,post请求等
* @author ldb 2019年8月19日 下午2:01:24
*
*/
@SuppressWarnings("deprecation")
public class WeChatPayUtils {
public static void main(String[] args) {
// String key = "";
// String url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
// Map<String, String> map = new HashMap<String, String>();
// map.put("nonce_str", getUUID());//32位随机字符串,微信端避免混淆作用。
// map.put("mch_billno", "xxx");//商户订单号,如有需要会从前台传过来,不需要随机生成一个就行
// map.put("mch_id", "xxxxxxxxxxxxxxxx");//商户号
// map.put("wxappid", "xxx");//商户appid
// map.put("send_name", "阿里巴巴以后的子公司");//用户名
// map.put("re_openid", "oDcZ6uPput41yWYtf_ul3ylJc2kU");//用户openid
// map.put("total_amount", "1");//付款金额单位分
// map.put("total_num", "1");//红包发送总人数
// map.put("wishing", "恭喜发财");//红包祝福语
// map.put("client_ip", "127.0.0.1");//ip地址
// map.put("act_name", "抽奖送现金红包");//活动名称
// map.put("remark", "备注");//备注
map.put("sign", createSign(map));//签名
//
Map<String, String> result = "";
// try {
// map = giveRedEnvelopes(url, "",map);
// } catch (Exception e) {
// e.printStackTrace();
// }
}
/**
* 发红包主方法
* @param url 调用微信的地址
* @param appKey 你的密钥,
* @param map 官网上要求传入的必填的字段放入此map
* @return
*/
public static Map<String, String> giveRedEnvelopes(String url,String appKey,Map<String,String> map) {
//加入随机数,微信为了要求保证每次签名不可预测,是32位随机的就行
map.put("nonce_str", getUUID());
//拼接参数
String prestr = MD5.createLinkString(map); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
//设置key
appKey = "&key=" + appKey; // 商户支付密钥
//MD5签名
String mysign = MD5.sign(prestr, appKey, "utf-8").toUpperCase();
String mysign1 = MD5.sign(prestr, appKey, "utf-8").toUpperCase();
map.put("sign", mysign);
//map转xml
String mapXml = mapToXml(map);
// System.out.println(mapXml);
//开始发红包post请求微信
Map<String, String> resultmap = new HashMap<String, String>();
try {
resultmap = doSendMoney(url, mapXml,map);
} catch (Exception e) {
e.printStackTrace();
}
// 返回状态码
String result_code = resultmap.get("result_code");
System.out.println("微信返回执行结果状态码:" + result_code);
System.out.println("微信返回结果:" + resultmap);
return resultmap;
}
/**
* 生成32位编码
* @return string
*/
public static String getUUID(){
String uuid = UUID.randomUUID().toString().trim().replaceAll("-", "");
return uuid;
}
//map转xml,微信个别功能请求只支持xml参数
public static String mapToXml(Map<String, String> map){
String xml = "<xml>";
Set<String> set = map.keySet();
Iterator<String> i = set.iterator();
while(i.hasNext()){
String str = i.next();
xml+="<"+str+">"+"<![CDATA["+map.get(str)+"]]>"+"</"+str+">";
}
xml+="</xml>";
return xml;
}
//签名成功后发送post请求。
public static Map<String, String> doSendMoney(String url, String data,Map<String,String> map) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//文件存放位置和文件名,此文件为微信开发平台下载的密钥证书,微信发红包和微信查询红包发送状态要求使用此证书
InputStream instream = WeChatPayUtils.class.getResourceAsStream("/wxpaycert/apiclient_cert.p12");
try {
// keyStore.load(instream, mch_id.toCharArray());//这里写密码..默认是你的mch_id
keyStore.load(instream, map.get("mch_id").toCharArray());//这里写密码..默认是你的mch_id
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, map.get("mch_id").toCharArray())//这里也是写密码的..默认是你的mch_id
.build();
// Allow TLSv1 protocol only
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();
Map<String, String> retMap = new HashMap<String, String>();
if (entity != null) {
// 从request中取得输入流
InputStream inputStream = entity.getContent();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList) {
retMap.put(e.getName(), e.getText());
}
// 释放资源
inputStream.close();
inputStream = null;
}
// String jsonStr = toStringInfo(response.getEntity(),"UTF-8"); //返回值根据自己情况转成所需格式
// System.out.println(jsonStr);
//微信返回的报文时GBK,直接使用httpcore解析乱码
// String jsonStr = EntityUtils.toString(response.getEntity(),"UTF-8");
EntityUtils.consume(entity);
return retMap;
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
/**
* 查询红包主方法,目前与发红包的一样,就是把需要的参数传进来,然后拼接,签名,发post请求,无其他特殊逻辑
* @param url
* @param appKey
* @param map
* @return
*/
public static Map<String, String> queryRedEnvelopes(String url,String appKey,Map<String,String> map){
//加入随机数,微信为了要求保证每次签名不可预测,是32位随机的就行
map.put("nonce_str", getUUID());
//拼接参数
String prestr = MD5.createLinkString(map); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
//设置key
appKey = "&key=" + appKey; // 商户支付密钥
//MD5签名
String mysign = MD5.sign(prestr, appKey, "utf-8").toUpperCase();
String mysign1 = MD5.sign(prestr, appKey, "utf-8").toUpperCase();
map.put("sign", mysign);
//map转xml
String mapXml = mapToXml(map);
System.out.println(mapXml);
//开始发红包post请求微信
Map<String, String> resultmap = new HashMap<String, String>();
try {
resultmap = doSendMoney(url, mapXml,map);
} catch (Exception e) {
e.printStackTrace();
}
// 返回状态码
String return_code = resultmap.get("return_code");
System.out.println("微信返回执行结果状态码:" + return_code);
System.out.println("微信返回结果:" + resultmap);
return resultmap;
}
}
1,按微信开发文档https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 准备好参数
类似此图。
2,调用上边的类
3,接到返回值然后就可以根据返回发送红包的结果处理相应的逻辑了
下边为上边发红包方法中需要的一个MD5工具类:(加个密,拼接个字符串,生成个随机数啥的)
package cn.obp_code.common.utils;
import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.codec.digest.DigestUtils;
public class MD5 {
/**
* 签名字符串
* @param text 需要签名的字符串
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static String sign(String text, String key, String input_charset) {
text = text + key;
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
/**
* 签名字符串
* @param text 需要签名的字符串
* @param sign 签名结果
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static boolean verify(String text, String sign, String key, String input_charset) {
text = text + key;
String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
if(mysign.equals(sign)) {
return true;
}
else {
return false;
}
}
/**
* @param content
* @param charset
* @return
* @throws SignatureException
* @throws UnsupportedEncodingException
*/
private static byte[] getContentBytes(String content, String charset) {
if (charset == null || "".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
/**
* 生成6位或10位随机数
* param codeLength(多少位)
* @return
*/
public static String createCode(int codeLength) {
String code="";
for(int i=0; i<codeLength; i++) {
code += (int)(Math.random() * 9);
}
return code;
}
/**
* 除去数组中的空值和签名参数
* @param sArray 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = new HashMap<String, String>();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
}