├── .editorconfig ├── .gitignore ├── .travis.yml ├── README.md ├── build ├── check-style-suppression.xml ├── check-style.xml ├── find-bugs-exclude-filter.xml └── pmd.xml ├── commit.sh ├── pom.xml └── src └── main ├── java └── com │ └── github │ └── weixin │ └── demo │ ├── config │ ├── MainConfiguration.java │ ├── MenuConfig.java │ └── WxPayConfiguration.java │ ├── controller │ ├── CoreController.java │ ├── GenericController.java │ ├── PaymentController.java │ └── TemplateMessageController.java │ ├── handler │ ├── AbstractHandler.java │ ├── LogHandler.java │ ├── MsgHandler.java │ └── SubscribeHandler.java │ ├── service │ ├── CoreService.java │ └── impl │ │ └── CoreServiceImpl.java │ └── util │ ├── GenericReturnModel.java │ ├── MD5Util.java │ ├── ReturnModel.java │ ├── Sha1Util.java │ ├── XMLUtil.java │ └── package-info.java ├── resources ├── applicationContext.xml ├── log4j.properties ├── spring-mvc.xml ├── wx.properties └── wxPay.properties └── webapp └── WEB-INF ├── views └── index.html └── web.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | *.class 4 | 5 | # Mobile Tools for Java (J2ME) 6 | .mtj.tmp/ 7 | 8 | # Package Files # 9 | *.jar 10 | *.war 11 | *.ear 12 | 13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 14 | hs_err_pid* 15 | 16 | /.settings 17 | /.classpath 18 | /.project 19 | /.tomcatplugin 20 | /target 21 | /work 22 | /src/main/webapp/.#webclasspath 23 | 24 | .idea/ 25 | *.iml 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | 5 | script: "mvn clean package -Dmaven.test.skip=true" 6 | 7 | branches: 8 | only: 9 | - master 10 | 11 | notifications: 12 | email: 13 | - binaryw@qq.com 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![doodles](https://www.google.com/logos/doodles/2016/teachers-day-2016-us-6296626244091904.2-hp2x.gif) 3 | 4 | ---- 5 | [![码云Gitee](https://gitee.com/binary/weixin-java-tools-springmvc/badge/star.svg?theme=blue)](https://gitee.com/binary/weixin-java-tools-springmvc) 6 | [![Github](http://github-svg-buttons.herokuapp.com/star.svg?user=Wechat-Group&repo=weixin-java-demo-springmvc&style=flat&background=1081C1)](https://github.com/Wechat-Group/weixin-java-demo-springmvc) 7 | [![Build Status](https://travis-ci.org/Wechat-Group/weixin-java-demo-springmvc.svg?branch=master)](https://travis-ci.org/Wechat-Group/weixin-java-demo-springmvc) 8 | 9 | This repository is a wechat springmvc demo based on **WxJava** 10 | 11 | 本仓库的项目是一个基 于**[WxJava](https://github.com/wechat-group/WxJava)** 的springmvc Demo. 12 | 13 | 新手遇到问题,请务必先阅读[【开发文档首页】](https://github.com/Wechat-Group/WxJava/wiki)的常见问题部分,可以少走很多弯路,节省不少时间。 14 | 15 | 16 | ## 本项目目前实现了如下功能: 17 | 18 | * 与微信服务器的AES加密通信 19 | * 微信支付 20 | * 商户号向个人用户转账 21 | * 自定义菜单 22 | * 关注公众号、客服消息路由处理 23 | * 模版消息 24 | * 通过openid获取用户基本信息 25 | * 通过code获得基本用户信息 26 | 27 | 28 | Welcome to Pull Requests! 29 | 30 | 欢迎大家积极Pull Requests来丰富此Demo的功能! 31 | 32 | ## 快速使用: 33 | 34 | **将项目下载到本地** 35 | ```shell 36 | git clone https://github.com/wechat-group/weixin-java-demo-springmvc 37 | ``` 38 | 39 | **配置公众号信息** 40 | 41 | 打开`src/main/resources/wx.properties`文件,配置公众号相关信息。 42 | 43 | **Maven打包** 44 | ```shell 45 | mvn war:war 46 | ``` 47 | 48 | **上传至服务器** 49 | 50 | 必须使用服务器的80端口才能与微信服务器进行交互。 51 | 52 | **微信公众平台服务器配置** 53 | 54 | 进入到**微信公众平台**的`开发/基本配置`页面,配置`URL(服务器地址)`、`Token(令牌)`、`EncodingAESKey(消息加解密密钥)`。 55 | ```shell 56 | URL(服务器地址):http://192.168.1.1/wechatTestService/core 57 | ``` 58 | 59 | ## License 60 | - 本项目的所有代码除另有说明外,均按照 [MIT License](https://github.com/racaljk/hosts/blob/master/LICENSE) 发布。 61 | - 本项目的.java,README.MD,wiki等资源基于[CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) 62 | 这意味着你可以拷贝、并再发行本项目的内容,但是你将必须同样**提供原作者信息以及协议声明**。同时你也**不能将本项目用于商业用途**, 63 | 按照我们狭义的理解(增加附属条款),凡是**任何盈利的活动皆属于商业用途**。 64 | - 请在遵守当地相关法律法规的前提下使用本项目。 65 | 66 | ![img-source-from-https://github.com/docker/dockercraft](https://github.com/docker/dockercraft/raw/master/docs/img/contribute.png?raw=true) 67 | -------------------------------------------------------------------------------- /build/check-style-suppression.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /build/check-style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /build/find-bugs-exclude-filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /build/pmd.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | PMD rules 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /commit.sh: -------------------------------------------------------------------------------- 1 | git commit -a -m ":arrow_up: 升级sdk版本为4.7.0" 2 | git pull --rebase 3 | git push origin master 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.firenzeseagle 8 | WxJava-springmvc 9 | war 10 | 1.0-SNAPSHOT 11 | 12 | 13 | UTF-8 14 | zh_CN 15 | 1.8 16 | 17 | 18 | 3.1 19 | 20 | 21 | 4.7.0 22 | 4.13.1 23 | 1.7.21 24 | 6.1.14 25 | 2.3.23 26 | 1.5 27 | 1.9 28 | 3.9.0 29 | 4.5.13 30 | 4.4.5 31 | 3.0.1 32 | 2.1.3 33 | 2.6.2 34 | 2.13.4.2 35 | 1.6.12 36 | 2.17 37 | 3.5 38 | 3.0.1 39 | 40 | 41 | 42 | wechatService 43 | 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-compiler-plugin 48 | ${plugin.maven-compiler} 49 | 50 | ${project.build.jdk} 51 | ${project.build.jdk} 52 | ${project.build.sourceEncoding} 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | commons-logging 62 | commons-logging 63 | 1.1.3 64 | 65 | 66 | 67 | com.github.binarywang 68 | weixin-java-mp 69 | ${WxJava.version} 70 | 71 | 72 | 73 | com.github.binarywang 74 | weixin-java-pay 75 | ${WxJava.version} 76 | 77 | 78 | 79 | 80 | junit 81 | junit 82 | ${junit.version} 83 | 84 | 85 | 86 | 87 | org.slf4j 88 | slf4j-api 89 | ${slf4j.version} 90 | 91 | 92 | org.slf4j 93 | slf4j-simple 94 | ${slf4j.version} 95 | 96 | 97 | 98 | 99 | org.springframework 100 | spring-core 101 | ${spring.version} 102 | 103 | 104 | 105 | org.springframework 106 | spring-web 107 | ${spring.version} 108 | 109 | 110 | 111 | org.springframework 112 | spring-oxm 113 | ${spring.version} 114 | 115 | 116 | 117 | org.springframework 118 | spring-tx 119 | ${spring.version} 120 | 121 | 122 | 123 | org.springframework 124 | spring-jdbc 125 | ${spring.version} 126 | 127 | 128 | 129 | org.springframework 130 | spring-webmvc 131 | ${spring.version} 132 | 133 | 134 | 135 | org.springframework 136 | spring-aop 137 | ${spring.version} 138 | 139 | 140 | 141 | org.springframework 142 | spring-context-support 143 | ${spring.version} 144 | 145 | 146 | 147 | org.springframework 148 | spring-test 149 | ${spring.version} 150 | 151 | 152 | 153 | 154 | 155 | javax.servlet 156 | javax.servlet-api 157 | ${javax.servlet-api.version} 158 | provided 159 | 160 | 161 | 162 | 163 | org.aspectj 164 | aspectjweaver 165 | ${aspectj.version} 166 | 167 | 168 | org.aspectj 169 | aspectjrt 170 | ${aspectj.version} 171 | 172 | 173 | 174 | 175 | org.freemarker 176 | freemarker 177 | ${freemarker.version} 178 | 179 | 180 | 181 | 182 | 183 | commons-fileupload 184 | commons-fileupload 185 | ${commons.fileupload.version} 186 | 187 | 188 | 189 | org.apache.httpcomponents 190 | httpclient 191 | ${httpclient.version} 192 | 193 | 194 | 195 | org.apache.httpcomponents 196 | httpcore 197 | ${httpcore.version} 198 | 199 | 200 | 201 | 202 | commons-codec 203 | commons-codec 204 | ${commons.codec.version} 205 | 206 | 207 | 208 | commons-net 209 | commons-net 210 | ${commons.net.version} 211 | 212 | 213 | 214 | commons-collections 215 | commons-collections 216 | 3.2.2 217 | 218 | 219 | 220 | 221 | 222 | 223 | org.dom4j 224 | dom4j 225 | ${dom4j.version} 226 | 227 | 228 | 229 | com.fasterxml.jackson.core 230 | jackson-databind 231 | ${jackson-core.version} 232 | 233 | 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/config/MainConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.config; 2 | 3 | import me.chanjar.weixin.mp.api.WxMpService; 4 | import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; 5 | import me.chanjar.weixin.mp.config.WxMpConfigStorage; 6 | import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | /** 12 | * 微信公众号主配置 13 | *

