• 我用dom4j解析微信xml消息时用这种方法Document document = reader.read(inputStream); 他报错Error on line 1 of document : The root element is required in a well-formed document. Nested exception: The ...

    我用dom4j解析微信xml消息时用这种方法Document document = reader.read(inputStream);
    他报错Error on line 1 of document : The root element is required in a well-formed document. Nested exception: The root element is required in a well-formed document.通过分析我感就是(如果我错了请兄弟们指出)用SAXReader他会严格检查xml头部但是微信回复消息如下

    <xml>
     <ToUserName><![CDATA[toUser]]></ToUserName>
     <FromUserName><![CDATA[fromUser]]></FromUserName> 
     <CreateTime>1348831860</CreateTime>
     <MsgType><![CDATA[text]]></MsgType>
     <Content><![CDATA[this is a test]]></Content>
     <MsgId>1234567890123456</MsgId>
     </xml>
    

    一点都不标准,但是可以采用下面的方法

        // 将解析结果存储在HashMap中
            Map<String, String> map = new HashMap<String, String>();
            // 从request中取得输入流
            InputStream inputStream = request.getInputStream();
            // 一定要utf-8
            InputStreamReader inputReader = new InputStreamReader(inputStream,
                    "UTF-8");
            BufferedReader buffer = new BufferedReader(inputReader);
            String message = "";
            String s = "";
            while ((s = buffer.readLine()) != null) {
                message += s;
            }
            System.out.println(message);
            System.out.println("获取输入流");
            // // 读取输入流
             SAXReader reader = new SAXReader();
    //       Document document = reader.read(inputStream);
            // 将字符串转化为XML文档对象
            Document document = DocumentHelper.parseText(message);
            Element root = document.getRootElement();
            List<Element> elements = root.elements();
            // 遍历根节点下所有子节点
            Iterator<?> iter = root.elementIterator();
    
            // // 遍历所有子节点
            // for (Element e : elementList) {
            // System.out.println(e.getName() + "|" + e.getText());
            // map.put(e.getName(), e.getText());
            // }
            while (iter.hasNext()) {
                Element ele = (Element) iter.next();
                map.put(ele.getName(), ele.getText());
    
            }
            // 释放资源
            inputStream.close();
    展开全文
  • java微信开发教程

    2014-08-17 23:25:47
    微信公众平台开发教程Java版(1)环境准备篇

    微信公众平台开发教程Java版(1)环境准备篇

    微信公众平台开发教程Java版(一)环境准备篇

    准备写系列博客,记录下我的微信公众平台学习记录,也为那些摸索中的开发者提供点参考。

    希望与大家共同进步。

    微信3.0的时候我开始做微信公众账号,那时候没时间研究开发,先用的是编辑者模式,后用开发者模式,托管于第三方。

    一直想自己写个服务端来实现个人定制化的需求。

    废话不多说,进入正题。

    想要开发微信公众平台需要一些环境

    一、申请微信公众账号

           这个就不用废话了。附上地址:
           https://mp.weixin.qq.com/cgi-bin/readtemplate?t=wxm2-realname-reg_tmpl&lang=zh_CN
           现在申请好严格的说,3.0的时候申请都不需要拍照什么的。

           友情提示:
                     1、微信公众账号的名字一旦申请,则不能更改。取名请慎重!

                     2、一个身份证只能申请两个公众号

                     3、公众号分两种:订阅号和服务号

                     订阅号可一天群发一次消息,目前不能申请自定义菜单。发送的消息将显示在“订阅号”文件夹中,适合媒体等提供咨询服务的公众号。

                     服务号一个月只能群发一条消息,能申请自定义菜单,发送的消息会显示在用户的聊天列表中,并会提醒用户新消息。适合为用户提供服务的公众号

     

    二、外网服务器

           你需要一台外网服务器,来发布你的代码,用于接收处理用户发送的请求。

           如果没有的话,也不用担心。可以使用百度BAE,或者是sina sae,国外比较多的是用google的gae。

           google gae支持的语言很多。但是在国内经常访问不了,不推荐使用。

           百度Bae 支持java和php(完全免费,百度对于资源方面还是一向很大方的,赞一个,哈哈)

           sina sae 支持java,php,python(可免费使用半年,收费的,但很便宜)

    三、至少会一种语言
            java,php,asp,python等,至少得会一样!

    微信公众平台开发教程Java版(2)接口配置

    微信公众账号申请完成后,默认开启的是编辑模式。

    我们需要修改为开发模式。

     

    登陆微信公众平台》功能》高级功能

    先关闭 编辑模式,再开启 开发模式。

     

    申请成为开发者,如果是服务号,需要则会有开发者凭证信息

    如图


     

    如果是订阅号,则只显示服务器配置。

     

    下一步就是配置接口服务器了。

    在公众平台网站的高级功能 – 开发模式页,点击“成为开发者”按钮,填写URL和Token,其中URL是开发者用来接收微信服务器数据的接口URL。(这就是我们开发的程序,并部署到公网上了)

    Token 官网描述:可由开发者任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。

    总之就是你的程序里面写的token和这里填入的token要一致。

     



     

     还没有url和token?

     

    首先需要新建一个java web工程。

     

     接下来就要看看验证url和token了。

     下面是官网的描述,已经写的很清楚了



     核心实现方式就是将三个参数排序,拼接成字符串进行sha1加密,然后与signature比较

     官网也给了实例,是php的,我们只需要装换成java就可以了。

    private function checkSignature()
    {
            $signature = $_GET["signature"];
            $timestamp = $_GET["timestamp"];
            $nonce = $_GET["nonce"];	
            		
    	$token = TOKEN;
    	$tmpArr = array($token, $timestamp, $nonce);
    	sort($tmpArr);
    	$tmpStr = implode( $tmpArr );
    	$tmpStr = sha1( $tmpStr );
    	
    	if( $tmpStr == $signature ){
    		return true;
    	}else{
    		return false;
    	}
    }

     

     

    java代码 我的 WeixinController 类

        我的项目架构是基于spring3.0的,用到了注解。当get请求的时候会执行get方法,post请求的时候会执行post方法,分别来处理不同的请求,各位也可用servlet等去实现,原理都一样

    package com.ifp.weixin.controller;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.UnsupportedEncodingException;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import com.ifp.weixin.biz.core.CoreService;
    import com.ifp.weixin.util.SignUtil;
    
    @Controller
    @RequestMapping("/weixinCore")
    public class WeixinController {
    
    	@Resource(name="coreService")
    	private CoreService coreService;
    	
    	@RequestMapping(method = RequestMethod.GET)
    	public void get(HttpServletRequest request, HttpServletResponse response) {
    		// 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
    		String signature = request.getParameter("signature");
    		// 时间戳
    		String timestamp = request.getParameter("timestamp");
    		// 随机数
    		String nonce = request.getParameter("nonce");
    		// 随机字符串
    		String echostr = request.getParameter("echostr");
    
    		PrintWriter out = null;
    		try {
    			out = response.getWriter();
    			// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则接入失败
    			if (SignUtil.checkSignature(signature, timestamp, nonce)) {
    				out.print(echostr);
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			out.close();
    			out = null;
    		}
    	}
    
    	@RequestMapping(method = RequestMethod.POST)
    	public void post(HttpServletRequest request, HttpServletResponse response) {
    		//暂时空着,在这里可处理用户请求
    	}
    
    }
    

     

     上面类中用到了SignUtil 类

    package com.ifp.weixin.util;
    
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Arrays;
    
    import com.ifp.weixin.constant.Constant;
    /**
     * 验证签名
     *
     */
    public class SignUtil {
    	
    
    	/**
    	 * 验证签名
    	 * @param signature
    	 * @param timestamp
    	 * @param nonce
    	 * @return
    	 */
    	public static boolean checkSignature(String signature, String timestamp, String nonce) {
    		String[] arr = new String[] { Constant.TOKEN, timestamp, nonce };
    		// 将token、timestamp、nonce三个参数进行字典排序
    		Arrays.sort(arr);
    		StringBuilder content = new StringBuilder();
    		for (int i = 0; i < arr.length; i++) {
    			content.append(arr[i]);
    		}
    		MessageDigest md = null;
    		String tmpStr = null;
    
    		try {
    			md = MessageDigest.getInstance("SHA-1");
    			// 将三个参数字符串拼接成一个字符串进行sha1加密
    			byte[] digest = md.digest(content.toString().getBytes());
    			tmpStr = byteToStr(digest);
    		} catch (NoSuchAlgorithmException e) {
    			e.printStackTrace();
    		}
    
    		content = null;
    		// 将sha1加密后的字符串可与signature对比
    		return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
    	}
    
    	/**
    	 * 将字节数组转换为十六进制字符串
    	 * 
    	 * @param byteArray
    	 * @return
    	 */
    	private static String byteToStr(byte[] byteArray) {
    		String strDigest = "";
    		for (int i = 0; i < byteArray.length; i++) {
    			strDigest += byteToHexStr(byteArray[i]);
    		}
    		return strDigest;
    	}
    
    	/**
    	 * 将字节转换为十六进制字符串
    	 * 
    	 * @param mByte
    	 * @return
    	 */
    	private static String byteToHexStr(byte mByte) {
    		char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    		char[] tempArr = new char[2];
    		tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
    		tempArr[1] = Digit[mByte & 0X0F];
    
    		String s = new String(tempArr);
    		return s;
    	}
    }
    

     
     我们看到 checkSignature 这个方法里使用到了Constant.TOKEN ,这个token,我声明的一个常量。

     

     要与微信配置接口里面的token值一样

    /**
    * 与接口配置信息中的Token要一致
    */
    public static String TOKEN = "infopower";

    也贴上web.xml的配置,我的后缀是.html 的请求都交给DispatcherServlet了。

     

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    	<display-name>weixinHelp</display-name>
    	<context-param>
    		<param-name>contextConfigLocation</param-name>
    		<param-value>classpath:/applicationContext.xml</param-value>
    	</context-param>
    
    	<context-param>
    		<param-name>log4jConfigLocation</param-name>
    		<param-value>classpath:/properties/log4j.properties</param-value>
    	</context-param>
    	<listener>
    		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    	</listener>
    
    	<filter>
    		<filter-name>encodingFilter</filter-name>
    		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    		<init-param>
    			<param-name>encoding</param-name>
    			<param-value>UTF-8</param-value>
    		</init-param>
    	</filter>
    	<filter-mapping>
    		<filter-name>encodingFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    	</filter-mapping>
    	<listener>
    		<description>spring 容器的监听器</description>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    	<servlet>
    		<servlet-name>action</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    	</servlet>
    	<servlet-mapping>
    		<servlet-name>action</servlet-name>
    		<url-pattern>*.html</url-pattern>
    	</servlet-mapping>
    
    	<welcome-file-list>
    		<welcome-file>index.jsp</welcome-file>
    	</welcome-file-list>
    </web-app>
    

      

     

     我们的代码已经写完了,访问请求地址试试



     

    什么都没有显示,看看后台



     

    报空指针异常

     

    别担心,我们的代码没问题。

    因为直接访问地址,默认是get请求,而什么参数都没有传给后台,当然会报空指针

    前台没有异常,是因为我做了异常处理。

    ok

     

    接下来就是把代码打成war包发布到外网。

    然后填入相应的url和token,接口的配置就完成了。

     

    注意1:一定要发布war包到外网,配置外网的url,有些开发者配置的是ip是localhost,那肯定是不行的啦。

               如果没有外网环境,请看我的第一篇,环境准备,里面有介绍可以使用百度bae

               http://tuposky.iteye.com/blog/2008583

      注意2:开发模式一定要开启,不然配置了url和token也没用,我犯过这个错,嘿嘿。

    微信公众平台开发教程Java版(3) 消息接收和发送

    微信公众平台开发教程Java版(三) 消息接收和发送

    前面两章已经介绍了如何接入微信公众平台,这一章说说消息的接收和发送

    可以先了解公众平台的消息api接口(接收消息,发送消息)

    http://mp.weixin.qq.com/wiki/index.php


     

    接收消息

    当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。

     

     http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E6%94%B6%E6%99%AE%E9%80%9A%E6%B6%88%E6%81%AF

     

    接收的消息类型有6种,分别为:

    • 1 文本消息
    • 2 图片消息
    • 3 语音消息
    • 4 视频消息
    • 5 地理位置消息
    • 6 链接消息

    可以根据官方的api提供的字段建立对应的实体类

    如:文本消息

     

    有很多属性是所有消息类型都需要的,可以把这些信息提取出来建立一个基类

     

    package com.ifp.weixin.entity.Message.req;
    
    /**
     * 消息基类(用户 -> 公众帐号)
     * 
     */
    public class BaseMessage {
    	/**
    	 * 开发者微信号
    	 */
    	private String ToUserName;
    	/**
    	 * 发送方帐号(一个OpenID)
    	 */
    	private String FromUserName;
    	/**
    	 * 消息创建时间 (整型)
    	 */
    	private long CreateTime;
    
    	/**
    	 * 消息类型 text、image、location、link
    	 */
    	private String MsgType;
    
    	/**
    	 * 消息id,64位整型
    	 */
    	private long MsgId;
    
    	public String getToUserName() {
    		return ToUserName;
    	}
    
    	public void setToUserName(String toUserName) {
    		ToUserName = toUserName;
    	}
    
    	public String getFromUserName() {
    		return FromUserName;
    	}
    
    	public void setFromUserName(String fromUserName) {
    		FromUserName = fromUserName;
    	}
    
    	public long getCreateTime() {
    		return CreateTime;
    	}
    
    	public void setCreateTime(long createTime) {
    		CreateTime = createTime;
    	}
    
    	public String getMsgType() {
    		return MsgType;
    	}
    
    	public void setMsgType(String msgType) {
    		MsgType = msgType;
    	}
    
    	public long getMsgId() {
    		return MsgId;
    	}
    
    	public void setMsgId(long msgId) {
    		MsgId = msgId;
    	}
    
    }
    

     接收的文本消息

     

    package com.ifp.weixin.entity.Message.req;
    
    /**
     * 文本消息
     */
    public class TextMessage extends BaseMessage {
    	/**
    	 * 回复的消息内容
    	 */
    	private String Content;
    
    	public String getContent() {
    		return Content;
    	}
    
    	public void setContent(String content) {
    		Content = content;
    	}
    }

     接收的图片消息

    package com.ifp.weixin.entity.Message.req;
    
    public class ImageMessage extends BaseMessage{
    
    	private String picUrl;
    
    	public String getPicUrl() {
    		return picUrl;
    	}
    
    	public void setPicUrl(String picUrl) {
    		this.picUrl = picUrl;
    	}
    	
    }
    

     

     

    接收的链接消息

    package com.ifp.weixin.entity.Message.req;
    
    
    public class LinkMessage extends BaseMessage {
    	/**
    	 * 消息标题
    	 */
    	private String Title;
    	/**
    	 * 消息描述
    	 */
    	private String Description;
    	/**
    	 * 消息链接
    	 */
    	private String Url;
    
    	public String getTitle() {
    		return Title;
    	}
    
    	public void setTitle(String title) {
    		Title = title;
    	}
    
    	public String getDescription() {
    		return Description;
    	}
    
    	public void setDescription(String description) {
    		Description = description;
    	}
    
    	public String getUrl() {
    		return Url;
    	}
    
    	public void setUrl(String url) {
    		Url = url;
    	}
    
    }
    

     

     接收的语音消息

     

    package com.ifp.weixin.entity.Message.req;
    
    /**
     * 语音消息
     * 
     * @author Caspar
     * 
     */
    public class VoiceMessage extends BaseMessage {
    	/**
    	 * 媒体ID
    	 */
    	private String MediaId;
    	/**
    	 * 语音格式
    	 */
    	private String Format;
    
    	public String getMediaId() {
    		return MediaId;
    	}
    
    	public void setMediaId(String mediaId) {
    		MediaId = mediaId;
    	}
    
    	public String getFormat() {
    		return Format;
    	}
    
    	public void setFormat(String format) {
    		Format = format;
    	}
    
    }
    

     接收的地理位置消息

     

    package com.ifp.weixin.entity.Message.req;
    
    
    /**
     * 位置消息
     * 
     * @author caspar
     * 
     */
    public class LocationMessage extends BaseMessage {
    	/**
    	 * 地理位置维度
    	 */
    	private String Location_X;
    	/**
    	 * 地理位置经度
    	 */
    	private String Location_Y;
    
    	/**
    	 * 地图缩放大小
    	 */
    	private String Scale;
    
    	/**
    	 * 地理位置信息
    	 */
    	private String Label;
    
    	public String getLocation_X() {
    		return Location_X;
    	}
    
    	public void setLocation_X(String location_X) {
    		Location_X = location_X;
    	}
    
    	public String getLocation_Y() {
    		return Location_Y;
    	}
    
    	public void setLocation_Y(String location_Y) {
    		Location_Y = location_Y;
    	}
    
    	public String getScale() {
    		return Scale;
    	}
    
    	public void setScale(String scale) {
    		Scale = scale;
    	}
    
    	public String getLabel() {
    		return Label;
    	}
    
    	public void setLabel(String label) {
    		Label = label;
    	}
    
    }
    

     

     

    发送被动响应消息

        对于每一个POST请求,开发者在响应包(Get)中返回特定XML结构,对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。请注意,回复图片等多媒体消息时需要预先上传多媒体文件到微信服务器,只支持认证服务号。

     

        同样,建立响应消息的对应实体类

        也把公共的属性提取出来,封装成基类

     

         响应消息的基类

    package com.ifp.weixin.entity.Message.resp;
    
    /**
     * 消息基类(公众帐号 -> 用户)
     */
    public class BaseMessage {
    	
    	/**
    	 * 接收方帐号(收到的OpenID)
    	 */
    	private String ToUserName;
    	/**
    	 * 开发者微信号
    	 */
    	private String FromUserName;
    	/**
    	 * 消息创建时间 (整型)
    	 */
    	private long CreateTime;
    	
    	/**
    	 * 消息类型
    	 */
    	private String MsgType;
    	
    	/**
    	 * 位0x0001被标志时,星标刚收到的消息
    	 */
    	private int FuncFlag;
    
    	public String getToUserName() {
    		return ToUserName;
    	}
    
    	public void setToUserName(String toUserName) {
    		ToUserName = toUserName;
    	}
    
    	public String getFromUserName() {
    		return FromUserName;
    	}
    
    	public void setFromUserName(String fromUserName) {
    		FromUserName = fromUserName;
    	}
    
    	public long getCreateTime() {
    		return CreateTime;
    	}
    
    	public void setCreateTime(long createTime) {
    		CreateTime = createTime;
    	}
    
    	public String getMsgType() {
    		return MsgType;
    	}
    
    	public void setMsgType(String msgType) {
    		MsgType = msgType;
    	}
    
    	public int getFuncFlag() {
    		return FuncFlag;
    	}
    
    	public void setFuncFlag(int funcFlag) {
    		FuncFlag = funcFlag;
    	}
    }

     

     

        响应文本消息

       

    package com.ifp.weixin.entity.Message.resp;
    
    
    /**
     * 文本消息
     */
    public class TextMessage extends BaseMessage {
    	/**
    	 * 回复的消息内容
    	 */
    	private String Content;
    
    	public String getContent() {
    		return Content;
    	}
    
    	public void setContent(String content) {
    		Content = content;
    	}
    }

     

     

    响应图文消息

       

    package com.ifp.weixin.entity.Message.resp;
    
    import java.util.List;
    
    /**
     * 多图文消息,
     * 单图文的时候 Articles 只放一个就行了
     * @author Caspar.chen
     */
    public class NewsMessage extends BaseMessage {
    	/**
    	 * 图文消息个数,限制为10条以内
    	 */
    	private int ArticleCount;
    	/**
    	 * 多条图文消息信息,默认第一个item为大图
    	 */
    	private List<Article> Articles;
    
    	public int getArticleCount() {
    		return ArticleCount;
    	}
    
    	public void setArticleCount(int articleCount) {
    		ArticleCount = articleCount;
    	}
    
    	public List<Article> getArticles() {
    		return Articles;
    	}
    
    	public void setArticles(List<Article> articles) {
    		Articles = articles;
    	}
    }

     图文消息的定义

     

     

    package com.ifp.weixin.entity.Message.resp;
    
    /**
     * 图文消息
     * 
     */
    public class Article {
    	/**
    	 * 图文消息名称
    	 */
    	private String Title;
    
    	/**
    	 * 图文消息描述
    	 */
    	private String Description;
    
    	/**
    	 * 图片链接,支持JPG、PNG格式,<br>
    	 * 较好的效果为大图640*320,小图80*80
    	 */
    	private String PicUrl;
    
    	/**
    	 * 点击图文消息跳转链接
    	 */
    	private String Url;
    
    	public String getTitle() {
    		return Title;
    	}
    
    	public void setTitle(String title) {
    		Title = title;
    	}
    
    	public String getDescription() {
    		return null == Description ? "" : Description;
    	}
    
    	public void setDescription(String description) {
    		Description = description;
    	}
    
    	public String getPicUrl() {
    		return null == PicUrl ? "" : PicUrl;
    	}
    
    	public void setPicUrl(String picUrl) {
    		PicUrl = picUrl;
    	}
    
    	public String getUrl() {
    		return null == Url ? "" : Url;
    	}
    
    	public void setUrl(String url) {
    		Url = url;
    	}
    
    }

     

     

    响应音乐消息

     

    package com.ifp.weixin.entity.Message.resp;
    
    
    
    /**
     * 音乐消息
     */
    public class MusicMessage extends BaseMessage {
    	/**
    	 * 音乐
    	 */
    	private Music Music;
    
    	public Music getMusic() {
    		return Music;
    	}
    
    	public void setMusic(Music music) {
    		Music = music;
    	}
    }

     

     

    音乐消息的定义

    package com.ifp.weixin.entity.Message.resp;
    
    /**
     * 音乐消息
     */
    public class Music {
    	/**
    	 * 音乐名称
    	 */
    	private String Title;
    	
    	/**
    	 * 音乐描述
    	 */
    	private String Description;
    	
    	/**
    	 * 音乐链接
    	 */
    	private String MusicUrl;
    	
    	/**
    	 * 高质量音乐链接,WIFI环境优先使用该链接播放音乐
    	 */
    	private String HQMusicUrl;
    
    	public String getTitle() {
    		return Title;
    	}
    
    	public void setTitle(String title) {
    		Title = title;
    	}
    
    	public String getDescription() {
    		return Description;
    	}
    
    	public void setDescription(String description) {
    		Description = description;
    	}
    
    	public String getMusicUrl() {
    		return MusicUrl;
    	}
    
    	public void setMusicUrl(String musicUrl) {
    		MusicUrl = musicUrl;
    	}
    
    	public String getHQMusicUrl() {
    		return HQMusicUrl;
    	}
    
    	public void setHQMusicUrl(String musicUrl) {
    		HQMusicUrl = musicUrl;
    	}
    
    }

     
     构建好之后的项目结构图为

     

     

    到这里,请求消息和响应消息的实体类都定义好了

     

    解析请求消息

     

    用户向微信公众平台发送消息后,微信公众平台会通过post请求发送给我们。

    上一章中WeixinController 类的post方法我们空着

     

     现在我们要在这里处理用户请求了。

     

    因为微信的发送和接收都是用xml格式的,所以我们需要处理请求过来的xml格式。

    发送的时候也需要转化成xml格式再发送给微信,所以封装了消息处理的工具类,用到dome4j和xstream两个jar包

    package com.ifp.weixin.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.Element;
    import org.dom4j.io.SAXReader;
    
    import com.ifp.weixin.entity.Message.resp.Article;
    import com.ifp.weixin.entity.Message.resp.MusicMessage;
    import com.ifp.weixin.entity.Message.resp.NewsMessage;
    import com.ifp.weixin.entity.Message.resp.TextMessage;
    import com.thoughtworks.xstream.XStream;
    import com.thoughtworks.xstream.core.util.QuickWriter;
    import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
    import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
    import com.thoughtworks.xstream.io.xml.XppDriver;
    
    /**
     * 消息工具类
     * 
     */
    public class MessageUtil {
    
    	/**
    	 * 解析微信发来的请求(XML)
    	 * 
    	 * @param request
    	 * @return
    	 * @throws Exception
    	 */
    	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();
    		// 得到根元素的所有子节点
    		
    		@SuppressWarnings("unchecked")
    		List<Element> elementList = root.elements();
    
    		// 遍历所有子节点
    		for (Element e : elementList)
    			map.put(e.getName(), e.getText());
    
    		// 释放资源
    		inputStream.close();
    		inputStream = null;
    
    		return map;
    	}
    
    	/**
    	 * 文本消息对象转换成xml
    	 * 
    	 * @param textMessage 文本消息对象
    	 * @return xml
    	 */
    	public static String textMessageToXml(TextMessage textMessage) {
    		xstream.alias("xml", textMessage.getClass());
    		return xstream.toXML(textMessage);
    	}
    
    	/**
    	 * 音乐消息对象转换成xml
    	 * 
    	 * @param musicMessage 音乐消息对象
    	 * @return xml
    	 */
    	public static String musicMessageToXml(MusicMessage musicMessage) {
    		xstream.alias("xml", musicMessage.getClass());
    		return xstream.toXML(musicMessage);
    	}
    
    	/**
    	 * 图文消息对象转换成xml
    	 * 
    	 * @param newsMessage 图文消息对象
    	 * @return xml
    	 */
    	public static String newsMessageToXml(NewsMessage newsMessage) {
    		xstream.alias("xml", newsMessage.getClass());
    		xstream.alias("item", new Article().getClass());
    		return xstream.toXML(newsMessage);
    	}
    
    	/**
    	 * 扩展xstream,使其支持CDATA块
    	 * 
    	 */
    	private static XStream xstream = new XStream(new XppDriver() {
    		public HierarchicalStreamWriter createWriter(Writer out) {
    			return new PrettyPrintWriter(out) {
    				// 对所有xml节点的转换都增加CDATA标记
    				boolean cdata = true;
    				protected void writeText(QuickWriter writer, String text) {
    					if (cdata) {
    						writer.write("<![CDATA[");
    						writer.write(text);
    						writer.write("]]>");
    					} else {
    						writer.write(text);
    					}
    				}
    			};
    		}
    	});
    	
    	
    }

     接下来在处理业务逻辑,建立一个接收并响应消息的service类,并针对用户输入的1或2回复不同的信息给用户

     

    package com.ifp.weixin.biz.core.impl;
    
    import java.util.Date;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.log4j.Logger;
    import org.springframework.stereotype.Service;
    
    import com.ifp.weixin.biz.core.CoreService;
    import com.ifp.weixin.constant.Constant;
    import com.ifp.weixin.entity.Message.resp.TextMessage;
    import com.ifp.weixin.util.MessageUtil;
    
    @Service("coreService")
    public class CoreServiceImpl implements CoreService{
    
    	public static Logger log = Logger.getLogger(CoreServiceImpl.class);
    	
    	
    	@Override
    	public String processRequest(HttpServletRequest request) {
    		String respMessage = null;
    		try {
    			// xml请求解析
    			Map<String, String> requestMap = MessageUtil.parseXml(request);
    
    			// 发送方帐号(open_id)
    			String fromUserName = requestMap.get("FromUserName");
    			// 公众帐号
    			String toUserName = requestMap.get("ToUserName");
    			// 消息类型
    			String msgType = requestMap.get("MsgType");
    
    			TextMessage textMessage = new TextMessage();
    			textMessage.setToUserName(fromUserName);
    			textMessage.setFromUserName(toUserName);
    			textMessage.setCreateTime(new Date().getTime());
    			textMessage.setMsgType(Constant.RESP_MESSAGE_TYPE_TEXT);
    			textMessage.setFuncFlag(0);
    			// 文本消息
    			if (msgType.equals(Constant.REQ_MESSAGE_TYPE_TEXT)) {
    				// 接收用户发送的文本消息内容
    				String content = requestMap.get("Content");
    
    				if ("1".equals(content)) {
    					textMessage.setContent("1是很好的");
    					// 将文本消息对象转换成xml字符串
    					respMessage = MessageUtil.textMessageToXml(textMessage);
    				}else if ("2".equals(content)) {
    					textMessage.setContent("我不是2货");
    					// 将文本消息对象转换成xml字符串
    					respMessage = MessageUtil.textMessageToXml(textMessage);
    				}
    			} 
    			
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return respMessage;
    	}
    
    
    }
    

     接下来在controller里面的post方法里面调用即可

     

    WeixinController类的完整代码

    package com.ifp.weixin.controller;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.UnsupportedEncodingException;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import com.ifp.weixin.biz.core.CoreService;
    import com.ifp.weixin.util.SignUtil;
    
    @Controller
    @RequestMapping("/weixinCore")
    public class WeixinController {
    
    	@Resource(name="coreService")
    	private CoreService coreService;
    	
    	@RequestMapping(method = RequestMethod.GET)
    	public void get(HttpServletRequest request, HttpServletResponse response) {
    		// 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
    		String signature = request.getParameter("signature");
    		// 时间戳
    		String timestamp = request.getParameter("timestamp");
    		// 随机数
    		String nonce = request.getParameter("nonce");
    		// 随机字符串
    		String echostr = request.getParameter("echostr");
    
    		PrintWriter out = null;
    		try {
    			out = response.getWriter();
    			// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,否则接入失败
    			if (SignUtil.checkSignature(signature, timestamp, nonce)) {
    				out.print(echostr);
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			out.close();
    			out = null;
    		}
    	}
    
    	@RequestMapping(method = RequestMethod.POST)
    	public void post(HttpServletRequest request, HttpServletResponse response) {
    		try {
    			request.setCharacterEncoding("UTF-8");
    		} catch (UnsupportedEncodingException e) {
    			e.printStackTrace();
    		}
    		response.setCharacterEncoding("UTF-8");
    
    		// 调用核心业务类接收消息、处理消息
    		String respMessage = coreService.processRequest(request);
    
    		// 响应消息
    		PrintWriter out = null;
    		try {
    			out = response.getWriter();
    			out.print(respMessage);
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			out.close();
    			out = null;
    		}
    	}
    
    }
    

     

     效果如下:

     

     ok,大功告成,消息的接收和发送就写完了。

    微信公众平台开发教程Java版(4) 图文消息

    微信公众平台开发教程Java版(四) 图文消息

    引言:

    上一章讲到了消息的接收和发送,但是讲的是最简单的文本信息。

    在微信中用的最多的信息还是图文消息,本章就为大家讲解下微信图文消息是如何实现的。

    包括单图文和多图文消息。

    图文消息的XML数据包结构:

    <xml>
    <ToUserName><![CDATA[toUser]]></ToUserName>
    <FromUserName><![CDATA[fromUser]]></FromUserName>
    <CreateTime>12345678</CreateTime>
    <MsgType><![CDATA[news]]></MsgType>
    <ArticleCount>2</ArticleCount>
    <Articles>
    <item>
    <Title><![CDATA[title1]]></Title> 
    <Description><![CDATA[description1]]></Description>
    <PicUrl><![CDATA[picurl]]></PicUrl>
    <Url><![CDATA[url]]></Url>
    </item>
    <item>
    <Title><![CDATA[title]]></Title>
    <Description><![CDATA[description]]></Description>
    <PicUrl><![CDATA[picurl]]></PicUrl>
    <Url><![CDATA[url]]></Url>
    </item>
    </Articles>
    </xml>

       

     

    从上面结构图中可以看出要注意的几点

    1、图文消息的条数最大限制为10,

    2、多图文中列表中的第一个为大图,其余为小图

    注意:在多图文模式下只有第一个可以显示描述信息,其余的都不显示

    了解了图文消息的结构后,要发送图文消息就简单了。

    我们之前已经封装过消息处理的代码和图文消息的实体类,这里就不啰嗦了,不知道的可以看上一章

    微信公众平台开发教程Java版(三) 消息接收和发送

     

    下面我就上单图文和多图文消息的源代码

     

    package com.ifp.weixin.biz.core.impl;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.log4j.Logger;
    import org.springframework.stereotype.Service;
    
    import com.ifp.weixin.biz.core.CoreService;
    import com.ifp.weixin.constant.Constant;
    import com.ifp.weixin.entity.Message.resp.Article;
    import com.ifp.weixin.entity.Message.resp.NewsMessage;
    import com.ifp.weixin.entity.Message.resp.TextMessage;
    import com.ifp.weixin.util.MessageUtil;
    
    @Service("coreService")
    public class CoreServiceImpl implements CoreService {
    
    	public static Logger log = Logger.getLogger(CoreServiceImpl.class);
    
    	@Override
    	public String processRequest(HttpServletRequest request) {
    		String respMessage = null;
    		try {
    			// xml请求解析
    			Map<String, String> requestMap = MessageUtil.parseXml(request);
    
    			// 发送方帐号(open_id)
    			String fromUserName = requestMap.get("FromUserName");
    			// 公众帐号
    			String toUserName = requestMap.get("ToUserName");
    			// 消息类型
    			String msgType = requestMap.get("MsgType");
    
    			TextMessage textMessage = new TextMessage();
    			textMessage.setToUserName(fromUserName);
    			textMessage.setFromUserName(toUserName);
    			textMessage.setCreateTime(new Date().getTime());
    			textMessage.setMsgType(Constant.RESP_MESSAGE_TYPE_TEXT);
    			textMessage.setFuncFlag(0);
    			
    			// 文本消息
    			if (msgType.equals(Constant.REQ_MESSAGE_TYPE_TEXT)) {
    				// 接收用户发送的文本消息内容
    				String content = requestMap.get("Content");
    
    				// 创建图文消息
    				NewsMessage newsMessage = new NewsMessage();
    				newsMessage.setToUserName(fromUserName);
    				newsMessage.setFromUserName(toUserName);
    				newsMessage.setCreateTime(new Date().getTime());
    				newsMessage.setMsgType(Constant.RESP_MESSAGE_TYPE_NEWS);
    				newsMessage.setFuncFlag(0);
    
    				List<Article> articleList = new ArrayList<Article>();
    				// 单图文消息
    				if ("1".equals(content)) {
    					Article article = new Article();
    					article.setTitle("我是一条单图文消息");
    					article.setDescription("我是描述信息,哈哈哈哈哈哈哈。。。");
    					article.setPicUrl("http://www.iteye.com/upload/logo/user/603624/2dc5ec35-073c-35e7-9b88-274d6b39d560.jpg");
    					article.setUrl("http://tuposky.iteye.com");
    					articleList.add(article);
    					// 设置图文消息个数
    					newsMessage.setArticleCount(articleList.size());
    					// 设置图文消息包含的图文集合
    					newsMessage.setArticles(articleList);
    					// 将图文消息对象转换成xml字符串
    					respMessage = MessageUtil.newsMessageToXml(newsMessage);
    				}
    				// 多图文消息
    				else if ("3".equals(content)) {
    					Article article1 = new Article();
    					article1.setTitle("我是一条多图文消息");
    					article1.setDescription("");
    					article1.setPicUrl("http://www.isic.cn/viewResourcesAction//logo/20130913/2013091314543416032.jpg");
    					article1.setUrl("http://tuposky.iteye.com/blog/2008583");
    
    					Article article2 = new Article();
    					article2.setTitle("微信公众平台开发教程Java版(二)接口配置 ");
    					article2.setDescription("");
    					article2.setPicUrl("http://www.isic.cn/viewResourcesAction//logo/20131021/2013102111243367254.jpg");
    					article2.setUrl("http://tuposky.iteye.com/blog/2008655");
    
    					Article article3 = new Article();
    					article3.setTitle("微信公众平台开发教程Java版(三) 消息接收和发送");
    					article3.setDescription("");
    					article3.setPicUrl("http://www.isic.cn/viewResourcesAction//logo/20131021/2013102111291287031.jpg");
    					article3.setUrl("http://tuposky.iteye.com/blog/2017429");
    
    					articleList.add(article1);
    					articleList.add(article2);
    					articleList.add(article3);
    					newsMessage.setArticleCount(articleList.size());
    					newsMessage.setArticles(articleList);
    					respMessage = MessageUtil.newsMessageToXml(newsMessage);
    				} 
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return respMessage;
    	}
    
    }
    

     

    单个图文和多图文的处理方式其实是一样的

    单图文的时候articleList 的size为1

    多图文的时候为多个。

     

    运行的效果截图如下:

    用户发送1,单图文消息

     

    用户发送3 多图文消息

     

     Ps: 图文消息中的图片是可以引用外部资源的!



    展开全文
  • 本篇文章实现模板、图文、文本、音乐、图片推送,前提是已经搭建了微信开发环境。读完本文后,实现的主要效果如下

    本篇文章实现模板、图文、文本、音乐、图片推送,前提是已经搭建了微信开发环境。读完本文后,实现的主要效果如下
    在这里插入图片描述

    1. 在测试账号中配置模板
      登录测试公众号/正式公众号(认证后的服务号),测试公众号:模板消息接口->新增测试模板中添加模板,正式公众号:在功能->模板消息中添加模板,模板可以在模板库中选择,如果没有你需要的模板,可以申请添加,一个月可以申请三条。模板添加成功后,有个模板ID(用于接口调用)。
      具体如何配置可以参看官方文档:https://mp.weixin.qq.com/wiki 中消息管理->发送消息-模板消息接口。

    2. 配置好后上代码:

    Controller:

    package com.zhongyitang.vehicle.common.weixin;
    
    import com.zhongyitang.vehicle.common.weixin.utils.MessageHandlerUtil;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.weixin4j.spring.web.WeixinJieruController;
    import org.weixin4j.util.TokenUtil;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Map;
    
    
    
    @Controller
    @RequestMapping("/check")
    public class JieruController {
    
        private static final String token = "oliver"; //开发者自行定义的Tooken
    
        @GetMapping("/signature")
        public void get(HttpServletRequest request, HttpServletResponse response) throws IOException {
            //消息来源可靠性验证
            String signature = request.getParameter("signature");// 微信加密签名
            String timestamp = request.getParameter("timestamp");// 时间戳
            String nonce = request.getParameter("nonce");       // 随机数
            //Token为weixin4j.properties中配置的Token
    //        String token = TokenUtil.get();
    
            //1.验证消息真实性
            //http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性
            //成为开发者验证
            String echostr = request.getParameter("echostr");
            //确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败
            if (TokenUtil.checkSignature(token, signature, timestamp, nonce)) {
                response.getWriter().write(echostr);
            }
    
        }
    
    
        //接收微信消息
        @PostMapping("/signature")
        public void post(HttpServletRequest request, HttpServletResponse response) throws IOException {
            //消息来源可靠性验证
            String signature = request.getParameter("signature");// 微信加密签名
            String timestamp = request.getParameter("timestamp");// 时间戳
            String nonce = request.getParameter("nonce");       // 随机数
            //Token为weixin4j.properties中配置的Token
    //        String token = TokenUtil.get();
            //确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败
            if (!TokenUtil.checkSignature(token, signature, timestamp, nonce)) {
                //消息不可靠,直接返回
                response.getWriter().write("");
                return;
            }
    
            String result = "";
            try {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("text/xml");
                Map<String,String> map = MessageHandlerUtil.parseXml(request);
                System.out.println("开始构造消息");
                result = MessageHandlerUtil.buildResponseMessage(map);
                System.out.println("result:"+result);
                if(result.equals("")){
                    result = "未正确响应或返回为空";
                    System.out.println("result:"+result);
                }
                else{
                    response.getWriter().println(result);
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("发生异常:"+ e.getMessage());
            }
    
    
        }
    
    
    
    
    
    
    
    }
    

    模板基类:

    /**
     1. 模板基类
     2. @author CLiang
     3.  */
    public class WxTemplate {
        private String template_id;//模板ID
        private String touser;//目标客户
        private String url;//用户点击模板信息的跳转页面
        private String topcolor;//字体颜色
        private Map<String,TemplateData> data;//模板里的数据
         
        public String getTemplate_id() {
            return template_id;
        }
        public void setTemplate_id(String template_id) {
            this.template_id = template_id;
        }
        public String getTouser() {
            return touser;
        }
        public void setTouser(String touser) {
            this.touser = touser;
        }
        public String getUrl() {
            return url;
        }
        public void setUrl(String url) {
            this.url = url;
        }
        public String getTopcolor() {
            return topcolor;
        }
        public void setTopcolor(String topcolor) {
            this.topcolor = topcolor;
        }
        public Map<String,TemplateData> getData() {
            return data;
        }
        public void setData(Map<String,TemplateData> data) {
            this.data = data;
        }
    }

    一条模板包含多条数据,模板数据类封装:

    /**
     4. 模板数据
     5. @author By Oliver_Deng
     6.  */
    public class TemplateData {
        private String value;//模板显示值
        private String color;//模板显示颜色
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
        public String getColor() {
            return color;
        }
        public void setColor(String color) {
            this.color = color;
        }
    }
    1. 获取ACCESS_TOKEN
      看文档,发送模板信息接口为:https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN ,前期模板数据都准备好了,现在缺少ACCESS_TOKEN,缺什么就去获取什么,查看文档可知获取ACCESS_TOKEN接口为:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET ,现在就封装个获取ACCESS_TOKEN的请求方法:
    /**
     * 发送模板消息前获取token
     * @param template_id_short 模板库中模板的编号
     * @param t
     * @param m
     */
    public static void sendMessageBefore(String template_id_short,WxTemplate t,Map<String,TemplateData> m){
        String accessToken = WeChatUtil.getAccessToken();
        System.out.println("accessToKen:         "+accessToken);
        t.setData(m);
        sendMessage(t,accessToken);
    }

    上述代码中用到的WeChatUtils:

    package com.zhongyitang.vehicle.common.weixin.utils;
    
    import net.sf.json.JSONObject;
    import java.util.Date;
    
    public class WeChatUtil {
        //URL验证时使用的token
    //    public static final String TOKEN = "wolfcode";
        //appid
        public static final String APPID = "你自己的";
        //secret
        public static final String SECRET = "你自己的";
        //创建菜单接口地址
        public static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
        //获取access_token的接口地址
        public static final String GET_ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        //发送模板消息的接口
        public static final String SEND_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
        //缓存的access_token
        private static String accessToken;
        //access_token的失效时间
        private static long expiresTime;
    
        /**
         * 获取accessToken
         * @return
         */
        public static String getAccessToken(){
            //判断accessToken是否已经过期,如果过期需要重新获取
            if(accessToken==null||expiresTime < new Date().getTime()){
                //发起请求获取accessToken
                String result = HTTPUtils.doGet(GET_ACCESSTOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET));
                //把json字符串转换为json对象
    //            JSONObject json = JSON.parseObject(result);
                JSONObject json = JSONObject.fromObject(result);
                //缓存accessToken
                accessToken = json.getString("access_token");
                //设置accessToken的失效时间
                long expires_in = json.getLong("expires_in");
                //失效时间 = 当前时间 + 有效期(提前一分钟)
                expiresTime = new Date().getTime()+ (expires_in-60) * 1000;
            }
            return accessToken;
        }
    
    }
    
    

    注意:ACCESS_TOKEN有请求次数限制,而且会在获取后7200秒后自动失效,所以要妥善保存好ACCESS_TOKEN。

    1. 我的测试模板:
    {{first.DATA}}
    违章时间:{{violationTime.DATA}}
    违章地点:{{violationAddress.DATA}}
    违章内容:{{violationType.DATA}}
    罚款金额:{{violationFine.DATA}}
    扣分情况:{{violationPoints.DATA}}
    {{remark.DATA}}
    
    1. 发送模板:
    /**
       * 发送模板消息调用实例
       * @param map 封装了解析结果的Map
    * @return 空 
       */
      private static String buildTempMessage(Map<String, String> map) {
       //发送方帐号
          String fromUserName = map.get("FromUserName");
          // 开发者微信号
          String toUserName = map.get("ToUserName");
       WxTemplate template = new WxTemplate();
       template.setUrl("www.baidu.com");
       template.setTouser(fromUserName);
       template.setTopcolor("#000000");
       template.setTemplate_id("9E9deiteIUNNJADRPJa5A5TbcrqFRK0UmsvIG2-0RbE"); //模板ID
       Map<String,TemplateData> m = new HashMap<String, TemplateData>();
       TemplateData first = new TemplateData();
       first.setColor("#000000");
       first.setValue("您好,您车牌号为"京56K453"的车辆有新的违章信息");
       m.put("first", first);
       TemplateData keyword1 = new TemplateData();
       keyword1.setColor("#328392");
       keyword1.setValue("2014年5月07日 16:00");
       m.put("violationTime", keyword1);
       TemplateData keyword2 = new TemplateData();
       keyword2.setColor("#328392");
       keyword2.setValue("北京市西城区开阳桥北天桥北向南");
       m.put("violationAddress", keyword2);
       TemplateData keyword3 = new TemplateData();
       keyword3.setColor("#328392");
       keyword3.setValue("不按规定停车");
       m.put("violationType", keyword3);
          TemplateData keyword4 = new TemplateData();
          keyword4.setColor("#328392");
          keyword4.setValue("200元");
          m.put("violationFine", keyword4);
          TemplateData keyword5 = new TemplateData();
          keyword5.setColor("#328392");
          keyword5.setValue("扣3分");
          m.put("violationPoints", keyword5);
       TemplateData remark = new TemplateData();
       remark.setColor("#929232");
       remark.setValue("点击查看更多内容");
       m.put("remark", remark);
          sendMessageBefore("", template, m);
          return "";
    
      }

    上述代码中 setTemplate_id 需要改成你自己的模板ID。
    上述代码中用到的方法:

    /**
         * 发送模板消息前获取token
         * @param template_id_short 模板库中模板的编号
         * @param t
         * @param m
         */
        public static void sendMessageBefore(String template_id_short,WxTemplate t,Map<String,TemplateData> m){
            String accessToken = WeChatUtil.getAccessToken();
            System.out.println("accessToKen:         "+accessToken);
            t.setData(m);
            sendMessage(t,accessToken);
        }
        /**
         * 发送模板消息
         * @param t
         * @param accessToken
         * @return
         */
        public static int sendMessage(WxTemplate t,String accessToken) {
            int result = 0;
            // 拼装发送模板消息的url
            String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN".replace("ACCESS_TOKEN", accessToken);
            // 将模板对象转换成json字符串
            String jsonMenu = JSONObject.toJSONString(t);
            // 调用接口发送模板
            String response = WeChatApiUtil.httpsRequestToString(url, "POST", jsonMenu);
            JSONObject jsonObject = JSON.parseObject(response);
    //        JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
            if (null != jsonObject) {
                if (0 != jsonObject.getIntValue("errcode")) {
                    result = jsonObject.getIntValue("errcode");
                    System.out.println("发送模板消息失败 errcode:{"
                    +jsonObject.getIntValue("errcode")+"} errmsg:{"+jsonObject.getString("errmsg")+"}");
                }
            }
            return result;
        }
    1. 完成上述代码可以实现“模板“功能:
      在这里插入图片描述

    主要工具类:
    MessageHandlerUtil:

    package com.zhongyitang.vehicle.common.weixin.utils;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.zhongyitang.vehicle.common.weixin.vo.*;
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import javax.servlet.http.HttpServletRequest;
    import java.io.InputStream;
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 消息处理工具类
     * Created By Oliver_Deng
     */
    public class MessageHandlerUtil {
    
    
        /**
         * 解析微信发来的请求(XML)
         *
         * @param request 封装了请求信息的HttpServletRequest对象
         * @return map 解析结果
         * @throws Exception
         */
        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) {
                System.out.println(e.getName() + "|" + e.getText());
                map.put(e.getName(), e.getText());
            }
    
            // 释放资源
            inputStream.close();
            inputStream = null;
            return map;
        }
    
        /**
         * 根据消息类型构造返回消息
         * @param map 封装了解析结果的Map
         * @return responseMessage(响应消息)
         */
        public static String buildResponseMessage(Map map) {
            //响应消息
            String responseMessage = "";
            //得到消息类型
            String msgType = map.get("MsgType").toString();
            System.out.println("MsgType:" + msgType);
            //消息类型
            MessageType messageEnumType = MessageType.valueOf(MessageType.class, msgType.toUpperCase());
            switch (messageEnumType) {
                case TEXT:
                    //处理文本消息
                    responseMessage = handleTextMessage(map);
                    break;
                case IMAGE:
                    //处理图片消息
                    responseMessage = handleImageMessage(map);
                    break;
                case VOICE:
                    //处理语音消息
                    responseMessage = handleVoiceMessage(map);
                    break;
                case VIDEO:
                    //处理视频消息
                    responseMessage = handleVideoMessage(map);
                    break;
                case SHORTVIDEO:
                    //处理小视频消息
                    responseMessage = handleSmallVideoMessage(map);
                    break;
                case LOCATION:
                    //处理位置消息
                    responseMessage = handleLocationMessage(map);
                    break;
                case LINK:
                    //处理链接消息
                    responseMessage = handleLinkMessage(map);
                    break;
                case EVENT:
                    //处理事件消息,用户在关注与取消关注公众号时,微信会向我们的公众号服务器发送事件消息,开发者接收到事件消息后就可以给用户下发欢迎消息
                    responseMessage = handleEventMessage(map);
                default:
                    break;
            }
            //返回响应消息
            return responseMessage;
        }
    
        /**
         * 接收到文本消息后处理
         * @param map 封装了解析结果的Map
         * @return
         */
        private static String handleTextMessage(Map<String, String> map) {
            //响应消息
            String responseMessage;
            // 消息内容
            String content = map.get("Content");
            switch (content) {
                case "文本":
                    String msgText = "欢迎光临飞梦名车\n" +
                            "<a href=\"https://www.baidu.com\">搜索飞梦名车</a>";
                    responseMessage = buildTextMessage(map, msgText);
                    break;
                case "模板":
                   responseMessage = buildTempMessage(map);
                    break;
                case "图片":
                    //通过素材管理接口上传图片时得到的media_id
                    String imgMediaId = "_Oeog5ScPfhcDtpOvCCwZMDDrA5mjS45NrxmtgYDqgv9ABQ9aqPtMAc2jq-66SOu";
                    responseMessage = buildImageMessage(map, imgMediaId);
                    break;
                case "图文":
                    responseMessage = buildNewsMessage(map);
                    break;
                case "音乐":
                    Music music = new Music();
                    music.title = "赵丽颖、许志安 - 乱世俱灭";
                    music.description = "电视剧《蜀山战纪》插曲";
                    music.musicUrl = "http://gacl.ngrok.natapp.cn/media/music/music.mp3";
                    music.hqMusicUrl = "http://gacl.ngrok.natapp.cn/media/music/music.mp3";
                    responseMessage = buildMusicMessage(map, music);
                    break;
                default:
                    responseMessage = buildWelcomeTextMessage(map);
                    break;
    
            }
            //返回响应消息
            return responseMessage;
        }
    
        /**
         * 生成消息创建时间 (整型)
         * @return 消息创建时间
         */
        private static String getMessageCreateTime() {
            Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是当前系统时间
            DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 设置显示格式
            String nowTime = df.format(dt);
            long dd = (long) 0;
            try {
                dd = df.parse(nowTime).getTime();
            } catch (Exception e) {
    
            }
            return String.valueOf(dd);
        }
    
    
        /**
         * 构建提示消息
         * @param map 封装了解析结果的Map
         * @return responseMessageXml
         */
        private static String buildWelcomeTextMessage(Map<String, String> map) {
            String responseMessageXml;
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
            responseMessageXml = String
                    .format(
                            "<xml>" +
                                    "<ToUserName><![CDATA[%s]]></ToUserName>" +
                                    "<FromUserName><![CDATA[%s]]></FromUserName>" +
                                    "<CreateTime>%s</CreateTime>" +
                                    "<MsgType><![CDATA[text]]></MsgType>" +
                                    "<Content><![CDATA[%s]]></Content>" +
                                    "</xml>",
                            fromUserName, toUserName, getMessageCreateTime(),
                            "感谢您关注我的公众号,请回复如下关键词来使用公众号提供的服务:\n文本\n模板\n图片\n音乐\n图文");
            return responseMessageXml;
        }
    
        /**
         * 构造文本消息
         * @param map 封装了解析结果的Map
         * @param content 文本消息内容
         * @return 文本消息XML字符串
         */
        private static String buildTextMessage(Map<String, String> map, String content) {
            //发送方帐号
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
            /**
             * 文本消息XML数据格式
             * <xml>
             <ToUserName><![CDATA[toUser]]></ToUserName>
             <FromUserName><![CDATA[fromUser]]></FromUserName>
             <CreateTime>1348831860</CreateTime>
             <MsgType><![CDATA[text]]></MsgType>
             <Content><![CDATA[this is a test]]></Content>
             <MsgId>1234567890123456</MsgId>
             </xml>
             */
            return String.format(
                    "<xml>" +
                            "<ToUserName><![CDATA[%s]]></ToUserName>" +
                            "<FromUserName><![CDATA[%s]]></FromUserName>" +
                            "<CreateTime>%s</CreateTime>" +
                            "<MsgType><![CDATA[text]]></MsgType>" +
                            "<Content><![CDATA[%s]]></Content>" +
                            "</xml>",
                    fromUserName, toUserName, getMessageCreateTime(), content);
        }
        /**
         * 发送模板消息调用实例
         * @param map 封装了解析结果的Map
        * @return 空 
         */
        private static String buildTempMessage(Map<String, String> map) {
           //发送方帐号
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
           WxTemplate template = new WxTemplate();
           template.setUrl("www.baidu.com");
           template.setTouser(fromUserName);
           template.setTopcolor("#000000");
           template.setTemplate_id("9E9deiteIUNNJADRPJa5A5TbcrqFRK0UmsvIG2-0RbE"); //模板ID
           Map<String,TemplateData> m = new HashMap<String, TemplateData>();
           TemplateData first = new TemplateData();
           first.setColor("#000000");
           first.setValue("您好,您车牌号为"京56K453"的车辆有新的违章信息");
           m.put("first", first);
           TemplateData keyword1 = new TemplateData();
           keyword1.setColor("#328392");
           keyword1.setValue("2014年5月07日 16:00");
           m.put("violationTime", keyword1);
           TemplateData keyword2 = new TemplateData();
           keyword2.setColor("#328392");
           keyword2.setValue("北京市西城区开阳桥北天桥北向南");
           m.put("violationAddress", keyword2);
           TemplateData keyword3 = new TemplateData();
           keyword3.setColor("#328392");
           keyword3.setValue("不按规定停车");
           m.put("violationType", keyword3);
            TemplateData keyword4 = new TemplateData();
            keyword4.setColor("#328392");
            keyword4.setValue("200元");
            m.put("violationFine", keyword4);
            TemplateData keyword5 = new TemplateData();
            keyword5.setColor("#328392");
            keyword5.setValue("扣3分");
            m.put("violationPoints", keyword5);
           TemplateData remark = new TemplateData();
           remark.setColor("#929232");
           remark.setValue("点击查看更多内容");
           m.put("remark", remark);
            sendMessageBefore("", template, m);
            return "";
    
           
        }
        /**
         * 发送模板消息前获取token
         * @param template_id_short 模板库中模板的编号
         * @param t
         * @param m
         */
        public static void sendMessageBefore(String template_id_short,WxTemplate t,Map<String,TemplateData> m){
            String accessToken = WeChatUtil.getAccessToken();
            System.out.println("accessToKen:         "+accessToken);
            t.setData(m);
            sendMessage(t,accessToken);
        }
        /**
         * 发送模板消息
         * @param t
         * @param accessToken
         * @return
         */
        public static int sendMessage(WxTemplate t,String accessToken) {
            int result = 0;
            // 拼装发送模板消息的url
            String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN".replace("ACCESS_TOKEN", accessToken);
            // 将模板对象转换成json字符串
            String jsonMenu = JSONObject.toJSONString(t);
            // 调用接口发送模板
            String response = WeChatApiUtil.httpsRequestToString(url, "POST", jsonMenu);
            JSONObject jsonObject = JSON.parseObject(response);
    //        JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
            if (null != jsonObject) {
                if (0 != jsonObject.getIntValue("errcode")) {
                    result = jsonObject.getIntValue("errcode");
                    System.out.println("发送模板消息失败 errcode:{"
                    +jsonObject.getIntValue("errcode")+"} errmsg:{"+jsonObject.getString("errmsg")+"}");
                }
            }
            return result;
        }
        /**
         * 构造图片消息
         * @param map 封装了解析结果的Map
         * @param mediaId 通过素材管理接口上传多媒体文件得到的id
         * @return 图片消息XML字符串
         */
        private static String buildImageMessage(Map<String, String> map, String mediaId) {
            //发送方帐号
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
            /**
             * 图片消息XML数据格式
             *<xml>
             <ToUserName><![CDATA[toUser]]></ToUserName>
             <FromUserName><![CDATA[fromUser]]></FromUserName>
             <CreateTime>12345678</CreateTime>
             <MsgType><![CDATA[image]]></MsgType>
             <Image>
             <MediaId><![CDATA[media_id]]></MediaId>
             </Image>
             </xml>
             */
            return String.format(
                    "<xml>" +
                            "<ToUserName><![CDATA[%s]]></ToUserName>" +
                            "<FromUserName><![CDATA[%s]]></FromUserName>" +
                            "<CreateTime>%s</CreateTime>" +
                            "<MsgType><![CDATA[image]]></MsgType>" +
                            "<Image>" +
                            "   <MediaId><![CDATA[%s]]></MediaId>" +
                            "</Image>" +
                            "</xml>",
                    fromUserName, toUserName, getMessageCreateTime(), mediaId);
        }
    
        /**
         * 构造音乐消息
         * @param map 封装了解析结果的Map
         * @param music 封装好的音乐消息内容
         * @return 音乐消息XML字符串
         */
        private static String buildMusicMessage(Map<String, String> map, Music music) {
            //发送方帐号
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
            /**
             * 音乐消息XML数据格式
             *<xml>
             <ToUserName><![CDATA[toUser]]></ToUserName>
             <FromUserName><![CDATA[fromUser]]></FromUserName>
             <CreateTime>12345678</CreateTime>
             <MsgType><![CDATA[music]]></MsgType>
             <Music>
             <Title><![CDATA[TITLE]]></Title>
             <Description><![CDATA[DESCRIPTION]]></Description>
             <MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
             <HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
             <ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
             </Music>
             </xml>
             */
            return String.format(
                    "<xml>" +
                            "<ToUserName><![CDATA[%s]]></ToUserName>" +
                            "<FromUserName><![CDATA[%s]]></FromUserName>" +
                            "<CreateTime>%s</CreateTime>" +
                            "<MsgType><![CDATA[music]]></MsgType>" +
                            "<Music>" +
                            "   <Title><![CDATA[%s]]></Title>" +
                            "   <Description><![CDATA[%s]]></Description>" +
                            "   <MusicUrl><![CDATA[%s]]></MusicUrl>" +
                            "   <HQMusicUrl><![CDATA[%s]]></HQMusicUrl>" +
                            "</Music>" +
                            "</xml>",
                    fromUserName, toUserName, getMessageCreateTime(), music.title, music.description, music.musicUrl, music.hqMusicUrl);
        }
    
        /**
         * 构造视频消息
         * @param map 封装了解析结果的Map
         * @param video 封装好的视频消息内容
         * @return 视频消息XML字符串
         */
        private static String buildVideoMessage(Map<String, String> map, Video video) {
            //发送方帐号
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
            /**
             * 音乐消息XML数据格式
             *<xml>
             <ToUserName><![CDATA[toUser]]></ToUserName>
             <FromUserName><![CDATA[fromUser]]></FromUserName>
             <CreateTime>12345678</CreateTime>
             <MsgType><![CDATA[video]]></MsgType>
             <Video>
             <MediaId><![CDATA[media_id]]></MediaId>
             <Title><![CDATA[title]]></Title>
             <Description><![CDATA[description]]></Description>
             </Video>
             </xml>
             */
            return String.format(
                    "<xml>" +
                            "<ToUserName><![CDATA[%s]]></ToUserName>" +
                            "<FromUserName><![CDATA[%s]]></FromUserName>" +
                            "<CreateTime>%s</CreateTime>" +
                            "<MsgType><![CDATA[video]]></MsgType>" +
                            "<Video>" +
                            "   <MediaId><![CDATA[%s]]></MediaId>" +
                            "   <Title><![CDATA[%s]]></Title>" +
                            "   <Description><![CDATA[%s]]></Description>" +
                            "</Video>" +
                            "</xml>",
                    fromUserName, toUserName, getMessageCreateTime(), video.mediaId, video.title, video.description);
        }
    
        /**
         * 构造语音消息
         * @param map 封装了解析结果的Map
         * @param mediaId 通过素材管理接口上传多媒体文件得到的id
         * @return 语音消息XML字符串
         */
        private static String buildVoiceMessage(Map<String, String> map, String mediaId) {
            //发送方帐号
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
            /**
             * 语音消息XML数据格式
             *<xml>
             <ToUserName><![CDATA[toUser]]></ToUserName>
             <FromUserName><![CDATA[fromUser]]></FromUserName>
             <CreateTime>12345678</CreateTime>
             <MsgType><![CDATA[voice]]></MsgType>
             <Voice>
             <MediaId><![CDATA[media_id]]></MediaId>
             </Voice>
             </xml>
             */
            return String.format(
                    "<xml>" +
                            "<ToUserName><![CDATA[%s]]></ToUserName>" +
                            "<FromUserName><![CDATA[%s]]></FromUserName>" +
                            "<CreateTime>%s</CreateTime>" +
                            "<MsgType><![CDATA[voice]]></MsgType>" +
                            "<Voice>" +
                            "   <MediaId><![CDATA[%s]]></MediaId>" +
                            "</Voice>" +
                            "</xml>",
                    fromUserName, toUserName, getMessageCreateTime(), mediaId);
        }
    
        /**
         * 构造图文消息
         * @param map 封装了解析结果的Map
         * @return 图文消息XML字符串
         */
        private static String buildNewsMessage(Map<String, String> map) {
            String fromUserName = map.get("FromUserName");
            // 开发者微信号
            String toUserName = map.get("ToUserName");
            NewsItem item = new NewsItem();
            item.Title = "微信开发学习总结(一)——微信开发环境搭建";
            item.Description = "工欲善其事,必先利其器。要做微信公众号开发,那么要先准备好两样必不可少的东西:\n" +
                    "\n" +
                    "  1、要有一个用来测试的公众号。\n" +
                    "\n" +
                    "  2、用来调式代码的开发环境";
            item.PicUrl = "http://images2015.cnblogs.com/blog/289233/201601/289233-20160121164317343-2145023644.png";
            item.Url = "http://www.cnblogs.com/xdp-gacl/p/5149171.html";
            String itemContent1 = buildSingleItem(item);
    
            NewsItem item2 = new NewsItem();
            item2.Title = "微信开发学习总结(二)——微信开发入门";
            item2.Description = "微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,微信服务器然后将请求转发给我们的应用服务器。应用服务器处理完毕后,将响应数据回发给微信服务器,微信服务器再将具体响应信息回复到微信App终端。";
            item2.PicUrl = "";
            item2.Url = "http://www.cnblogs.com/xdp-gacl/p/5151857.html";
            String itemContent2 = buildSingleItem(item2);
    
    
            String content = String.format("<xml>\n" +
                    "<ToUserName><![CDATA[%s]]></ToUserName>\n" +
                    "<FromUserName><![CDATA[%s]]></FromUserName>\n" +
                    "<CreateTime>%s</CreateTime>\n" +
                    "<MsgType><![CDATA[news]]></MsgType>\n" +
                    "<ArticleCount>%s</ArticleCount>\n" +
                    "<Articles>\n" + "%s" +
                    "</Articles>\n" +
                    "</xml> ", fromUserName, toUserName, getMessageCreateTime(), 2, itemContent1 + itemContent2);
            return content;
    
        }
    
        /**
         * 生成图文消息的一条记录
         *
         * @param item
         * @return
         */
        private static String buildSingleItem(NewsItem item) {
            String itemContent = String.format("<item>\n" +
                    "<Title><![CDATA[%s]]></Title> \n" +
                    "<Description><![CDATA[%s]]></Description>\n" +
                    "<PicUrl><![CDATA[%s]]></PicUrl>\n" +
                    "<Url><![CDATA[%s]]></Url>\n" +
                    "</item>", item.Title, item.Description, item.PicUrl, item.Url);
            return itemContent;
        }
    
    
        /**
         * 处理接收到图片消息
         *
         * @param map
         * @return
         */
        private static String handleImageMessage(Map<String, String> map) {
            String picUrl = map.get("PicUrl");
            String mediaId = map.get("MediaId");
            System.out.print("picUrl:" + picUrl);
            System.out.print("mediaId:" + mediaId);
            String result = String.format("已收到您发来的图片,图片Url为:%s\n图片素材Id为:%s", picUrl, mediaId);
            return buildTextMessage(map, result);
        }
    
        /**
         * 处理接收到语音消息
         * @param map
         * @return
         */
        private static String handleVoiceMessage(Map<String, String> map) {
            String format = map.get("Format");
            String mediaId = map.get("MediaId");
            System.out.print("format:" + format);
            System.out.print("mediaId:" + mediaId);
            String result = String.format("已收到您发来的语音,语音格式为:%s\n语音素材Id为:%s", format, mediaId);
            return buildTextMessage(map, result);
        }
    
        /**
         * 处理接收到的视频消息
         * @param map
         * @return
         */
        private static String handleVideoMessage(Map<String, String> map) {
            String thumbMediaId = map.get("ThumbMediaId");
            String mediaId = map.get("MediaId");
            System.out.print("thumbMediaId:" + thumbMediaId);
            System.out.print("mediaId:" + mediaId);
            String result = String.format("已收到您发来的视频,视频中的素材ID为:%s\n视频Id为:%s", thumbMediaId, mediaId);
            return buildTextMessage(map, result);
        }
    
        /**
         * 处理接收到的小视频消息
         * @param map
         * @return
         */
        private static String handleSmallVideoMessage(Map<String, String> map) {
            String thumbMediaId = map.get("ThumbMediaId");
            String mediaId = map.get("MediaId");
            System.out.print("thumbMediaId:" + thumbMediaId);
            System.out.print("mediaId:" + mediaId);
            String result = String.format("已收到您发来的小视频,小视频中素材ID为:%s,\n小视频Id为:%s", thumbMediaId, mediaId);
            return buildTextMessage(map, result);
        }
    
        /**
         * 处理接收到的地理位置消息
         * @param map
         * @return
         */
        private static String handleLocationMessage(Map<String, String> map) {
            String latitude = map.get("Location_X");  //纬度
            String longitude = map.get("Location_Y");  //经度
            String label = map.get("Label");  //地理位置精度
            String result = String.format("纬度:%s\n经度:%s\n地理位置:%s", latitude, longitude, label);
            return buildTextMessage(map, result);
        }
    
        /**
         * 处理接收到的链接消息
         * @param map
         * @return
         */
        private static String handleLinkMessage(Map<String, String> map) {
            String title = map.get("Title");
            String description = map.get("Description");
            String url = map.get("Url");
            String result = String.format("已收到您发来的链接,链接标题为:%s,\n描述为:%s\n,链接地址为:%s", title, description, url);
            return buildTextMessage(map, result);
        }
    
        /**
         * 处理消息Message
         * @param map 封装了解析结果的Map
         * @return
         */
        private static String handleEventMessage(Map<String, String> map) {
           String content = map.get("Event");
           String responseMessage;
           switch (content) {
           case "SCAN"://扫码进入
               responseMessage="";
                break;
           case "SUBSCRIBE"://关注
               responseMessage=buildWelcomeTextMessage(map);
                break;
            case "TEMPLATESENDJOBFINISH"://模板发送完成
               responseMessage="";
                break;
                
            default:
                responseMessage = buildWelcomeTextMessage(map);
                break;
    
           }
            return responseMessage;
        }
    
    }
    
    

    HTTPUtils:

    package com.zhongyitang.vehicle.common.weixin.utils;
    
    import java.io.BufferedReader;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    /**
     * By Oliver_Deng
     */
    public class HTTPUtils {
    
        private static final int TIMEOUT_IN_MILLIONS = 5000;
    
        public interface CallBack
        {
            void onRequestComplete(String result);
        }
    
    
        /**
         * 异步的Get请求
         *
         * @param urlStr
         * @param callBack
         */
        public static void doGetAsyn(final String urlStr, final CallBack callBack)
        {
            new Thread()
            {
                public void run()
                {
                    try
                    {
                        String result = doGet(urlStr);
                        if (callBack != null)
                        {
                            callBack.onRequestComplete(result);
                        }
                    } catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                };
            }.start();
        }
    
        /**
         * 异步的Post请求
         * @param urlStr
         * @param params
         * @param callBack
         * @throws Exception
         */
        public static void doPostAsyn(final String urlStr, final String params,
                                      final CallBack callBack) throws Exception
        {
            new Thread()
            {
                public void run()
                {
                    try
                    {
                        String result = doPost(urlStr, params);
                        if (callBack != null)
                        {
                            callBack.onRequestComplete(result);
                        }
                    } catch (Exception e)
                    {
                        e.printStackTrace();
                    }
    
                };
            }.start();
    
        }
    
        /**
         * Get请求,获得返回数据
         *
         * @param urlStr
         * @return
         * @throws Exception
         */
        public static String doGet(String urlStr)
        {
            URL url = null;
            HttpURLConnection conn = null;
            InputStream is = null;
            ByteArrayOutputStream baos = null;
            try
            {
                url = new URL(urlStr);
                conn = (HttpURLConnection) url.openConnection();
                conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
                conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
                conn.setRequestMethod("GET");
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("connection", "Keep-Alive");
                if (conn.getResponseCode() == 200)
                {
                    is = conn.getInputStream();
                    baos = new ByteArrayOutputStream();
                    int len = -1;
                    byte[] buf = new byte[128];
    
                    while ((len = is.read(buf)) != -1)
                    {
                        baos.write(buf, 0, len);
                    }
                    baos.flush();
                    return baos.toString();
                } else
                {
                    throw new RuntimeException(" responseCode is not 200 ... ");
                }
    
            } catch (Exception e)
            {
                e.printStackTrace();
            } finally
            {
                try
                {
                    if (is != null)
                        is.close();
                } catch (IOException e)
                {
                }
                try
                {
                    if (baos != null)
                        baos.close();
                } catch (IOException e)
                {
                }
                conn.disconnect();
            }
    
            return null ;
    
        }
    
        /**
         * 向指定 URL 发送POST方法的请求
         *
         * @param url
         *            发送请求的 URL
         * @param param
         *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
         * @return 所代表远程资源的响应结果
         * @throws Exception
         */
        public static String doPost(String url, String param)
        {
            PrintWriter out = null;
            BufferedReader in = null;
            String result = "";
            try
            {
                URL realUrl = new URL(url);
                // 打开和URL之间的连接
                HttpURLConnection conn = (HttpURLConnection) realUrl
                        .openConnection();
                // 设置通用的请求属性
                conn.setRequestProperty("accept", "*/*");
                conn.setRequestProperty("connection", "Keep-Alive");
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type",
                        "application/x-www-form-urlencoded");
                conn.setRequestProperty("charset", "utf-8");
                conn.setUseCaches(false);
                // 发送POST请求必须设置如下两行
                conn.setDoOutput(true);
                conn.setDoInput(true);
                conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
                conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
    
                if (param != null && !param.trim().equals(""))
                {
                    // 获取URLConnection对象对应的输出流
                    out = new PrintWriter(conn.getOutputStream());
                    // 发送请求参数
                    out.print(param);
                    // flush输出流的缓冲
                    out.flush();
                }
                // 定义BufferedReader输入流来读取URL的响应
                in = new BufferedReader(
                        new InputStreamReader(conn.getInputStream()));
                String line;
                while ((line = in.readLine()) != null)
                {
                    result += line;
                }
            } catch (Exception e)
            {
                e.printStackTrace();
            }
            // 使用finally块来关闭输出流、输入流
            finally
            {
                try
                {
                    if (out != null)
                    {
                        out.close();
                    }
                    if (in != null)
                    {
                        in.close();
                    }
                } catch (IOException ex)
                {
                    ex.printStackTrace();
                }
            }
            return result;
        }
    }
    

    MessageType:

    package com.zhongyitang.vehicle.common.weixin.utils;
    
    /**
     * 接收到的消息类型
     * By Oliver_Deng
     */
    public enum MessageType {
        TEXT,//文本消息
        IMAGE,//图片消息
        VOICE,//语音消息
        VIDEO,//视频消息
        SHORTVIDEO,//小视频消息
        LOCATION,//地理位置消息
        LINK,//链接消息
        EVENT//事件消息
    }
    

    WeChatApiUtil:

    package com.zhongyitang.vehicle.common.weixin.utils;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONException;
    import com.alibaba.fastjson.JSONObject;
    
    import javax.net.ssl.*;
    import java.io.*;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    /**
     * Created by Oliver_Deng
     */
    public class WeChatApiUtil {
        // token 接口(GET)
        private static final String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
        // 素材上传(POST)https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
        private static final String UPLOAD_MEDIA = "https://api.weixin.qq.com/cgi-bin/media/upload";
        // 素材下载:不支持视频文件的下载(GET)
        private static final String DOWNLOAD_MEDIA = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s";
    
        public static String getTokenUrl(String appId, String appSecret) {
            return String.format(ACCESS_TOKEN, appId, appSecret);
        }
    
        public static String getDownloadUrl(String token, String mediaId) {
            return String.format(DOWNLOAD_MEDIA, token, mediaId);
        }
    
        /**
         * 通用接口获取Token凭证
         *
         * @param appId
         * @param appSecret
         * @return
         */
        public static String getToken(String appId, String appSecret) {
            if (appId == null || appSecret == null) {
                return null;
            }
    
            String token = null;
            String tockenUrl = WeChatApiUtil.getTokenUrl(appId, appSecret);
            String response = httpsRequestToString(tockenUrl, "GET", null);
            JSONObject jsonObject = JSON.parseObject(response);
            if (null != jsonObject) {
                try {
                    token = jsonObject.getString("access_token");
                } catch (JSONException e) {
                    token = null;// 获取token失败
                }
            }
            return token;
        }
    
        /**
         * 微信服务器素材上传
         *
         * @param file  表单名称media
         * @param token access_token
         * @param type  type只支持四种类型素材(video/image/voice/thumb)
         */
    //    public static JSONObject uploadMedia(File file, String token, String type) {
    //        if (file == null || token == null || type == null) {
    //            return null;
    //        }
    //
    //        if (!file.exists()) {
    //            System.out.println("上传文件不存在,请检查!");
    //            return null;
    //        }
    //
    //        String url = UPLOAD_MEDIA;
    //        JSONObject jsonObject = null;
    //        PostMethod post = new PostMethod(url);
    //        post.setRequestHeader("Connection", "Keep-Alive");
    //        post.setRequestHeader("Cache-Control", "no-cache");
    //        FilePart media;
    //        HttpClient httpClient = new HttpClient();
    //        //信任任何类型的证书
    //        Protocol myhttps = new Protocol("https", new SSLProtocolSocketFactory(), 443);
    //        Protocol.registerProtocol("https", myhttps);
    //
    //        try {
    //            media = new FilePart("media", file);
    //            Part[] parts = new Part[]{new StringPart("access_token", token),
    //                    new StringPart("type", type), media};
    //            MultipartRequestEntity entity = new MultipartRequestEntity(parts,
    //                    post.getParams());
    //            post.setRequestEntity(entity);
    //            int status = httpClient.executeMethod(post);
    //            if (status == HttpStatus.SC_OK) {
    //                String text = post.getResponseBodyAsString();
    //                jsonObject = JSONObject.parseObject(text);
    //            } else {
    //                System.out.println("upload Media failure status is:" + status);
    //            }
    //        } catch (FileNotFoundException e) {
    //            e.printStackTrace();
    //        } catch (HttpException e) {
    //            e.printStackTrace();
    //        } catch (IOException e) {
    //            e.printStackTrace();
    //        }
    //        return jsonObject;
    //    }
    
        /**
         * 多媒体下载接口
         *
         * @param fileName 素材存储文件路径
         * @param token    认证token
         * @param mediaId  素材ID(对应上传后获取到的ID)
         * @return 素材文件
         * @comment 不支持视频文件的下载
         */
        public static File downloadMedia(String fileName, String token,
                                         String mediaId) {
            String url = getDownloadUrl(token, mediaId);
            return httpRequestToFile(fileName, url, "GET", null);
        }
    
        /**
         * 多媒体下载接口
         *
         * @param fileName 素材存储文件路径
         * @param mediaId  素材ID(对应上传后获取到的ID)
         * @return 素材文件
         * @comment 不支持视频文件的下载
         */
        public static File downloadMedia(String fileName, String mediaId) {
            String appId = "wx2cf6086315216ab0";
            String appSecret = "a4362add70c148da80e7442100782e4e";
            String token = WeChatApiUtil.getToken(appId, appSecret);
            return downloadMedia(fileName,token,mediaId);
        }
    
        /**
         * 以http方式发送请求,并将请求响应内容输出到文件
         *
         * @param path   请求路径
         * @param method 请求方法
         * @param body   请求数据
         * @return 返回响应的存储到文件
         */
        public static File httpRequestToFile(String fileName, String path, String method, String body) {
            if (fileName == null || path == null || method == null) {
                return null;
            }
    
            File file = null;
            HttpURLConnection conn = null;
            InputStream inputStream = null;
            FileOutputStream fileOut = null;
            try {
                URL url = new URL(path);
                conn = (HttpURLConnection) url.openConnection();
                conn.setDoOutput(true);
                conn.setDoInput(true);
                conn.setUseCaches(false);
                conn.setRequestMethod(method);
                if (null != body) {
                    OutputStream outputStream = conn.getOutputStream();
                    outputStream.write(body.getBytes("UTF-8"));
                    outputStream.close();
                }
    
                inputStream = conn.getInputStream();
                if (inputStream != null) {
                    file = new File(fileName);
                } else {
                    return file;
                }
    
                //写入到文件
                fileOut = new FileOutputStream(file);
                if (fileOut != null) {
                    int c = inputStream.read();
                    while (c != -1) {
                        fileOut.write(c);
                        c = inputStream.read();
                    }
                }
            } catch (Exception e) {
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
    
                /*
                 * 必须关闭文件流
                 * 否则JDK运行时,文件被占用其他进程无法访问
                 */
                try {
                    inputStream.close();
                    fileOut.close();
                } catch (IOException execption) {
                }
            }
            return file;
        }
    
        /**
         * 上传素材
         * @param filePath 媒体文件路径(绝对路径)
         * @param type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
         * @return
         */
    //    public static JSONObject uploadMedia(String filePath,String type){
    //        File f = new File(filePath); // 获取本地文件
    //        String appId = "wx2cf6086315216ab0";
    //        String appSecret = "a4362add70c148da80e7442100782e4e";
    //        String token = WeChatApiUtil.getToken(appId, appSecret);
    //        JSONObject jsonObject = uploadMedia(f, token, type);
    //        return jsonObject;
    //    }
    
        /**
         * 发送请求以https方式发送请求并将请求响应内容以String方式返回
         *
         * @param path   请求路径
         * @param method 请求方法
         * @param body   请求数据体
         * @return 请求响应内容转换成字符串信息
         */
        public static String httpsRequestToString(String path, String method, String body) {
            if (path == null || method == null) {
                return null;
            }
    
            String response = null;
            InputStream inputStream = null;
            InputStreamReader inputStreamReader = null;
            BufferedReader bufferedReader = null;
            HttpsURLConnection conn = null;
            try {
                TrustManager[] tm = {new JEEWeiXinX509TrustManager()};
                SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
                sslContext.init(null, tm, new java.security.SecureRandom());
                SSLSocketFactory ssf = sslContext.getSocketFactory();
                System.out.println(path);
                URL url = new URL(path);
                conn = (HttpsURLConnection) url.openConnection();
                conn.setSSLSocketFactory(ssf);
    
                conn.setDoOutput(true);
                conn.setDoInput(true);
                conn.setUseCaches(false);
                conn.setRequestMethod(method);
                if (null != body) {
                    OutputStream outputStream = conn.getOutputStream();
                    outputStream.write(body.getBytes("UTF-8"));
                    outputStream.close();
                }
    
                inputStream = conn.getInputStream();
                inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
                bufferedReader = new BufferedReader(inputStreamReader);
                String str = null;
                StringBuffer buffer = new StringBuffer();
                while ((str = bufferedReader.readLine()) != null) {
                    buffer.append(str);
                }
    
                response = buffer.toString();
            } catch (Exception e) {
    
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
                try {
                    bufferedReader.close();
                    inputStreamReader.close();
                    inputStream.close();
                } catch (IOException execption) {
    
                }
            }
            return response;
        }
    
    //    public static void main(String[] args) throws Exception{
    //        //媒体文件路径
    //        String filePath = "D:/我.jpg";
    //        //String filePath = "D:/JavaSoftwareDevelopeFolder/IntelliJ IDEA_Workspace/WxStudy/web/media/voice/voice.mp3";
    //        //String filePath = "D:\\JavaSoftwareDevelopeFolder\\IntelliJ IDEA_Workspace\\WxStudy\\web\\media\\video\\小苹果.mp4";
    //        //媒体文件类型
    //        String type = "image";
    //        //String type = "voice";
    //        //String type = "video";
    //        JSONObject uploadResult = uploadMedia(filePath, type);
    //        //{"media_id":"dSQCiEHYB-pgi7ib5KpeoFlqpg09J31H28rex6xKgwWrln3HY0BTsoxnRV-xC_SQ","created_at":1455520569,"type":"image"}
    //        System.out.println(uploadResult.toString());
    //
    //        //下载刚刚上传的图片以id命名
    //        String media_id = uploadResult.getString("media_id");
    //        File file = downloadMedia("D:/" + media_id + ".png", media_id);
    //        System.out.println(file.getName());
    //
    //    }
    }
    
    class JEEWeiXinX509TrustManager implements X509TrustManager {
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }
    
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }
    
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

    TokenUtil:

    /*
     * 微信公众平台(JAVA) SDK
     *
     * Copyright (c) 2014, Ansitech Network Technology Co.,Ltd All rights reserved.
     * 
     * http://www.weixin4j.org/
     *
     * Licensed 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 org.weixin4j.util;
    
    import org.weixin4j.Configuration;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    /**
     * <p>
     * Title: 微信公众平台Token算法工具类</p>
     *
     * <p>
     * Description: 为应用提供URL算法 根据不同的URL返回不同的Token,以适应多微站的需求
     * 例如:Url:http://www.weixin4j.org/api/tiexinqiao
     * 则默认Token:为jEvQdLxi0PvtgK8N+HzUpA== 根据配置的系统Token不同,而改变</p>
     *
     * @author yangqisheng
     * @since 0.0.1
     */
    public class TokenUtil {
    
        //此加密密钥用于加密公众号Token,一经配置,不能修改,一旦修改,所有公众号需要重新填写Token
        private static String systemToken = null;
    
        /**
         * 获取配置文件配置的Token
         *
         * @return 微站Token
         */
        public static String get() {
            if (systemToken == null) {
                systemToken = Configuration.getProperty("weixin4j.token", "weixin4j");
            }
            return systemToken;
        }
    
        /**
         * 加密/校验流程如下:
         *
         * <p>
         * 1. 将token、timestamp、nonce三个参数进行字典序排序<br>
         * 2.将三个参数字符串拼接成一个字符串进行sha1加密<br>
         * 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信<br>
         * </p>
         *
         * @param token Token验证密钥
         * @param signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数,nonce参数
         * @param timestamp 时间戳
         * @param nonce 随机数
         * @return 验证成功返回true,否则返回false
         */
        public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {
            List<String> params = new ArrayList<String>();
            params.add(token);
            params.add(timestamp);
            params.add(nonce);
            //1. 将token、timestamp、nonce三个参数进行字典序排序
            Collections.sort(params, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return o1.compareTo(o2);
                }
            });
            //2. 将三个参数字符串拼接成一个字符串进行sha1加密
            String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2));
            //3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
            return temp.equals(signature);
        }
    }
    

    还有上面刚刚发的WeChatUtil。

    用到的VO:

    Music:

    package com.zhongyitang.vehicle.common.weixin.vo;
    
    import lombok.Data;
    
    /**
     * 音乐消息
     * By Oliver_Deng
     */
    @Data
    public class Music {
        public String title;
        public String description;
        public String musicUrl;
        public String hqMusicUrl;
    }
    

    NewsItem:

    package com.zhongyitang.vehicle.common.weixin.vo;
    
    import lombok.Data;
    
    /**
     * 图文消息
     * By Oliver_Deng
     */
    @Data
    public class NewsItem {
        public String Title;
        public String Description;
        public String PicUrl;
        public String Url;
    }
    

    TemplateData:

    package com.zhongyitang.vehicle.common.weixin.vo;
    /**
     * 模板数据
     * @author By Oliver_Deng
     *
     */
    public class TemplateData {
        private String value;//模板显示值
        private String color;//模板显示颜色
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
        public String getColor() {
            return color;
        }
        public void setColor(String color) {
            this.color = color;
        }
    }

    Video:

    package com.zhongyitang.vehicle.common.weixin.vo;
    
    import lombok.Data;
    
    /**
     * 视频消息
     * BY Oliver_Deng
     */
    @Data
    public class Video {
        public String title;
        public String description;
        public String mediaId;
    }
    

    WxTemplate:

    package com.zhongyitang.vehicle.common.weixin.vo;
    
    import java.util.Map;
    
    /**
     * 模板基类
     * @author CLiang
     *
     */
    public class WxTemplate {
        private String template_id;//模板ID
        private String touser;//目标客户
        private String url;//用户点击模板信息的跳转页面
        private String topcolor;//字体颜色
        private Map<String,TemplateData> data;//模板里的数据
         
        public String getTemplate_id() {
            return template_id;
        }
        public void setTemplate_id(String template_id) {
            this.template_id = template_id;
        }
        public String getTouser() {
            return touser;
        }
        public void setTouser(String touser) {
            this.touser = touser;
        }
        public String getUrl() {
            return url;
        }
        public void setUrl(String url) {
            this.url = url;
        }
        public String getTopcolor() {
            return topcolor;
        }
        public void setTopcolor(String topcolor) {
            this.topcolor = topcolor;
        }
        public Map<String,TemplateData> getData() {
            return data;
        }
        public void setData(Map<String,TemplateData> data) {
            this.data = data;
        }
    }

    复制完上述代码即可实现模板、图文、文本、音乐、图片推送。

    参考自博客:https://blog.csdn.net/u011752195/article/details/81675818

    展开全文
  • 本节书摘来自华章出版社《微信小程序:开发入门及案例详解》一 书中的第2章,第2.1节,作者李骏 边思...小程序框架可让开发者在微信中用尽可能简单、高效的方式开发出具有原生App体验的服务,这套框架控制着小程序完...

    本节书摘来自华章出版社《微信小程序:开发入门及案例详解》一 书中的第2章,第2.1节,作者李骏 边思,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

    第2章 小程序开发核心

    上一章讲解了小程序创建流程,本章主要为大家讲解小程序框架及核心内容。小程序框架可让开发者在微信中用尽可能简单、高效的方式开发出具有原生App体验的服务,这套框架控制着小程序完整的生命周期,负责页面的加载、渲染、销毁等工作,它是小程序的核心,学习小程序前,我们一定要对这套框架有深入的了解。本章主要对小程序目录结构、文件类型进行详细分析,重点介绍小程序视图层WXML、MXSS,逻辑层JS,这些是小程序开发的核心内容。本章个别小节内容比较深,学习过程中不必过于深究,能对框架有个整体认识即可。

    2.1 简介

    小程序框架将整个系统划分为视图层和逻辑层,视图层是由框架设计的标签语言WXML(WeiXin Markup Language)和用于描述WXML组件样式的WXSS(WeiXin Style Sheets)组成,它们的关系就像HTML和CSS的关系。WXML和WXSS在渲染时会被框架解析为不同端的本地渲染文件,这样保证一套代码能在多处运行,并且能最大化地接近原生App。渲染原理和React Native、Weex十分接近,开发过程中我们不必深究WXML的渲染原理,只需要有个大致了解即可。小程序逻辑层是一套运行在本地JavaScript引擎的JavaScript代码,在此基础上框架实现了一套模块化机制,让每个JS文件有独立的作用域和模块化能力,这套模块化机制遵循CommonJS规范,熟悉NodeJs的开发者应该有一定了解。

    小程序整体开发流程非常接近前端HTML + CSS +JavaScript的开发模式,与前端开发不同的是,在小程序中没有DOM的概念,在本地的JavaScript引擎中也没有window、document等对象,我们不能想当然地通过操作DOM来操作页面,小程序中的视图层和逻辑层的交互是通过数据绑定和事件响应实现的,这是一种单向绑定的机制。这套机制需要首先将逻辑层和视图层的数据和事件进行绑定,当需要修改页面时,逻辑层只需要调用特定的setData方法修改已绑定的数据,这时框架会自动触发WXML重新渲染,达到逻辑层对视图层的控制;当框架接收到用户交互操作时,会根据视图层绑定的事件,执行逻辑层中对应的事件函数,达到逻辑层对视图层的响应,视图层与逻辑层的关系如图2-1所示。这套机制是小程序框架的工作原理,在后续内容中我们将反复提及,加深大家对它的理解。

    3c4cdde00bed130d987960386572dbd5f23ffea7
    展开全文
  • 1、前面的申请微信支付流程我在这里就不介绍了,今天主要讲的是微信退款这一块。主要是我会把退款的所有代码贡献出来给大家,方便大家可以直接copy到自己工程中就可以使用,不用再去自己写! 2、微信退款 首选需要一...

    这篇文章已经给很多人解决了问题,所以很多人发邮件给我要项目,我想说:

    最近很多人发邮件给我要MobilePay。我想说 自己加群下载,群链接会点吧?有问题随时群里问我!群主。这项目中包含了支付宝支付、退款、微信支付、退款。

    加群加群...

    作者:HONGLINCHEN.上海·黄埔

    作者邮箱:xychenhonglin@163.com

    请关注作者的今日头条号:《专注JavaWeb开发》 博客地址:

    请关注作者的CSDN博客:《专注JavaWeb开发》 博客地址:

    作者的QQ群:专注JavaWeb开发.群号:162582394

    1、前面的申请微信支付流程我在这里就不介绍了,今天主要讲的是微信退款这一块。主要是我会把退款的所有代码贡献出来给大家,方便大家可以直接copy到自己工程中就可以使用,不用再去自己写!

    2、微信退款 首选需要一个商户证书,可以百度“微信商户平台”,点击api安全去下载这个证书,证书名字是“apiclient_cert.p12”

    3、随后就是代码片段

    这些代码copy下来的确是可以跑起来,小编不像有些人,贴代码的时候不贴import的jar和pom文件,导致别人copy下来一堆jar找不到(请叫我良心小编)...大家在手机端看的时候可能代码过于长,大家可以主要看核心代码部分,生成签名,生成订单,这类代码!有什么疑问随时私我。下面有好东西哦!

    ClientCustomSSL类代码如下

    package com.mobilepay.wxpay.utils;
    import com.alibaba.fastjson.JSONObject;
    import com.mobilepay.wxpay.constants.WXPayConstants;
    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 org.dom4j.Document;
    import org.dom4j.DocumentHelper;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import javax.net.ssl.SSLContext;
    import java.io.File;
    import java.io.FileInputStream;
    import java.security.KeyStore;
    
    /**
     * @Author: HONGLINCHEN
     * @Description: 微信退款
     * @Date: 2017-9-12 13:15
     */
    public class ClientCustomSSL {
        /**
         * @Author: HONGLINCHEN
         * @Description:微信退款方法封装   注意::微信金额的单位是分 所以这里要X100 转成int是因为 退款的时候不能有小数点
         * @param merchantNumber 商户这边的订单号
         * @param wxTransactionNumber 微信那边的交易单号
         * @param totalFee 订单的金额
         * @Date: 2017-9-12 11:18
         */
        public static Object setUrl(String merchantNumber,String wxTransactionNumber,double totalFee) {
            try{
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                FileInputStream instream = new FileInputStream(new File("D:\\微信商户平台支付证书\\apiclient_cert.p12"));
                try {
                    keyStore.load(instream, WXPayConstants.MCH_ID.toCharArray());
                }finally {
                    instream.close();
                }
                // Trust own CA and all self-signed certs
                SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WXPayConstants.MCH_ID.toCharArray()).build();
                // Allow TLSv1 protocol only
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                        sslcontext, new String[] { "TLSv1" }, null,
                        SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                CloseableHttpClient httpclient = HttpClients.custom()
                        .setSSLSocketFactory(sslsf).build();
                HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
                String xml = com.mobilepay.wxpay.wxpayutils.WXPayUtil.wxPayRefund(merchantNumber,wxTransactionNumber,String.valueOf((int)(totalFee*100)));
    
                try {
                    StringEntity se = new StringEntity(xml);
                    httppost.setEntity(se);
                    System.out.println("executing request" + httppost.getRequestLine());
                    CloseableHttpResponse responseEntry = httpclient.execute(httppost);
                    try {
                        HttpEntity entity = responseEntry.getEntity();
                        System.out.println(responseEntry.getStatusLine());
                        if (entity != null) {
                            System.out.println("Response content length: "
                                    + entity.getContentLength());
                            SAXReader saxReader = new SAXReader();
                            Document document = saxReader.read(entity.getContent());
                            Element rootElt = document.getRootElement();
                            System.out.println("根节点:" + rootElt.getName());
                            System.out.println("==="+rootElt.elementText("result_code"));
                            System.out.println("==="+rootElt.elementText("return_msg"));
                            String resultCode = rootElt.elementText("result_code");
                            JSONObject result = new JSONObject();
    
                            Document documentXml = DocumentHelper.parseText(xml);
                            Element rootEltXml = documentXml.getRootElement();
                            if(resultCode.equals("SUCCESS")){
                                System.out.println("=================prepay_id===================="+ rootElt.elementText("prepay_id"));
                                System.out.println("=================sign===================="+ rootEltXml.elementText("sign"));
                                result.put("weixinPayUrl", rootElt.elementText("code_url"));
                                result.put("prepayId", rootElt.elementText("prepay_id"));
                                result.put("status","success");
                                result.put("msg","success");
                            }else{
                                result.put("status","false");
                                result.put("msg",rootElt.elementText("err_code_des"));
                            }
                            return result;
                        }
                        EntityUtils.consume(entity);
                    }
                    finally {
                        responseEntry.close();
                    }
                }
                finally {
                    httpclient.close();
                }
                return null;
            }catch(Exception e){
                e.printStackTrace();
                JSONObject result = new JSONObject();
                result.put("status","error");
                result.put("msg",e.getMessage());
                return result;
            }
        }
    
    }



    WXPayUtil类代码如下
    package com.mobilepay.wxpay.wxpayutils;
    import com.mobilepay.wxpay.constants.WXPayConstants;
    import com.mobilepay.wxpay.utils.MD5Util;
    import org.apache.commons.httpclient.HttpClient;
    import org.apache.commons.httpclient.methods.PostMethod;
    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.util.EntityUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import javax.net.ssl.SSLContext;
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import java.io.*;
    import java.security.KeyStore;
    import java.security.MessageDigest;
    import java.util.*;
    
    /**
     * @Author: HONGLINCHEN
     * @Description:微信支付
     * @Date: 2017-9-7 17:14
     */
    public class WXPayUtil {
        public static String PostRequest(String url, String data) throws IOException {
            HttpClient client = new HttpClient();
            PostMethod post=new PostMethod(url);
            String result = "";
            post.addRequestHeader("Content-Type", "text/html; charset=utf-8");
            post.addRequestHeader("content", "text/html; charset=utf-8");
            post.setRequestBody(data);
            try {
                int status=client.executeMethod(post);
                result = post.getResponseBodyAsString();
                result = new String(result.getBytes(post.getResponseCharSet()), "utf-8");
            } catch (IOException e){
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return result;
        }
    
        /**
         * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
         */
        public static String createSign(SortedMap<String, String> packageParams, String AppKey) {
            StringBuffer sb = new StringBuffer();
            Set es = packageParams.entrySet();
            Iterator it = es.iterator();
            while (it.hasNext()) {
                Map.Entry entry = (Map.Entry) it.next();
                String k = (String) entry.getKey();
                String v = (String) entry.getValue();
                if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                    sb.append(k + "=" + v + "&");
                }
            }
            sb.append("key=" + AppKey);
            String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
            return sign;
        }
        /**
         * @Author: HONGLINCHEN
         * @Description:微信支付 统一下单
         * @param out_trade_no
         * @param body
         * @param detail
         * @param total_fee
         * @param ip_address
         * @Date: 2017-9-11 14:35
         * @return:
         */
        public static String unifiedOrder(String out_trade_no, String body, String detail, int total_fee,String ip_address) {
            StringBuffer xml = new StringBuffer();
            String data = null;
            try{
                xml.append("</xml>");
                if (body.length() > 32) {
                    body = body.substring(0, 32);
                }
                SortedMap<String, String> parameters = new TreeMap();
                parameters.put("appid", WXPayConstants.APP_ID);
                parameters.put("body", body);
                parameters.put("detail", detail);
                parameters.put("mch_id", WXPayConstants.MCH_ID);
                parameters.put("nonce_str", genNonceStr());
                parameters.put("notify_url", "http://www.aidongsports.com/wx");
                parameters.put("out_trade_no", out_trade_no);
                parameters.put("fee_type", "CNY");
                parameters.put("spbill_create_ip", ip_address);
                parameters.put("total_fee", String.valueOf(total_fee));
                parameters.put("trade_type", "APP");
                parameters.put("sign", createSign(parameters, WXPayConstants.API_KEY));
                data = PostRequest("https://api.mch.weixin.qq.com/pay/unifiedorder",SortedMaptoXml(parameters));
            }catch (Exception e){
                e.printStackTrace();
            }
            return data;
        }
        /**
         * @Author: HONGLINCHEN
         * @Description:微信退款
         * @param out_trade_no
         * @param total_fee
         * @Date: 2017-9-11 14:35
         * @return:
         */
        public static String wxPayRefund(String out_trade_no, String transaction_id,String total_fee) {
            StringBuffer xml = new StringBuffer();
            String data = null;
            try {
                String nonceStr = genNonceStr();
                xml.append("</xml>");
                SortedMap<String,String> parameters = new TreeMap<String,String>();
                parameters.put("appid", WXPayConstants.APP_ID);
                parameters.put("mch_id", WXPayConstants.MCH_ID);
                parameters.put("nonce_str", nonceStr);
                parameters.put("out_trade_no", out_trade_no);
                parameters.put("transaction_id", transaction_id);
                parameters.put("out_refund_no", nonceStr);
                parameters.put("fee_type", "CNY");
                parameters.put("total_fee", total_fee);
                parameters.put("refund_fee", total_fee);
                parameters.put("op_user_id", WXPayConstants.MCH_ID);
                parameters.put("sign", createSign(parameters, WXPayConstants.API_KEY));
                data =SortedMaptoXml(parameters);
            } catch (Exception e) {
                System.err.println(e.getMessage());
                return null;
            }
            return data;
        }
        /**
         * 证书使用
         * 微信退款
         */
        public static String wxPayBack(String url, String data) throws Exception {
            KeyStore keyStore  = KeyStore.getInstance("PKCS12");
            FileInputStream instream = new FileInputStream(new File("D:\\微信商户平台支付证书\\apiclient_cert.p12"));
            String result="";
            try {
                keyStore.load(instream, WXPayConstants.MCH_ID.toCharArray());
            } finally {
                instream.close();
            }
    
            // Trust own CA and all self-signed certs
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, WXPayConstants.MCH_ID.toCharArray())
                    .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 httppost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
                StringEntity entitys = new StringEntity(data);
                httppost.setEntity((HttpEntity) entitys);
                CloseableHttpResponse response = httpclient.execute(httppost);
                try {
                    HttpEntity entity = response.getEntity();
    
                    if (entity != null) {
                        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                        String text="";
                        String t="";
                        while ((text=bufferedReader.readLine()) != null) {
                            t+=text;
                        }
                        byte[] temp=t.getBytes("gbk");//这里写原编码方式
                        String newStr=new String(temp,"utf-8");//这里写转换后的编码方式
                        result=newStr;
                    }
                    EntityUtils.consume(entity);
                } finally {
                    response.close();
                }
            } finally {
                httpclient.close();
            }
            return result;
        }
    
        /**
         * XML格式字符串转换为Map
         * 微信支付 解析xml xmlmap  获取prepay_id
         * @param strXML XML字符串
         * @return XML数据转换后的Map
         * @throws Exception
         */
        public static Map<String, String> xmlToMap(String strXML) throws Exception {
            try {
                Map<String, String> data = new HashMap<String, String>();
                DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
                org.w3c.dom.Document doc = documentBuilder.parse(stream);
                doc.getDocumentElement().normalize();
                NodeList nodeList = doc.getDocumentElement().getChildNodes();
                for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                    Node node = nodeList.item(idx);
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                        data.put(element.getNodeName(), element.getTextContent());
                    }
                }
                try {
                    stream.close();
                } catch (Exception ex) {
                    // do nothing
                }
                return data;
            } catch (Exception ex) {
                WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
                throw ex;
            }
    
        }
    
        /**
         * 获取随机字符串 Nonce Str
         *
         * @return String 随机字符串
         */
        public static String generateNonceStr() {
            return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
        }
    
        /**
         * 生成 MD5
         *
         * @param data 待处理数据
         * @return MD5结果
         */
        public static String MD5(String data) throws Exception {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString().toUpperCase();
        }
    
        /**
         * 生成 HMACSHA256
         * @param data 待处理数据
         * @param key 密钥
         * @return 加密结果
         * @throws Exception
         */
        public static String HMACSHA256(String data, String key) throws Exception {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
            return sb.toString().toUpperCase();
        }
    
        /**
         * @Author: HONGLINCHEN
         * @Description:通过prepay_id 生成微信支付参数
         * @param prepay_id
         * @Date: 2017-9-8 10:17
         */
        public static  SortedMap<Object,Object> genPayRequest(String prepay_id) {
            SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
            parameters.put("appid", WXPayConstants.APP_ID);
            parameters.put("noncestr", genNonceStr());
            parameters.put("package", "Sign=WXPay");
            parameters.put("partnerid", WXPayConstants.MCH_ID);
            parameters.put("prepayid", prepay_id);
            parameters.put("timestamp",getCurrentTimestamp());
            parameters.put("sign", MD5.createSign("utf-8", parameters).toUpperCase());
            return parameters;
        }
        /**
         * @Author: HONGLINCHEN
         * @Description:请求值转换为xml格式 SortedMapxml
         * @param params
         * @Date: 2017-9-7 17:18
         */
        private static String SortedMaptoXml(SortedMap<String,String> params) {
            StringBuilder sb = new StringBuilder();
            Set es = params.entrySet();
            Iterator it = es.iterator();
            sb.append("<xml>\n");
            while(it.hasNext()) {
                Map.Entry entry = (Map.Entry)it.next();
                String k = (String)entry.getKey();
                Object v = entry.getValue();
                sb.append("<"+k+">");
                sb.append(v);
                sb.append("</"+k+">\n");
            }
            sb.append("</xml>");
            return sb.toString();
        }
        /**
         * 日志
         * @return
         */
        public static Logger getLogger() {
            Logger logger = LoggerFactory.getLogger("wxpay java sdk");
            return logger;
        }
    
        /**
         * 生成32位随机数字
         */
        public static String genNonceStr() {
            Random random = new Random();
            return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
        }
        /**
         * 获取当前时间戳,单位秒
         * @return
         */
        public static long getCurrentTimestamp() {
            return System.currentTimeMillis()/1000;
        }
    
    
        /**
         * 生成 uuid, 即用来标识一笔单,也用做 nonce_str
         * @return
         */
        public static String generateUUID() {
            return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
        }
    
    }
    

    MD5Util类代码如下
    package com.mobilepay.wxpay.utils;
    import java.security.MessageDigest;
    public class MD5Util {
       private static String byteArrayToHexString(byte b[]) {
          StringBuffer resultSb = new StringBuffer();
          for (int i = 0; i < b.length; i++)
             resultSb.append(byteToHexString(b[i]));
    
          return resultSb.toString();
       }
    
       private static String byteToHexString(byte b) {
          int n = b;
          if (n < 0)
             n += 256;
          int d1 = n / 16;
          int d2 = n % 16;
          return hexDigits[d1] + hexDigits[d2];
       }
    
       public static String MD5Encode(String origin, String charsetname) {
          String resultString = null;
          try {
             resultString = new String(origin);
             MessageDigest md = MessageDigest.getInstance("MD5");
             if (charsetname == null || "".equals(charsetname))
                resultString = byteArrayToHexString(md.digest(resultString
                      .getBytes()));
             else
                resultString = byteArrayToHexString(md.digest(resultString
                      .getBytes(charsetname)));
          } catch (Exception exception) {
          }
          return resultString;
       }
    
       private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
             "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
    
    }
    

    WeiXinPayController类代码如下
    package com.mobilepay.wxpay.controller;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.mobilepay.wxpay.constants.WXPayConstants;
    import com.mobilepay.wxpay.utils.ClientCustomSSL;
    import com.mobilepay.wxpay.utils.RequestUtils;
    import com.mobilepay.wxpay.wxpayutils.WXPayUtil;
    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 org.apache.log4j.Logger;
    import org.dom4j.Document;
    import org.dom4j.DocumentHelper;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import javax.net.ssl.SSLContext;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.security.KeyStore;
    import java.util.SortedMap;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import static com.mobilepay.wxpay.wxpayutils.WXPayUtil.genPayRequest;
    
    /**
     * @Author: HONGLINCHEN
     * @Description:微信支付
     * @Date: 2017-9-7 10:14
     */
    @Controller
    public class WeiXinPayController {
    
        private static final Logger logger = Logger.getLogger(WeiXinPayController.class);
    
        @RequestMapping(value = "/weixinpay",method = {RequestMethod.GET})
        public String weixinpay(HttpServletRequest request, HttpServletResponse response, Model model){
            return "wxpay";
        }
    
        @RequestMapping(value = "/wechatRefund",method = {RequestMethod.GET})
        public String wecharrefund(HttpServletRequest request, HttpServletResponse response, Model model){
            return "wechatrefund";
        }
    
        /**
         * @Author: HONGLINCHEN
         * @Description: 微信支付
         * @param number
         * @param money
         * @param request
         * @param response
         * @param model
         * @Date: 2017-9-11 13:51
         */
        @RequestMapping(value = "/wxPay",method = {RequestMethod.GET,RequestMethod.POST})
        public String weixinpay(String number, double money, HttpServletRequest request, HttpServletResponse response, Model model) throws IOException {
    
            String ipAddress = RequestUtils.getClientIpAddress(request);
            String bodyStr = "参与活动", detailStr = "微信支付测试 1.0*1";
            String paySignXml = WXPayUtil.unifiedOrder(number, bodyStr, detailStr, (int)(money*100), ipAddress);
            System.err.println("加密以后的支付参数\n"+paySignXml);
            try {
                String prepay_id = WXPayUtil.xmlToMap(paySignXml).get("prepay_id").toString();
                if(prepay_id!=null&&!"".equals(prepay_id)){
                    SortedMap<Object,Object> pay = genPayRequest(prepay_id);
                    System.err.println("实际支付参数\n"+pay);
                    response.getWriter().print(JSON.toJSON(pay));
                }else{
                    model.addAttribute("result","请求支付参数错误!");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
    
        /**
         * @Author: HONGLINCHEN
         * @Description: 微信取消订单
         * @param number
         * @param money
         * @param request
         * @param response
         * @param model
         * @Date: 2017-9-11 13:51
         */
        @RequestMapping(value = "/wxPayCancelOrder",method = {RequestMethod.GET,RequestMethod.POST})
        public String wxPayCancelOrder(String number, double money, HttpServletRequest request, HttpServletResponse response, Model model) throws IOException {
    
            String ipAddress = RequestUtils.getClientIpAddress(request);
            String bodyStr = "参与活动", detailStr = "微信支付测试 1.0*1";
            String paySignXml = WXPayUtil.unifiedOrder(number, bodyStr, detailStr, (int)(money*100), ipAddress);
            System.err.println("加密以后的支付参数\n"+paySignXml);
            try {
                String prepay_id = WXPayUtil.xmlToMap(paySignXml).get("prepay_id").toString();
                if(prepay_id!=null&&!"".equals(prepay_id)){
                    SortedMap<Object,Object> pay = genPayRequest(prepay_id);
                    System.err.println("实际支付参数\n"+pay);
                    response.getWriter().print(JSON.toJSON(pay));
                }else{
                    model.addAttribute("result","请求支付参数错误!");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
    
    
        /**
         * @Author: HONGLINCHEN  微信退款三种方式,随便哪一个都可以
         * @Description:微信退款   注意::微信金额的单位是分 所以这里要X100 转成int是因为 退款的时候不能有小数点
         * @param merchantNumber 商户这边的订单号
         * @param wxTransactionNumber 微信那边的交易单号
         * @param totalFee 订单的金额
         * @Date: 2017-9-12 11:18
         */
        @RequestMapping(value = "/wxPayRefundTwo", method = { RequestMethod.GET, RequestMethod.POST })
        public String qxwxsign(String merchantNumber,String wxTransactionNumber,double totalFee,HttpServletResponse response, HttpServletRequest request) throws IOException {
            String param = WXPayUtil.wxPayRefund(merchantNumber,wxTransactionNumber,String.valueOf((int)(totalFee*100)));
            System.err.println("param"+param);
            String result = "";
            String url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
            try {
                result = WXPayUtil.wxPayBack(url, param);
            } catch (Exception e) {
                e.printStackTrace();
            }
            String tt = "2007072001201611180592733503";
            Pattern p = Pattern.compile("\\.*(\\w{" + tt.length() + "})\\.*");
            int st = result.indexOf("<refund_id>");
            String res = "";
            if (st >= 0) {
                int en = result.indexOf("</refund_id>");
                res = result.substring(st, en);
                Matcher m = p.matcher(res);
                if (m.find()) {
                    res = m.group(1);
                }
                if (res.length() > 0) {
                    result = "code:1,msg:退款成功";
                } else {
                    result = "code:-1,msg:退款失败";
                }
                response.getWriter().print(JSON.toJSON(result));
            }
            return null;
        }
    
        /**
         * @Author: HONGLINCHEN
         * @Description:微信退款   注意::微信金额的单位是分 所以这里要X100 转成int是因为 退款的时候不能有小数点
         * @param merchantNumber 商户这边的订单号
         * @param wxTransactionNumber 微信那边的交易单号
         * @param totalFee 订单的金额
         * @Date: 2017-9-12 11:18
         */
        @RequestMapping(value = "/wxPayRefund", method = { RequestMethod.GET, RequestMethod.POST })
        public Object wxPayRefund(String merchantNumber,String wxTransactionNumber,double totalFee) {
            try{
                KeyStore keyStore = KeyStore.getInstance("PKCS12");
                FileInputStream instream = new FileInputStream(new File("D:\\微信商户平台支付证书\\apiclient_cert.p12"));
                try {
                    keyStore.load(instream, WXPayConstants.MCH_ID.toCharArray());
                }finally {
                    instream.close();
                }
                // Trust own CA and all self-signed certs
                SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WXPayConstants.MCH_ID.toCharArray()).build();
                // Allow TLSv1 protocol only
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                        sslcontext, new String[] { "TLSv1" }, null,
                        SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
                CloseableHttpClient httpclient = HttpClients.custom()
                        .setSSLSocketFactory(sslsf).build();
                // HttpGet httpget = new
                // HttpGet("https://api.mch.weixin.qq.com/secapi/pay/refund");
                HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
                //微信金额的单位是分 所以这里要X100 转成int是因为 退款的时候不能有小数点
                String xml = WXPayUtil.wxPayRefund(merchantNumber,wxTransactionNumber,String.valueOf((int)(totalFee*100)));
                try {
                    StringEntity se = new StringEntity(xml);
                    httppost.setEntity(se);
                    System.out.println("executing request" + httppost.getRequestLine());
                    CloseableHttpResponse responseEntry = httpclient.execute(httppost);
                    try {
                        HttpEntity entity = responseEntry.getEntity();
                        System.out.println(responseEntry.getStatusLine());
                        if (entity != null) {
                            System.out.println("Response content length: "+ entity.getContentLength());
                            SAXReader saxReader = new SAXReader();
                            Document document = saxReader.read(entity.getContent());
                            Element rootElt = document.getRootElement();
                            System.out.println("根节点:" + rootElt.getName());
                            System.out.println("==="+rootElt.elementText("result_code"));
                            System.out.println("==="+rootElt.elementText("return_msg"));
                            String resultCode = rootElt.elementText("result_code");
                            JSONObject result = new JSONObject();
                            Document documentXml = DocumentHelper.parseText(xml);
                            Element rootEltXml = documentXml.getRootElement();
                            if(resultCode.equals("SUCCESS")){
                                System.out.println("=================prepay_id===================="+ rootElt.elementText("prepay_id"));
                                System.out.println("=================sign===================="+ rootEltXml.elementText("sign"));
                                result.put("weixinPayUrl", rootElt.elementText("code_url"));
                                result.put("prepayId", rootElt.elementText("prepay_id"));
                                result.put("status","success");
                                result.put("msg","success");
                            }else{
                                result.put("status","false");
                                result.put("msg",rootElt.elementText("err_code_des"));
                            }
                            return result;
                        }
                        EntityUtils.consume(entity);
                    }
                    finally {
                        responseEntry.close();
                    }
                }
                finally {
                    httpclient.close();
                }
                return null;
            }catch(Exception e){
                e.printStackTrace();
                JSONObject result = new JSONObject();
                result.put("status","error");
                result.put("msg",e.getMessage());
                return result;
            }
        }
    
    
        /**
         * @Author: HONGLINCHEN
         * @Description:微信退款   注意::微信金额的单位是分 所以这里要X100 转成int是因为 退款的时候不能有小数点
         * @param merchantNumber 商户这边的订单号
         * @param wxTransactionNumber 微信那边的交易单号
         * @param totalFee 订单的金额
         * @Date: 2017-9-12 11:18
         */
        @RequestMapping(value = "/wxPayRefundone", method = { RequestMethod.GET, RequestMethod.POST })
        public Object wxPayRefundone(String merchantNumber,String wxTransactionNumber,double totalFee) {
            Object object = ClientCustomSSL.setUrl(merchantNumber,wxTransactionNumber,totalFee);
            return object;
        }
    }
    
    WXPayConstants类代码如下
    package com.mobilepay.wxpay.constants;
    /**
     * @Author: HONGLINCHEN
     * @Description:微信支付参数
     * @Date: 2017-9-7 10:08
     */
    public class WXPayConstants {
    
        //请同时修改  androidmanifest.xml里面 .PayActivityd里的属性<data android:scheme="wxb4ba3c02aa476ea1"/>为新设置的appid
        public static final String APP_ID = "替换成自己的";
    
        //商户号
        public static final String MCH_ID = "替换成自己的";
    
        //API密钥,在商户平台设置
        public static final  String API_KEY="替换成自己的";
    
        //商户号 微信小程序使用
        public static final String APPLET_MCHID = "替换成自己的";
    
        //appid 微信小程序使用
        public static final String APPLET_APPID = "替换成自己的";
    
    
    }
    
    

    XMLUtil类代码如下
    package com.mobilepay.wxpay.wxpayutils;
    import org.dom4j.DocumentException;
    import org.dom4j.DocumentHelper;
    import org.jdom.Document;
    import org.jdom.Element;
    import org.jdom.JDOMException;
    import org.jdom.input.SAXBuilder;
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @Author: HONGLINCHEN
     * @Description: xml 工具类
     * @Date: 2017-9-12 13:50
     */
    public class XMLUtil {
       /**
        * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 微信支付 解析xml xmlmap  获取prepay_id
        * @param strxml
        * @throws JDOMException
        * @throws IOException
        */
       public static Map doXMLParse(String strxml) throws JDOMException, IOException {
          strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
    
          if(null == strxml || "".equals(strxml)) {
             return null;
          }
          
          Map m = new HashMap();
          
          InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
          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 = XMLUtil.getChildrenText(children);
             }
             
             m.put(k, v);
          }
          
          //关闭流
          in.close();
          
          return m;
       }
       /**
        * @Author: HONGLINCHEN
        * @Description: 微信支付 解析xml xmlmap  获取prepay_id
        * @param xml
        * @Date: 2017-9-8 10:13
        */
       public static Map<String,Object> getResult(String xml){
          Map<String,Object> map = new HashMap<String, Object>();
          try {
             org.dom4j.Document document = DocumentHelper.parseText(xml);
             org.dom4j.Element root = document.getRootElement();
             Iterator<org.dom4j.Element> it = root.elementIterator();
             while (it.hasNext()) {
                org.dom4j.Element element = it.next();
                map.put(element.getName(), element.getTextTrim());
             }
          } catch (DocumentException e) {
             e.printStackTrace();
          }
          return map;
       }
       /**
        * 获取子结点的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(XMLUtil.getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
             }
          }
          
          return sb.toString();
       }
       
       /**
        * 获取xml编码字符集
        * @param strxml
        * @return
        * @throws IOException 
        * @throws JDOMException 
        */
       public static String getXMLEncoding(String strxml) throws JDOMException, IOException {
          InputStream in = String2Inputstream(strxml);
          SAXBuilder builder = new SAXBuilder();
          Document doc = builder.build(in);
          in.close();
          return (String)doc.getProperty("encoding");
       }
       public static InputStream String2Inputstream(String str) {
          return new ByteArrayInputStream(str.getBytes());
       }
       
    }
    

    MD5类代码如下
    package com.mobilepay.wxpay.wxpayutils;
    import com.mobilepay.wxpay.constants.WXPayConstants;
    import java.security.MessageDigest;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    import java.util.SortedMap;
    /**
     * @Author: HONGLINCHEN
     * @Description: MD5 工具类
     * @Date: 2017-9-12 13:51
     */
    public class MD5 {
       public final static String getMessageDigest(byte[] buffer) {
          char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
          try {
             MessageDigest mdTemp = MessageDigest.getInstance("MD5");
             mdTemp.update(buffer);
             byte[] md = mdTemp.digest();
             int j = md.length;
             char str[] = new char[j * 2];
             int k = 0;
             for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
             }
             return new String(str);
          } catch (Exception e) {
             return null;
          }
       }
       private static String byteArrayToHexString(byte b[]) {
            StringBuffer resultSb = new StringBuffer();  
            for (int i = 0; i < b.length; i++)  
                resultSb.append(byteToHexString(b[i]));  
      
            return resultSb.toString();  
        }  
      
        private static String byteToHexString(byte b) {  
            int n = b;  
            if (n < 0)  
                n += 256;  
            int d1 = n / 16;  
            int d2 = n % 16;  
            return hexDigits[d1] + hexDigits[d2];  
        }  
      
        public static String MD5Encode(String origin, String charsetname) {  
            String resultString = null;
            try {  
                resultString = new String(origin);  
                MessageDigest md = MessageDigest.getInstance("MD5");  
                if (charsetname == null || "".equals(charsetname))  
                    resultString = byteArrayToHexString(md.digest(resultString  
                            .getBytes()));  
                else  
                    resultString = byteArrayToHexString(md.digest(resultString  
                            .getBytes(charsetname)));  
            } catch (Exception exception) {  
            }  
            return resultString;  
        }  
      
        private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",  
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
    
        /**
         * @Author: HONGLINCHEN
         * @Description:支付参数生成签名
         * @param characterEncoding
         * @param parameters
         * @Date: 2017-9-8 10:29
         */
        public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){  
    
            StringBuffer sb = new StringBuffer();
            Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)
            Iterator it = es.iterator();
            while(it.hasNext()) {  
                Map.Entry entry = (Map.Entry)it.next();  
                String k = (String)entry.getKey();  
                Object v = entry.getValue();  
                if(null != v && !"".equals(v)&& !"sign".equals(k) && !"key".equals(k)) {
                    sb.append(k + "=" + v + "&");  
                }
            }  
            sb.append("key=" + WXPayConstants.API_KEY);
            String sign = MD5Encode(sb.toString(), characterEncoding).toUpperCase();
            return sign;  
        }
    
    }
    

    HttpUtil工具类代码如下
    package com.mobilepay.wxpay.wxpayutils;
    import org.apache.http.Consts;
    import org.apache.http.HttpEntity;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.entity.mime.HttpMultipartMode;
    import org.apache.http.entity.mime.MultipartEntityBuilder;
    import org.apache.http.entity.mime.content.FileBody;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.util.EntityUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    /**
     * @Author: HONGLINCHEN
     * @Description: Http工具类,发送Http请求, Get请求请将参数放在urlPost请求请将参数放在Map * @Date: 2017-9-12 13:52
     */
    public class HttpUtil {
        private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);
        private static final CloseableHttpClient httpclient = HttpClients.createDefault();
        private static final String userAgent = "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36";
    
        /**
         * 发送HttpGet请求
         *
         * @param url
         *            请求地址
         * @return 返回字符串
         */
        public static String sendGet(String url) {
            String result = null;
            CloseableHttpResponse response = null;
            try {
                HttpGet httpGet = new HttpGet(url);
                httpGet.setHeader("User-Agent", userAgent);
                response = httpclient.execute(httpGet);
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    result = EntityUtils.toString(entity);
                }
            } catch (Exception e) {
                log.error("处理失败 {}" + e);
                e.printStackTrace();
            } finally {
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
            return result;
        }
    
        /**
         * 发送HttpPost请求,参数为map
         *
         * @param url
         *            请求地址
         * @param map
         *            请求参数
         * @return 返回字符串
         */
        public static String sendPost(String url, Map<String, String> map) {
            // 设置参数
            List<NameValuePair> formparams = new ArrayList<NameValuePair>();
            for (Map.Entry<String, String> entry : map.entrySet()) {
                formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            // 编码
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
            // 取得HttpPost对象
            HttpPost httpPost = new HttpPost(url);
            // 防止被当成攻击添加的
            httpPost.setHeader("User-Agent", userAgent);
            // 参数放入Entity
            httpPost.setEntity(formEntity);
            CloseableHttpResponse response = null;
            String result = null;
            try {
                // 执行post请求
                response = httpclient.execute(httpPost);
                // 得到entity
                HttpEntity entity = response.getEntity();
                // 得到字符串
                result = EntityUtils.toString(entity);
            } catch (IOException e) {
                log.error(e.getMessage());
            } finally {
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
            return result;
        }
    
        /**
         * 发送HttpPost请求,参数为文件,适用于微信上传素材
         *
         * @param url
         *            请求地址
         * @param file
         *            上传的文件
         * @return 返回字符串
         * @throws IOException
         * @throws ClientProtocolException
         */
        public static String sendPost(String url, File file) {
            String result = null;
            HttpPost httpPost = new HttpPost(url);
            // 防止被当成攻击添加的
            httpPost.setHeader("User-Agent", userAgent);
            MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create();
            multipartEntity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            multipartEntity.addPart("media", new FileBody(file));
            httpPost.setEntity(multipartEntity.build());
            CloseableHttpResponse response = null;
            try {
                response = httpclient.execute(httpPost);
                HttpEntity entity = response.getEntity();
                result = EntityUtils.toString(entity);
            } catch (IOException e) {
                log.error(e.getMessage());
            } finally {
                // 关闭CloseableHttpResponse
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
            return result;
    
        }
    
        /**
         * 发送HttpPost请求,参数为json字符串
         *
         * @param url
         * @param jsonStr
         * @return
         */
        public static String sendPost(String url, String jsonStr) {
            String result = null;
            // 字符串编码
            StringEntity entity = new StringEntity(jsonStr, Consts.UTF_8);
            // 设置content-type
            entity.setContentType("application/json");
            HttpPost httpPost = new HttpPost(url);
            // 防止被当成攻击添加的
            httpPost.setHeader("User-Agent", userAgent);
            httpPost.setEntity(entity);
            CloseableHttpResponse response = null;
            try {
                response = httpclient.execute(httpPost);
                HttpEntity httpEntity = response.getEntity();
                result = EntityUtils.toString(httpEntity);
            } catch (IOException e) {
                log.error(e.getMessage());
            } finally {
                // 关闭CloseableHttpResponse
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
            return result;
        }
    
        /**
         * 发送不带参数的HttpPost请求
         *
         * @param url
         * @return
         */
        public static String sendPost(String url) {
            String result = null;
            // 得到一个HttpPost对象
            HttpPost httpPost = new HttpPost(url);
            // 防止被当成攻击添加的
            httpPost.setHeader("User-Agent", userAgent);
            CloseableHttpResponse response = null;
            try {
                // 执行HttpPost请求,并得到一个CloseableHttpResponse
                response = httpclient.execute(httpPost);
                // CloseableHttpResponse中拿到HttpEntity
                HttpEntity entity = response.getEntity();
                // HttpEntity转换为字符串
                result = EntityUtils.toString(entity);
            } catch (IOException e) {
                log.error(e.getMessage());
            } finally {
                // 关闭CloseableHttpResponse
                if (response != null) {
                    try {
                        response.close();
                    } catch (IOException e) {
                        log.error(e.getMessage());
                    }
                }
            }
            return result;
        }
    }

    pom文件代码如下:
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.mobilepay.example</groupId>
      <artifactId>MobilePay</artifactId>
      <packaging>war</packaging>
      <version>1.0-SNAPSHOT</version>
      <name>MobilePay Maven Webapp</name>
      <url>http://maven.apache.org</url>
      <properties>
        <!-- spring版本号 -->
        <spring.version>4.0.2.RELEASE</spring.version>
        <!-- mybatis版本号 -->
        <mybatis.version>3.2.8</mybatis.version>
        <!-- log4j日志文件管理包版本 -->
        <log4j.version>1.2.17</log4j.version>
        <slf4j.version>1.7.21</slf4j.version>
    
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
    
      <dependencies>
        <!--Spring-->
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
          <version>${spring.version}</version>
          <exclusions>
            <exclusion>
              <artifactId>commons-logging</artifactId>
              <groupId>commons-logging</groupId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-oxm</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-tx</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-jdbc</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context-support</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>${spring.version}</version>
        </dependency>
        <!--Spring end-->
    
        <!-- log start -->
        <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>${log4j.version}</version>
        </dependency>
        <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
        </dependency>
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
          <version>${slf4j.version}</version>
        </dependency>
    
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-simple</artifactId>
          <version>${slf4j.version}</version>
          <exclusions>
            <exclusion>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <!-- log end -->
    
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
        <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>fastjson</artifactId>
          <version>1.2.32</version>
        </dependency>
        <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpcore</artifactId>
          <version>4.4.6</version>
        </dependency>
    
        <!-- 微信支付工具类需要的jar start-->
        <dependency>
          <groupId>com.github.wxpay</groupId>
          <artifactId>wxpay-sdk</artifactId>
          <version>0.0.3</version>
        </dependency>
        <!-- 微信支付工具类需要的jar end-->
    
        <dependency>
          <groupId>jdom</groupId>
          <artifactId>jdom</artifactId>
          <version>1.1</version>
        </dependency>
        <!-- 支付宝支付需要的jar start 这个没用到,因为不知道为啥下载不下来,下次有时间在解决吧-->
        <!--<dependency>
          <groupId>com.pentahohub.nexus</groupId>
          <artifactId>alipay-sdk-java</artifactId>
          <version>20170725114550</version>
        </dependency>-->
        <!-- 支付宝支付需要的jar end-->
    
        <!-- 支付宝支付需要的jar start 项目中用的是这个,是我用maven命令安装到本地仓库然后引用的-->
        <!-- maven安装jar包到本地仓库命令 mvn install:install-file -DgroupId=com.alipay -DartifactId=alipay-sdk-java -Dversion=1.0.0 -Dpackaging=jar -Dfile=F:\alipay-sdk-java20170829142630.jar-->
        <dependency>
          <groupId>com.alipay</groupId>
          <artifactId>alipay-sdk-java</artifactId>
          <version>1.0.0</version>
        </dependency>
        <!-- 支付宝支付需要的jar end-->
    
        <dependency>
          <groupId>commons-httpclient</groupId>
          <artifactId>commons-httpclient</artifactId>
          <version>3.1</version>
          <exclusions>
            <exclusion>
              <artifactId>commons-codec</artifactId>
              <groupId>commons-codec</groupId>
            </exclusion>
            <exclusion>
              <artifactId>commons-logging</artifactId>
              <groupId>commons-logging</groupId>
            </exclusion>
            <exclusion>
              <artifactId>commons-codec</artifactId>
              <groupId>commons-codec</groupId>
            </exclusion>
            <exclusion>
              <artifactId>commons-logging</artifactId>
              <groupId>commons-logging</groupId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>commons-cli</groupId>
          <artifactId>commons-cli</artifactId>
          <version>1.4</version>
        </dependency>
        <dependency>
          <groupId>commons-beanutils</groupId>
          <artifactId>commons-beanutils</artifactId>
          <version>1.9.3</version>
          <exclusions>
            <exclusion>
                <artifactId>commons-logging</artifactId>
                <groupId>commons-logging</groupId>
            </exclusion>
            <exclusion>
              <groupId>commons-collections</groupId>
              <artifactId>commons-collections</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>commons-codec</groupId>
          <artifactId>commons-codec</artifactId>
          <version>1.10</version>
        </dependency>
        <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpclient</artifactId>
          <version>4.5.3</version>
          <exclusions>
            <exclusion>
              <artifactId>commons-codec</artifactId>
              <groupId>commons-codec</groupId>
            </exclusion>
            <exclusion>
              <artifactId>commons-logging</artifactId>
              <groupId>commons-logging</groupId>
            </exclusion>
            <exclusion>
              <groupId>org.apache.httpcomponents</groupId>
              <artifactId>httpcore</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>commons-collections</groupId>
          <artifactId>commons-collections</artifactId>
          <version>3.2.2</version>
        </dependency>
        <dependency>
          <groupId>commons-dbcp</groupId>
          <artifactId>commons-dbcp</artifactId>
          <version>1.4</version>
          <exclusions>
            <exclusion>
              <artifactId>commons-pool</artifactId>
              <groupId>commons-pool</groupId>
            </exclusion>
          </exclusions>
        </dependency>
        <dependency>
          <groupId>commons-pool</groupId>
          <artifactId>commons-pool</artifactId>
          <version>1.6</version>
        </dependency>
        <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpmime</artifactId>
          <version>4.5.3</version>
          <exclusions>
            <exclusion>
              <groupId>org.apache.httpcomponents</groupId>
              <artifactId>httpclient</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
    
        <dependency>
          <groupId>net.sf.ezmorph</groupId>
          <artifactId>ezmorph</artifactId>
          <version>1.0.6</version>
        </dependency>
    
        <dependency>
          <groupId>dom4j</groupId>
          <artifactId>dom4j</artifactId>
          <version>1.6.1</version>
        </dependency>
    
    
    
        <dependency>
          <groupId>net.sf.json-lib</groupId>
          <artifactId>json-lib</artifactId>
          <version>2.4</version>
          <classifier>jdk15</classifier><!--指定jdk版本2.4对应jdk1.5不指定导入到idea就会报错-->
          <scope>compile</scope>
        </dependency>
    
    
    
      </dependencies>
      <build>
        <finalName>MobilePay</finalName>
        <plugins>
            <plugin>
              <groupId>org.apache.maven.plugins</groupId>
              <artifactId>maven-compiler-plugin</artifactId>
              <version>2.5.1</version>
              <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <compilerArgument>-Xlint:all</compilerArgument>
                <showWarnings>true</showWarnings>
                <showDeprecation>true</showDeprecation>
              </configuration>
            </plugin>
    
        </plugins>
      </build>
    </project>
    

    好了,把这些 代码复制到自己工程中并替换自己的东西,就可以跑起来了。

    如果需要源代码,或者整个工程(MobilePay)可以私聊我,我会解压发给大家!

    MobilePay这个项目中集成了所有的移动支付,包括2大平台支付宝、微信 的支付模块的 登录模块

    java-微信支付-微信退款

    下面这个gif和你们写完代码,测试成功后的表情是不是很像呢?哈哈哈哈哈.....

    java-微信支付-微信退款

    作者:HONGLINCHEN.上海·黄埔

    作者邮箱:xychenhonglin@163.com

    请关注作者的今日头条号:《专注JavaWeb开发》 博客地址:

    请关注作者的CSDN博客:《专注JavaWeb开发》 博客地址:

    作者的QQ群:专注JavaWeb开发.群号:162582394

    展开全文
  • 本着学点新东西和赚点外快的念头,自学了一下微信公众平台开发。在这篇文章里,主要描述了一下我搭建一个微信公众平台的"helloworld"级程序的历程。其中,柳峰老师的视频和文章给了我很大的帮助。非常感谢。  搭建...
  • 微信小程序开发笔记

    2020-04-22 19:27:52
    0.1、在实际的开发中,图片资源不会存储在小程序的目录中,因为小程序的大小不能超过1MB(现在改为2M)。超过则无法真机运行和发布项目。我们应该将图片都存放在服务器上,让小程序通过网络来加载图片资源。 0.2、在...
  • 不同应用指微信开放平台下的不同用户。) 1、 申请测试号(获得appID、appsecret) 2、 填写服务器配置并验证。 服务端编写接口,参数为(signature,timestamp,nonce,echostr),并原样返回echostr    ...
  •   目录结构 项目结构图 增加相关源代码 日志实体类 ...微信客户端测试 查看SAE数据库 参考文档 完整项目源代码 项目结构图   源代码文件说明 序号 文件名 说明 操作 1 ...
  • 原文链接:基于EGRET的大型项目转微信小游戏开发总结——各种坑处理http://51meaning.cn/blog/?p=412 这阵在做基于Egret的微信小游戏,项目很大,也踩了很多坑,特总结如下,供各位难兄难弟参考! 1、window.Main=...
  • 小程序开发中常常会用到微信支付。微信支付里面的坑谁做谁知道。 先来个官方贴图谈一下支付的流程 对,支付是个异步的过程。 收集到用户的openid,支付金额,ip地址之后,我们生成随机串nonce_str和时间戳、...
  • 微信小程序

    2019-08-11 02:53:08
    微信小程序运行环境并非完整的浏览器,开发过程中用到H5 相关的技术,微信小程序的运行环境是微信基于浏览器内核完全重构的一个内置解析器,针对小程序专门做了优化,配合自己定义的开发语言标准(基于H5 进行了优化...
  • 得益于前辈的分享,做了一个h5录音的demo。效果图如下: 点击开始录音会先弹出确认框: 首次确认允许后,再次录音不需要再确认,但如果用户点击禁止,则无法录音: 点击发送 将录音内容发送到对话框... th...
  • 作者 | 凌华彬、王哲 ...今天就由我和王哲从技术角度给大家科普一下微信小游戏的开发知识,这一系列文章源自我们 Cocos Creator 引擎团队和微信团队合作过程的总结,目前,Cocos Creator v1.8 编...
  • 简介:本章主要写的是,在微信开发中,有发送xml数据,其中用到了xstream这个框架进行javaBean和xml数据的转换,但是在每个标签中,微信需要的接口都需要封装上一个东西,所以使用了其中的解析器来,在本章中用到了xpp这个...
  • 【年终总结】微信前端社招有感 时间飞快,转眼间8102还差一个月就over了,顺了顺好几天没理的胡渣儿,好像已经老了不少。 不,我还很年轻!虽然年终还没到,但好像也差不多了。 几经辗转,年底前终于拿到了微信...
  • Spring解析xml理解

    2019-03-20 20:55:00
    Sprign解析xml配置使用dom4j. 第一步:获取Document public class DocumentHelper{ //声明map存放节点 private Map<String, Document> docs = new HashMap<String, Document>()...
  • 你可以管理你的小程序的权限,查看数据报表,发布小程序等操作。 ...2.安装开发者工具 登录后新建项目时,需输入申请的小程序的 AppID(相当于一个身份证,很多地方需要用到) 勾选 "创建 QuickStart 项...
1 2 3 4 5 ... 20
收藏数 486
精华内容 194