经过最近一段时间的学习使用,我对微信的支付和分享都相当熟悉,前几天冒出个想法为什么不能将这些封装成jar包,以后在自己需要支付开发的时候这部分代码肯定不用在写了,在微信大版本不变的情况下能一直使用,好嘛,有了想法当然也就有了动力,话不多少直接开搞。
一、介绍整理出来的项目
先来个项目整体结构
一)bean包
bean包主要包含8个java类,分别是
1. BaseAccessToken(基础access_token对象),
2. JSAPIConfig(JSAPI使用的配置信息),
3. JSAPITicket(微信jsapi_ticket实体类),
4. Oauth2AccessToken(网页授权accessToken),
5. PayCallback(回调结束返回微信内容),
6. UnifiedOrder(统一下单对象),
7. WechatJSShareBean(微信分享 微信好友/朋友圈分享…模型),
8. WechatUserInfo(微信关注用户基本信息)
二)util包
1.ClassLoaderUtils:类加载起,主要在解析配置文件时使用
/*
* $Id: ClassLoaderUtils.java 471756 2006-11-06 15:01:43Z husted $
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.wtp.wechat.util;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* This class is extremely useful for loading resources and classes in a fault tolerant manner
* that works across different applications servers.
* <p/>
* It has come out of many months of frustrating use of multiple application servers at Atlassian,
* please don't change things unless you're sure they're not going to break in one server or another!
*
*/
public class ClassLoaderUtils {
/**
* Load a given resource.
* <p/>
* This method will try to load the resource using the following methods (in order):
* <ul>
* <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
* <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
* <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
* </ul>
*
* @param resourceName The name of the resource to load
* @param callingClass The Class object of the calling object
*/
public static URL getResource(String resourceName, Class callingClass) {
URL url = null;
url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
if (url == null) {
url = ClassLoaderUtils.class.getClassLoader().getResource(resourceName);
}
if (url == null) {
url = callingClass.getClassLoader().getResource(resourceName);
}
return url;
}
/**
* This is a convenience method to load a resource as a stream.
* <p/>
* The algorithm used to find the resource is given in getResource()
*
* @param resourceName The name of the resource to load
* @param callingClass The Class object of the calling object
*/
public static InputStream getResourceAsStream(String resourceName, Class callingClass) {
URL url = getResource(resourceName, callingClass);
try {
return (url != null) ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}
/**
* Load a class with a given name.
* <p/>
* It will try to load the class in the following order:
* <ul>
* <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
* <li>Using the basic {@link Class#forName(java.lang.String) }
* <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
* <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
* </ul>
*
* @param className The name of the class to load
* @param callingClass The Class object of the calling object
* @throws ClassNotFoundException If the class cannot be found anywhere.
*/
public static Class loadClass(String className, Class callingClass) throws ClassNotFoundException {
try {
return Thread.currentThread().getContextClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
try {
return Class.forName(className);
} catch (ClassNotFoundException ex) {
try {
return ClassLoaderUtils.class.getClassLoader().loadClass(className);
} catch (ClassNotFoundException exc) {
return callingClass.getClassLoader().loadClass(className);
}
}
}
}
/**
* Prints the current classloader hierarchy - useful for debugging.
*/
public static void printClassLoader() {
System.out.println("ClassLoaderUtils.printClassLoader");
printClassLoader(Thread.currentThread().getContextClassLoader());
}
/**
* Prints the classloader hierarchy from a given classloader - useful for debugging.
*/
public static void printClassLoader(ClassLoader cl) {
System.out.println("ClassLoaderUtils.printClassLoader(cl = " + cl + ")");
if (cl != null) {
printClassLoader(cl.getParent());
}
}
public static void main(String s[]){
System.out.println(ClassLoaderUtils.getResource("", ClassLoaderUtils.class));
}
}
2.CommonUtil:工具类
package com.wtp.wechat.util;
import java.io.UnsupportedEncodingException;
import java.util.UUID;
/**
* @ClassName: CommonUtil
* @Description: 通用工具类
* @author tianpengw
* @date 2017年10月12日 下午5:09:46
*
*/
public class CommonUtil {
/**
*
* @Description: 字符串为null时转换为"",不为null时不作处理
* @author tianpengw
* @return String
*/
public static String filterStr(String obj){
if(null == obj || obj.toLowerCase().equals("null")){
return "";
}
return obj;
}
/**
*
* @Description: 判断字符串或者对象是否为null,或者字符串是否为""
* @author tianpengw
* @return String
*/
public static boolean isEmpty(Object obj){
if(null == obj || obj.toString().isEmpty()){
return true;
}
return false;
}
/**
*
* @Description: 获得一个UUID
* @author tianpengw
* @return String
*/
public static String getUUID(){
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
*
* @Description: 转换字符串的编码格式
* @author tianpengw
* @param str
* @return
* @throws UnsupportedEncodingException
*/
public static String coventToUTF8(String str) throws UnsupportedEncodingException{
return new String(str.trim().getBytes("ISO-8859-1"), "UTF-8");
}
}
3.HttpHelper:HTTP工具类
package com.wtp.wechat.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName: HttpHelper
* @Description: HTTP工具类
* @author tianpengw
* @date 2017年10月12日 上午9:43:37
*
*/
public class HttpHelper {
/**
*
* @Description: 获取客户端地址
* @author tianpengw
* @param request
* @return
*/
public static String getClientIP(HttpServletRequest request) {
String ipAddress = request.getHeader("x-forwarded-for");
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){
//根据网卡取本机配置的IP
InetAddress inet=null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress= inet.getHostAddress();
}
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
if(ipAddress.indexOf(",")>0){
ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
}
}
return ipAddress;
}
/**
*
* @Description: 发起Http请求
* @author tianpengw
* @param requestUrl 请求地址
* @param requestMethod 请求方式 GET/POST
* @param outputStr 请求参数,如果没有置 null
* @return
*/
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
return buffer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
*
* @Description: 根据req对象获得当前请求地址,带参数
* @author tianpengw
* @param req
* @return
*/
public static String getFullRequestUrl(HttpServletRequest req){
String url = req.getRequestURL().toString();
String queryStr = req.getQueryString();
if(CommonUtil.isEmpty(queryStr)){
return url;
}
return url + "?" + queryStr;
}
/**
*
* @Description: 判断是否是微信浏览器发起的请求,如果是返回true,反之返回false
* @author tianpengw
* @param req
* @return
*/
public static boolean isWechatBrowser(HttpServletRequest req){
String ua = req.getHeader("user-agent").toLowerCase();
if (ua.indexOf("micromessenger") > 0) {// 是微信浏览器
return true;
}
return false;
}
}
4.PropertiesUtil:读取配置文件工具类
package com.wtp.wechat.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* @ClassName: PropertiesUtil
* @Description: 读取配置文件工具类
* @author tianpengw
* @date 2017年10月12日 上午9:13:13
*
*/
public class PropertiesUtil {
private static Logger log = LogManager.getLogger(PropertiesUtil.class);
private static Properties prop;
static{
try{
if(null == prop){
prop = new Properties();
}
InputStream fis = null;
fis = ClassLoaderUtils.getResourceAsStream("wechat.properties", PropertiesUtil.class);
if(fis!=null){
prop.load(fis);// 将属性文件流装载到Properties对象中
fis.close();// 关闭流
}
}catch (Exception e) {
log.error("读取配置文件出错:" + e );
}
}
/**
*
* @Description: 根据key获取配置的值
* @author tianpengw
* @param key
* @return
*/
public static String getProperties(String key){
if(key==null) return null;
return prop.getProperty(key);
}
/**
*
* @Description:
* @author tianpengw
* @param proper 读取配置的文件名称
* @param key
* @return
*/
public static String getPropertie(String resourceName,String key){
InputStream fis = ClassLoaderUtils.getResourceAsStream(resourceName, PropertiesUtil.class);
if(fis!=null){
try {
prop.load(fis);
fis.close();// 关闭流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(key==null) return null;
return prop.getProperty(key);
}
/**
*
* @Description: 根据key获取配置的值,若没有,则取传过来的默认的值
* @author tianpengw
* @param key
* @param defaultValue 默认值
* @return
*/
public static String getProperties(String key,String defaultValue){
if(key==null) return null;
return prop.getProperty(key, defaultValue);
}
}
5.SignatureUtil: 加密工具类
package com.wtp.wechat.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @ClassName: SignatureUtil
* @Description: 加密工具类
* @author tianpengw
* @date 2017年9月26日 下午6:22:34
*
*/
public class SignatureUtil {
/**
*
* @Description: 生成 MD5
* @author tianpengw
* @param data
* @return
*/
public static String MD5(String data){
StringBuilder sb = new StringBuilder();
try {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* SHA1 安全加密算法
* @param maps 参数key=value字符串
* @return
* @throws DigestException
*/
public static String SHA1(String decrypt) {
StringBuffer hexString = new StringBuffer();
try {
//指定sha1算法
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(decrypt.getBytes());
//获取字节数组
byte messageDigest[] = digest.digest();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return hexString.toString();
}
/**
*
* @Description: 微信签名算法
* @author tianpengw
* @param params 参数格式
* @param signatureType 加密类型 SHA1/MD5,默认MD5
* @return
*/
public static String signatures(Map<String, String> params, String signatureType){
String str="";
try {
List<String> paramsStr = new ArrayList<String>();
for (String key : params.keySet()) {
paramsStr.add(key);
}
Collections.sort(paramsStr);
StringBuilder sbff = new StringBuilder();
for (String kk : paramsStr) {
String value = params.get(kk);
if (CommonUtil.isEmpty(sbff.toString())) {
sbff.append(kk + "=" + value);
} else {
sbff.append("&" + kk + "=" + value);
}
}
if("SHA1".equals(signatureType)){
str = SHA1(sbff.toString());
}else{
str = MD5(sbff.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
}
6.XMLBeanUtils:xml格式解析封装工具类
package com.wtp.wechat.util;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.XppDriver;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.core.util.QuickWriter;
/**
*
* @ClassName: XMLBeanUtils
* @Description: xml格式解析封装工具类
* @author tianpengw
* @date 2017年10月13日 上午11:32:25
*
*/
public class XMLBeanUtils {
private static XStream xStream = new XStream(new XppDriver(new NoNameCoder()){
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 对所有xml节点的转换都增加CDATA标记
boolean cdata = true;
@Override
@SuppressWarnings("rawtypes")
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
}
@Override
public String encodeNode(String name) {
return name;
}
@Override
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write("<![CDATA[");
writer.write(text);
writer.write("]]>");
} else {
writer.write(text);
}
}
};
}
});
/**
*
* @Description: bean转xml字符串
* @author tianpengw
* @param obj
* @return
*/
public static String objectToXMLStr(Object obj){
xStream.alias("xml", obj.getClass());
return xStream.toXML(obj);
}
/**
* 解析微信发来的请求(XML)
*
* @param request
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList){
map.put(e.getName(), e.getText());
}
// 释放资源
inputStream.close();
inputStream = null;
return map;
}
/**
* @description 将xml字符串转换成map
* @param xml
* @return Map
*/
public static Map<String, String> readStringXmlOut(String xml) {
Map<String, String> map = new HashMap<String, String>();
Document doc = null;
try {
doc = DocumentHelper.parseText(xml); // 将字符串转为XML
Element rootElt = doc.getRootElement(); // 获取根节点
@SuppressWarnings("unchecked")
List<Element> list = rootElt.elements();// 获取根节点下所有节点
for (Element element : list) { // 遍历节点
map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
}
7.【重要】WechatUtil:微信基础工具类
package com.wtp.wechat.util;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.gson.Gson;
import com.wtp.wechat.bean.BaseAccessToken;
import com.wtp.wechat.bean.JSAPITicket;
import com.wtp.wechat.bean.Oauth2AccessToken;
import com.wtp.wechat.bean.WechatJSShareBean;
import com.wtp.wechat.bean.WechatUserInfo;
/**
* @ClassName: WechatUtil
* @Description: 微信基础工具类
* @author tianpengw
* @date 2017年10月12日 上午9:53:03
*
*/
public class WechatUtil {
private static Logger log = LogManager.getLogger(WechatUtil.class);
/**
* 微信公众号appid
*/
public static final String appid = PropertiesUtil.getProperties("wechat.appid");
/**
* 微信公众号appsecret
*/
public static final String appsecret = PropertiesUtil.getProperties("wechat.appsecret");
/**
* state
*/
public static final String state = PropertiesUtil.getProperties("wechat.state");
/**
* 基础token/jsApiTicket 有效时间(单位毫秒)
* api中是2小时(7200s) 此处设为7190s
*/
private static long expiresInMillisecond = 7190000L;
/**
* 缓存map
* 存储信息 :
* accessToken ----> 【基础 accessToken】
* baseExpireMillis ----> 【基础token存储时间】
* jsApiTicket ----> 【jsapi_ticket】
* ticketExpireMillis ----> 【ticket存储时间】
*
*/
private static Map<String,Object> wechatCacheMap = new HashMap<String,Object>();
/**
* 获取accessToken凭证请求地址
*/
private static final String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* 授权获取code地址
*/
private static final String oauth2Url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
/**
* 通过code换取网页授权地址
*/
private static final String oauth2TokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
/**
* 拉取用户信息
*/
private static final String userinfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
/**
* 获取jsApiTicket地址
*/
private static final String jsApiTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
/**
* 授权方式-snsapi_base
*/
public static final String scope_base = "snsapi_base";
/**
* 授权方式-snsapi_userinfo
*/
public static final String scope_userinfo = "snsapi_userinfo";
/**
*
* @Description: 获取access_token
* @author tianpengw
* @return
*/
public static BaseAccessToken getBaseToken(){
BaseAccessToken token = new BaseAccessToken();
if(wechatCacheMap.containsKey("accessToken") && !CommonUtil.isEmpty(wechatCacheMap.get("accessToken"))){
Long expireTime = (Long) wechatCacheMap.get("baseExpireMillis");
if(new Date().getTime() <= expireTime){//accessToken 仍然有效
token.setAccess_token((String)wechatCacheMap.get("accessToken"));
token.setExpires_in(7200);
return token;
}
}
String url = tokenUrl.replace("APPID", appid).replace("APPSECRET", appsecret);
String tokenResp = HttpHelper.httpsRequest(url, "GET", null);
if(!CommonUtil.isEmpty(tokenResp)){
log.info("获得基础access_token:" + tokenResp);
Gson json=new Gson();
token = json.fromJson(tokenResp,BaseAccessToken.class);
if(!CommonUtil.isEmpty(token.getAccess_token())){
wechatCacheMap.put("accessToken", token.getAccess_token());
wechatCacheMap.put("baseExpireMillis", (new Date().getTime() + expiresInMillisecond));
}
}
return token;
}
/**
* 生成微信授权地址
* @param scope
* @param redirectUrl
* @return
*/
public static String getOauth2Url(String scope,String redirectUrl){
return oauth2Url.replace("APPID", appid).replace("REDIRECT_URI", redirectUrl).replace("STATE", state).replace("SCOPE", scope);
}
/**
*
* 通过code换取网页授权access_token(与基础支持中的access_token不同)
* @param code
* @return
*/
public static Oauth2AccessToken getUserOpenId(String code){
Oauth2AccessToken token = new Oauth2AccessToken();
String url = oauth2TokenUrl.replace("APPID", appid).replace("SECRET", appsecret).replace("CODE", code);
String aouth2TokenResp = HttpHelper.httpsRequest(url, "POST", null);
if(!CommonUtil.isEmpty(aouth2TokenResp)){
log.info("根据code获得用户aouth2Token数据:" + aouth2TokenResp);
Gson json=new Gson();
token = json.fromJson(aouth2TokenResp, Oauth2AccessToken.class);
}
return token;
}
/**
*
* @Description: 获取用户的基本信息,注意需scope为 snsapi_userinfo
* @author tianpengw
* @param oauth2Token
* @return
* @throws UnsupportedEncodingException
*/
public static WechatUserInfo getUserInfo(Oauth2AccessToken oauth2Token) throws UnsupportedEncodingException{
WechatUserInfo userInfo = new WechatUserInfo();
String accessToken = oauth2Token.getAccess_token();
String openId = oauth2Token.getOpenid();
if(!CommonUtil.isEmpty(accessToken) && !CommonUtil.isEmpty(openId)){
String url = userinfoUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
String userInfoResp = HttpHelper.httpsRequest(url, "POST", null);
if(!CommonUtil.isEmpty(userInfoResp)){
log.info("根据code获得用户基本信息:" + userInfoResp);
Gson json=new Gson();
userInfo = json.fromJson(userInfoResp, WechatUserInfo.class);
}
}
return userInfo;
}
/**
*
* @Description: 获取微信jsapi_ticket
* @author tianpengw
* @return
*/
private static JSAPITicket getJsApiTicket(){
JSAPITicket ticket = new JSAPITicket();
BaseAccessToken token = getBaseToken();
if(wechatCacheMap.containsKey("jsApiTicket") && null != wechatCacheMap.get("jsApiTicket")){
Long expireTime = (Long) wechatCacheMap.get("ticketExpireMillis");
if(new Date().getTime() <= expireTime){//ticket 仍然有效
ticket.setTicket((String)wechatCacheMap.get("jsApiTicket"));
ticket.setErrcode("0");
ticket.setErrmsg("ok");
ticket.setExpires_in(7200);
return ticket;
}
}
String url = jsApiTicketUrl.replace("ACCESS_TOKEN", token.getAccess_token());
String ticketResp = HttpHelper.httpsRequest(url, "POST", null);
if(!CommonUtil.isEmpty(ticketResp)){
Gson json=new Gson();
ticket = json.fromJson(ticketResp,JSAPITicket.class);
if(!"".equals(token.getAccess_token())){
wechatCacheMap.put("jsApiTicket", ticket.getTicket());
wechatCacheMap.put("ticketExpireMillis", (new Date().getTime() + expiresInMillisecond));
}
}
return ticket;
}
/**
*
* @Description: 微信自定义分享配置
* @author tianpengw
* @param commonShare
* @param req
*/
public static void wechatShareConfig(WechatJSShareBean commonShare, HttpServletRequest req){
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
commonShare.setAppId(WechatUtil.appid);
commonShare.setTimestamp(timestamp);//获取时间戳
commonShare.setNonceStr(CommonUtil.getUUID());//获取指定长度的随机字符串
commonShare.setLink(HttpHelper.getFullRequestUrl(req));//签名用的url必须是调用JS接口页面的完整URL
JSAPITicket ticket = getJsApiTicket();
Map<String, String> params = new HashMap<String, String>();
params.put("noncestr", commonShare.getNonceStr());
params.put("timestamp", commonShare.getTimestamp());
params.put("url", HttpHelper.getFullRequestUrl(req));//签名用的url必须是调用JS接口页面的完整URL
params.put("jsapi_ticket", ticket.getTicket());
commonShare.setSignature(SignatureUtil.signatures(params,"SHA1"));
}
}
8.【重要】WechatPayUtil:微信支付工具类
package com.wtp.wechat.util;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.wtp.wechat.bean.UnifiedOrder;
/**
*
* @ClassName: WechatPayUtil
* @Description: 微信支付工具类
* @author tianpengw
* @date 2017年10月12日 下午3:38:25
*
*/
public class WechatPayUtil {
private static Logger log = LogManager.getLogger(WechatPayUtil.class);
/**
* 微信支付分配的商户号
*/
public static final String mchId = PropertiesUtil.getProperties("pay.mchid");
/**
* 商户平台密钥
*/
public static final String apiKey = PropertiesUtil.getProperties("pay.apikey");
/**
* 微信支付分配的商户号
*/
public static final String notifyUrl = PropertiesUtil.getProperties("pay.notifyUrl");
/**
* 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
*/
public static String deviceInfo = "WEB";
/**
* JSAPI -- 公众号支付
*/
public static String tradeTypeJs = "JSAPI";
/**
* MWEB -- H5支付
*/
public static String tradeTypeH5 = "MWEB";
/**
* 统一订单地址
*/
private static String unifiedUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**
*
* @Description: 公众号支付-统一订单类型获得prepay_id
* @author tianpengw
* @param uo
* @return
*/
public static String getUnifiedOrderPId(UnifiedOrder uo){
String sign = createUnifiedOrderSign(uo);
uo.setSign(sign);
String xml = XMLBeanUtils.objectToXMLStr(uo);
log.info("公账号支付统一订单请求参数:"+xml);
String res = HttpHelper.httpsRequest(unifiedUrl,"POST",xml);
log.info("公账号支付统一订单返回结果:"+res);
Map<String, String> responseMap = XMLBeanUtils.readStringXmlOut(res);
return responseMap.get("prepay_id");
}
/**
*
* @Description: 微信支付 -统一订单类型获得mweb_url
* @author tianpengw
* @param uo
* @return
*/
public static String getUnifiedOrderMWebUrl(UnifiedOrder uo){
String sign = createUnifiedOrderSign(uo);
uo.setSign(sign);
String xml = XMLBeanUtils.objectToXMLStr(uo);
log.info("H5支付统一订单请求参数:"+xml);
String res = HttpHelper.httpsRequest(unifiedUrl,"POST",xml);
log.info("H5支付统一订单返回结果:"+res);
Map<String, String> responseMap = XMLBeanUtils.readStringXmlOut(res);
return responseMap.get("mweb_url");
}
/**
* 获取统一下单签名
* @param unifiedOrder
* @return
*/
private static String createUnifiedOrderSign(UnifiedOrder unifiedOrder){
StringBuffer sign = new StringBuffer();
sign.append("appid=").append(unifiedOrder.getAppid());
sign.append("&body=").append(unifiedOrder.getBody());
sign.append("&device_info=").append(unifiedOrder.getDevice_info());
sign.append("&mch_id=").append(unifiedOrder.getMch_id());
sign.append("&nonce_str=").append(unifiedOrder.getNonce_str());
sign.append("¬ify_url=").append(unifiedOrder.getNotify_url());
/**
* H5支付签名时没有用户的openId
*/
if(!CommonUtil.isEmpty(unifiedOrder.getOpenid())){
sign.append("&openid=").append(unifiedOrder.getOpenid());
}
sign.append("&out_trade_no=").append(unifiedOrder.getOut_trade_no());
sign.append("&spbill_create_ip=").append(unifiedOrder.getSpbill_create_ip());
sign.append("&total_fee=").append(unifiedOrder.getTotal_fee());
sign.append("&trade_type=").append(unifiedOrder.getTrade_type());
sign.append("&key=").append(apiKey);
return SignatureUtil.MD5(sign.toString()).toUpperCase();
}
}
三)配置文件
wechat.properties:这里配置了微信公众号和支付相关的几个关键数据,不多介绍了,贴出来自己看:
#basic wechat settings
wechat.appid=wx**************
wechat.appsecret=12d3400*********************
wechat.state=*****
#basic wechat pay settings
pay.mchid=145*******
pay.apikey=ab123d******************
pay.notifyUrl=******
log4j2.xml:这个是日志配置文件
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!--输出控制台的配置-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- 按天每天备份一个日志 -->
<RollingFile name="RollingFile" fileName="d:/log/projectLog.log" filePattern="d:/log/projectLog_%d{yyyy-MM-dd}_%i.log">
<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n" />
<Policies>
<!-- 每24小时更新一次 -->
<TimeBasedTriggeringPolicy modulate="true" interval="24" />
</Policies>
<!-- 最多备份30个 -->
<DefaultRolloverStrategy max="30" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
<AppenderRef ref="RollingFile"/>
</Root>
</Loggers>
</Configuration>
最后分享下我封装的jar吧 点击进入下载页面
(如果你觉得5分太贵,自己从博客里整理也是可以的,我基本也都贴出了代码)
附上将本地包加在maven仓库的方法(默认你已经装了apache-maven工具并配好环境变量):
1) 下载jar包后扔在d盘根目录(当然也可其他目录,如果是别的下面的地址需要相应变更);
2) cmd命令行执行如下命令:
mvn install:install-file -Dfile=D:/wechat_api_20171016.jar -DgroupId=com.yytp.lib -DartifactId=wechat_api_20171016 -Dversion=1.0.0 -Dpackaging=jar
3)看到如下界面提示说明生成成功
4)在你的c:>用户>计算机名>.m2>repository>com>yytp>lib>wechat_api_20171016>1.0.0下就生成好了
然后你就能将其放在其他的仓库下了(注意要带上整个目录,如果没有就一层一层的建立)
<dependency>
<groupId>com.yytp.lib</groupId>
<artifactId>wechat_api_20171016</artifactId>
<version>1.0.0</version>
</dependency>
最后,代码毕竟是人写出来的,如果有什么疑问,不妥或待优化之处,希望留下你的思路和想法。
到此为止,本整理结束了,希望能给你带来收益。