package com.project.common.utils;
|
|
import com.project.common.constant.WechatConstants;
|
import com.project.common.dto.UnifiedOrderRequestBean;
|
import com.project.common.dto.UnifiedOrderResponseBean;
|
import com.project.common.dto.WxPayDataDTO;
|
import com.project.common.exception.base.BaseException;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.http.HttpEntity;
|
import org.apache.http.ParseException;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
import org.apache.http.conn.ssl.TrustStrategy;
|
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.SSLContextBuilder;
|
import org.apache.http.util.EntityUtils;
|
import org.jdom2.Document;
|
import org.jdom2.Element;
|
import org.jdom2.input.SAXBuilder;
|
|
import javax.net.ssl.SSLContext;
|
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletResponse;
|
import javax.xml.bind.JAXBContext;
|
import javax.xml.bind.JAXBException;
|
import javax.xml.bind.Marshaller;
|
import javax.xml.bind.Unmarshaller;
|
import java.beans.BeanInfo;
|
import java.beans.Introspector;
|
import java.beans.PropertyDescriptor;
|
import java.io.*;
|
import java.lang.reflect.Field;
|
import java.lang.reflect.Method;
|
import java.security.MessageDigest;
|
import java.security.NoSuchAlgorithmException;
|
import java.security.cert.CertificateException;
|
import java.security.cert.X509Certificate;
|
import java.util.*;
|
|
/**
|
* 微信支付工具类
|
**/
|
@Slf4j
|
public class WxPayUtils {
|
/**
|
* 获取签名(MD5加密)
|
* @throws Exception
|
*/
|
public static String getSign(Object bean, String mchSecret){
|
String signTemp = null;
|
Map<String, Object> fieldMap = new HashMap<>();
|
// 获取对象bean中的所有变量
|
Field[] fields = bean.getClass().getDeclaredFields();
|
// 提取bean中的所有变量名及变量值
|
try {
|
for (Field field : fields) {
|
// 获取原来的变量访问控制权限
|
boolean accessFlag = field.isAccessible();
|
// 修改访问权限
|
field.setAccessible(true);
|
// 获取变量值
|
Object value = field.get(bean);
|
if (value == null) {
|
continue;
|
} else {
|
fieldMap.put(field.getName(), value);
|
}
|
// 恢复访问控制权限
|
field.setAccessible(accessFlag);
|
}
|
//字符串拼接
|
Set<String> keySet = fieldMap.keySet();
|
String[] keyArray = keySet.toArray(new String[keySet.size()]);
|
Arrays.sort(keyArray);
|
StringBuilder sb = new StringBuilder();
|
for (String key : keyArray) {
|
if (fieldMap.get(key).toString().trim().length() > 0) // 参数值为空,则不参与签名
|
sb.append(key.replaceAll("[A-Z]", "_$0").toLowerCase()).append("=").append(fieldMap.get(key).toString().trim()).append("&");
|
}
|
//商户支付密钥
|
sb.append("key=").append(mchSecret);
|
// // system.out.println(sb);
|
signTemp = ToMD5Upper(sb.toString());
|
}catch (Exception e){
|
e.printStackTrace();
|
}
|
return signTemp;
|
}
|
|
/**
|
*
|
* @param inStr
|
* @return
|
*/
|
public static String ToMD5Upper(String inStr){
|
StringBuffer hexString = null;
|
byte[] defaultBytes = inStr.getBytes();
|
try {
|
MessageDigest algorithm = MessageDigest.getInstance("MD5");
|
algorithm.reset();
|
algorithm.update(defaultBytes);
|
byte messageDigest[] = algorithm.digest();
|
|
hexString = new StringBuffer();
|
for (int i = 0; i < messageDigest.length; i++) {
|
if (Integer.toHexString(0xFF & messageDigest[i]).length() == 1) {
|
hexString.append(0);
|
}
|
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
|
}
|
messageDigest.toString();
|
inStr = hexString + "";
|
} catch (NoSuchAlgorithmException nsae) {
|
nsae.printStackTrace();
|
}
|
return hexString.toString().toUpperCase();
|
}
|
|
/**
|
* 生成随机字符串
|
* @param length
|
* @return
|
*/
|
public static String getRandomString(int length){
|
//1.定义一个字符串(A-Z,a-z,0-9)即62个数字字母
|
String str = "zxcvbnmlkjhgfdsaqwertyuiopQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
|
//2.由Random生成随机数
|
Random random = new Random();
|
StringBuffer sb = new StringBuffer();
|
//3. 长度为几就循环几次
|
for(int i = 0; i < length; ++i){
|
//从62个的数字或字母中选择
|
int number = random.nextInt(62);
|
//将产生的数字通过length次承载到sb中
|
sb.append(str.charAt(number));
|
}
|
//将承载的字符转换成字符串
|
return sb.toString();
|
}
|
|
/**
|
* 实体类转换XML
|
* @param bean 实体类对象
|
* @return xml字符串
|
* @throws JAXBException xml格式转换异常
|
*/
|
public static String beanToStr(Object bean) throws JAXBException {
|
if (bean == null) {
|
return "";
|
}
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
JAXBContext context = JAXBContext.newInstance(bean.getClass());
|
Marshaller marshaller = context.createMarshaller();
|
marshaller.marshal(bean, baos);
|
|
String result = new String(baos.toByteArray());
|
// // system.out.println("xmlStr : " + result);
|
return result;
|
}
|
|
/**
|
* XML转换实体
|
* @param xmlStr
|
* @param clazz
|
* @param <T>
|
* @return
|
* @throws JAXBException
|
*/
|
@SuppressWarnings("unchecked")
|
public static <T> T strToBean(String xmlStr, Class<T> clazz) throws JAXBException {
|
JAXBContext context = JAXBContext.newInstance(clazz);
|
Unmarshaller unmarshaller = context.createUnmarshaller();
|
T bean = (T) unmarshaller.unmarshal(new StringReader(xmlStr));
|
return bean;
|
}
|
|
/**
|
* 向指定URL发送POST方法的请求,请求参数为json或xml格式,获取响应结果
|
* @param url
|
* @param paramStr
|
* @return
|
*/
|
public static String sendPostBodyString(String url, String paramStr) {
|
if (url == null || url.trim().length() == 0) {
|
try {
|
throw new Exception("url不能为空");
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
}
|
//HttpPost httpPost = new HttpPost(url);
|
CloseableHttpClient httpClient = null;
|
CloseableHttpResponse response = null;
|
SSLContext sslContext = null;
|
try {
|
/*httpPost.setEntity(new StringEntity(paramStr, "UTF-8"));
|
// 创建默认的httpClient实例.
|
httpClient = HttpClients.createDefault();
|
// 执行请求
|
response = httpClient.execute(httpPost);*/
|
HttpPost httpPost = new HttpPost(url);
|
StringEntity reqEntity = new StringEntity(paramStr, "UTF-8");
|
// 设置类型
|
reqEntity.setContentType("application/x-www-form-urlencoded");
|
httpPost.setEntity(reqEntity);
|
// 创建默认的httpClient实例.
|
httpClient = createIgnoreSSLHttpClient();
|
if (httpClient == null) {
|
log.error("HttpClient create fail.");
|
return "HttpClient create fail.";
|
}
|
// 执行请求
|
response = httpClient.execute(httpPost);
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return getString(response);
|
}
|
|
public static String getString(CloseableHttpResponse response) {
|
String reString = null;
|
HttpEntity entity = response.getEntity();
|
try {
|
reString = EntityUtils.toString(entity, "UTF-8");
|
} catch (ParseException e) {
|
e.printStackTrace();
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
return reString;
|
}
|
|
/**
|
* 实体类转Map
|
* @param obj
|
* @return
|
*/
|
public static Map<String, Object> convertBeanToMap(Object obj) {
|
if (obj == null) {
|
return null;
|
}
|
Map<String, Object> map = new HashMap<String, Object>();
|
try {
|
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
|
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
|
for (PropertyDescriptor property : propertyDescriptors) {
|
String key = property.getName();
|
//过滤class属性
|
if (!key.equals("class")) {// 得到property对应的getter方法
|
Method getter = property.getReadMethod();
|
Object value = getter.invoke(obj);
|
if(null==value){
|
map.put(key,"");
|
}else{
|
map.put(key,value);
|
}
|
}
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return map;
|
}
|
|
|
/**
|
* 二次签名
|
* @param map
|
* @return
|
* @throws Exception
|
*/
|
public static String getSign2(Map<String, Object> map){
|
StringBuilder sb = new StringBuilder();
|
sb.append("appId=" + map.get("appId")).append("&nonceStr=" + map.get("nonceStr"));
|
sb.append("&package=prepay_id=" + map.get("prepayId")).append("&signType=MD5");
|
sb.append("&timeStamp=" + map.get("timeStamp"));
|
//商户支付密钥
|
sb.append("&key=").append(map.get("mchSecret"));
|
// // system.out.println(sb);
|
|
return ToMD5Upper(sb.toString());
|
}
|
|
/**
|
* 将微信的返回结果进行拼接
|
* @param request
|
* @return
|
*/
|
public static String getResponseStream(HttpServletRequest request){
|
StringBuffer xmlSb = new StringBuffer();
|
try {
|
BufferedReader reader = request.getReader();
|
String line = "";
|
while ((line = reader.readLine()) != null) {
|
xmlSb.append(line);
|
}
|
request.getReader().close();
|
} catch (Exception e) {
|
throw new BaseException(e.getMessage());
|
}
|
return xmlSb.toString();
|
}
|
|
/**
|
* 告诉微信回调成功,且处理完数据了
|
*/
|
public static void callBackWeChat(HttpServletResponse response){
|
String resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
|
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
|
BufferedOutputStream out = null;
|
try {
|
out = new BufferedOutputStream(
|
response.getOutputStream());
|
out.write(resXml.getBytes());
|
out.flush();
|
out.close();
|
} catch (IOException e) {
|
e.printStackTrace();
|
}
|
}
|
|
/**
|
* 调起支付,返回给前端的数据
|
* @param unifiedOrder
|
* @param mchSecret
|
* @return
|
*/
|
public static WxPayDataDTO returnWxPayDataDTO(UnifiedOrderRequestBean unifiedOrder, String mchSecret){
|
WxPayDataDTO wxPayDataDTO = new WxPayDataDTO();
|
try{
|
String paramXml = WxPayUtils.beanToStr(unifiedOrder);
|
log.info("----------paramXml-----------" + paramXml);
|
String result = WxPayUtils.sendPostBodyString(WechatConstants.UNIFIED_ORDER, paramXml);
|
log.info("----------result-----------" + result);
|
//调用统一下单,接收响应数据
|
UnifiedOrderResponseBean unifiedOrderResponse = WxPayUtils.strToBean(result, UnifiedOrderResponseBean.class);
|
log.info("----------unifiedOrderResponse-----------" + unifiedOrderResponse);
|
String returnCode = "SUCCESS";
|
if (unifiedOrderResponse.getReturnCode().equals(returnCode)) {
|
// 数据组合再次签名
|
Long timeStamp = System.currentTimeMillis();
|
Map<String, Object> sign2Map = new HashMap<String, Object>();
|
sign2Map.put("appId", unifiedOrder.getAppid());
|
sign2Map.put("nonceStr", unifiedOrderResponse.getNonceStr());
|
sign2Map.put("signType", WechatConstants.SIGN_TYPE);
|
sign2Map.put("prepayId", unifiedOrderResponse.getPrepayId());
|
sign2Map.put("timeStamp", timeStamp);
|
sign2Map.put("mchSecret", mchSecret);
|
//生成二次签名
|
String sign2 = WxPayUtils.getSign2(sign2Map).toUpperCase();
|
|
wxPayDataDTO.setAppid(unifiedOrder.getAppid());
|
wxPayDataDTO.setTimeStamp(String.valueOf(timeStamp));
|
wxPayDataDTO.setNonceStr(unifiedOrderResponse.getNonceStr());
|
wxPayDataDTO.setPackages("prepay_id=" + unifiedOrderResponse.getPrepayId());
|
wxPayDataDTO.setPrepayId(unifiedOrderResponse.getPrepayId());
|
wxPayDataDTO.setSign(unifiedOrderResponse.getSign());
|
wxPayDataDTO.setPaySign(sign2);
|
wxPayDataDTO.setSignType(WechatConstants.SIGN_TYPE);
|
}else {
|
log.info("----------微信支付错误代码:" + unifiedOrderResponse.getResultCode());
|
log.info("----------微信支付错误信息:" + unifiedOrderResponse.getReturnMsg());
|
}
|
}catch (Exception e){
|
e.printStackTrace();
|
}
|
return wxPayDataDTO;
|
}
|
|
/**
|
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
|
*
|
* @param strxml
|
* @return
|
* @throws
|
* @throws IOException
|
*/
|
public static Map doXMLParse(String strxml) throws Exception {
|
if (null == strxml || "".equals(strxml)) {
|
return null;
|
}
|
|
Map m = new HashMap();
|
InputStream in = String2Inputstream(strxml);
|
SAXBuilder builder = new SAXBuilder();
|
Document doc = builder.build(in);
|
Element root = doc.getRootElement();
|
List list = root.getChildren();
|
Iterator it = list.iterator();
|
while (it.hasNext()) {
|
Element e = (Element) it.next();
|
String k = e.getName();
|
String v = "";
|
List children = e.getChildren();
|
if (children.isEmpty()) {
|
v = e.getTextNormalize();
|
} else {
|
v = getChildrenText(children);
|
}
|
|
m.put(k, v);
|
}
|
//关闭流
|
in.close();
|
return m;
|
}
|
|
/**
|
* 获取子结点的xml
|
*
|
* @param children
|
* @return String
|
*/
|
public static String getChildrenText(List children) {
|
StringBuffer sb = new StringBuffer();
|
if (!children.isEmpty()) {
|
Iterator it = children.iterator();
|
while (it.hasNext()) {
|
Element e = (Element) it.next();
|
String name = e.getName();
|
String value = e.getTextNormalize();
|
List list = e.getChildren();
|
sb.append("<" + name + ">");
|
if (!list.isEmpty()) {
|
sb.append(getChildrenText(list));
|
}
|
sb.append(value);
|
sb.append("</" + name + ">");
|
}
|
}
|
|
return sb.toString();
|
}
|
|
public static InputStream String2Inputstream(String str) {
|
return new ByteArrayInputStream(str.getBytes());
|
}
|
|
|
|
public static CloseableHttpClient createIgnoreSSLHttpClient() {
|
try {
|
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
|
public boolean isTrusted(X509Certificate[] chain,
|
String authType) throws CertificateException {
|
return true;
|
}
|
}).build();
|
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
|
return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
return null;
|
}
|
}
|