├── .gitignore
├── README.md
├── app-demo
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── riversoft
│ │ └── weixin
│ │ └── demo
│ │ └── app
│ │ ├── Application.java
│ │ └── WxCallbackController.java
│ └── resources
│ ├── application.properties
│ └── wx-app-settings-test.xml
├── commons
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── riversoft
│ └── weixin
│ └── demo
│ └── commons
│ ├── DefaultDuplicatedMessageChecker.java
│ └── DuplicatedMessageChecker.java
├── mp-demo
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── riversoft
│ │ └── weixin
│ │ └── demo
│ │ └── mp
│ │ ├── Application.java
│ │ ├── MpOAuthCallbackController.java
│ │ └── WxCallbackController.java
│ └── resources
│ ├── application.properties
│ └── wx-mp-settings-test.xml
├── open-demo
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── riversoft
│ │ └── weixin
│ │ └── demo
│ │ └── open
│ │ └── Application.java
│ └── resources
│ ├── application.properties
│ └── wx-open-settings-test.xml
├── pay-demo
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── riversoft
│ │ └── weixin
│ │ └── demo
│ │ └── pay
│ │ └── Application.java
│ └── resources
│ ├── application.properties
│ └── wx-pay-settings-test.xml
├── pom.xml
└── qydev-demo
├── pom.xml
└── src
└── main
├── java
└── com
│ └── riversoft
│ └── weixin
│ └── demo
│ └── qydev
│ ├── Application.java
│ └── WxCallbackController.java
└── resources
├── application.properties
└── wx-qy-settings-test.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | .idea/
4 | *.iml
5 | target/
6 | pom.xml.tag
7 | pom.xml.releaseBackup
8 | pom.xml.versionsBackup
9 | pom.xml.next
10 | release.properties
11 | dependency-reduced-pom.xml
12 | buildNumber.properties
13 | .mvn/timing.properties
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # weixin-sdk-demo
2 |
3 | 使用spring boot,基于weixin-sdk开发的web应用。
4 |
5 | ## 如何使用?
6 |
7 | ### 企业号
8 | - 修改 wx-qy-settings-test.xml 文件, 根据您的情况修改corpId, corpSecret相关信息。
9 | - 修改application.properties 中默认的agent配置
10 | - agent.default.token=xxxxxxxxx
11 | - agent.default.aesKey=xxxxxxxxxxxxxxxxx
12 |
13 | ### 公众号
14 | - 修改wx-mp-settings-test.xml 文件, 根据您的情况修改appId, appSecret相关信息。
15 |
16 | ### 开放平台
17 | - 修改wx-open-settings-test.xml 文件, 根据您的情况修改appId, appSecret相关信息。
18 |
19 | ### 微信支付
20 | - 修改wx-pay-settings-test.xml 文件, 根据您的情况修改appId, mchID, key, certPath, certPass相关信息。
21 |
22 | ### 小程序
23 | - 修改wx-app-settings-test.xml 文件, 根据您的情况修改appId, appSecret相关信息。
24 |
25 | ### 修改应用服务器端口
26 | - 修改文件:application.properties
27 |
28 | ### 启动
29 | - com.riversoft.weixin.demo.mp.Application
30 | - com.riversoft.weixin.demo.qydev.Application
31 | - com.riversoft.weixin.demo.open.Application
32 | - com.riversoft.weixin.demo.pay.Application
33 | - com.riversoft.weixin.demo.app.Application
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | com.riversoft
7 | weixin-sdk-demo
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | app-demo
13 | Demo for APP
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 |
22 | com.riversoft
23 | commons
24 | ${project.version}
25 |
26 |
27 |
28 | cn.com.riversoft
29 | weixin-app
30 |
31 |
32 |
33 | commons-io
34 | commons-io
35 |
36 |
37 |
38 | com.fasterxml.jackson.core
39 | jackson-databind
40 |
41 |
42 | com.fasterxml.jackson.core
43 | jackson-annotations
44 |
45 |
46 |
47 | org.codehaus.jackson
48 | jackson-mapper-asl
49 |
50 |
51 | com.fasterxml.jackson.dataformat
52 | jackson-dataformat-xml
53 |
54 |
55 | junit
56 | junit
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app-demo/src/main/java/com/riversoft/weixin/demo/app/Application.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.app;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * Created by exizhai on 10/7/2015.
8 | */
9 | @SpringBootApplication
10 | public class Application {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(Application.class, args);
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/app-demo/src/main/java/com/riversoft/weixin/demo/app/WxCallbackController.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.app;
2 |
3 | import com.riversoft.weixin.app.base.AppSetting;
4 | import com.riversoft.weixin.app.care.CareMessages;
5 | import com.riversoft.weixin.app.message.AppXmlMessages;
6 | import com.riversoft.weixin.app.template.Message;
7 | import com.riversoft.weixin.app.template.Templates;
8 | import com.riversoft.weixin.common.decrypt.AesException;
9 | import com.riversoft.weixin.common.decrypt.MessageDecryption;
10 | import com.riversoft.weixin.common.decrypt.SHA1;
11 | import com.riversoft.weixin.common.message.XmlMessageHeader;
12 | import com.riversoft.weixin.demo.commons.DuplicatedMessageChecker;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.stereotype.Controller;
17 | import org.springframework.util.StringUtils;
18 | import org.springframework.web.bind.annotation.RequestBody;
19 | import org.springframework.web.bind.annotation.RequestMapping;
20 | import org.springframework.web.bind.annotation.RequestParam;
21 | import org.springframework.web.bind.annotation.ResponseBody;
22 |
23 | /**
24 | * @borball on 12/29/2016.
25 | */
26 | @Controller
27 | public class WxCallbackController {
28 |
29 | private static Logger logger = LoggerFactory.getLogger(WxCallbackController.class);
30 |
31 | @Autowired
32 | private DuplicatedMessageChecker duplicatedMessageChecker;
33 |
34 | public void setDuplicatedMessageChecker(DuplicatedMessageChecker duplicatedMessageChecker) {
35 | this.duplicatedMessageChecker = duplicatedMessageChecker;
36 | }
37 |
38 | /**
39 | * 小程序回调接口
40 | *
41 | * @param signature
42 | * @param msg_signature
43 | * @param timestamp
44 | * @param nonce
45 | * @param echostr
46 | * @param encrypt_type
47 | * @param content
48 | * @return
49 | */
50 | @RequestMapping("/wx/app")
51 | @ResponseBody
52 | public String mp(@RequestParam(value="signature") String signature,
53 | @RequestParam(value="msg_signature", required = false) String msg_signature,
54 | @RequestParam(value="timestamp") String timestamp,
55 | @RequestParam(value="nonce") String nonce,
56 | @RequestParam(value="echostr", required = false) String echostr,
57 | @RequestParam(value="encrypt_type", required = false) String encrypt_type,
58 | @RequestBody(required = false) String content) {
59 |
60 | logger.info("signature={}, msg_signature={}, timestamp={}, nonce={}, echostr={}, encrypt_type={}", signature, msg_signature, timestamp, nonce, echostr, encrypt_type);
61 |
62 | AppSetting appSetting = AppSetting.defaultSettings();
63 | try {
64 | if(!SHA1.getSHA1(appSetting.getToken(), timestamp, nonce).equals(signature)) {
65 | logger.warn("invalid request.");
66 | return "invalid request.";
67 | }
68 | } catch (AesException e) {
69 | logger.error("check signature failed:", e);
70 | return "invalid request.";
71 | }
72 |
73 | if (!StringUtils.isEmpty(echostr)) {
74 | return echostr;
75 | }
76 |
77 | XmlMessageHeader xmlRequest = null;
78 | if("aes".equals(encrypt_type)) {
79 | try {
80 | MessageDecryption messageDecryption = new MessageDecryption(appSetting.getToken(), appSetting.getAesKey(), appSetting.getAppId());
81 | xmlRequest = AppXmlMessages.fromXml(messageDecryption.decrypt(msg_signature, timestamp, nonce, content));
82 | } catch (AesException e) {
83 | }
84 | } else {
85 | xmlRequest = AppXmlMessages.fromXml(content);
86 | }
87 |
88 | dispatch(xmlRequest);
89 |
90 | return "";
91 | }
92 |
93 | /**
94 | * 具体业务逻辑
95 | * @param xmlRequest
96 | */
97 | private void dispatch(XmlMessageHeader xmlRequest) {
98 | if(!duplicatedMessageChecker.isDuplicated(xmlRequest.getFromUser() + xmlRequest.getCreateTime().getTime())) {
99 | //如果有需要可以调用客服接口或者模板消息接口发送消息给用户
100 | //Message message = new Message();
101 | //Templates.defaultTemplates().send(message);
102 |
103 | //CareMessages.defaultCareMessages().text(xmlRequest.getFromUser(), "Hello!");
104 | //CareMessages.defaultCareMessages().image(xmlRequest.getFromUser(), "image_media_id");
105 | } else {
106 | logger.warn("Duplicated message: {} @ {}", xmlRequest.getMsgType(), xmlRequest.getFromUser());
107 | }
108 |
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/app-demo/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=48084
2 |
--------------------------------------------------------------------------------
/app-demo/src/main/resources/wx-app-settings-test.xml:
--------------------------------------------------------------------------------
1 |
2 | wx122233344555555
3 | zzzzzzzzzzzzzzzzzzz
4 | xxxxxxxxxxxxxxxx
5 | tttttttttttttttttttttttttttttttt
6 |
9 |
--------------------------------------------------------------------------------
/commons/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | com.riversoft
7 | weixin-sdk-demo
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | commons
13 | Demo commons
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 | com.google.guava
22 | guava
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/riversoft/weixin/demo/commons/DefaultDuplicatedMessageChecker.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.commons;
2 |
3 | import com.google.common.cache.Cache;
4 | import com.google.common.cache.CacheBuilder;
5 | import org.springframework.stereotype.Component;
6 |
7 | import java.util.concurrent.TimeUnit;
8 |
9 | /**
10 | * Created by exizhai on 11/15/2015.
11 | */
12 | @Component
13 | public class DefaultDuplicatedMessageChecker implements DuplicatedMessageChecker {
14 |
15 | private static Cache cache = CacheBuilder.newBuilder().expireAfterWrite(15, TimeUnit.SECONDS).maximumSize(10000).build();
16 |
17 | @Override
18 | public boolean isDuplicated(String msgKey) {
19 | if(cache.getIfPresent(msgKey) == null) {
20 | cache.put(msgKey, msgKey);
21 | return false;
22 | } else {
23 | return true;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/commons/src/main/java/com/riversoft/weixin/demo/commons/DuplicatedMessageChecker.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.commons;
2 |
3 | /**
4 | * Created by exizhai on 11/15/2015.
5 | */
6 | public interface DuplicatedMessageChecker {
7 |
8 | boolean isDuplicated(String msgKey);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/mp-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | com.riversoft
7 | weixin-sdk-demo
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | mp-demo
13 | Demo for MP
14 |
15 |
16 |
17 | com.riversoft
18 | commons
19 | ${project.version}
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-web
25 |
26 |
27 |
28 | cn.com.riversoft
29 | weixin-mp
30 |
31 |
32 |
33 | commons-io
34 | commons-io
35 |
36 |
37 |
38 | com.fasterxml.jackson.core
39 | jackson-databind
40 |
41 |
42 | com.fasterxml.jackson.core
43 | jackson-annotations
44 |
45 |
46 |
47 | org.codehaus.jackson
48 | jackson-mapper-asl
49 |
50 |
51 | com.fasterxml.jackson.dataformat
52 | jackson-dataformat-xml
53 |
54 |
55 | junit
56 | junit
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/mp-demo/src/main/java/com/riversoft/weixin/demo/mp/Application.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.mp;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * Created by exizhai on 10/7/2015.
8 | */
9 | @SpringBootApplication
10 | public class Application {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(Application.class, args);
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/mp-demo/src/main/java/com/riversoft/weixin/demo/mp/MpOAuthCallbackController.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.mp;
2 |
3 | import com.riversoft.weixin.common.oauth2.AccessToken;
4 | import com.riversoft.weixin.common.oauth2.OpenUser;
5 | import com.riversoft.weixin.mp.oauth2.MpOAuth2s;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RequestParam;
9 |
10 | /**
11 | * Created by exizhai on 12/17/2015.
12 | */
13 | public class MpOAuthCallbackController {
14 |
15 | private static org.slf4j.Logger logger = LoggerFactory.getLogger(WxCallbackController.class);
16 |
17 | /**
18 | * 公众号OAuth回调接口
19 | * @return
20 | */
21 | @RequestMapping("/wx/oauth/mp")
22 | public void mp(@RequestParam(value="code") String code, @RequestParam(value="state", required = false) String state) {
23 |
24 | logger.info("code:{}, state:{}", code, state);
25 | AccessToken accessToken = MpOAuth2s.defaultOAuth2s().getAccessToken(code);
26 | OpenUser openUser = MpOAuth2s.defaultOAuth2s().userInfo(accessToken.getAccessToken(), accessToken.getOpenId());
27 | //do your logic
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/mp-demo/src/main/java/com/riversoft/weixin/demo/mp/WxCallbackController.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.mp;
2 |
3 | import com.riversoft.weixin.common.decrypt.AesException;
4 | import com.riversoft.weixin.common.decrypt.MessageDecryption;
5 | import com.riversoft.weixin.common.decrypt.SHA1;
6 | import com.riversoft.weixin.common.event.EventRequest;
7 | import com.riversoft.weixin.common.exception.WxRuntimeException;
8 | import com.riversoft.weixin.common.message.XmlMessageHeader;
9 | import com.riversoft.weixin.demo.commons.DuplicatedMessageChecker;
10 | import com.riversoft.weixin.mp.base.AppSetting;
11 | import com.riversoft.weixin.mp.care.CareMessages;
12 | import com.riversoft.weixin.mp.message.MpXmlMessages;
13 | import com.riversoft.weixin.mp.user.Users;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 | import org.springframework.beans.factory.annotation.Autowired;
17 | import org.springframework.stereotype.Controller;
18 | import org.springframework.util.StringUtils;
19 | import org.springframework.web.bind.annotation.*;
20 |
21 | /**
22 | * Created by exizhai on 10/7/2015.
23 | */
24 | @Controller
25 | public class WxCallbackController {
26 |
27 | private static Logger logger = LoggerFactory.getLogger(WxCallbackController.class);
28 |
29 | @Autowired
30 | private DuplicatedMessageChecker duplicatedMessageChecker;
31 |
32 | public void setDuplicatedMessageChecker(DuplicatedMessageChecker duplicatedMessageChecker) {
33 | this.duplicatedMessageChecker = duplicatedMessageChecker;
34 | }
35 |
36 | /**
37 | * 公众号回调接口
38 | * 这里为了演示方便使用单个固定URL,实际使用的时候一个一个系统可能有多个公众号,这样的话需要有个分发逻辑:
39 | * 比如callback url可以定义为 /wx/mp/[公众号的appId],通过appId构造不同的AppSetting
40 | *
41 | * @param signature
42 | * @param msg_signature
43 | * @param timestamp
44 | * @param nonce
45 | * @param echostr
46 | * @param encrypt_type
47 | * @param content
48 | * @return
49 | */
50 | @RequestMapping("/wx/mp")
51 | @ResponseBody
52 | public String mp(@RequestParam(value="signature") String signature,
53 | @RequestParam(value="msg_signature", required = false) String msg_signature,
54 | @RequestParam(value="timestamp") String timestamp,
55 | @RequestParam(value="nonce") String nonce,
56 | @RequestParam(value="echostr", required = false) String echostr,
57 | @RequestParam(value="encrypt_type", required = false) String encrypt_type,
58 | @RequestBody(required = false) String content) {
59 |
60 | logger.info("signature={}, msg_signature={}, timestamp={}, nonce={}, echostr={}, encrypt_type={}", signature, msg_signature, timestamp, nonce, echostr, encrypt_type);
61 |
62 | AppSetting appSetting = AppSetting.defaultSettings();
63 | try {
64 | if(!SHA1.getSHA1(appSetting.getToken(), timestamp, nonce).equals(signature)) {
65 | logger.warn("非法请求.");
66 | return "非法请求.";
67 | }
68 | } catch (AesException e) {
69 | logger.error("check signature failed:", e);
70 | return "非法请求.";
71 | }
72 |
73 | if (!StringUtils.isEmpty(echostr)) {
74 | return echostr;
75 | }
76 |
77 | XmlMessageHeader xmlRequest = null;
78 | if("aes".equals(encrypt_type)) {
79 | try {
80 | MessageDecryption messageDecryption = new MessageDecryption(appSetting.getToken(), appSetting.getAesKey(), appSetting.getAppId());
81 | xmlRequest = MpXmlMessages.fromXml(messageDecryption.decrypt(msg_signature, timestamp, nonce, content));
82 | XmlMessageHeader xmlResponse = mpDispatch(xmlRequest);
83 |
84 | if(xmlResponse != null) {
85 | try {
86 | return messageDecryption.encrypt(MpXmlMessages.toXml(xmlResponse), timestamp, nonce);
87 | } catch (WxRuntimeException e) {
88 |
89 | }
90 | }
91 | } catch (AesException e) {
92 | }
93 | } else {
94 | xmlRequest = MpXmlMessages.fromXml(content);
95 | XmlMessageHeader xmlResponse = mpDispatch(xmlRequest);
96 | if(xmlResponse != null) {
97 | try {
98 | return MpXmlMessages.toXml(xmlResponse);
99 | } catch (WxRuntimeException e) {
100 | }
101 | }
102 | }
103 |
104 | return "";
105 | }
106 |
107 | private XmlMessageHeader mpDispatch(XmlMessageHeader xmlRequest) {
108 | if(!duplicatedMessageChecker.isDuplicated(xmlRequest.getFromUser() + xmlRequest.getCreateTime().getTime())) {
109 | String welcome = "您好:" + Users.defaultUsers().get(xmlRequest.getFromUser()).getNickName();
110 | CareMessages.defaultCareMessages().text(xmlRequest.getFromUser(), welcome);
111 |
112 | if (xmlRequest instanceof EventRequest) {
113 | EventRequest eventRequest = (EventRequest) xmlRequest;
114 | logger.debug("事件请求[{}]", eventRequest.getEventType().name());
115 | CareMessages.defaultCareMessages().text(xmlRequest.getFromUser(), "事件请求:" + eventRequest.getEventType().name());
116 | } else {
117 | logger.debug("消息请求[{}]", xmlRequest.getMsgType().name());
118 | CareMessages.defaultCareMessages().text(xmlRequest.getFromUser(), "消息请求:" + xmlRequest.getMsgType().name());
119 | }
120 | } else {
121 | logger.warn("Duplicated message: {} @ {}", xmlRequest.getMsgType(), xmlRequest.getFromUser());
122 | }
123 |
124 | //需要同步返回消息(被动回复)给用户则构造一个XmlMessageHeader类型,比较鸡肋,因为处理逻辑如果比较复杂响应太慢会影响用户感知,建议直接返回null;
125 | //要发送消息给用户可以参考上面的例子使用客服消息接口进行异步发送
126 | return null;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/mp-demo/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=48080
2 |
--------------------------------------------------------------------------------
/mp-demo/src/main/resources/wx-mp-settings-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | wx74755fbba35c24da
4 | d4624c36b6795d1d99dcf0547af5443d
5 | wonskd113n
6 | lRE3qfnBHxqzBE5i12v6CzU257ERsOIeMKqfDPdFu5c
7 |
10 |
--------------------------------------------------------------------------------
/open-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | com.riversoft
7 | weixin-sdk-demo
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | open-demo
13 | Demo for OPEN
14 |
15 |
16 |
17 | com.riversoft
18 | commons
19 | ${project.version}
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-web
25 |
26 |
27 |
28 | cn.com.riversoft
29 | weixin-open
30 |
31 |
32 |
33 | commons-io
34 | commons-io
35 |
36 |
37 |
38 | com.fasterxml.jackson.core
39 | jackson-databind
40 |
41 |
42 | com.fasterxml.jackson.core
43 | jackson-annotations
44 |
45 |
46 |
47 | org.codehaus.jackson
48 | jackson-mapper-asl
49 |
50 |
51 | com.fasterxml.jackson.dataformat
52 | jackson-dataformat-xml
53 |
54 |
55 | junit
56 | junit
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/open-demo/src/main/java/com/riversoft/weixin/demo/open/Application.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.open;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * Created by exizhai on 10/7/2015.
8 | */
9 | @SpringBootApplication
10 | public class Application {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(Application.class, args);
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/open-demo/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=48083
2 |
--------------------------------------------------------------------------------
/open-demo/src/main/resources/wx-open-settings-test.xml:
--------------------------------------------------------------------------------
1 |
2 | wx1111111111111111
3 | aaa111222333444555fffrrreeeddd1233
4 |
7 |
--------------------------------------------------------------------------------
/pay-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | com.riversoft
7 | weixin-sdk-demo
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | pay-demo
13 | Demo for PAY
14 |
15 |
16 |
17 | com.riversoft
18 | commons
19 | ${project.version}
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-web
25 |
26 |
27 |
28 | cn.com.riversoft
29 | weixin-pay
30 |
31 |
32 |
33 | commons-io
34 | commons-io
35 |
36 |
37 |
38 | com.fasterxml.jackson.core
39 | jackson-databind
40 |
41 |
42 | com.fasterxml.jackson.core
43 | jackson-annotations
44 |
45 |
46 |
47 | org.codehaus.jackson
48 | jackson-mapper-asl
49 |
50 |
51 | com.fasterxml.jackson.dataformat
52 | jackson-dataformat-xml
53 |
54 |
55 | junit
56 | junit
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/pay-demo/src/main/java/com/riversoft/weixin/demo/pay/Application.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.pay;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * Created by exizhai on 10/7/2015.
8 | */
9 | @SpringBootApplication
10 | public class Application {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(Application.class, args);
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/pay-demo/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=48082
2 |
--------------------------------------------------------------------------------
/pay-demo/src/main/resources/wx-pay-settings-test.xml:
--------------------------------------------------------------------------------
1 |
2 | xxxxxxxxxxx
3 | yyyyyyyyyyy
4 | zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
5 | /opt/weixin/...
6 | 12345678
7 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 1.4.3.RELEASE
9 |
10 | 4.0.0
11 |
12 | weixin-sdk-demo
13 | com.riversoft
14 | weixin SDK Demo
15 | 0.0.1-SNAPSHOT
16 | pom
17 |
18 |
19 | 2.4
20 | 0.9.6
21 | 2.8.5
22 | 1.9.13
23 | 18.0
24 |
25 |
26 |
27 | commons
28 | mp-demo
29 | qydev-demo
30 | open-demo
31 | pay-demo
32 | app-demo
33 |
34 |
35 |
36 |
37 |
38 | org.springframework.boot
39 | spring-boot-starter-web
40 | 1.4.3.RELEASE
41 |
42 |
43 |
44 | cn.com.riversoft
45 | weixin-mp
46 | ${weixin-version}
47 |
48 |
49 |
50 | cn.com.riversoft
51 | weixin-qydev
52 | ${weixin-version}
53 |
54 |
55 |
56 | cn.com.riversoft
57 | weixin-open
58 | ${weixin-version}
59 |
60 |
61 |
62 | cn.com.riversoft
63 | weixin-pay
64 | ${weixin-version}
65 |
66 |
67 |
68 | cn.com.riversoft
69 | weixin-app
70 | ${weixin-version}
71 |
72 |
73 |
74 | commons-io
75 | commons-io
76 | ${commons-io.version}
77 |
78 |
79 |
80 | com.fasterxml.jackson.core
81 | jackson-databind
82 | ${jackson.version}
83 |
84 |
85 | com.fasterxml.jackson.core
86 | jackson-annotations
87 | ${jackson.version}
88 |
89 |
90 |
91 | org.codehaus.jackson
92 | jackson-mapper-asl
93 | ${jackson-mapper-asl.version}
94 |
95 |
96 | com.fasterxml.jackson.dataformat
97 | jackson-dataformat-xml
98 | ${jackson.version}
99 |
100 |
101 |
102 | com.google.guava
103 | guava
104 | ${guava.version}
105 |
106 |
107 |
108 | junit
109 | junit
110 | 4.12
111 | test
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/qydev-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | com.riversoft
7 | weixin-sdk-demo
8 | 0.0.1-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | qydev-demo
13 | Demo for QYDEV
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-web
19 |
20 |
21 |
22 | cn.com.riversoft
23 | weixin-qydev
24 |
25 |
26 |
27 | commons-io
28 | commons-io
29 |
30 |
31 |
32 | com.fasterxml.jackson.core
33 | jackson-databind
34 |
35 |
36 | com.fasterxml.jackson.core
37 | jackson-annotations
38 |
39 |
40 |
41 | org.codehaus.jackson
42 | jackson-mapper-asl
43 |
44 |
45 | com.fasterxml.jackson.dataformat
46 | jackson-dataformat-xml
47 |
48 |
49 | junit
50 | junit
51 |
52 |
53 | com.riversoft
54 | commons
55 | 0.0.1-SNAPSHOT
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/qydev-demo/src/main/java/com/riversoft/weixin/demo/qydev/Application.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.qydev;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * Created by exizhai on 10/7/2015.
8 | */
9 | @SpringBootApplication
10 | public class Application {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(Application.class, args);
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/qydev-demo/src/main/java/com/riversoft/weixin/demo/qydev/WxCallbackController.java:
--------------------------------------------------------------------------------
1 | package com.riversoft.weixin.demo.qydev;
2 |
3 | import com.riversoft.weixin.common.decrypt.MessageDecryption;
4 | import com.riversoft.weixin.common.exception.WxRuntimeException;
5 | import com.riversoft.weixin.common.message.XmlMessageHeader;
6 | import com.riversoft.weixin.demo.commons.DuplicatedMessageChecker;
7 | import com.riversoft.weixin.qy.base.CorpSetting;
8 | import com.riversoft.weixin.qy.message.QyXmlMessages;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.beans.factory.annotation.Value;
13 | import org.springframework.stereotype.Controller;
14 | import org.springframework.util.StringUtils;
15 | import org.springframework.web.bind.annotation.*;
16 |
17 | /**
18 | * Created by exizhai on 10/7/2015.
19 | */
20 | @Controller
21 | public class WxCallbackController {
22 |
23 | private static Logger logger = LoggerFactory.getLogger(WxCallbackController.class);
24 |
25 | /**
26 | * token/aesKey 是和企业号应用绑定的,这里为了演示方便使用外部注入,实际使用的时候一个企业号可能有多个应用,这样的话需要有个分发逻辑。
27 | * 比如callback url可以定义为 /wx/qy/[应用的ID],通过ID查询不同的token和aesKey
28 | */
29 | @Value("${agent.default.token}")
30 | private String token;
31 |
32 | @Value("${agent.default.aesKey}")
33 | private String aesKey;
34 |
35 | @Autowired
36 | private DuplicatedMessageChecker duplicatedMessageChecker;
37 |
38 | public void setDuplicatedMessageChecker(DuplicatedMessageChecker duplicatedMessageChecker) {
39 | this.duplicatedMessageChecker = duplicatedMessageChecker;
40 | }
41 |
42 | /**
43 | * 企业号回调接口
44 | * 这里为了演示方便使用单个URL,实际使用的时候一个企业号可能有多个应用,这样的话需要有个分发逻辑:
45 | * 比如callback url可以定义为 /wx/qy/[应用的ID],通过ID查询不同的token和aesKey
46 | *
47 | * @param signature
48 | * @param timestamp
49 | * @param nonce
50 | * @param echostr
51 | * @param content
52 | * @return
53 | */
54 | @RequestMapping("/wx/qy")
55 | @ResponseBody
56 | public String qy(@RequestParam(value="msg_signature") String signature,
57 | @RequestParam(value="timestamp") String timestamp,
58 | @RequestParam(value="nonce") String nonce,
59 | @RequestParam(value="echostr", required = false) String echostr,
60 | @RequestBody(required = false) String content) {
61 |
62 | logger.info("msg_signature={}, nonce={}, timestamp={}, echostr={}", signature, nonce, timestamp, echostr);
63 |
64 | CorpSetting corpSetting = CorpSetting.defaultSettings();
65 |
66 | try {
67 | MessageDecryption messageDecryption = new MessageDecryption(token, aesKey, corpSetting.getCorpId());
68 | if (!StringUtils.isEmpty(echostr)) {
69 | String echo = messageDecryption.decryptEcho(signature, timestamp, nonce, echostr);
70 | logger.info("消息签名验证成功.");
71 | return echo;
72 | } else {
73 | XmlMessageHeader xmlRequest = QyXmlMessages.fromXml(messageDecryption.decrypt(signature, timestamp, nonce, content));
74 | XmlMessageHeader xmlResponse = qyDispatch(xmlRequest);
75 | if(xmlResponse != null) {
76 | try {
77 | return messageDecryption.encrypt(QyXmlMessages.toXml(xmlResponse), timestamp, nonce);
78 | } catch (WxRuntimeException e) {
79 | }
80 | }
81 | }
82 | } catch (Exception e) {
83 | logger.error("callback failed.", e);
84 | }
85 |
86 | return "";
87 | }
88 |
89 | private XmlMessageHeader qyDispatch(XmlMessageHeader xmlRequest) {
90 | //添加处理逻辑
91 |
92 | //需要同步返回消息(被动响应消息)给用户则构造一个XmlMessageHeader类型,比较鸡肋,因为处理逻辑如果比较复杂响应太慢会影响用户感知,建议直接返回null;
93 | //如果有消息需要发送给用户则可以调用主动消息发送接口进行异步发送
94 | return null;
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/qydev-demo/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=48081
2 | agent.default.token=xxxxxxxxx
3 | agent.default.aesKey=xxxxxxxxxxxxxxxxx
--------------------------------------------------------------------------------
/qydev-demo/src/main/resources/wx-qy-settings-test.xml:
--------------------------------------------------------------------------------
1 |
2 | xxxxxxxxxxx
3 | xxxxxxxxxxxxxx
4 |
7 |
--------------------------------------------------------------------------------