微信开发整理生成jar包

经过最近一段时间的学习使用,我对微信的支付和分享都相当熟悉,前几天冒出个想法为什么不能将这些封装成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("&notify_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>

最后,代码毕竟是人写出来的,如果有什么疑问,不妥或待优化之处,希望留下你的思路和想法。
到此为止,本整理结束了,希望能给你带来收益。

发布了22 篇原创文章 · 获赞 15 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/niaoer2010/article/details/78258016