(一)controller
@PostMapping("/sendBizRedPacket")
public AjaxResult sendRedPacket(@ApiParam(value = "用户openid") @RequestParam String openid) throws Exception {
// 1、订单号生成
String orderNo = getTransid();
Map<String, String> returnMap = new HashMap<>();
returnMap.put("mch_billno",orderNo);//订单号
returnMap.put("re_openid",openid);//用户openid
//发送红包
AjaxResult resultMap = wxPayService.sendRedPacket(returnMap);
return AjaxResult.success("发送成功!",resultMap);
}
(2)service
@Override
public AjaxResult
sendRedPacket(Map<String, String> returnMap) throws Exception {
Map<String, String> Map = new HashMap<>();
//保存订单信息
returnMap.put("mch_id",wxPaywebConfig.getMchID());//商户号
returnMap.put("send_name","srzp");//红包发送者名称
returnMap.put("wxappid",wxPaywebConfig.getAppID());//公众号appid
returnMap.put("total_amount","1");//付款金额,单位分
returnMap.put("total_num","1");//红包发放总个数
returnMap.put("wishing","thank");红包祝福语 最大长度128位
returnMap.put("client_ip","192.168.0.114");//调用接口的机器Ip地址
returnMap.put("act_name","asd");//活动名称
/* returnMap.put("remark","分享越多得到越多");*/
returnMap.put("nonce_str", WXPayRedUtil.getNonce_str());
/* returnMap.put("key",wxPaywebConfig.getKey());//随机字符串*/
//生成签名
Long time = System.currentTimeMillis() / 1000;
String timestamp = time.toString();
Map.put("wxappid",wxPaywebConfig.getAppID());//公众账号appid
Map.put("mch_id",wxPaywebConfig.getMchID());//商户号
Map.put("device_info","1");
Map.put("body","test");
Map.put("nonce_str",WXPayRedUtil.getNonce_str());
returnMap.put("sign", WXPayRedUtil.createLinkString(returnMap,wxPaywebConfig.getKey()));//生成签名
String reqStr = WXPayRedUtil.map2Xml(returnMap);
String resultXml = HttpRedRequest.sendPost(reqStr);
System.out.println("微信请求返回:"+resultXml);
//解析微信返回串 如果状态成功 则返回给前端
if (WXPayRedUtil.getReturnCode(resultXml) != null && WXPayRedUtil.getReturnCode(resultXml).equals("SUCCESS")) {
//成功
Map<String, Object> resultMap = new TreeMap<>(
new Comparator<String>() {
public int compare(String obj1, String obj2) {
// 升序排序
return obj1.compareTo(obj2);
}
});
resultMap.put("appId", wxPaywebConfig.getAppID());
resultMap.put("nonceStr", WXPayRedUtil.getNonce_str());//解析随机字符串
resultMap.put("package", reqStr);
resultMap.put("signType", "MD5");
resultMap.put("timeStamp", String.valueOf((System.currentTimeMillis() / 1000)));//时间戳
String paySign = WXPayRedUtil.getSign(resultMap);
resultMap.put("paySign", paySign);
return AjaxResult.success("resultMap",resultMap);
}else{
return AjaxResult.error(999, "微信请求支付失败");
}
}
(三)工具包(含map转xml文件、解析xml文件获取标签信息)
package com.kdm.project.wx.util;
import com.kdm.common.utils.security.Md5Utils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.*;
/**
* Create by 2021/4/1
*
* @Author GZS
*/
public class WXPayRedUtil {
private final Logger logger = LoggerFactory.getLogger(WXPayRedUtil.class);
//生成随机字符串
public static String getNonce_str() {
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 15; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
//map转xml 加上签名信息
public static String map2Xml(Map<String, String> map) throws UnsupportedEncodingException {
StringBuffer sb = new StringBuffer();
StringBuilder sb2 = new StringBuilder();
sb2.append("<xml>");
for (String key : map.keySet()) {
sb.append(key);
sb.append('=');
sb.append(map.get(key));
sb.append('&');
// sb2是用来做请求的xml参数
sb2.append("<" + key + ">");
// sb2.append("<![CDATA[" + map.get(key) + "]]>");
sb2.append(map.get(key));
sb2.append("</" + key + ">");
}
sb.append(System.getenv("signKey"));
/* String sign = Md5Utils.hash(sb.toString()).toUpperCase();
sb2.append("<sign>");
sb2.append(sign);
sb2.append("</sign>");*/
sb2.append("</xml>");
return sb2.toString();
}
//解析微信返回return_code SUCCESS或FILE
//根据微信返回resultXml再次生成签名
public static String getSign(Map<String, Object> map) {
StringBuffer sb = new StringBuffer();
for (String key : map.keySet()) {
sb.append(key);
sb.append('=');
sb.append(map.get(key));
sb.append('&');
}
sb.append(System.getenv("signKey"));
System.out.println("第二次签名内容:" + sb);
System.out.println("第二次签名SING:" + Md5Utils.hash(sb.toString()).toUpperCase());
return Md5Utils.hash(sb.toString()).toUpperCase();
}
//解析微信返回return_code SUCCESS或FILE
public static String getReturnCode(String resultXml) {
try {
Document document = DocumentHelper.parseText(resultXml); //将给定的内容解析为一个xml文档,并返回一个新的Dom Document对象
Element root = document.getRootElement();//获取根节点
// 下面开始读取
Element node=root.element("return_code");
String info = node.getStringValue();
return info;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//解析微信返回return_msg
public static String getReturn_msg(String resultXml) {
String nonceStr;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = dbf.newDocumentBuilder();
InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
org.w3c.dom.Document doc = builder.parse(inputStream); //
// 下面开始读取
org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
NodeList nl = root.getElementsByTagName("return_msg");
org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
org.w3c.dom.Node nd = el.getFirstChild();
nonceStr = nd.getNodeValue();
return nonceStr;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//解析微信返回appid
public static String getAppId(String resultXml) {
String nonceStr;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = dbf.newDocumentBuilder();
InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
org.w3c.dom.Document doc = builder.parse(inputStream); //
// 下面开始读取
org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
NodeList nl = root.getElementsByTagName("appid");
org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
org.w3c.dom.Node nd = el.getFirstChild();
nonceStr = nd.getNodeValue();
return nonceStr;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//解析微信返回mch_id
public static String getMchId(String resultXml) {
String nonceStr;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = dbf.newDocumentBuilder();
InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
org.w3c.dom.Document doc = builder.parse(inputStream); //
// 下面开始读取
org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
NodeList nl = root.getElementsByTagName("mch_id");
org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
org.w3c.dom.Node nd = el.getFirstChild();
nonceStr = nd.getNodeValue();
return nonceStr;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//解析微信返回nonce_str
public static String getNonceStr(String resultXml) {
String nonceStr;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = dbf.newDocumentBuilder();
InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
org.w3c.dom.Document doc = builder.parse(inputStream); //
// 下面开始读取
org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
NodeList nl = root.getElementsByTagName("nonce_str");
org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
org.w3c.dom.Node nd = el.getFirstChild();
nonceStr = nd.getNodeValue();
return nonceStr;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
//解析微信返回prepay_id
public static String getPrepayId(String resultXml) {
String nonceStr;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try {
builder = dbf.newDocumentBuilder();
InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
org.w3c.dom.Document doc = builder.parse(inputStream); //
// 下面开始读取
org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
NodeList nl = root.getElementsByTagName("prepay_id");
org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
org.w3c.dom.Node nd = el.getFirstChild();
nonceStr = nd.getNodeValue();
return nonceStr;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params, String keyee) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
String preStr = "";
String sign = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if ("sign".equals(key)) {
continue;
}
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
preStr = preStr + key + "=" + value;
} else {
preStr = preStr + key + "=" + value + "&";
}
}
String msg = preStr+"&key="+keyee;
sign = encryptMd5(msg.getBytes()).toUpperCase();
return sign;
}
/**
* 功能:Md5加密
*
* @param
* @return
*/
public static String encryptMd5(byte[] bytes) {
StringBuffer sb = new StringBuffer();
try {
byte[] md5Str = null;
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(bytes);
md5Str = mdTemp.digest();
for (int i = 0; i < md5Str.length; i++) {
sb.append(Integer.toHexString(
(0x000000ff & md5Str[i]) | 0xffffff00).substring(6));
}
} catch (Exception ex) {
ex.printStackTrace();
}
return sb.toString();
}
/**
* MD5加密
* @param
* @param keys
* @return
*/
public static String createSign(Map<String, String> params, String keys){
/* String sign = "";
try {
List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(params.entrySet());
// 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
infoIds.sort(new Comparator<Map.Entry<String, String>>() {
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
return (o1.getKey()).compareTo(o2.getKey());
}
});
// 将参数以参数名的字典升序排序
Map<String, Object> sortParams = new TreeMap<String, String>(params);
// 构造签名键值对的格式
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> item : sortParams) {
if (item.getKey() != null || item.getKey() != "") {
String key = item.getKey();
String val = (String) item.getValue();
if (!(val == "" || val == null)) {
sb.append(key + "=" + val + "&");
}
}
}
String msg = sb.substring(0,sb.length()-1).toString()+keys;//sb.substring(0,sb.length()-1).toString():截取最后一个&
//String result = (sb.toString().length()-1)+keys;
System.out.println("================accsii排序==============="+msg);
sign = Md5Utils.hash(msg).toUpperCase();//MD5加密,toUpperCase():大小写转换
System.out.println("================signMD5加密==============="+sign);
} catch (Exception e) {
return null;
}*/
return "sign";
}
}
(四)向指定URL发送post请求(附带证书post请求)
package com.kdm.project.wx.util;
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.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyStore;
import java.util.List;
import java.util.Map;
/**
* Create by 2021/4/1
*
* @Author GZS
*/
public class HttpRedRequest {
/**
* 向指定URL发送GET方法的请求
*
* @param url 发送请求的URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return URL 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
// 打开和URL之间的连接
URLConnection connection = realUrl.openConnection();
// 设置通用的请求属性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立实际的连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍历所有的响应头字段
for (String key : map.keySet()) {
System.out.println(key + "--->" + map.get(key));
}
// 定义 BufferedReader输入流来读取URL的响应
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送GET请求出现异常!" + e);
e.printStackTrace();
}
// 使用finally块来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
/**
* 向指定 URL 发送POST方法的请求
* 带证书验证
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String param) throws Exception {
//1.带证书
KeyStore keyStore = KeyStore.getInstance("PKCS12");//证书类型
FileInputStream instream = new FileInputStream(new File("E:\\1602640894_20201102_cert\\apiclient_cert.p12"));//读取文件
try {
keyStore.load(instream, "1602640894".toCharArray());
} finally {
instream.close();
}
//信任自己的ca和所有的签名证书
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, "1602640894".toCharArray())
.build();
//只允许TLSv1协议
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
//2.微信小程序
String mess = "";
try {
HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/sendminiprogramhb");
System.out.println("executing request" + httppost.getRequestLine());
httppost.setEntity(new StringEntity(param.trim()));
System.out.println("param+" + param);
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
String text = "";//返回内容
while ((text = bufferedReader.readLine()) != null) {
System.out.println(text);
mess+=text;
}
System.out.println("mess="+mess);
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
return mess;
}
}
(五)使用dom4J操作dom文件获取返回信息
dom4j
dom4j
1.6.1