14 | * Created by bjliumingbo on 2017/5/12. 15 | * 16 | * @author FirenzesEagle 17 | * @author BinaryWang 18 | */ 19 | @Configuration 20 | public class MainConfiguration { 21 | @Value("#{wxProperties.appId}") 22 | private String appId; 23 | 24 | @Value("#{wxProperties.appSecret}") 25 | private String appSecret; 26 | 27 | @Value("#{wxProperties.token}") 28 | private String token; 29 | 30 | @Value("#{wxProperties.aesKey}") 31 | private String aesKey; 32 | 33 | @Bean 34 | public WxMpConfigStorage wxMpConfigStorage() { 35 | WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl(); 36 | configStorage.setAppId(this.appId); 37 | configStorage.setSecret(this.appSecret); 38 | configStorage.setToken(this.token); 39 | configStorage.setAesKey(this.aesKey); 40 | return configStorage; 41 | } 42 | 43 | @Bean 44 | public WxMpService wxMpService() { 45 | WxMpService wxMpService = new WxMpServiceImpl(); 46 | wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); 47 | return wxMpService; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/config/MenuConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.config; 2 | 3 | import me.chanjar.weixin.common.bean.menu.WxMenu; 4 | import me.chanjar.weixin.common.bean.menu.WxMenuButton; 5 | import me.chanjar.weixin.common.error.WxErrorException; 6 | import me.chanjar.weixin.mp.api.WxMpService; 7 | import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; 8 | import me.chanjar.weixin.mp.config.WxMpConfigStorage; 9 | import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; 10 | 11 | import static me.chanjar.weixin.common.api.WxConsts.MenuButtonType; 12 | 13 | /** 14 | * Created by FirenzesEagle on 2016/6/1 0001. 15 | * Email:liumingbo2008@gmail.com 16 | */ 17 | public class MenuConfig { 18 | 19 | /** 20 | * 定义菜单结构 21 | * 22 | * @return 23 | */ 24 | protected static WxMenu getMenu() { 25 | 26 | MainConfig mainConfig = new MainConfig("appid", "appsecret", "token", "aesKey"); 27 | WxMpService wxMpService = mainConfig.wxMpService(); 28 | 29 | WxMenu menu = new WxMenu(); 30 | WxMenuButton button1 = new WxMenuButton(); 31 | button1.setType(MenuButtonType.VIEW); 32 | button1.setName("买家订单"); 33 | button1.setUrl(wxMpService.getOAuth2Service().buildAuthorizationUrl("", "snsapi_base", "")); 34 | 35 | WxMenuButton button2 = new WxMenuButton(); 36 | button2.setName("我是卖家"); 37 | 38 | WxMenuButton button21 = new WxMenuButton(); 39 | button21.setType(MenuButtonType.VIEW); 40 | button21.setName("我的订单"); 41 | button21.setUrl(wxMpService.getOAuth2Service().buildAuthorizationUrl("", "snsapi_base", "")); 42 | 43 | WxMenuButton button22 = new WxMenuButton(); 44 | button22.setType(MenuButtonType.VIEW); 45 | button22.setName("收入统计"); 46 | button22.setUrl(wxMpService.getOAuth2Service().buildAuthorizationUrl("", "snsapi_base", "")); 47 | 48 | WxMenuButton button23 = new WxMenuButton(); 49 | button23.setType(MenuButtonType.VIEW); 50 | button23.setName("发布商品"); 51 | button23.setUrl(wxMpService.getOAuth2Service().buildAuthorizationUrl("", "snsapi_base", "")); 52 | 53 | WxMenuButton button24 = new WxMenuButton(); 54 | button24.setType(MenuButtonType.VIEW); 55 | button24.setName("商品管理"); 56 | button24.setUrl(wxMpService.getOAuth2Service().buildAuthorizationUrl("", "snsapi_base", "")); 57 | 58 | button2.getSubButtons().add(button21); 59 | button2.getSubButtons().add(button22); 60 | button2.getSubButtons().add(button23); 61 | button2.getSubButtons().add(button24); 62 | 63 | WxMenuButton button3 = new WxMenuButton(); 64 | button3.setType(MenuButtonType.CLICK); 65 | button3.setName("使用帮助"); 66 | button3.setKey("help"); 67 | 68 | menu.getButtons().add(button1); 69 | menu.getButtons().add(button2); 70 | menu.getButtons().add(button3); 71 | 72 | return menu; 73 | } 74 | 75 | 76 | public static class MainConfig { 77 | 78 | private String appId; 79 | private String appSecret; 80 | private String token; 81 | private String aesKey; 82 | 83 | /** 84 | * 为了生成自定义菜单使用的构造函数 85 | * 86 | * @param appId 87 | * @param appSecret 88 | * @param token 89 | * @param aesKey 90 | */ 91 | protected MainConfig(String appId, String appSecret, String token, String aesKey) { 92 | this.appId = appId; 93 | this.appSecret = appSecret; 94 | this.token = token; 95 | this.aesKey = aesKey; 96 | } 97 | 98 | public WxMpConfigStorage wxMpConfigStorage() { 99 | WxMpDefaultConfigImpl configStorage = new WxMpDefaultConfigImpl(); 100 | configStorage.setAppId(this.appId); 101 | configStorage.setSecret(this.appSecret); 102 | configStorage.setToken(this.token); 103 | configStorage.setAesKey(this.aesKey); 104 | return configStorage; 105 | } 106 | 107 | public WxMpService wxMpService() { 108 | WxMpService wxMpService = new WxMpServiceImpl(); 109 | wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); 110 | return wxMpService; 111 | } 112 | 113 | } 114 | 115 | /** 116 | * 运行此main函数即可生成公众号自定义菜单,注意要修改MainConfig构造方法行代码的对应四个参数为实际值 117 | * 118 | * @param args 119 | */ 120 | public static void main(String[] args) { 121 | MainConfig mainConfig = new MainConfig("appid", "appsecret", "token", "aesKey"); 122 | WxMpService wxMpService = mainConfig.wxMpService(); 123 | try { 124 | wxMpService.getMenuService().menuCreate(getMenu()); 125 | } catch (WxErrorException e) { 126 | e.printStackTrace(); 127 | } 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/config/WxPayConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.config; 2 | 3 | import com.github.binarywang.wxpay.config.WxPayConfig; 4 | import com.github.binarywang.wxpay.service.WxPayService; 5 | import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | /** 11 | * 微信支付相关配置 12 | *

13 | * Created by bjliumingbo on 2017/5/12. 14 | */ 15 | @Configuration 16 | public class WxPayConfiguration { 17 | @Value("#{wxPayProperties.appId}") 18 | private String appId; 19 | 20 | @Value("#{wxPayProperties.mchId}") 21 | private String mchId; 22 | 23 | @Value("#{wxPayProperties.mchKey}") 24 | private String mchKey; 25 | 26 | @Value("#{wxPayProperties.subAppId}") 27 | private String subAppId; 28 | 29 | @Value("#{wxPayProperties.subMchId}") 30 | private String subMchId; 31 | 32 | @Value("#{wxPayProperties.keyPath}") 33 | private String keyPath; 34 | 35 | @Bean 36 | public WxPayConfig payConfig() { 37 | WxPayConfig payConfig = new WxPayConfig(); 38 | payConfig.setAppId(this.appId); 39 | payConfig.setMchId(this.mchId); 40 | payConfig.setMchKey(this.mchKey); 41 | payConfig.setSubAppId(this.subAppId); 42 | payConfig.setSubMchId(this.subMchId); 43 | payConfig.setKeyPath(this.keyPath); 44 | 45 | return payConfig; 46 | } 47 | 48 | @Bean 49 | public WxPayService payService() { 50 | WxPayService payService = new WxPayServiceImpl(); 51 | payService.setConfig(payConfig()); 52 | return payService; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/controller/CoreController.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.controller; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken; 7 | import me.chanjar.weixin.common.error.WxErrorException; 8 | import me.chanjar.weixin.mp.config.WxMpConfigStorage; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestParam; 14 | 15 | import com.github.weixin.demo.service.CoreService; 16 | import com.github.weixin.demo.util.ReturnModel; 17 | import me.chanjar.weixin.mp.api.WxMpService; 18 | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 19 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 20 | import me.chanjar.weixin.mp.bean.result.WxMpUser; 21 | 22 | /** 23 | * Created by FirenzesEagle on 2016/5/30 0030. 24 | * Email:liumingbo2008@gmail.com 25 | */ 26 | @Controller 27 | public class CoreController extends GenericController { 28 | 29 | @Autowired 30 | protected WxMpConfigStorage configStorage; 31 | @Autowired 32 | protected WxMpService wxMpService; 33 | @Autowired 34 | protected CoreService coreService; 35 | 36 | @RequestMapping(value = "/index") 37 | public String index() { 38 | return "index"; 39 | } 40 | 41 | /** 42 | * 微信公众号webservice主服务接口,提供与微信服务器的信息交互 43 | */ 44 | @RequestMapping(value = "/core") 45 | public void wechatCore(HttpServletRequest request, HttpServletResponse response) throws Exception { 46 | response.setContentType("text/html;charset=utf-8"); 47 | response.setStatus(HttpServletResponse.SC_OK); 48 | 49 | String signature = request.getParameter("signature"); 50 | String nonce = request.getParameter("nonce"); 51 | String timestamp = request.getParameter("timestamp"); 52 | 53 | if (!this.wxMpService.checkSignature(timestamp, nonce, signature)) { 54 | // 消息签名不正确,说明不是公众平台发过来的消息 55 | response.getWriter().println("非法请求"); 56 | return; 57 | } 58 | 59 | String echoStr = request.getParameter("echostr"); 60 | if (StringUtils.isNotBlank(echoStr)) { 61 | // 说明是一个仅仅用来验证的请求,回显echostr 62 | String echoStrOut = String.copyValueOf(echoStr.toCharArray()); 63 | response.getWriter().println(echoStrOut); 64 | return; 65 | } 66 | 67 | String encryptType = StringUtils.isBlank(request.getParameter("encrypt_type")) 68 | ? "raw" 69 | : request.getParameter("encrypt_type"); 70 | 71 | if ("raw".equals(encryptType)) { 72 | // 明文传输的消息 73 | WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream()); 74 | WxMpXmlOutMessage outMessage = this.coreService.route(inMessage); 75 | if (outMessage == null) { 76 | response.getWriter().write(""); 77 | } else { 78 | response.getWriter().write(outMessage.toXml()); 79 | } 80 | return; 81 | } 82 | 83 | if ("aes".equals(encryptType)) { 84 | // 是aes加密的消息 85 | String msgSignature = request.getParameter("msg_signature"); 86 | WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml( 87 | request.getInputStream(), this.configStorage, timestamp, nonce, 88 | msgSignature); 89 | log.debug("\n消息解密后内容为:\n{} ", inMessage.toString()); 90 | WxMpXmlOutMessage outMessage = this.coreService.route(inMessage); 91 | if (outMessage == null) { 92 | response.getWriter().write(""); 93 | } else { 94 | response.getWriter().write(outMessage.toEncryptedXml(this.configStorage)); 95 | } 96 | 97 | return; 98 | } 99 | 100 | response.getWriter().println("不可识别的加密类型"); 101 | } 102 | 103 | /** 104 | * 通过openid获得基本用户信息 105 | * 详情请见: http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html 106 | * 107 | * @param openid openid 108 | * @param lang zh_CN, zh_TW, en 109 | */ 110 | @RequestMapping(value = "/getUserInfo") 111 | public WxMpUser getUserInfo(HttpServletResponse response, @RequestParam(value = "openid") String openid, 112 | @RequestParam(value = "lang") String lang) { 113 | ReturnModel returnModel = new ReturnModel(); 114 | WxMpUser wxMpUser = null; 115 | try { 116 | wxMpUser = this.wxMpService.getUserService().userInfo(openid, lang); 117 | returnModel.setResult(true); 118 | returnModel.setDatum(wxMpUser); 119 | renderString(response, returnModel); 120 | } catch (WxErrorException e) { 121 | returnModel.setResult(false); 122 | returnModel.setReason(e.getError().toString()); 123 | renderString(response, returnModel); 124 | log.error(e.getError().toString()); 125 | } 126 | return wxMpUser; 127 | } 128 | 129 | /** 130 | * 通过code获得基本用户信息 131 | * 详情请见: http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html 132 | * 133 | * @param code code 134 | * @param lang zh_CN, zh_TW, en 135 | */ 136 | @RequestMapping(value = "/getOAuth2UserInfo") 137 | public void getOAuth2UserInfo(HttpServletResponse response, @RequestParam(value = "code") String code, 138 | @RequestParam(value = "lang") String lang) { 139 | ReturnModel returnModel = new ReturnModel(); 140 | WxOAuth2AccessToken accessToken; 141 | WxMpUser wxMpUser; 142 | try { 143 | accessToken = this.wxMpService.getOAuth2Service().getAccessToken(code); 144 | wxMpUser = this.wxMpService.getUserService() 145 | .userInfo(accessToken.getOpenId(), lang); 146 | returnModel.setResult(true); 147 | returnModel.setDatum(wxMpUser); 148 | renderString(response, returnModel); 149 | } catch (WxErrorException e) { 150 | returnModel.setResult(false); 151 | returnModel.setReason(e.getError().toString()); 152 | renderString(response, returnModel); 153 | log.error(e.getError().toString()); 154 | } 155 | } 156 | 157 | /** 158 | * 用code换取oauth2的openid 159 | * 详情请见: http://mp.weixin.qq.com/wiki/1/8a5ce6257f1d3b2afb20f83e72b72ce9.html 160 | * 161 | * @param code code 162 | */ 163 | @RequestMapping(value = "/getOpenid") 164 | public void getOpenid(HttpServletResponse response, @RequestParam(value = "code") String code) { 165 | ReturnModel returnModel = new ReturnModel(); 166 | WxOAuth2AccessToken accessToken; 167 | try { 168 | accessToken = this.wxMpService.getOAuth2Service().getAccessToken(code); 169 | returnModel.setResult(true); 170 | returnModel.setDatum(accessToken.getOpenId()); 171 | renderString(response, returnModel); 172 | } catch (WxErrorException e) { 173 | returnModel.setResult(false); 174 | returnModel.setReason(e.getError().toString()); 175 | renderString(response, returnModel); 176 | log.error(e.getError().toString()); 177 | } 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/controller/GenericController.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.controller; 2 | 3 | import com.google.gson.Gson; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import javax.servlet.http.HttpServletResponse; 8 | import java.io.IOException; 9 | 10 | /** 11 | * 所有自定义Controller的顶级接口,封装常用的与session和response、request相关的操作 12 | *

13 | * Created by FirenzesEagle on 2016/4/19 0019. 14 | */ 15 | public abstract class GenericController { 16 | 17 | /** 18 | * 日志对象 19 | */ 20 | protected Logger logger = LoggerFactory.getLogger(getClass()); 21 | 22 | /** 23 | * 客户端返回JSON字符串 24 | * 25 | * @param response 26 | * @param object 27 | * @return 28 | */ 29 | protected String renderString(HttpServletResponse response, Object object) { 30 | return renderString(response, new Gson().toJson(object), "application/json"); 31 | } 32 | 33 | /** 34 | * 客户端返回字符串 35 | * 36 | * @param response 37 | * @param string 38 | * @return 39 | */ 40 | protected String renderString(HttpServletResponse response, String string, String type) { 41 | try { 42 | response.reset(); 43 | response.setContentType(type); 44 | response.setCharacterEncoding("utf-8"); 45 | //解决跨域问题 46 | response.setHeader("Access-Control-Allow-Origin", "*"); 47 | response.getWriter().print(string); 48 | return null; 49 | } catch (IOException e) { 50 | return null; 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/controller/PaymentController.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.controller; 2 | 3 | import java.util.Map; 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | 11 | import com.github.binarywang.wxpay.bean.entpay.EntPayRequest; 12 | import com.github.binarywang.wxpay.bean.entpay.EntPayResult; 13 | import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; 14 | import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult; 15 | import com.github.binarywang.wxpay.config.WxPayConfig; 16 | import com.github.binarywang.wxpay.exception.WxPayException; 17 | import com.github.binarywang.wxpay.service.WxPayService; 18 | import com.github.binarywang.wxpay.util.SignUtils; 19 | import com.github.weixin.demo.util.ReturnModel; 20 | import com.github.weixin.demo.util.Sha1Util; 21 | import com.github.weixin.demo.util.XMLUtil; 22 | import com.google.gson.Gson; 23 | 24 | /** 25 | * 微信支付Controller 26 | *

27 | * Created by FirenzesEagle on 2016/6/20 0020. 28 | * Email:liumingbo2008@gmail.com 29 | */ 30 | @Controller 31 | @RequestMapping(value = "wxPay") 32 | public class PaymentController extends GenericController { 33 | 34 | @Autowired 35 | private WxPayConfig payConfig; 36 | @Autowired 37 | private WxPayService payService; 38 | 39 | /** 40 | * 用于返回预支付的结果 WxMpPrepayIdResult,一般不需要使用此接口 41 | * 42 | * @param response 43 | * @param request 44 | */ 45 | @RequestMapping(value = "getPrepayIdResult") 46 | public void getPrepayId(HttpServletResponse response, 47 | HttpServletRequest request) throws WxPayException { 48 | WxPayUnifiedOrderRequest payInfo = WxPayUnifiedOrderRequest.newBuilder() 49 | .openid(request.getParameter("openid")) 50 | .outTradeNo(request.getParameter("out_trade_no")) 51 | .totalFee(Integer.valueOf(request.getParameter("total_fee"))) 52 | .body(request.getParameter("body")) 53 | .tradeType(request.getParameter("trade_type")) 54 | .spbillCreateIp(request.getParameter("spbill_create_ip")) 55 | .notifyUrl("") 56 | .build(); 57 | log 58 | .info("PartnerKey is :" + this.payConfig.getMchKey()); 59 | WxPayUnifiedOrderResult result = this.payService.unifiedOrder(payInfo); 60 | log.info(new Gson().toJson(result)); 61 | renderString(response, result); 62 | } 63 | 64 | /** 65 | * 返回前台H5调用JS支付所需要的参数,公众号支付调用此接口 66 | * 67 | * @param response 68 | * @param request 69 | */ 70 | @RequestMapping(value = "getJSSDKPayInfo") 71 | public void getJSSDKPayInfo(HttpServletResponse response, 72 | HttpServletRequest request) { 73 | ReturnModel returnModel = new ReturnModel(); 74 | WxPayUnifiedOrderRequest prepayInfo = WxPayUnifiedOrderRequest.newBuilder() 75 | .openid(request.getParameter("openid")) 76 | .outTradeNo(request.getParameter("out_trade_no")) 77 | .totalFee(Integer.valueOf(request.getParameter("total_fee"))) 78 | .body(request.getParameter("body")) 79 | .tradeType(request.getParameter("trade_type")) 80 | .spbillCreateIp(request.getParameter("spbill_create_ip")) 81 | .notifyUrl("// TODO 填写通知回调地址") 82 | .build(); 83 | 84 | try { 85 | Map payInfo = this.payService.getPayInfo(prepayInfo); 86 | returnModel.setResult(true); 87 | returnModel.setDatum(payInfo); 88 | renderString(response, returnModel); 89 | } catch (WxPayException e) { 90 | returnModel.setResult(false); 91 | returnModel.setReason(e.getErrCodeDes()); 92 | renderString(response, returnModel); 93 | log.error(e.getErrCodeDes()); 94 | } 95 | } 96 | 97 | /** 98 | * 微信通知支付结果的回调地址,notify_url 99 | * 100 | * @param request 101 | * @param response 102 | */ 103 | @RequestMapping(value = "getJSSDKCallbackData") 104 | public void getJSSDKCallbackData(HttpServletRequest request, 105 | HttpServletResponse response) { 106 | try { 107 | synchronized (this) { 108 | Map kvm = XMLUtil.parseRequestXmlToMap(request); 109 | if (SignUtils.checkSign(kvm, null, this.payConfig.getMchKey())) { 110 | if (kvm.get("result_code").equals("SUCCESS")) { 111 | //TODO(user) 微信服务器通知此回调接口支付成功后,通知给业务系统做处理 112 | logger.info("out_trade_no: " + kvm.get("out_trade_no") + " pay SUCCESS!"); 113 | response.getWriter().write(""); 114 | } else { 115 | log.error("out_trade_no: " 116 | + kvm.get("out_trade_no") + " result_code is FAIL"); 117 | response.getWriter().write( 118 | ""); 119 | } 120 | } else { 121 | response.getWriter().write( 122 | ""); 123 | log.error("out_trade_no: " + kvm.get("out_trade_no") 124 | + " check signature FAIL"); 125 | } 126 | } 127 | } catch (Exception e) { 128 | e.printStackTrace(); 129 | } 130 | } 131 | 132 | @RequestMapping(value = "entPay") 133 | public void payToIndividual(HttpServletResponse response, 134 | HttpServletRequest request) { 135 | EntPayRequest wxEntPayRequest = new EntPayRequest(); 136 | wxEntPayRequest.setAppid(payConfig.getAppId()); 137 | wxEntPayRequest.setMchId(payConfig.getMchId()); 138 | wxEntPayRequest.setNonceStr(Sha1Util.getNonceStr()); 139 | wxEntPayRequest.setPartnerTradeNo(request.getParameter("partner_trade_no")); 140 | wxEntPayRequest.setOpenid(request.getParameter("openid")); 141 | wxEntPayRequest.setCheckName("NO_CHECK"); 142 | wxEntPayRequest.setAmount(Integer.valueOf(request.getParameter("amount"))); 143 | wxEntPayRequest.setDescription(request.getParameter("desc")); 144 | wxEntPayRequest.setSpbillCreateIp(request.getParameter("spbill_create_ip")); 145 | 146 | try { 147 | EntPayResult wxEntPayResult = payService.getEntPayService().entPay(wxEntPayRequest); 148 | if ("SUCCESS".equals(wxEntPayResult.getResultCode().toUpperCase()) 149 | && "SUCCESS".equals(wxEntPayResult.getReturnCode().toUpperCase())) { 150 | log.info("企业对个人付款成功!\n付款信息:\n" + wxEntPayResult.toString()); 151 | } else { 152 | log.error("err_code: " + wxEntPayResult.getErrCode() 153 | + " err_code_des: " + wxEntPayResult.getErrCodeDes()); 154 | } 155 | } catch (Exception e) { 156 | e.printStackTrace(); 157 | } 158 | } 159 | 160 | } 161 | 162 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/controller/TemplateMessageController.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.controller; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import me.chanjar.weixin.common.error.WxErrorException; 7 | import me.chanjar.weixin.mp.config.WxMpConfigStorage; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | 12 | import me.chanjar.weixin.mp.api.WxMpService; 13 | import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; 14 | import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; 15 | 16 | /** 17 | * 模板消息Controller 18 | *

19 | * Created by FirenzesEagle on 2016/7/11 0011. 20 | * Email:liumingbo2008@gmail.com 21 | */ 22 | @Controller 23 | @RequestMapping(value = "templateMessage") 24 | public class TemplateMessageController extends GenericController { 25 | 26 | // 模板消息字体颜色 27 | private static final String TEMPLATE_FRONT_COLOR = "#32CD32"; 28 | @Autowired 29 | protected WxMpConfigStorage configStorage; 30 | @Autowired 31 | protected WxMpService wxMpService; 32 | 33 | @RequestMapping(value = "notifyOrderPaySuccessTemplate") 34 | public void notifyOrderPaySuccessTemplate(HttpServletResponse response, 35 | HttpServletRequest request) { 36 | WxMpTemplateMessage orderPaySuccessTemplate = WxMpTemplateMessage.builder().build(); 37 | orderPaySuccessTemplate.setToUser(request.getParameter("openid")); 38 | orderPaySuccessTemplate.setTemplateId("ENp7UwpOtlhvieebUvDm0mK4n0hTvbH0Me83HdBUvC0"); 39 | orderPaySuccessTemplate.setUrl(request.getParameter("url")); 40 | WxMpTemplateData firstData = new WxMpTemplateData("first", "订单支付成功", TEMPLATE_FRONT_COLOR); 41 | WxMpTemplateData orderMoneySumData = new WxMpTemplateData("orderMoneySum", request.getParameter("orderMoneySum"), TEMPLATE_FRONT_COLOR); 42 | WxMpTemplateData orderProductNameData = new WxMpTemplateData("orderProductName", request.getParameter("orderProductName"), TEMPLATE_FRONT_COLOR); 43 | WxMpTemplateData remarkData = new WxMpTemplateData("Remark", request.getParameter("remark"), TEMPLATE_FRONT_COLOR); 44 | orderPaySuccessTemplate.addData(firstData) 45 | .addData(orderMoneySumData) 46 | .addData(orderProductNameData) 47 | .addData(remarkData); 48 | try { 49 | wxMpService.getTemplateMsgService() 50 | .sendTemplateMsg(orderPaySuccessTemplate); 51 | } catch (WxErrorException e) { 52 | logger.error(e.getMessage()); 53 | } 54 | } 55 | 56 | @RequestMapping(value = "notifyOrderStatusUpdateTemplate") 57 | public void notifyOrderStatusUpdateTemplate(HttpServletResponse response, 58 | HttpServletRequest request) { 59 | WxMpTemplateMessage orderPaySuccessTemplate = WxMpTemplateMessage.builder().build(); 60 | orderPaySuccessTemplate.setToUser(request.getParameter("openid")); 61 | orderPaySuccessTemplate.setTemplateId("X8ccwRF4EAx7VHFQGzi78Gl0C3GcpGpYgWk-HFFOWA0"); 62 | orderPaySuccessTemplate.setUrl(request.getParameter("url")); 63 | WxMpTemplateData firstData = new WxMpTemplateData("first", "订单状态更新", TEMPLATE_FRONT_COLOR); 64 | WxMpTemplateData orderMoneySumData = new WxMpTemplateData("OrderSn", request.getParameter("OrderSn"), TEMPLATE_FRONT_COLOR); 65 | WxMpTemplateData orderProductNameData = new WxMpTemplateData("OrderStatus", request.getParameter("OrderStatus"), TEMPLATE_FRONT_COLOR); 66 | WxMpTemplateData remarkData = new WxMpTemplateData("remark", request.getParameter("remark"), TEMPLATE_FRONT_COLOR); 67 | orderPaySuccessTemplate.addData(firstData) 68 | .addData(orderMoneySumData) 69 | .addData(orderProductNameData) 70 | .addData(remarkData); 71 | try { 72 | wxMpService.getTemplateMsgService() 73 | .sendTemplateMsg(orderPaySuccessTemplate); 74 | } catch (WxErrorException e) { 75 | logger.error(e.getMessage()); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/handler/AbstractHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.handler; 2 | 3 | import com.google.gson.Gson; 4 | import me.chanjar.weixin.mp.api.WxMpMessageHandler; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * 消息处理Handler的父类 10 | *

11 | * Created by FirenzesEagle on 2016/7/27 0027. 12 | * Email:liumingbo2008@gmail.com 13 | */ 14 | public abstract class AbstractHandler implements WxMpMessageHandler { 15 | protected Logger logger = LoggerFactory.getLogger(getClass()); 16 | protected final Gson gson = new Gson(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/handler/LogHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.handler; 2 | 3 | import java.util.Map; 4 | 5 | import org.springframework.stereotype.Component; 6 | 7 | import me.chanjar.weixin.common.session.WxSessionManager; 8 | import me.chanjar.weixin.mp.api.WxMpService; 9 | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 10 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 11 | 12 | /** 13 | * 对所有接收到的消息输出日志,也可进行持久化处理 14 | *

15 | * Created by FirenzesEagle on 2016/7/27 0027. 16 | * Email:liumingbo2008@gmail.com 17 | */ 18 | @Component 19 | public class LogHandler extends AbstractHandler { 20 | @Override 21 | public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, 22 | Map context, WxMpService wxMpService, 23 | WxSessionManager sessionManager) { 24 | log.info("\n接收到请求消息,内容:【{}】", wxMessage.toString()); 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/handler/MsgHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.handler; 2 | 3 | import java.util.Map; 4 | 5 | import me.chanjar.weixin.common.error.WxErrorException; 6 | import org.springframework.stereotype.Component; 7 | 8 | import me.chanjar.weixin.common.session.WxSessionManager; 9 | import me.chanjar.weixin.mp.api.WxMpService; 10 | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 11 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 12 | 13 | /** 14 | * 转发客户消息给客服Handler 15 | *

16 | * Created by FirenzesEagle on 2016/7/27 0027. 17 | * Email:liumingbo2008@gmail.com 18 | */ 19 | @Component 20 | public class MsgHandler extends AbstractHandler { 21 | @Override 22 | public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxMpService, 23 | WxSessionManager sessionManager) { 24 | return WxMpXmlOutMessage 25 | .TRANSFER_CUSTOMER_SERVICE().fromUser(wxMessage.getToUser()) 26 | .toUser(wxMessage.getFromUser()).build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/handler/SubscribeHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.handler; 2 | 3 | import java.util.Map; 4 | 5 | import com.github.weixin.demo.service.CoreService; 6 | import me.chanjar.weixin.mp.config.WxMpConfigStorage; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import me.chanjar.weixin.common.session.WxSessionManager; 11 | import me.chanjar.weixin.mp.api.WxMpService; 12 | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 13 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 14 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage; 15 | import me.chanjar.weixin.mp.bean.result.WxMpUser; 16 | 17 | /** 18 | * 用户关注公众号Handler 19 | *

20 | * Created by FirenzesEagle on 2016/7/27 0027. 21 | * Email:liumingbo2008@gmail.com 22 | */ 23 | @Component 24 | public class SubscribeHandler extends AbstractHandler { 25 | 26 | @Autowired 27 | protected WxMpConfigStorage configStorage; 28 | @Autowired 29 | protected WxMpService wxMpService; 30 | @Autowired 31 | protected CoreService coreService; 32 | 33 | 34 | @Override 35 | public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, 36 | WxMpService wxMpService, WxSessionManager sessionManager) { 37 | WxMpUser wxMpUser = coreService.getUserInfo(wxMessage.getFromUser(), "zh_CN"); 38 | /*List params = new ArrayList<>(); 39 | params.add(new BasicNameValuePair("openId", wxMpUser.getOpenId())); 40 | params.add(new BasicNameValuePair("nickname", wxMpUser.getNickname())); 41 | params.add(new BasicNameValuePair("headImgUrl", wxMpUser.getHeadImgUrl()));*/ 42 | 43 | //TODO(user) 在这里可以进行用户关注时对业务系统的相关操作(比如新增用户) 44 | 45 | WxMpXmlOutTextMessage m 46 | = WxMpXmlOutMessage.TEXT() 47 | .content("尊敬的" + wxMpUser.getNickname() + ",您好!") 48 | .fromUser(wxMessage.getToUser()) 49 | .toUser(wxMessage.getFromUser()) 50 | .build(); 51 | logger.info("subscribeMessageHandler" + m.getContent()); 52 | return m; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/service/CoreService.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.service; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | import org.apache.http.NameValuePair; 7 | import org.apache.http.client.ClientProtocolException; 8 | 9 | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 10 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 11 | import me.chanjar.weixin.mp.bean.result.WxMpUser; 12 | 13 | /** 14 | * Created by FirenzesEagle on 2016/5/30 0030. 15 | * Email:liumingbo2008@gmail.com 16 | */ 17 | public interface CoreService { 18 | 19 | /** 20 | * HttpGet请求 21 | * 22 | * @param urlWithParams 23 | * @throws Exception 24 | */ 25 | void requestGet(String urlWithParams) throws IOException; 26 | 27 | /** 28 | * HttpPost请求 29 | * 30 | * @param url 31 | * @param params 32 | * @throws ClientProtocolException 33 | * @throws IOException 34 | */ 35 | void requestPost(String url, List params) throws ClientProtocolException, IOException; 36 | 37 | /** 38 | * 刷新消息路由器 39 | */ 40 | void refreshRouter(); 41 | 42 | /** 43 | * 路由消息 44 | * 45 | * @param inMessage 46 | * @return 47 | */ 48 | WxMpXmlOutMessage route(WxMpXmlMessage inMessage); 49 | 50 | /** 51 | * 通过openid获得基本用户信息 52 | * 53 | * @param openid 54 | * @param lang 55 | * @return 56 | */ 57 | WxMpUser getUserInfo(String openid, String lang); 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/service/impl/CoreServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.service.impl; 2 | 3 | import com.github.weixin.demo.handler.LogHandler; 4 | import com.github.weixin.demo.handler.MsgHandler; 5 | import com.github.weixin.demo.handler.SubscribeHandler; 6 | import com.github.weixin.demo.service.CoreService; 7 | import me.chanjar.weixin.common.api.WxConsts; 8 | import me.chanjar.weixin.common.error.WxErrorException; 9 | import me.chanjar.weixin.mp.api.WxMpMessageRouter; 10 | import me.chanjar.weixin.mp.api.WxMpService; 11 | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; 12 | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; 13 | import me.chanjar.weixin.mp.bean.result.WxMpUser; 14 | import org.apache.http.HttpEntity; 15 | import org.apache.http.NameValuePair; 16 | import org.apache.http.client.ClientProtocolException; 17 | import org.apache.http.client.config.RequestConfig; 18 | import org.apache.http.client.entity.UrlEncodedFormEntity; 19 | import org.apache.http.client.methods.CloseableHttpResponse; 20 | import org.apache.http.client.methods.HttpGet; 21 | import org.apache.http.client.methods.HttpPost; 22 | import org.apache.http.impl.client.CloseableHttpClient; 23 | import org.apache.http.impl.client.HttpClientBuilder; 24 | import org.apache.http.util.EntityUtils; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.stereotype.Service; 29 | 30 | import javax.annotation.PostConstruct; 31 | import java.io.IOException; 32 | import java.nio.charset.StandardCharsets; 33 | import java.util.List; 34 | 35 | /** 36 | * Created by FirenzesEagle on 2016/5/30 0030. 37 | * Email:liumingbo2008@gmail.com 38 | */ 39 | @Service 40 | public class CoreServiceImpl implements CoreService { 41 | 42 | @Autowired 43 | protected WxMpService wxMpService; 44 | @Autowired 45 | protected LogHandler logHandler; 46 | @Autowired 47 | protected SubscribeHandler subscribeHandler; 48 | @Autowired 49 | protected MsgHandler msgHandler; 50 | protected Logger logger = LoggerFactory.getLogger(getClass()); 51 | private WxMpMessageRouter router; 52 | 53 | @PostConstruct 54 | public void init() { 55 | this.refreshRouter(); 56 | } 57 | 58 | @Override 59 | public void requestGet(String urlWithParams) throws IOException { 60 | CloseableHttpClient httpclient = HttpClientBuilder.create().build(); 61 | HttpGet httpget = new HttpGet(urlWithParams); 62 | httpget.addHeader("Content-Type", "text/html;charset=UTF-8"); 63 | //配置请求的超时设置 64 | RequestConfig requestConfig = RequestConfig.custom() 65 | .setConnectionRequestTimeout(50) 66 | .setConnectTimeout(50) 67 | .setSocketTimeout(50).build(); 68 | httpget.setConfig(requestConfig); 69 | 70 | CloseableHttpResponse response = httpclient.execute(httpget); 71 | System.out.println("StatusCode -> " + response.getStatusLine().getStatusCode()); 72 | 73 | HttpEntity entity = response.getEntity(); 74 | String jsonStr = EntityUtils.toString(entity); 75 | System.out.println(jsonStr); 76 | 77 | httpget.releaseConnection(); 78 | } 79 | 80 | @Override 81 | public void requestPost(String url, List params) throws ClientProtocolException, IOException { 82 | CloseableHttpClient httpclient = HttpClientBuilder.create().build(); 83 | 84 | HttpPost httppost = new HttpPost(url); 85 | httppost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8)); 86 | 87 | CloseableHttpResponse response = httpclient.execute(httppost); 88 | System.out.println(response.toString()); 89 | 90 | HttpEntity entity = response.getEntity(); 91 | String jsonStr = EntityUtils.toString(entity, "utf-8"); 92 | System.out.println(jsonStr); 93 | 94 | httppost.releaseConnection(); 95 | } 96 | 97 | @Override 98 | public void refreshRouter() { 99 | final WxMpMessageRouter newRouter = new WxMpMessageRouter( 100 | this.wxMpService); 101 | // 记录所有事件的日志 102 | newRouter.rule().handler(this.logHandler).next(); 103 | // 关注事件 104 | newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT) 105 | .event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler) 106 | .end(); 107 | // 默认,转发消息给客服人员 108 | newRouter.rule().async(false).handler(this.msgHandler).end(); 109 | this.router = newRouter; 110 | } 111 | 112 | @Override 113 | public WxMpXmlOutMessage route(WxMpXmlMessage inMessage) { 114 | try { 115 | return this.router.route(inMessage); 116 | } catch (Exception e) { 117 | log.error(e.getMessage(), e); 118 | } 119 | 120 | return null; 121 | } 122 | 123 | @Override 124 | public WxMpUser getUserInfo(String openid, String lang) { 125 | WxMpUser wxMpUser = null; 126 | try { 127 | wxMpUser = this.wxMpService.getUserService().userInfo(openid, lang); 128 | } catch (WxErrorException e) { 129 | log.error(e.getError().toString()); 130 | } 131 | return wxMpUser; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/util/GenericReturnModel.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.util; 2 | 3 | public class GenericReturnModel { 4 | 5 | private boolean result; 6 | private String reason; 7 | 8 | private T datum; 9 | 10 | public boolean isResult() { 11 | return result; 12 | } 13 | 14 | public void setResult(boolean result) { 15 | this.result = result; 16 | } 17 | 18 | public String getReason() { 19 | return reason; 20 | } 21 | 22 | public void setReason(String reason) { 23 | this.reason = reason; 24 | } 25 | 26 | public T getDatum() { 27 | return datum; 28 | } 29 | 30 | public void setDatum(T datum) { 31 | this.datum = datum; 32 | } 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/util/MD5Util.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.util; 2 | 3 | import java.security.MessageDigest; 4 | 5 | import static java.nio.charset.StandardCharsets.UTF_8; 6 | 7 | public class MD5Util { 8 | 9 | private static final String HEX_DIGITS[] = {"0", "1", "2", "3", "4", "5", 10 | "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; 11 | 12 | public static String md5Encode(String origin, String charsetname) { 13 | String resultString = null; 14 | try { 15 | resultString = origin; 16 | MessageDigest md = MessageDigest.getInstance("MD5"); 17 | if (charsetname == null || "".equals(charsetname)) 18 | resultString = byteArrayToHexString(md.digest(resultString 19 | .getBytes(UTF_8))); 20 | else 21 | resultString = byteArrayToHexString(md.digest(resultString 22 | .getBytes(charsetname))); 23 | } catch (Exception e) { 24 | e.printStackTrace(); 25 | } 26 | return resultString; 27 | } 28 | 29 | private static String byteArrayToHexString(byte b[]) { 30 | StringBuffer resultSb = new StringBuffer(); 31 | for (int i = 0; i < b.length; i++) 32 | resultSb.append(byteToHexString(b[i])); 33 | 34 | return resultSb.toString(); 35 | } 36 | 37 | private static String byteToHexString(byte b) { 38 | int n = b; 39 | if (n < 0) 40 | n += 256; 41 | int d1 = n / 16; 42 | int d2 = n % 16; 43 | return HEX_DIGITS[d1] + HEX_DIGITS[d2]; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/util/ReturnModel.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.util; 2 | 3 | public class ReturnModel extends GenericReturnModel { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/util/Sha1Util.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.util; 2 | 3 | import java.security.MessageDigest; 4 | import java.util.Iterator; 5 | import java.util.Map; 6 | import java.util.Random; 7 | import java.util.Set; 8 | import java.util.SortedMap; 9 | 10 | /** 11 | * createSHA1Sign创建签名SHA1 12 | * getSha1()Sha1签名 13 | */ 14 | public class Sha1Util { 15 | 16 | public static String getNonceStr() { 17 | Random random = new Random(); 18 | return MD5Util.md5Encode(String.valueOf(random.nextInt(10000)), "UTF-8"); 19 | } 20 | 21 | public static String getTimeStamp() { 22 | return String.valueOf(System.currentTimeMillis() / 1000); 23 | } 24 | 25 | //创建签名SHA1 26 | public static String createSHA1Sign(SortedMap signParams) throws Exception { 27 | StringBuffer sb = new StringBuffer(); 28 | Set es = signParams.entrySet(); 29 | Iterator it = es.iterator(); 30 | while (it.hasNext()) { 31 | Map.Entry entry = (Map.Entry) it.next(); 32 | String k = (String) entry.getKey(); 33 | String v = (String) entry.getValue(); 34 | sb.append(k + "=" + v + "&"); 35 | //要采用URLENCODER的原始值! 36 | } 37 | String params = sb.substring(0, sb.lastIndexOf("&")); 38 | System.out.println("sha1 sb:" + params); 39 | return getSha1(params); 40 | } 41 | 42 | //Sha1签名 43 | public static String getSha1(String str) { 44 | if (str == null || str.length() == 0) { 45 | return null; 46 | } 47 | char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 48 | 'a', 'b', 'c', 'd', 'e', 'f'}; 49 | 50 | try { 51 | MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); 52 | mdTemp.update(str.getBytes("GBK")); 53 | 54 | byte[] md = mdTemp.digest(); 55 | int j = md.length; 56 | char buf[] = new char[j * 2]; 57 | int k = 0; 58 | for (int i = 0; i < j; i++) { 59 | byte byte0 = md[i]; 60 | buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; 61 | buf[k++] = hexDigits[byte0 & 0xf]; 62 | } 63 | return new String(buf); 64 | } catch (Exception e) { 65 | e.printStackTrace(); 66 | return null; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/util/XMLUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.weixin.demo.util; 2 | 3 | import org.dom4j.Document; 4 | import org.dom4j.DocumentException; 5 | import org.dom4j.Element; 6 | import org.dom4j.io.SAXReader; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.io.ByteArrayInputStream; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | /** 17 | * Created by FirenzesEagle on 2016/7/7 0007. 18 | * Email:liumingbo2008@gmail.com 19 | */ 20 | public class XMLUtil { 21 | 22 | /** 23 | * 将微信服务器发送的Request请求中Body的XML解析为Map 24 | * 25 | * @param request 26 | * @return . 27 | * @throws Exception 28 | */ 29 | public static Map parseRequestXmlToMap(HttpServletRequest request) throws Exception { 30 | // 解析结果存储在HashMap中 31 | Map resultMap; 32 | InputStream inputStream = request.getInputStream(); 33 | resultMap = parseInputStreamToMap(inputStream); 34 | return resultMap; 35 | } 36 | 37 | /** 38 | * 将输入流中的XML解析为Map 39 | * 40 | * @param inputStream 41 | * @return . 42 | * @throws DocumentException 43 | * @throws IOException 44 | */ 45 | public static Map parseInputStreamToMap(InputStream inputStream) throws DocumentException, IOException { 46 | // 解析结果存储在HashMap中 47 | Map map = new HashMap(); 48 | // 读取输入流 49 | SAXReader reader = new SAXReader(); 50 | Document document = reader.read(inputStream); 51 | //得到xml根元素 52 | Element root = document.getRootElement(); 53 | // 得到根元素的所有子节点 54 | List elementList = root.elements(); 55 | //遍历所有子节点 56 | for (Element e : elementList) { 57 | map.put(e.getName(), e.getText()); 58 | } 59 | //释放资源 60 | inputStream.close(); 61 | return map; 62 | } 63 | 64 | /** 65 | * 将String类型的XML解析为Map 66 | * 67 | * @param str 68 | * @return . 69 | * @throws Exception 70 | */ 71 | public static Map parseXmlStringToMap(String str) throws Exception { 72 | Map resultMap; 73 | InputStream inputStream = new ByteArrayInputStream(str.getBytes("UTF-8")); 74 | resultMap = parseInputStreamToMap(inputStream); 75 | return resultMap; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/github/weixin/demo/util/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 工具类包 3 | *

4 | * Created by FirenzesEagle on 2016/6/27 0027. 5 | * Email:liumingbo2008@gmail.com 6 | */ 7 | package com.github.weixin.demo.util; 8 | -------------------------------------------------------------------------------- /src/main/resources/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 0 24 | UTF-8 25 | 0.########## 26 | yyyy-MM-dd HH:mm:ss 27 | true 28 | ignore 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG, stdout 2 | 3 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 4 | log4j.appender.stdout.Target=System.out 5 | log4j.appender.stdout.layout=org.apache.log4j.EnhancedPatternLayout 6 | log4j.appender.stdout.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} [%t]:%c:%L#%M() %m%n 7 | 8 | log4j.logger.org.apache.http.impl.conn.PoolingHttpClientConnectionManager=INFO -------------------------------------------------------------------------------- /src/main/resources/spring-mvc.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | text/plain;charset=UTF-8 45 | application/json;charset=UTF-8 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.web.servlet.view.freemarker.FreeMarkerView 57 | 58 | 59 | true 60 | 61 | 63 | 64 | 65 | .html 66 | 67 | 68 | text/html; charset=UTF-8 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/resources/wx.properties: -------------------------------------------------------------------------------- 1 | #公众号 APP_ID 2 | appId= 3 | #公众号 APP_SECRET 4 | appSecret= 5 | #公众号 TOKEN 6 | token= 7 | #公众号 AES_KEY 8 | aesKey= 9 | -------------------------------------------------------------------------------- /src/main/resources/wxPay.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wechat-Group/weixin-java-demo-springmvc/7b9ed47b875f3f4a7b938649c526b9cdaa789b26/src/main/resources/wxPay.properties -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Wechat Service System 6 | 7 | 8 | Welcome to Wechat Service System ! 9 | 10 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | contextConfigLocation 10 | 11 | classpath*:applicationContext.xml 12 | 13 | 14 | 15 | 16 | org.springframework.web.context.ContextLoaderListener 17 | 18 | 19 | 20 | 21 | 22 | encodingFilter 23 | org.springframework.web.filter.CharacterEncodingFilter 24 | 25 | encoding 26 | UTF-8 27 | 28 | 29 | forceEncoding 30 | true 31 | 32 | 33 | 34 | encodingFilter 35 | /* 36 | 37 | 38 | 39 | 40 | dispatcher 41 | org.springframework.web.servlet.DispatcherServlet 42 | 43 | contextConfigLocation 44 | classpath*:spring-mvc.xml 45 | 46 | 1 47 | 48 | 49 | dispatcher 50 | 51 | /* 52 | 53 | 54 | 55 | 56 | 57 | index 58 | 59 | 60 | --------------------------------------------------------------------------------