From 8dadf49cdda3244e650a7750560e27fd7ba95c19 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Tue, 1 Nov 2016 11:48:42 +0800 Subject: [PATCH 01/41] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7=E4=B8=BAsnapshot=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- pom.xml | 2 +- weixin-java-common/pom.xml | 2 +- weixin-java-cp/pom.xml | 2 +- weixin-java-mp/pom.xml | 2 +- weixin-java-osgi/pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 4e454b1a31..6fe43d14df 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ allprojects { apply plugin: 'maven' group = 'com.github.binarywang' - version = '2.3.0' + version = '2.4.0-SNAPSHOT' } subprojects { diff --git a/pom.xml b/pom.xml index 4621e60d98..763587e9f5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.github.binarywang weixin-java-parent - 2.3.0 + 2.4.0-SNAPSHOT pom WeiXin Java Tools - Parent 微信公众号、企业号上级POM diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index e5d5d73aa1..1f67e0dae1 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.3.0 + 2.4.0-SNAPSHOT weixin-java-common diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index e7fbd2557e..6fa3bde27d 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.3.0 + 2.4.0-SNAPSHOT weixin-java-cp diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index c1b6a13684..58b634ec10 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.3.0 + 2.4.0-SNAPSHOT weixin-java-mp WeiXin Java Tools - MP diff --git a/weixin-java-osgi/pom.xml b/weixin-java-osgi/pom.xml index 7f4a597390..3833499cf9 100644 --- a/weixin-java-osgi/pom.xml +++ b/weixin-java-osgi/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.3.0 + 2.4.0-SNAPSHOT weixin-java-osgi From fad9abaa40da09648f1c10d777d1ef42306b72e9 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Tue, 1 Nov 2016 19:15:15 +0800 Subject: [PATCH 02/41] =?UTF-8?q?=E5=B0=86httpclient=E6=98=BE=E5=BC=8F?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E4=BE=9D=E8=B5=96=EF=BC=8C=E4=BB=A5=E4=BE=BF?= =?UTF-8?q?=E4=BA=8E=E5=AE=A2=E6=88=B7=E7=AB=AF=E4=BC=98=E5=85=88=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E9=85=8D=E7=BD=AE=E7=9A=84=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 763587e9f5..add5301997 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,11 @@ ${logback.version} test + + org.apache.httpcomponents + httpclient + ${httpclient.version} + org.apache.httpcomponents httpmime From 8c00299e1aa6215f8dbfcadf870788ca0d5247e8 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Tue, 1 Nov 2016 19:16:17 +0800 Subject: [PATCH 03/41] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0gradl?= =?UTF-8?q?e=E7=9B=B8=E5=85=B3=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + weixin-java-common/build.gradle | 4 ++-- weixin-java-cp/build.gradle | 4 ++-- weixin-java-mp/build.gradle | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 6fe43d14df..a285821074 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ subprojects { dependencies { compile group: 'org.slf4j', name: 'slf4j-api', version:'1.7.10' compile group: 'org.apache.httpcomponents', name: 'httpmime', version:'4.5' + compile group: 'org.apache.httpcomponents', name: 'httpclient', version:'4.5' compile group: 'com.google.code.gson', name: 'gson', version:'2.7' compile group: 'com.google.guava', name: 'guava', version:'19.0' compile group: 'commons-codec', name: 'commons-codec', version:'1.10' diff --git a/weixin-java-common/build.gradle b/weixin-java-common/build.gradle index 07b5036aa7..718004931c 100644 --- a/weixin-java-common/build.gradle +++ b/weixin-java-common/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile group: 'org.testng', name: 'testng', version:'6.8.7' testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' testCompile group: 'com.google.inject', name: 'guice', version:'3.0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.M0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.M0' + testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' + testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' } test.useTestNG() diff --git a/weixin-java-cp/build.gradle b/weixin-java-cp/build.gradle index f58703ca4b..85d7c4bba2 100644 --- a/weixin-java-cp/build.gradle +++ b/weixin-java-cp/build.gradle @@ -6,7 +6,7 @@ dependencies { testCompile group: 'org.testng', name: 'testng', version:'6.8.7' testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' testCompile group: 'com.google.inject', name: 'guice', version:'3.0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.M0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.M0' + testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' + testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' } test.useTestNG() diff --git a/weixin-java-mp/build.gradle b/weixin-java-mp/build.gradle index dcc015480a..de1534a3a9 100644 --- a/weixin-java-mp/build.gradle +++ b/weixin-java-mp/build.gradle @@ -6,8 +6,8 @@ dependencies { testCompile group: 'org.testng', name: 'testng', version:'6.8.7' testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5' testCompile group: 'com.google.inject', name: 'guice', version:'3.0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.M0' - testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.M0' + testCompile group: 'org.eclipse.jetty', name: 'jetty-server', version:'9.3.0.RC0' + testCompile group: 'org.eclipse.jetty', name: 'jetty-servlet', version:'9.3.0.RC0' testCompile group: 'joda-time', name: 'joda-time', version:'2.9.4' } test.useTestNG() From 3ed717a448c62f6cd7909c68defb44e685aaec51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Binary=EF=BC=88=E7=8F=AD=E7=BA=B3=E7=9D=BF=EF=BC=89?= Date: Fri, 4 Nov 2016 11:11:48 +0800 Subject: [PATCH 04/41] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 035ec8ad4c..7d7b23b0ff 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# Weixin Java Tools 微信公众号/企业号开发Java SDK -## ![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/weixin-java-parent.svg) [![Build Status](https://travis-ci.org/wechat-group/weixin-java-tools.svg?branch=develop)](https://travis-ci.org/wechat-group/weixin-java-tools) [![Join QQ Group](http://pub.idqqimg.com/wpa/images/group.png)](http://jq.qq.com/?_wv=1027&k=40lRskK) - +Weixin Java Tools 微信公众号/企业号开发Java SDK +===================================== +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent) +[![Build Status](https://travis-ci.org/wechat-group/weixin-java-tools.svg?branch=develop)](https://travis-ci.org/wechat-group/weixin-java-tools) ### 注意: 1. ***本项目Fork自chanjarster/weixin-java-tools,但由于原项目已停止维护,故单独维护和发布,且发布到maven上的groupId也会不同,详细信息见下文。*** @@ -24,7 +25,7 @@ ## 版本说明 1. 本项目定为每月发布一次正式版,版本号格式为X.X.0(如2.1.0,2.2.0等),月初或月底发布新版本,遇到重大问题需修复会及时提交新版本,欢迎大家随时提交Pull Request; 1. BUG修复和新特性一般会先发布成小版本作为临时版本(如2.0.1,2.0.2等,即尾号不为0,以区别于正式版); -1. 目前最新版本号为 ![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/weixin-java-parent.svg) ,也可以通过访问链接 [【公众号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-mp%22) 、[【企业号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-cp%22) +1. 目前最新版本号为 [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.binarywang/weixin-java-parent) ,也可以通过访问链接 [【公众号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-mp%22) 、[【企业号】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-cp%22) 分别查看所有最新的版本。 ## Maven & Gradle From 6cf7b0889793ad7e5164220a55bb338f6d04efe4 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Fri, 4 Nov 2016 16:58:39 +0800 Subject: [PATCH 05/41] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=A8=A1=E7=89=88?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E9=87=8C=E6=97=A0=E7=94=A8=E7=9A=84topcolor?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mp/bean/template/WxMpTemplateMessage.java | 24 ++++--------------- .../json/WxMpTemplateMessageGsonAdapter.java | 3 --- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java index 106e5fb6ec..df8e708dad 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java @@ -1,18 +1,20 @@ package me.chanjar.weixin.mp.bean.template; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - +/** + * 参考 http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277&token=&lang=zh_CN 发送模板消息接口部分 + */ public class WxMpTemplateMessage implements Serializable { private static final long serialVersionUID = 5063374783759519418L; private String toUser; private String templateId; private String url; - private String topColor; private List data = new ArrayList<>(); public String getToUser() { @@ -39,14 +41,6 @@ public void setUrl(String url) { this.url = url; } - public String getTopColor() { - return this.topColor; - } - - public void setTopColor(String topColor) { - this.topColor = topColor; - } - public List getData() { return this.data; } @@ -71,7 +65,6 @@ public static class WxMpTemplateMessageBuilder { private String toUser; private String templateId; private String url; - private String topColor; private List data = new ArrayList<>(); public WxMpTemplateMessageBuilder toUser(String toUser) { @@ -89,11 +82,6 @@ public WxMpTemplateMessageBuilder url(String url) { return this; } - public WxMpTemplateMessageBuilder topColor(String topColor) { - this.topColor = topColor; - return this; - } - public WxMpTemplateMessageBuilder data(List data) { this.data = data; return this; @@ -103,7 +91,6 @@ public WxMpTemplateMessageBuilder from(WxMpTemplateMessage origin) { this.toUser(origin.toUser); this.templateId(origin.templateId); this.url(origin.url); - this.topColor(origin.topColor); this.data(origin.data); return this; } @@ -113,7 +100,6 @@ public WxMpTemplateMessage build() { m.toUser = this.toUser; m.templateId = this.templateId; m.url = this.url; - m.topColor = this.topColor; m.data = this.data; return m; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java index 4e26302092..5cc0d9c8cd 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java @@ -28,9 +28,6 @@ public JsonElement serialize(WxMpTemplateMessage message, Type typeOfSrc, JsonSe if (message.getUrl() != null) { messageJson.addProperty("url", message.getUrl()); } - if (message.getTopColor() != null) { - messageJson.addProperty("topcolor", message.getTopColor()); - } JsonObject data = new JsonObject(); messageJson.add("data", data); From 1bd4f681b6cfc5292b98dc588b05b4c38c3a3d5e Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Fri, 4 Nov 2016 17:19:55 +0800 Subject: [PATCH 06/41] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=8F=91=E9=80=81=E7=9A=84=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=8C=E5=8A=A0=E5=85=A5=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java index 9b50d5df6a..ee975181fc 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java @@ -9,6 +9,7 @@ import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; import me.chanjar.weixin.mp.bean.template.WxMpTemplateIndustry; import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; +import org.apache.commons.lang3.RandomStringUtils; import org.testng.Assert; import org.testng.annotations.Guice; import org.testng.annotations.Test; @@ -28,7 +29,7 @@ public class WxMpTemplateMsgServiceImplTest { @Inject protected WxMpService wxService; - @Test(invocationCount = 10, threadPoolSize = 10) + @Test(invocationCount = 5, threadPoolSize = 3) public void testSendTemplateMsg() throws WxErrorException { SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS"); @@ -39,6 +40,8 @@ public void testSendTemplateMsg() throws WxErrorException { .templateId(configStorage.getTemplateId()).build(); templateMessage.addWxMpTemplateData( new WxMpTemplateData("first", dateFormat.format(new Date()))); + templateMessage.addWxMpTemplateData( + new WxMpTemplateData("remark", RandomStringUtils.randomAlphanumeric(100), "#FF00FF")); String msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage); Assert.assertNotNull(msgId); System.out.println(msgId); From f478ceb245df64a93c991949dbf26c574f5c3ac0 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Fri, 4 Nov 2016 18:54:28 +0800 Subject: [PATCH 07/41] =?UTF-8?q?=E6=8C=89openid=E7=BE=A4=E5=8F=91?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=97=B6=E6=8F=90=E4=BE=9BsetToUsers?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E6=96=B9=E4=BE=BF=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E7=9B=B4=E6=8E=A5=E8=AE=BE=E7=BD=AE=E6=89=80=E6=9C=89?= =?UTF-8?q?=E7=BE=A4=E5=8F=91=E5=AF=B9=E8=B1=A1=E7=9A=84openid=E5=88=97?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mp/bean/WxMpMassOpenIdsMessage.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java index 38bd8ea7f0..982cb78f5b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassOpenIdsMessage.java @@ -1,13 +1,13 @@ package me.chanjar.weixin.mp.bean; -import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; - import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + /** - * OpenId列表群发的消息 + * openid列表群发的消息 * * @author chanjarster */ @@ -64,17 +64,26 @@ public String toJson() { } /** - * OpenId列表,最多支持10,000个 + * openid列表,最多支持10,000个 */ public List getToUsers() { return this.toUsers; } /** - * 添加OpenId,最多支持10,000个 - * @param openId + * 添加openid,最多支持10,000个 + * @param openid */ - public void addUser(String openId) { - this.toUsers.add(openId); + public void addUser(String openid) { + this.toUsers.add(openid); } + + /** + * 提供set方法,方便客户端直接设置所有群发对象的openid列表 + * @param toUsers + */ + public void setToUsers(List toUsers) { + this.toUsers = toUsers; + } + } From e3fd10858b0351ad3787f4df89012d795e62c011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Binary=EF=BC=88=E7=8F=AD=E7=BA=B3=E7=9D=BF=EF=BC=89?= Date: Tue, 8 Nov 2016 10:47:57 +0800 Subject: [PATCH 08/41] Update README.md --- README.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d7b23b0ff..dfa6f5112b 100644 --- a/README.md +++ b/README.md @@ -81,5 +81,29 @@ compile 'com.github.binarywang:weixin-java-cp:2.3.0' 1. 为了便于设置,本项目引入editorconfig插件,请使用eclipse的同学在贡献代码前安装相关插件,IntelliJ IDEA则自带支持,无需额外安装插件。 1. 本项目可以采用两种方式接受代码贡献: - 1. 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支。 - 1. 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询管理员。 + 1. 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。 + 1. 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询QQ群管理员。 + +## PR方式贡献代码步骤 +* 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/weixin-java-tools`,然后 `clone` 到本地,并设置用户信息。 +``` +$ git clone git@github.com:my_user/weixin-java-tools.git +$ cd weixin-java-tools +$ git config user.name "yourname" +$ git config user.email "your email" +``` +* 修改代码后提交,并推送到自己的仓库。 +``` +$ #do some change on the content +$ git commit -am "Fix issue #1: change something" +$ git push +``` +* 在 GitHub 网站上提交 Pull Request。 +* 定期使用项目仓库内容更新自己仓库内容。 +``` +$ git remote add upstream https://github.com/wechat-group/weixin-java-tools +$ git fetch upstream +$ git checkout develop +$ git rebase upstream/develop +$ git push -f origin develop +``` From 109cab8c54e77455b5462f102a7aace90dab9fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Binary=EF=BC=88=E7=8F=AD=E7=BA=B3=E7=9D=BF=EF=BC=89?= Date: Tue, 8 Nov 2016 18:24:00 +0800 Subject: [PATCH 09/41] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dfa6f5112b..3f69044af9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Weixin Java Tools 微信公众号/企业号开发Java SDK =========== ## 开发交流方式及注意事项: -1. QQ群:343954419 [![Join QQ Group](http://pub.idqqimg.com/wpa/images/group.png)](http://jq.qq.com/?_wv=1027&k=40lRskK) +1. QQ群:343954419 [![Join QQ Group](http://pub.idqqimg.com/wpa/images/group.png)](http://jq.qq.com/?_wv=1027&k=40lRskK) (由于群容量有限即将爆满,现开启付费入群模式,并不定期清理长时间不活跃人士) 1. 微信群: 因微信群已达到100人限制,故如有想加入微信群的,请入QQ群后联系管理员,提供微信号以便邀请加入; 1. 新手提问前,请先阅读此文章:http://t.cn/RV93MRB 1. 寻求帮助时需贴代码或大长串异常信息的,请利用http://paste.ubuntu.com @@ -81,7 +81,7 @@ compile 'com.github.binarywang:weixin-java-cp:2.3.0' 1. 为了便于设置,本项目引入editorconfig插件,请使用eclipse的同学在贡献代码前安装相关插件,IntelliJ IDEA则自带支持,无需额外安装插件。 1. 本项目可以采用两种方式接受代码贡献: - 1. 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。 +  1. 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。 1. 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询QQ群管理员。 ## PR方式贡献代码步骤 From e4df6c8a306465b55b810a1766ddac6399409333 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Wed, 9 Nov 2016 18:55:53 +0800 Subject: [PATCH 10/41] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BC=98=E5=8C=96XStre?= =?UTF-8?q?amTransformer=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mp/util/xml/XStreamTransformer.java | 139 ++++++------------ 1 file changed, 43 insertions(+), 96 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java index 76f97be70c..fe690a855a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java @@ -1,25 +1,25 @@ package me.chanjar.weixin.mp.util.xml; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - import com.thoughtworks.xstream.XStream; - import me.chanjar.weixin.common.util.xml.XStreamInitializer; -import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMusicMessage; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutNewsMessage; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTextMessage; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutTransferKefuMessage; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVideoMessage; -import me.chanjar.weixin.mp.bean.message.WxMpXmlOutVoiceMessage; +import me.chanjar.weixin.mp.bean.message.*; -public class XStreamTransformer { +import java.io.InputStream; +import java.util.*; - protected static final Map, XStream> CLASS_2_XSTREAM_INSTANCE = configXStreamInstance(); +public class XStreamTransformer { + private static final Map, XStream> CLASS_2_XSTREAM_INSTANCE = new HashMap<>(); + + static { + registerClass(WxMpXmlMessage.class); + registerClass(WxMpXmlOutMusicMessage.class); + registerClass(WxMpXmlOutNewsMessage.class); + registerClass(WxMpXmlOutTextMessage.class); + registerClass(WxMpXmlOutImageMessage.class); + registerClass(WxMpXmlOutVideoMessage.class); + registerClass(WxMpXmlOutVoiceMessage.class); + registerClass(WxMpXmlOutTransferKefuMessage.class); + } /** * xml -> pojo @@ -36,16 +36,6 @@ public static T fromXml(Class clazz, InputStream is) { return object; } - /** - * 注册扩展消息的解析器 - * @param clz 类型 - * @param xStream xml解析器 - */ - public static void register(Class clz, XStream xStream) { - CLASS_2_XSTREAM_INSTANCE.put(clz,xStream); - } - - /** * pojo -> xml */ @@ -53,83 +43,40 @@ public static String toXml(Class clazz, T object) { return CLASS_2_XSTREAM_INSTANCE.get(clazz).toXML(object); } - private static Map, XStream> configXStreamInstance() { - Map, XStream> map = new HashMap<>(); - map.put(WxMpXmlMessage.class, config_WxMpXmlMessage()); - map.put(WxMpXmlOutMusicMessage.class, config_WxMpXmlOutMusicMessage()); - map.put(WxMpXmlOutNewsMessage.class, config_WxMpXmlOutNewsMessage()); - map.put(WxMpXmlOutTextMessage.class, config_WxMpXmlOutTextMessage()); - map.put(WxMpXmlOutImageMessage.class, config_WxMpXmlOutImageMessage()); - map.put(WxMpXmlOutVideoMessage.class, config_WxMpXmlOutVideoMessage()); - map.put(WxMpXmlOutVoiceMessage.class, config_WxMpXmlOutVoiceMessage()); - map.put(WxMpXmlOutTransferKefuMessage.class, config_WxMpXmlOutTransferCustomerServiceMessage()); - - return map; - } - - private static XStream config_WxMpXmlMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxMpXmlMessage.class); - xstream.processAnnotations(WxMpXmlMessage.ScanCodeInfo.class); - xstream.processAnnotations(WxMpXmlMessage.SendPicsInfo.class); - xstream.processAnnotations(WxMpXmlMessage.SendPicsInfo.Item.class); - xstream.processAnnotations(WxMpXmlMessage.SendLocationInfo.class); - - xstream.aliasField("MsgID", WxMpXmlMessage.class, "msgId"); - return xstream; - } - - private static XStream config_WxMpXmlOutImageMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxMpXmlOutMessage.class); - xstream.processAnnotations(WxMpXmlOutImageMessage.class); - return xstream; - } - - private static XStream config_WxMpXmlOutNewsMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxMpXmlOutMessage.class); - xstream.processAnnotations(WxMpXmlOutNewsMessage.class); - xstream.processAnnotations(WxMpXmlOutNewsMessage.Item.class); - return xstream; + /** + * 注册扩展消息的解析器 + * + * @param clz 类型 + * @param xStream xml解析器 + */ + public static void register(Class clz, XStream xStream) { + CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); } - private static XStream config_WxMpXmlOutMusicMessage() { + public static void registerClass(Class clz) { XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxMpXmlOutMessage.class); - xstream.processAnnotations(WxMpXmlOutMusicMessage.class); - xstream.processAnnotations(WxMpXmlOutMusicMessage.Music.class); - return xstream; + xstream.processAnnotations(clz); + xstream.processAnnotations(getInnerClasses(clz)); + register(clz, xstream); } - private static XStream config_WxMpXmlOutTextMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxMpXmlOutMessage.class); - xstream.processAnnotations(WxMpXmlOutTextMessage.class); - return xstream; - } + private static Class[] getInnerClasses(Class clz) { + Class[] innerClasses = clz.getClasses(); + if (innerClasses == null) { + return null; + } - private static XStream config_WxMpXmlOutVideoMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxMpXmlOutMessage.class); - xstream.processAnnotations(WxMpXmlOutVideoMessage.class); - xstream.processAnnotations(WxMpXmlOutVideoMessage.Video.class); - return xstream; - } + List> result = new ArrayList<>(); + result.addAll(Arrays.asList(innerClasses)); + for (Class inner : innerClasses) { + Class[] innerClz = getInnerClasses(inner); + if (innerClz == null) { + continue; + } - private static XStream config_WxMpXmlOutVoiceMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxMpXmlOutMessage.class); - xstream.processAnnotations(WxMpXmlOutVoiceMessage.class); - return xstream; - } + result.addAll(Arrays.asList(innerClz)); + } - private static XStream config_WxMpXmlOutTransferCustomerServiceMessage() { - XStream xstream = XStreamInitializer.getInstance(); - xstream.processAnnotations(WxMpXmlOutMessage.class); - xstream.processAnnotations(WxMpXmlOutTransferKefuMessage.class); - xstream.processAnnotations(WxMpXmlOutTransferKefuMessage.TransInfo.class); - return xstream; + return result.toArray(new Class[0]); } - } From a8871984e1feb64764f144154c90fa29a38405af Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Wed, 9 Nov 2016 19:37:15 +0800 Subject: [PATCH 11/41] =?UTF-8?q?=E8=A1=A5=E5=85=85public=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../me/chanjar/weixin/mp/util/xml/XStreamTransformer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java index fe690a855a..6edd653612 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java @@ -53,6 +53,10 @@ public static void register(Class clz, XStream xStream) { CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); } + /** + * 会自动注册该类及其子类 + * @param clz 要注册的类 + */ public static void registerClass(Class clz) { XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(clz); From fce1c2f95109b366373daa70cc64e4860c80b486 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Thu, 10 Nov 2016 11:41:48 +0800 Subject: [PATCH 12/41] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f69044af9..392d453030 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ Weixin Java Tools 微信公众号/企业号开发Java SDK 1. 寻求帮助时需贴代码或大长串异常信息的,请利用http://paste.ubuntu.com 1. 有功能需求或由于微信官方接口调整导致的代码问题,可以直接提出issue,便于讨论追踪问题; 1. 详细开发文档请看 [Wiki](https://github.com/wechat-group/weixin-java-tools/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。 -1. 微信公众号官方文档入口地址:http://mp.weixin.qq.com/wiki (注意,从网上搜到的文档有的虽然地址前面跟这个一样,但明显左侧菜单不一致,是旧的文档,注意不要看错了文档)。 +1. 微信公众号官方文档入口地址:http://mp.weixin.qq.com/wiki (注意,从网上搜到的文档有的虽然地址前面跟这个一样,但明显左侧菜单不一致,是旧的文档,注意不要看错文档)。 +1. 各个模块的Javadoc可以在线查看:[weixin-java-mp](https://binarywang.github.io/weixin-java-mp-javadoc/)、[weixin-java-common](https://binarywang.github.io/weixin-java-common-javadoc/)、[weixin-java-cp](https://binarywang.github.io/weixin-java-cp-javadoc/) =========== @@ -81,7 +82,7 @@ compile 'com.github.binarywang:weixin-java-cp:2.3.0' 1. 为了便于设置,本项目引入editorconfig插件,请使用eclipse的同学在贡献代码前安装相关插件,IntelliJ IDEA则自带支持,无需额外安装插件。 1. 本项目可以采用两种方式接受代码贡献: -  1. 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。 + 1. 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。 1. 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询QQ群管理员。 ## PR方式贡献代码步骤 From 6dccfc7a5b70df5d674dfc81a1e3e8260da9f15c Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Mon, 14 Nov 2016 13:28:13 +0800 Subject: [PATCH 13/41] upgrade version for maven-compiler-plugin --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index add5301997..05b03233c3 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,9 @@ + 1.7 + 1.7 + UTF-8 true true @@ -301,10 +304,8 @@ org.apache.maven.plugins maven-compiler-plugin - 2.3.2 + 3.6.0 - 1.7 - 1.7 UTF-8 From 44d6b952081dcdabeab6fa9f5aac8955ee09dda2 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Mon, 14 Nov 2016 15:16:09 +0800 Subject: [PATCH 14/41] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 392d453030..f4caf4aad0 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ Weixin Java Tools 微信公众号/企业号开发Java SDK =========== ## 开发交流方式及注意事项: -1. QQ群:343954419 [![Join QQ Group](http://pub.idqqimg.com/wpa/images/group.png)](http://jq.qq.com/?_wv=1027&k=40lRskK) (由于群容量有限即将爆满,现开启付费入群模式,并不定期清理长时间不活跃人士) +1. QQ群:343954419(推荐点击按钮入群: [![Join QQ Group](http://pub.idqqimg.com/wpa/images/group.png)](http://shang.qq.com/wpa/qunwpa?idkey=731dc3e7ea31ebe25376cc1a791445468612c63fd0e9e05399b088ec81fd9e15) 或 [![Join QQ Group](http://pub.idqqimg.com/wpa/images/group.png)](http://jq.qq.com/?_wv=1027&k=40lRskK),如果无反应,可以自行搜索群号进行添加 ) +1. 由于群容量有限即将爆满,现开启付费入群模式,并不定期清理长时间不活跃人士; 1. 微信群: 因微信群已达到100人限制,故如有想加入微信群的,请入QQ群后联系管理员,提供微信号以便邀请加入; 1. 新手提问前,请先阅读此文章:http://t.cn/RV93MRB 1. 寻求帮助时需贴代码或大长串异常信息的,请利用http://paste.ubuntu.com From 86160caa8026e70f5323a8ecfc27255d7726919c Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Wed, 16 Nov 2016 16:20:15 +0800 Subject: [PATCH 15/41] =?UTF-8?q?=E6=9B=B4=E6=96=B0javadoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java index 70b11fbf84..f67a8cb4fa 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/material/WxMpMaterialNews.java @@ -31,7 +31,7 @@ public boolean isEmpty() { /** *
    * 群发图文消息article
-   * 1. thumbMediaId  (必填) 图文消息缩略图的media_id,可以在基础支持-上传多媒体文件接口中获得
+   * 1. thumbMediaId  (必填) 图文消息的封面图片素材id(必须是永久mediaID)
    * 2. author          图文消息的作者
    * 3. title           (必填) 图文消息的标题
    * 4. contentSourceUrl 在图文消息页面点击“阅读原文”后的页面链接

From efa59d594c8c3af64ae3f297207d8b90c6b2191c Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Wed, 16 Nov 2016 16:30:53 +0800
Subject: [PATCH 16/41] Update README.md

---
 README.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/README.md b/README.md
index f4caf4aad0..8136cc89c5 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,6 @@ compile 'com.github.binarywang:weixin-java-cp:2.3.0'
 1. 非常欢迎和感谢对本项目发起Pull Request的同学,本项目代码风格为使用2个空格代表一个Tab,因此在提交代码时请注意一下,否则很容易在IDE格式化代码后与原代码产生大量diff,这样会给其他人阅读代码带来极大的困扰。
 1. 为了便于设置,本项目引入editorconfig插件,请使用eclipse的同学在贡献代码前安装相关插件,IntelliJ IDEA则自带支持,无需额外安装插件。
 1. 本项目可以采用两种方式接受代码贡献:
-
   1. 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。
   1. 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询QQ群管理员。
 

From c84767c8a00cc02f6d7daeca437bc8e5638b00fb Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Wed, 16 Nov 2016 17:24:58 +0800
Subject: [PATCH 17/41] Update README.md

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 8136cc89c5..dc7ea4554b 100644
--- a/README.md
+++ b/README.md
@@ -82,8 +82,8 @@ compile 'com.github.binarywang:weixin-java-cp:2.3.0'
 1. 非常欢迎和感谢对本项目发起Pull Request的同学,本项目代码风格为使用2个空格代表一个Tab,因此在提交代码时请注意一下,否则很容易在IDE格式化代码后与原代码产生大量diff,这样会给其他人阅读代码带来极大的困扰。
 1. 为了便于设置,本项目引入editorconfig插件,请使用eclipse的同学在贡献代码前安装相关插件,IntelliJ IDEA则自带支持,无需额外安装插件。
 1. 本项目可以采用两种方式接受代码贡献:
-  1. 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。
-  1. 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询QQ群管理员。
+  * 第一种就是基于[Git Flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)开发流程,因此在发起Pull Request的时候请选择develop分支,详细步骤参考后文。
+  * 另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题,具体加入方式,请咨询QQ群管理员[![点击这里给我发消息](http://wpa.qq.com/pa?p=2:1211415707:51)](http://wpa.qq.com/msgrd?v=3&uin=1211415707&site=qq&menu=yes)。
 
 ## PR方式贡献代码步骤
 * 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/weixin-java-tools`,然后 `clone` 到本地,并设置用户信息。

From 0b0fbcd5fe0307e8a4e21e52c8c90f3162e1efc1 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Thu, 17 Nov 2016 09:54:10 +0800
Subject: [PATCH 18/41] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=A4=A7=E5=86=99L?=
 =?UTF-8?q?=E4=BB=A3=E6=9B=BF=E5=B0=8F=E5=86=99l=EF=BC=8C=E4=BB=A5?=
 =?UTF-8?q?=E5=85=8D=E5=AE=B9=E6=98=93=E8=AF=AF=E8=A7=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
index e75b78da97..d497261e9f 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
@@ -63,7 +63,7 @@ public synchronized void updateAccessToken(WxAccessToken accessToken) {
   @Override
   public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
     this.accessToken = accessToken;
-    this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
+    this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
   }
 
   @Override
@@ -97,7 +97,7 @@ public boolean isJsapiTicketExpired() {
   public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
     this.jsapiTicket = jsapiTicket;
     // 预留200秒的时间
-    this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
+    this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
   }
 
   @Override
@@ -122,7 +122,7 @@ public boolean isCardApiTicketExpired() {
   public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) {
     this.cardApiTicket = cardApiTicket;
     // 预留200秒的时间
-    this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
+    this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
   }
 
   @Override

From 25c792b6b2bdd97eb62ef52f74f268b3f0a06de0 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Thu, 17 Nov 2016 11:15:05 +0800
Subject: [PATCH 19/41] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=AE=8C=E5=96=84?=
 =?UTF-8?q?=E9=83=A8=E5=88=86=E7=BC=BA=E5=A4=B1=E7=9A=84javadoc?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../weixin/mp/api/WxMpConfigStorage.java       | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
index 3a2e7654c9..0852cae2bc 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
@@ -24,16 +24,16 @@ public interface WxMpConfigStorage {
 
   /**
    * 应该是线程安全的
-   * @param accessToken
+   * @param accessToken 要更新的WxAccessToken对象
    */
   void updateAccessToken(WxAccessToken accessToken);
 
   /**
    * 应该是线程安全的
-   * @param accessToken
-   * @param expiresIn
+   * @param accessToken 新的accessToken值
+   * @param expiresInSeconds 过期时间,以秒为单位
    */
-  void updateAccessToken(String accessToken, int expiresIn);
+  void updateAccessToken(String accessToken, int expiresInSeconds);
 
   String getJsapiTicket();
 
@@ -46,7 +46,8 @@ public interface WxMpConfigStorage {
 
   /**
    * 应该是线程安全的
-   * @param jsapiTicket
+   * @param jsapiTicket 新的jsapi ticket值
+   * @param expiresInSeconds 过期时间,以秒为单位
    */
   void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
 
@@ -61,7 +62,8 @@ public interface WxMpConfigStorage {
 
   /**
    * 应该是线程安全的
-   * @param cardApiTicket
+   * @param cardApiTicket 新的cardApi ticket值
+   * @param expiresInSeconds 过期时间,以秒为单位
    */
   void updateCardApiTicket(String cardApiTicket, int expiresInSeconds);
 
@@ -70,7 +72,7 @@ public interface WxMpConfigStorage {
   String getSecret();
 
   String getPartnerId();
-  
+
   String getPartnerKey();
 
   String getToken();
@@ -88,7 +90,7 @@ public interface WxMpConfigStorage {
   String getHttpProxyUsername();
 
   String getHttpProxyPassword();
-  
+
   File getTmpDirFile();
 
   SSLContext getSSLContext();

From 88f0eb83aaa122c355a5dde897daa765581a6067 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Thu, 17 Nov 2016 11:22:19 +0800
Subject: [PATCH 20/41] Update README.md

---
 README.md | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index dc7ea4554b..7774fa6afb 100644
--- a/README.md
+++ b/README.md
@@ -72,10 +72,13 @@ compile 'com.github.binarywang:weixin-java-cp:2.3.0'
 * https://git.coding.net/binarywang/weixin-java-tools.git
 
 ===========
-## 目前可参考的Demo项目有三个(目前都是公众号的,风格不同,欢迎提供更多的demo供新手参考):
-1. https://github.com/wechat-group/weixin-mp-demo
-1. https://github.com/wechat-group/weixin-mp-demo-springboot
+## 可参考的Demo项目
+#### 目前都是公众号的,风格不同,欢迎提供更多的demo供新手参考:
+1. https://github.com/wechat-group/weixin-mp-demo 
+1. https://github.com/wechat-group/weixin-mp-multi-demo (支持多公众号)
 1. https://github.com/wechat-group/weixin-java-tools-springmvc
+1. https://github.com/wechat-group/weixin-mp-demo-springboot
+
 
 ===========
 ## 关于代码贡献

From 6d01fa5210abc5dd8f44c03a6bbdfafa202347de Mon Sep 17 00:00:00 2001
From: cty 
Date: Wed, 16 Nov 2016 15:05:29 +0800
Subject: [PATCH 21/41] =?UTF-8?q?=E4=BF=AE=E6=94=B9WxCpMessage=E4=B8=ADage?=
 =?UTF-8?q?ntId=E7=9A=84=E6=95=B0=E6=8D=AE=E7=B1=BB=E5=9E=8B,=20String->?=
 =?UTF-8?q?=20Integer?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .gitignore                                    |   1 +
 .../weixin/cp/api/WxCpConfigStorage.java      |   2 +-
 .../cp/api/WxCpInMemoryConfigStorage.java     |   6 +-
 .../weixin/cp/api/WxCpJedisConfigStorage.java | 534 +++++++++---------
 .../me/chanjar/weixin/cp/api/WxCpService.java |  20 +-
 .../weixin/cp/api/WxCpServiceImpl.java        |   8 +-
 .../chanjar/weixin/cp/bean/WxCpMessage.java   |  17 +-
 .../cp/bean/messagebuilder/BaseBuilder.java   |   4 +-
 8 files changed, 294 insertions(+), 298 deletions(-)

diff --git a/.gitignore b/.gitignore
index a2b51e4aa4..fbf348aef9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@ test-config.xml
 /gradle/
 *.bat
 /gradlew
+**/build/
 
 # OSX
 # Icon must end with two \r
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java
index b334b85ba8..eb1e57cafa 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java
@@ -45,7 +45,7 @@ public interface WxCpConfigStorage {
 
   String getCorpSecret();
 
-  String getAgentId();
+  Integer getAgentId();
 
   String getToken();
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java
index fb7bacfff8..867dd6bb68 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java
@@ -19,7 +19,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
   protected volatile String token;
   protected volatile String accessToken;
   protected volatile String aesKey;
-  protected volatile String agentId;
+  protected volatile Integer agentId;
   protected volatile long expiresTime;
 
   protected volatile String oauth2redirectUri;
@@ -146,11 +146,11 @@ public void setAesKey(String aesKey) {
   }
 
   @Override
-  public String getAgentId() {
+  public Integer getAgentId() {
     return this.agentId;
   }
 
-  public void setAgentId(String agentId) {
+  public void setAgentId(Integer agentId) {
     this.agentId = agentId;
   }
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java
index 5209479ffa..d9cfc06fe5 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java
@@ -1,267 +1,267 @@
-package me.chanjar.weixin.cp.api;
-
-import java.io.File;
-
-import me.chanjar.weixin.common.bean.WxAccessToken;
-import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
-
-/**
- * Jedis client implementor for wechat config storage
- *
- * @author gaigeshen
- */
-public class WxCpJedisConfigStorage implements WxCpConfigStorage {
-
-	/* Redis keys here */
-	private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN";
-	private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME";
-	private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET";
-	private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME";
-
-	private volatile String corpId;
-	private volatile String corpSecret;
-
-	private volatile String token;
-	private volatile String aesKey;
-	private volatile String agentId;
-
-	private volatile String oauth2redirectUri;
-
-	private volatile String httpProxyHost;
-	private volatile int httpProxyPort;
-	private volatile String httpProxyUsername;
-	private volatile String httpProxyPassword;
-
-	private volatile File tmpDirFile;
-
-	private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
-
-	/* Redis clients pool */
-	private final JedisPool jedisPool;
-
-	public WxCpJedisConfigStorage(String host, int port) {
-		this.jedisPool = new JedisPool(host, port);
-	}
-
-	/**
-	 *
-	 * This method will be destroy jedis pool
-	 */
-	public void destroy() {
-		this.jedisPool.destroy();
-	}
-
-	@Override
-	public String getAccessToken() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			return jedis.get(ACCESS_TOKEN_KEY);
-		}
-	}
-
-	@Override
-	public boolean isAccessTokenExpired() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
-
-			if (expiresTimeStr != null) {
-				Long expiresTime = Long.parseLong(expiresTimeStr);
-				return System.currentTimeMillis() > expiresTime;
-			}
-
-			return true;
-
-		}
-	}
-
-	@Override
-	public void expireAccessToken() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-		  jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "0");
-		}
-	}
-
-	@Override
-	public synchronized void updateAccessToken(WxAccessToken accessToken) {
-		this.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
-	}
-
-	@Override
-	public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			jedis.set(ACCESS_TOKEN_KEY, accessToken);
-
-			jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY,
-					(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L) + "");
-		}
-	}
-
-	@Override
-	public String getJsapiTicket() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-		  return jedis.get(JS_API_TICKET_KEY);
-		}
-	}
-
-	@Override
-	public boolean isJsapiTicketExpired() {
-
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY);
-
-			if (expiresTimeStr != null) {
-				Long expiresTime = Long.parseLong(expiresTimeStr);
-				return System.currentTimeMillis() > expiresTime;
-			}
-
-			return true;
-
-		}
-	}
-
-	@Override
-	public void expireJsapiTicket() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, "0");
-		}
-	}
-
-	@Override
-	public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
-
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			jedis.set(JS_API_TICKET_KEY, jsapiTicket);
-
-			jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY,
-					(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + ""));
-		}
-
-	}
-
-	@Override
-	public String getCorpId() {
-		return this.corpId;
-	}
-
-	@Override
-	public String getCorpSecret() {
-		return this.corpSecret;
-	}
-
-	@Override
-	public String getAgentId() {
-		return this.agentId;
-	}
-
-	@Override
-	public String getToken() {
-		return this.token;
-	}
-
-	@Override
-	public String getAesKey() {
-		return this.aesKey;
-	}
-
-	@Override
-	public long getExpiresTime() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
-
-			if (expiresTimeStr != null) {
-				Long expiresTime = Long.parseLong(expiresTimeStr);
-				return expiresTime;
-			}
-
-			return 0L;
-
-		}
-	}
-
-	@Override
-	public String getOauth2redirectUri() {
-		return this.oauth2redirectUri;
-	}
-
-	@Override
-	public String getHttpProxyHost() {
-		return this.httpProxyHost;
-	}
-
-	@Override
-	public int getHttpProxyPort() {
-		return this.httpProxyPort;
-	}
-
-	@Override
-	public String getHttpProxyUsername() {
-		return this.httpProxyUsername;
-	}
-
-	@Override
-	public String getHttpProxyPassword() {
-		return this.httpProxyPassword;
-	}
-
-	@Override
-	public File getTmpDirFile() {
-		return this.tmpDirFile;
-	}
-
-	@Override
-	public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
-		return this.apacheHttpClientBuilder;
-	}
-
-	public void setCorpId(String corpId) {
-		this.corpId = corpId;
-	}
-
-	public void setCorpSecret(String corpSecret) {
-		this.corpSecret = corpSecret;
-	}
-
-	public void setToken(String token) {
-		this.token = token;
-	}
-
-	public void setAesKey(String aesKey) {
-		this.aesKey = aesKey;
-	}
-
-	public void setAgentId(String agentId) {
-		this.agentId = agentId;
-	}
-
-	// ============================ Setters below
-
-	public void setOauth2redirectUri(String oauth2redirectUri) {
-		this.oauth2redirectUri = oauth2redirectUri;
-	}
-
-	public void setHttpProxyHost(String httpProxyHost) {
-		this.httpProxyHost = httpProxyHost;
-	}
-
-	public void setHttpProxyPort(int httpProxyPort) {
-		this.httpProxyPort = httpProxyPort;
-	}
-
-	public void setHttpProxyUsername(String httpProxyUsername) {
-		this.httpProxyUsername = httpProxyUsername;
-	}
-
-	public void setHttpProxyPassword(String httpProxyPassword) {
-		this.httpProxyPassword = httpProxyPassword;
-	}
-
-	public void setTmpDirFile(File tmpDirFile) {
-		this.tmpDirFile = tmpDirFile;
-	}
-
-	public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
-		this.apacheHttpClientBuilder = apacheHttpClientBuilder;
-	}
-
-}
+package me.chanjar.weixin.cp.api;
+
+import java.io.File;
+
+import me.chanjar.weixin.common.bean.WxAccessToken;
+import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+/**
+ * Jedis client implementor for wechat config storage
+ *
+ * @author gaigeshen
+ */
+public class WxCpJedisConfigStorage implements WxCpConfigStorage {
+
+	/* Redis keys here */
+	private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN";
+	private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME";
+	private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET";
+	private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME";
+
+	private volatile String corpId;
+	private volatile String corpSecret;
+
+	private volatile String token;
+	private volatile String aesKey;
+	private volatile Integer agentId;
+
+	private volatile String oauth2redirectUri;
+
+	private volatile String httpProxyHost;
+	private volatile int httpProxyPort;
+	private volatile String httpProxyUsername;
+	private volatile String httpProxyPassword;
+
+	private volatile File tmpDirFile;
+
+	private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
+
+	/* Redis clients pool */
+	private final JedisPool jedisPool;
+
+	public WxCpJedisConfigStorage(String host, int port) {
+		this.jedisPool = new JedisPool(host, port);
+	}
+
+	/**
+	 *
+	 * This method will be destroy jedis pool
+	 */
+	public void destroy() {
+		this.jedisPool.destroy();
+	}
+
+	@Override
+	public String getAccessToken() {
+		try (Jedis jedis = this.jedisPool.getResource()) {
+			return jedis.get(ACCESS_TOKEN_KEY);
+		}
+	}
+
+	@Override
+	public boolean isAccessTokenExpired() {
+		try (Jedis jedis = this.jedisPool.getResource()) {
+			String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
+
+			if (expiresTimeStr != null) {
+				Long expiresTime = Long.parseLong(expiresTimeStr);
+				return System.currentTimeMillis() > expiresTime;
+			}
+
+			return true;
+
+		}
+	}
+
+	@Override
+	public void expireAccessToken() {
+		try (Jedis jedis = this.jedisPool.getResource()) {
+		  jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "0");
+		}
+	}
+
+	@Override
+	public synchronized void updateAccessToken(WxAccessToken accessToken) {
+		this.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+	}
+
+	@Override
+	public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
+		try (Jedis jedis = this.jedisPool.getResource()) {
+			jedis.set(ACCESS_TOKEN_KEY, accessToken);
+
+			jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY,
+					(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L) + "");
+		}
+	}
+
+	@Override
+	public String getJsapiTicket() {
+		try (Jedis jedis = this.jedisPool.getResource()) {
+		  return jedis.get(JS_API_TICKET_KEY);
+		}
+	}
+
+	@Override
+	public boolean isJsapiTicketExpired() {
+
+		try (Jedis jedis = this.jedisPool.getResource()) {
+			String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY);
+
+			if (expiresTimeStr != null) {
+				Long expiresTime = Long.parseLong(expiresTimeStr);
+				return System.currentTimeMillis() > expiresTime;
+			}
+
+			return true;
+
+		}
+	}
+
+	@Override
+	public void expireJsapiTicket() {
+		try (Jedis jedis = this.jedisPool.getResource()) {
+			jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, "0");
+		}
+	}
+
+	@Override
+	public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
+
+		try (Jedis jedis = this.jedisPool.getResource()) {
+			jedis.set(JS_API_TICKET_KEY, jsapiTicket);
+
+			jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY,
+					(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + ""));
+		}
+
+	}
+
+	@Override
+	public String getCorpId() {
+		return this.corpId;
+	}
+
+	@Override
+	public String getCorpSecret() {
+		return this.corpSecret;
+	}
+
+	@Override
+	public Integer getAgentId() {
+		return this.agentId;
+	}
+
+	@Override
+	public String getToken() {
+		return this.token;
+	}
+
+	@Override
+	public String getAesKey() {
+		return this.aesKey;
+	}
+
+	@Override
+	public long getExpiresTime() {
+		try (Jedis jedis = this.jedisPool.getResource()) {
+			String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
+
+			if (expiresTimeStr != null) {
+				Long expiresTime = Long.parseLong(expiresTimeStr);
+				return expiresTime;
+			}
+
+			return 0L;
+
+		}
+	}
+
+	@Override
+	public String getOauth2redirectUri() {
+		return this.oauth2redirectUri;
+	}
+
+	@Override
+	public String getHttpProxyHost() {
+		return this.httpProxyHost;
+	}
+
+	@Override
+	public int getHttpProxyPort() {
+		return this.httpProxyPort;
+	}
+
+	@Override
+	public String getHttpProxyUsername() {
+		return this.httpProxyUsername;
+	}
+
+	@Override
+	public String getHttpProxyPassword() {
+		return this.httpProxyPassword;
+	}
+
+	@Override
+	public File getTmpDirFile() {
+		return this.tmpDirFile;
+	}
+
+	@Override
+	public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
+		return this.apacheHttpClientBuilder;
+	}
+
+	public void setCorpId(String corpId) {
+		this.corpId = corpId;
+	}
+
+	public void setCorpSecret(String corpSecret) {
+		this.corpSecret = corpSecret;
+	}
+
+	public void setToken(String token) {
+		this.token = token;
+	}
+
+	public void setAesKey(String aesKey) {
+		this.aesKey = aesKey;
+	}
+
+	public void setAgentId(Integer agentId) {
+		this.agentId = agentId;
+	}
+
+	// ============================ Setters below
+
+	public void setOauth2redirectUri(String oauth2redirectUri) {
+		this.oauth2redirectUri = oauth2redirectUri;
+	}
+
+	public void setHttpProxyHost(String httpProxyHost) {
+		this.httpProxyHost = httpProxyHost;
+	}
+
+	public void setHttpProxyPort(int httpProxyPort) {
+		this.httpProxyPort = httpProxyPort;
+	}
+
+	public void setHttpProxyUsername(String httpProxyUsername) {
+		this.httpProxyUsername = httpProxyUsername;
+	}
+
+	public void setHttpProxyPassword(String httpProxyPassword) {
+		this.httpProxyPassword = httpProxyPassword;
+	}
+
+	public void setTmpDirFile(File tmpDirFile) {
+		this.tmpDirFile = tmpDirFile;
+	}
+
+	public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
+		this.apacheHttpClientBuilder = apacheHttpClientBuilder;
+	}
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
index e6f8f2ad2f..b5bf50c9be 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
@@ -160,7 +160,7 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i
    *
    * @param menu
    * @throws WxErrorException
-   * @see #menuCreate(String, me.chanjar.weixin.common.bean.menu.WxMenu)
+   * @see #menuCreate(Integer, WxMenu)
    */
   void menuCreate(WxMenu menu) throws WxErrorException;
 
@@ -177,7 +177,7 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i
    * @throws WxErrorException
    * @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu)
    */
-  void menuCreate(String agentId, WxMenu menu) throws WxErrorException;
+  void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException;
 
   /**
    * 
@@ -188,7 +188,7 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i
    * 
* * @throws WxErrorException - * @see #menuDelete(String) + * @see #menuDelete(Integer) */ void menuDelete() throws WxErrorException; @@ -204,7 +204,7 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i * @throws WxErrorException * @see #menuDelete() */ - void menuDelete(String agentId) throws WxErrorException; + void menuDelete(Integer agentId) throws WxErrorException; /** *
@@ -215,7 +215,7 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i
    * 
* * @throws WxErrorException - * @see #menuGet(String) + * @see #menuGet(Integer) */ WxMenu menuGet() throws WxErrorException; @@ -231,7 +231,7 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i * @throws WxErrorException * @see #menuGet() */ - WxMenu menuGet(String agentId) throws WxErrorException; + WxMenu menuGet(Integer agentId) throws WxErrorException; /** *
@@ -396,12 +396,12 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i
    *  
    * 构造oauth2授权的url连接
    * 
- * + * * @param state * @return url */ String oauth2buildAuthorizationUrl(String state); - + /** *
    * 构造oauth2授权的url连接
@@ -425,7 +425,7 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i
    *
    * @param code
    * @return [userid, deviceid]
-   * @see #oauth2getUserInfo(String, String)
+   * @see #oauth2getUserInfo(Integer, String)
    */
   String[] oauth2getUserInfo(String code) throws WxErrorException;
 
@@ -443,7 +443,7 @@ WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream i
    * @return [userid, deviceid]
    * @see #oauth2getUserInfo(String)
    */
-  String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException;
+  String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException;
 
 
   /**
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
index b044b46801..30f9914c57 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
@@ -191,7 +191,7 @@ public void menuCreate(WxMenu menu) throws WxErrorException {
   }
 
   @Override
-  public void menuCreate(String agentId, WxMenu menu) throws WxErrorException {
+  public void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid="
  + this.configStorage.getAgentId();
     post(url, menu.toJson());
@@ -203,7 +203,7 @@ public void menuDelete() throws WxErrorException {
   }
 
   @Override
-  public void menuDelete(String agentId) throws WxErrorException {
+  public void menuDelete(Integer agentId) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete?agentid=" + agentId;
     get(url, null);
   }
@@ -214,7 +214,7 @@ public WxMenu menuGet() throws WxErrorException {
   }
 
   @Override
-  public WxMenu menuGet(String agentId) throws WxErrorException {
+  public WxMenu menuGet(Integer agentId) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/get?agentid=" + agentId;
     try {
       String resultContent = get(url, null);
@@ -487,7 +487,7 @@ public String[] oauth2getUserInfo(String code) throws WxErrorException {
   }
 
   @Override
-  public String[] oauth2getUserInfo(String agentId, String code) throws WxErrorException {
+  public String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?"
             + "code=" + code
             + "&agentid=" + agentId;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
index e2422e3b6d..c25c170b9a 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
@@ -1,17 +1,12 @@
 package me.chanjar.weixin.cp.bean;
 
+import me.chanjar.weixin.cp.bean.messagebuilder.*;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 
-import me.chanjar.weixin.cp.bean.messagebuilder.FileBuilder;
-import me.chanjar.weixin.cp.bean.messagebuilder.ImageBuilder;
-import me.chanjar.weixin.cp.bean.messagebuilder.NewsBuilder;
-import me.chanjar.weixin.cp.bean.messagebuilder.TextBuilder;
-import me.chanjar.weixin.cp.bean.messagebuilder.VideoBuilder;
-import me.chanjar.weixin.cp.bean.messagebuilder.VoiceBuilder;
-import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
-
 /**
  * 消息
  *
@@ -23,7 +18,7 @@ public class WxCpMessage implements Serializable {
   private String toUser;
   private String toParty;
   private String toTag;
-  private String agentId;
+  private Integer agentId;
   private String msgType;
   private String content;
   private String mediaId;
@@ -101,11 +96,11 @@ public void setToTag(String toTag) {
     this.toTag = toTag;
   }
 
-  public String getAgentId() {
+  public Integer getAgentId() {
     return this.agentId;
   }
 
-  public void setAgentId(String agentId) {
+  public void setAgentId(Integer agentId) {
     this.agentId = agentId;
   }
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java
index 035777d83b..7800f1c61e 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/BaseBuilder.java
@@ -5,13 +5,13 @@
 
 public class BaseBuilder {
   protected String msgType;
-  protected String agentId;
+  protected Integer agentId;
   protected String toUser;
   protected String toParty;
   protected String toTag;
   protected String safe;
 
-  public T agentId(String agentId) {
+  public T agentId(Integer agentId) {
     this.agentId = agentId;
     return (T) this;
   }

From 088a97221eeb659aa08d5d4b78f7ffb1e014152e Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Fri, 18 Nov 2016 17:32:22 +0800
Subject: [PATCH 22/41] =?UTF-8?q?=E4=B8=BAWxMpConfigStorage=E6=8E=A5?=
 =?UTF-8?q?=E5=8F=A3=E5=A2=9E=E5=8A=A0autoRefreshToken=E6=96=B9=E6=B3=95?=
 =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E6=96=B9=E4=BE=BF=E5=AE=A2=E6=88=B7=E7=AB=AF?=
 =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E6=98=AF=E5=90=A6=E8=87=AA=E5=8A=A8=E5=88=B7?=
 =?UTF-8?q?=E6=96=B0token.=20for=20#77?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java    | 5 +++++
 .../me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java | 5 +++++
 .../java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java | 6 +++++-
 3 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
index 0852cae2bc..67d39a7865 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
@@ -100,4 +100,9 @@ public interface WxMpConfigStorage {
    * @return ApacheHttpClientBuilder
    */
   ApacheHttpClientBuilder getApacheHttpClientBuilder();
+
+  /**
+   * 是否自动刷新token
+   */
+  boolean autoRefreshToken();
 }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
index d497261e9f..bb4a298534 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
@@ -270,6 +270,11 @@ public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
     return this.apacheHttpClientBuilder;
   }
 
+  @Override
+  public boolean autoRefreshToken() {
+    return true;
+  }
+
   public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
     this.apacheHttpClientBuilder = apacheHttpClientBuilder;
   }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
index 87cf7397dd..6a6044ab1d 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
@@ -101,6 +101,7 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
     if (forceRefresh) {
       this.configStorage.expireAccessToken();
     }
+
     if (this.configStorage.isAccessTokenExpired()) {
       synchronized (this.globalAccessTokenRefreshLock) {
         if (this.configStorage.isAccessTokenExpired()) {
@@ -411,8 +412,11 @@ protected synchronized  T executeInternal(RequestExecutor executor,
       if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) {
         // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
         this.configStorage.expireAccessToken();
-        return this.execute(executor, uri, data);
+        if(this.configStorage.autoRefreshToken()){
+          return this.execute(executor, uri, data);
+        }
       }
+
       if (error.getErrorCode() != 0) {
         this.log.error("\n[URL]:  {}\n[PARAMS]: {}\n[RESPONSE]: {}", uri, data,
             error);

From e4d5545f3da3ec8241f31ff5f05e2de4f2412a07 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Mon, 21 Nov 2016 11:06:27 +0800
Subject: [PATCH 23/41] reformat some code

---
 .../weixin/cp/api/WxCpJedisConfigStorage.java | 494 +++++++++---------
 .../weixin/cp/api/WxCpMessageRouter.java      |  20 +-
 .../weixin/cp/api/WxCpMessageRouterRule.java  |  36 +-
 3 files changed, 271 insertions(+), 279 deletions(-)

diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java
index d9cfc06fe5..9251396560 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpJedisConfigStorage.java
@@ -1,12 +1,12 @@
 package me.chanjar.weixin.cp.api;
 
-import java.io.File;
-
 import me.chanjar.weixin.common.bean.WxAccessToken;
 import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder;
 import redis.clients.jedis.Jedis;
 import redis.clients.jedis.JedisPool;
 
+import java.io.File;
+
 /**
  * Jedis client implementor for wechat config storage
  *
@@ -14,254 +14,246 @@
  */
 public class WxCpJedisConfigStorage implements WxCpConfigStorage {
 
-	/* Redis keys here */
-	private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN";
-	private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME";
-	private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET";
-	private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME";
-
-	private volatile String corpId;
-	private volatile String corpSecret;
-
-	private volatile String token;
-	private volatile String aesKey;
-	private volatile Integer agentId;
-
-	private volatile String oauth2redirectUri;
-
-	private volatile String httpProxyHost;
-	private volatile int httpProxyPort;
-	private volatile String httpProxyUsername;
-	private volatile String httpProxyPassword;
-
-	private volatile File tmpDirFile;
-
-	private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
-
-	/* Redis clients pool */
-	private final JedisPool jedisPool;
-
-	public WxCpJedisConfigStorage(String host, int port) {
-		this.jedisPool = new JedisPool(host, port);
-	}
-
-	/**
-	 *
-	 * This method will be destroy jedis pool
-	 */
-	public void destroy() {
-		this.jedisPool.destroy();
-	}
-
-	@Override
-	public String getAccessToken() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			return jedis.get(ACCESS_TOKEN_KEY);
-		}
-	}
-
-	@Override
-	public boolean isAccessTokenExpired() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
-
-			if (expiresTimeStr != null) {
-				Long expiresTime = Long.parseLong(expiresTimeStr);
-				return System.currentTimeMillis() > expiresTime;
-			}
-
-			return true;
-
-		}
-	}
-
-	@Override
-	public void expireAccessToken() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-		  jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "0");
-		}
-	}
-
-	@Override
-	public synchronized void updateAccessToken(WxAccessToken accessToken) {
-		this.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
-	}
-
-	@Override
-	public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			jedis.set(ACCESS_TOKEN_KEY, accessToken);
-
-			jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY,
-					(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L) + "");
-		}
-	}
-
-	@Override
-	public String getJsapiTicket() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-		  return jedis.get(JS_API_TICKET_KEY);
-		}
-	}
-
-	@Override
-	public boolean isJsapiTicketExpired() {
-
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY);
-
-			if (expiresTimeStr != null) {
-				Long expiresTime = Long.parseLong(expiresTimeStr);
-				return System.currentTimeMillis() > expiresTime;
-			}
-
-			return true;
-
-		}
-	}
-
-	@Override
-	public void expireJsapiTicket() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, "0");
-		}
-	}
-
-	@Override
-	public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
-
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			jedis.set(JS_API_TICKET_KEY, jsapiTicket);
-
-			jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY,
-					(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + ""));
-		}
-
-	}
-
-	@Override
-	public String getCorpId() {
-		return this.corpId;
-	}
-
-	@Override
-	public String getCorpSecret() {
-		return this.corpSecret;
-	}
-
-	@Override
-	public Integer getAgentId() {
-		return this.agentId;
-	}
-
-	@Override
-	public String getToken() {
-		return this.token;
-	}
-
-	@Override
-	public String getAesKey() {
-		return this.aesKey;
-	}
-
-	@Override
-	public long getExpiresTime() {
-		try (Jedis jedis = this.jedisPool.getResource()) {
-			String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
-
-			if (expiresTimeStr != null) {
-				Long expiresTime = Long.parseLong(expiresTimeStr);
-				return expiresTime;
-			}
-
-			return 0L;
-
-		}
-	}
-
-	@Override
-	public String getOauth2redirectUri() {
-		return this.oauth2redirectUri;
-	}
-
-	@Override
-	public String getHttpProxyHost() {
-		return this.httpProxyHost;
-	}
-
-	@Override
-	public int getHttpProxyPort() {
-		return this.httpProxyPort;
-	}
-
-	@Override
-	public String getHttpProxyUsername() {
-		return this.httpProxyUsername;
-	}
-
-	@Override
-	public String getHttpProxyPassword() {
-		return this.httpProxyPassword;
-	}
-
-	@Override
-	public File getTmpDirFile() {
-		return this.tmpDirFile;
-	}
-
-	@Override
-	public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
-		return this.apacheHttpClientBuilder;
-	}
-
-	public void setCorpId(String corpId) {
-		this.corpId = corpId;
-	}
-
-	public void setCorpSecret(String corpSecret) {
-		this.corpSecret = corpSecret;
-	}
-
-	public void setToken(String token) {
-		this.token = token;
-	}
-
-	public void setAesKey(String aesKey) {
-		this.aesKey = aesKey;
-	}
-
-	public void setAgentId(Integer agentId) {
-		this.agentId = agentId;
-	}
-
-	// ============================ Setters below
-
-	public void setOauth2redirectUri(String oauth2redirectUri) {
-		this.oauth2redirectUri = oauth2redirectUri;
-	}
-
-	public void setHttpProxyHost(String httpProxyHost) {
-		this.httpProxyHost = httpProxyHost;
-	}
-
-	public void setHttpProxyPort(int httpProxyPort) {
-		this.httpProxyPort = httpProxyPort;
-	}
-
-	public void setHttpProxyUsername(String httpProxyUsername) {
-		this.httpProxyUsername = httpProxyUsername;
-	}
-
-	public void setHttpProxyPassword(String httpProxyPassword) {
-		this.httpProxyPassword = httpProxyPassword;
-	}
-
-	public void setTmpDirFile(File tmpDirFile) {
-		this.tmpDirFile = tmpDirFile;
-	}
-
-	public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
-		this.apacheHttpClientBuilder = apacheHttpClientBuilder;
-	}
+  /* Redis keys here */
+  private static final String ACCESS_TOKEN_KEY = "WX_CP_ACCESS_TOKEN";
+  private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_CP_ACCESS_TOKEN_EXPIRES_TIME";
+  private static final String JS_API_TICKET_KEY = "WX_CP_JS_API_TICKET";
+  private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_CP_JS_API_TICKET_EXPIRES_TIME";
+  /* Redis clients pool */
+  private final JedisPool jedisPool;
+  private volatile String corpId;
+  private volatile String corpSecret;
+  private volatile String token;
+  private volatile String aesKey;
+  private volatile Integer agentId;
+  private volatile String oauth2redirectUri;
+  private volatile String httpProxyHost;
+  private volatile int httpProxyPort;
+  private volatile String httpProxyUsername;
+  private volatile String httpProxyPassword;
+  private volatile File tmpDirFile;
+  private volatile ApacheHttpClientBuilder apacheHttpClientBuilder;
+
+  public WxCpJedisConfigStorage(String host, int port) {
+    this.jedisPool = new JedisPool(host, port);
+  }
+
+  /**
+   * This method will be destroy jedis pool
+   */
+  public void destroy() {
+    this.jedisPool.destroy();
+  }
+
+  @Override
+  public String getAccessToken() {
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      return jedis.get(ACCESS_TOKEN_KEY);
+    }
+  }
+
+  @Override
+  public boolean isAccessTokenExpired() {
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
+
+      if (expiresTimeStr != null) {
+        Long expiresTime = Long.parseLong(expiresTimeStr);
+        return System.currentTimeMillis() > expiresTime;
+      }
+
+      return true;
+
+    }
+  }
+
+  @Override
+  public void expireAccessToken() {
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "0");
+    }
+  }
+
+  @Override
+  public synchronized void updateAccessToken(WxAccessToken accessToken) {
+    this.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
+  }
+
+  @Override
+  public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      jedis.set(ACCESS_TOKEN_KEY, accessToken);
+
+      jedis.set(ACCESS_TOKEN_EXPIRES_TIME_KEY,
+        (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L) + "");
+    }
+  }
+
+  @Override
+  public String getJsapiTicket() {
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      return jedis.get(JS_API_TICKET_KEY);
+    }
+  }
+
+  @Override
+  public boolean isJsapiTicketExpired() {
+
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      String expiresTimeStr = jedis.get(JS_API_TICKET_EXPIRES_TIME_KEY);
+
+      if (expiresTimeStr != null) {
+        Long expiresTime = Long.parseLong(expiresTimeStr);
+        return System.currentTimeMillis() > expiresTime;
+      }
+
+      return true;
+
+    }
+  }
+
+  @Override
+  public void expireJsapiTicket() {
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY, "0");
+    }
+  }
+
+  @Override
+  public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
+
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      jedis.set(JS_API_TICKET_KEY, jsapiTicket);
+
+      jedis.set(JS_API_TICKET_EXPIRES_TIME_KEY,
+        (System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L + ""));
+    }
+
+  }
+
+  @Override
+  public String getCorpId() {
+    return this.corpId;
+  }
+
+  public void setCorpId(String corpId) {
+    this.corpId = corpId;
+  }
+
+  @Override
+  public String getCorpSecret() {
+    return this.corpSecret;
+  }
+
+  public void setCorpSecret(String corpSecret) {
+    this.corpSecret = corpSecret;
+  }
+
+  @Override
+  public Integer getAgentId() {
+    return this.agentId;
+  }
+
+  public void setAgentId(Integer agentId) {
+    this.agentId = agentId;
+  }
+
+  @Override
+  public String getToken() {
+    return this.token;
+  }
+
+  public void setToken(String token) {
+    this.token = token;
+  }
+
+  @Override
+  public String getAesKey() {
+    return this.aesKey;
+  }
+
+  public void setAesKey(String aesKey) {
+    this.aesKey = aesKey;
+  }
+
+  @Override
+  public long getExpiresTime() {
+    try (Jedis jedis = this.jedisPool.getResource()) {
+      String expiresTimeStr = jedis.get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
+
+      if (expiresTimeStr != null) {
+        Long expiresTime = Long.parseLong(expiresTimeStr);
+        return expiresTime;
+      }
+
+      return 0L;
+
+    }
+  }
+
+  @Override
+  public String getOauth2redirectUri() {
+    return this.oauth2redirectUri;
+  }
+
+  public void setOauth2redirectUri(String oauth2redirectUri) {
+    this.oauth2redirectUri = oauth2redirectUri;
+  }
+
+  @Override
+  public String getHttpProxyHost() {
+    return this.httpProxyHost;
+  }
+
+  public void setHttpProxyHost(String httpProxyHost) {
+    this.httpProxyHost = httpProxyHost;
+  }
+
+  @Override
+  public int getHttpProxyPort() {
+    return this.httpProxyPort;
+  }
+
+  public void setHttpProxyPort(int httpProxyPort) {
+    this.httpProxyPort = httpProxyPort;
+  }
+
+  @Override
+  public String getHttpProxyUsername() {
+    return this.httpProxyUsername;
+  }
+
+  // ============================ Setters below
+
+  public void setHttpProxyUsername(String httpProxyUsername) {
+    this.httpProxyUsername = httpProxyUsername;
+  }
+
+  @Override
+  public String getHttpProxyPassword() {
+    return this.httpProxyPassword;
+  }
+
+  public void setHttpProxyPassword(String httpProxyPassword) {
+    this.httpProxyPassword = httpProxyPassword;
+  }
+
+  @Override
+  public File getTmpDirFile() {
+    return this.tmpDirFile;
+  }
+
+  public void setTmpDirFile(File tmpDirFile) {
+    this.tmpDirFile = tmpDirFile;
+  }
+
+  @Override
+  public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
+    return this.apacheHttpClientBuilder;
+  }
+
+  public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
+    this.apacheHttpClientBuilder = apacheHttpClientBuilder;
+  }
 
 }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java
index 6d4a8a9769..da18e28e4f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java
@@ -163,12 +163,12 @@ public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) {
       // 返回最后一个非异步的rule的执行结果
       if (rule.isAsync()) {
         futures.add(
-                this.executorService.submit(new Runnable() {
-                  @Override
-                  public void run() {
-                    rule.service(wxMessage, WxCpMessageRouter.this.wxCpService, WxCpMessageRouter.this.sessionManager, WxCpMessageRouter.this.exceptionHandler);
-                  }
-                })
+          this.executorService.submit(new Runnable() {
+            @Override
+            public void run() {
+              rule.service(wxMessage, WxCpMessageRouter.this.wxCpService, WxCpMessageRouter.this.sessionManager, WxCpMessageRouter.this.exceptionHandler);
+            }
+          })
         );
       } else {
         res = rule.service(wxMessage, this.wxCpService, this.sessionManager, this.exceptionHandler);
@@ -205,10 +205,10 @@ protected boolean isDuplicateMessage(WxCpXmlMessage wxMessage) {
     String messageId = "";
     if (wxMessage.getMsgId() == null) {
       messageId = String.valueOf(wxMessage.getCreateTime())
-              + "-" + String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId())
-              + "-" + wxMessage.getFromUserName()
-              + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey())
-              + "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent())
+        + "-" + String.valueOf(wxMessage.getAgentId() == null ? "" : wxMessage.getAgentId())
+        + "-" + wxMessage.getFromUserName()
+        + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey())
+        + "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent())
       ;
     } else {
       messageId = String.valueOf(wxMessage.getMsgId());
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java
index c81f045f96..7bd332ad9b 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouterRule.java
@@ -202,24 +202,24 @@ public WxCpMessageRouter next() {
 
   protected boolean test(WxCpXmlMessage wxMessage) {
     return
-            (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
-                    &&
-                    (this.agentId == null || this.agentId.equals(wxMessage.getAgentId()))
-                    &&
-                    (this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
-                    &&
-                    (this.event == null || this.event.equals(wxMessage.getEvent()))
-                    &&
-                    (this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey()))
-                    &&
-                    (this.content == null || this.content
-                            .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim()))
-                    &&
-                    (this.rContent == null || Pattern
-                            .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim()))
-                    &&
-                    (this.matcher == null || this.matcher.match(wxMessage))
-            ;
+      (this.fromUser == null || this.fromUser.equals(wxMessage.getFromUserName()))
+        &&
+        (this.agentId == null || this.agentId.equals(wxMessage.getAgentId()))
+        &&
+        (this.msgType == null || this.msgType.equals(wxMessage.getMsgType()))
+        &&
+        (this.event == null || this.event.equals(wxMessage.getEvent()))
+        &&
+        (this.eventKey == null || this.eventKey.equals(wxMessage.getEventKey()))
+        &&
+        (this.content == null || this.content
+          .equals(wxMessage.getContent() == null ? null : wxMessage.getContent().trim()))
+        &&
+        (this.rContent == null || Pattern
+          .matches(this.rContent, wxMessage.getContent() == null ? "" : wxMessage.getContent().trim()))
+        &&
+        (this.matcher == null || this.matcher.match(wxMessage))
+      ;
   }
 
   /**

From 72d6aadd9703c33f0d8ae6591c82fb9799024ad3 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Mon, 21 Nov 2016 11:58:41 +0800
Subject: [PATCH 24/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20#80=20menuCreate()?=
 =?UTF-8?q?=E6=96=B9=E6=B3=95=E4=BB=A3=E7=A0=81=E7=9A=84=E9=97=AE=E9=A2=98?=
 =?UTF-8?q?=EF=BC=8Cand=20reformat=20code?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../weixin/cp/api/WxCpServiceImpl.java        | 131 +++++++++---------
 1 file changed, 63 insertions(+), 68 deletions(-)

diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
index 30f9914c57..d44bbf5f0d 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
@@ -23,7 +23,6 @@
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.HttpHost;
-import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.config.RequestConfig;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
@@ -69,7 +68,7 @@ public class WxCpServiceImpl implements WxCpService {
   public boolean checkSignature(String msgSignature, String timestamp, String nonce, String data) {
     try {
       return SHA1.gen(this.configStorage.getToken(), timestamp, nonce, data)
-          .equals(msgSignature);
+        .equals(msgSignature);
     } catch (Exception e) {
       return false;
     }
@@ -95,18 +94,18 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
       synchronized (this.globalAccessTokenRefreshLock) {
         if (this.configStorage.isAccessTokenExpired()) {
           String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
- + "&corpid=" + this.configStorage.getCorpId()
-              + "&corpsecret=" + this.configStorage.getCorpSecret();
+            + "&corpid=" + this.configStorage.getCorpId()
+            + "&corpsecret=" + this.configStorage.getCorpSecret();
           try {
             HttpGet httpGet = new HttpGet(url);
             if (this.httpProxy != null) {
               RequestConfig config = RequestConfig.custom()
-                  .setProxy(this.httpProxy).build();
+                .setProxy(this.httpProxy).build();
               httpGet.setConfig(config);
             }
             String resultContent = null;
             try (CloseableHttpClient httpclient = getHttpclient();
-                CloseableHttpResponse response = httpclient.execute(httpGet)) {
+                 CloseableHttpResponse response = httpclient.execute(httpGet)) {
               resultContent = new BasicResponseHandler().handleResponse(response);
             } finally {
               httpGet.releaseConnection();
@@ -117,9 +116,7 @@ public String getAccessToken(boolean forceRefresh) throws WxErrorException {
             }
             WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
             this.configStorage.updateAccessToken(
-                accessToken.getAccessToken(), accessToken.getExpiresIn());
-          } catch (ClientProtocolException e) {
-            throw new RuntimeException(e);
+              accessToken.getAccessToken(), accessToken.getExpiresIn());
           } catch (IOException e) {
             throw new RuntimeException(e);
           }
@@ -149,7 +146,7 @@ public String getJsapiTicket(boolean forceRefresh) throws WxErrorException {
           String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
           int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
           this.configStorage.updateJsapiTicket(jsapiTicket,
-              expiresInSeconds);
+            expiresInSeconds);
         }
       }
     }
@@ -162,10 +159,10 @@ public WxJsapiSignature createJsapiSignature(String url) throws WxErrorException
     String noncestr = RandomUtils.getRandomStr();
     String jsapiTicket = getJsapiTicket(false);
     String signature = SHA1.genWithAmple(
-            "jsapi_ticket=" + jsapiTicket,
-            "noncestr=" + noncestr,
-            "timestamp=" + timestamp,
-            "url=" + url
+      "jsapi_ticket=" + jsapiTicket,
+      "noncestr=" + noncestr,
+      "timestamp=" + timestamp,
+      "url=" + url
     );
     WxJsapiSignature jsapiSignature = new WxJsapiSignature();
     jsapiSignature.setTimestamp(timestamp);
@@ -193,7 +190,7 @@ public void menuCreate(WxMenu menu) throws WxErrorException {
   @Override
   public void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/menu/create?agentid="
- + this.configStorage.getAgentId();
+      + this.configStorage.getAgentId();
     post(url, menu.toJson());
   }
 
@@ -230,7 +227,7 @@ public WxMenu menuGet(Integer agentId) throws WxErrorException {
 
   @Override
   public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream)
-          throws WxErrorException, IOException {
+    throws WxErrorException, IOException {
     return mediaUpload(mediaType, FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType));
   }
 
@@ -244,9 +241,9 @@ public WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErr
   public File mediaDownload(String media_id) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get";
     return execute(
-        new MediaDownloadRequestExecutor(
-this.configStorage.getTmpDirFile()),
-        url, "media_id=" + media_id);
+      new MediaDownloadRequestExecutor(
+        this.configStorage.getTmpDirFile()),
+      url, "media_id=" + media_id);
   }
 
 
@@ -254,9 +251,9 @@ public File mediaDownload(String media_id) throws WxErrorException {
   public Integer departCreate(WxCpDepart depart) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create";
     String responseContent = execute(
-            new SimplePostRequestExecutor(),
-            url,
-            depart.toJson());
+      new SimplePostRequestExecutor(),
+      url,
+      depart.toJson());
     JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
     return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id"));
   }
@@ -283,11 +280,11 @@ public List departGet() throws WxErrorException {
      */
     JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
     return WxCpGsonBuilder.INSTANCE.create()
-            .fromJson(
-                    tmpJsonElement.getAsJsonObject().get("department"),
-                    new TypeToken>() {
-                    }.getType()
-            );
+      .fromJson(
+        tmpJsonElement.getAsJsonObject().get("department"),
+        new TypeToken>() {
+        }.getType()
+      );
   }
 
   @Override
@@ -313,8 +310,8 @@ public void userDelete(String[] userids) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/user/batchdelete";
     JsonObject jsonObject = new JsonObject();
     JsonArray jsonArray = new JsonArray();
-    for (int i = 0; i < userids.length; i++) {
-      jsonArray.add(new JsonPrimitive(userids[i]));
+    for (String userid : userids) {
+      jsonArray.add(new JsonPrimitive(userid));
     }
     jsonObject.add("useridlist", jsonArray);
     post(url, jsonObject.toString());
@@ -343,11 +340,11 @@ public List userList(Integer departId, Boolean fetchChild, Integer sta
     String responseContent = get(url, params);
     JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
     return WxCpGsonBuilder.INSTANCE.create()
-            .fromJson(
-                    tmpJsonElement.getAsJsonObject().get("userlist"),
-                    new TypeToken>() {
-                    }.getType()
-            );
+      .fromJson(
+        tmpJsonElement.getAsJsonObject().get("userlist"),
+        new TypeToken>() {
+        }.getType()
+      );
   }
 
   @Override
@@ -366,11 +363,11 @@ public List departGetUsers(Integer departId, Boolean fetchChild, Integ
     String responseContent = get(url, params);
     JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
     return WxCpGsonBuilder.INSTANCE.create()
-            .fromJson(
-                    tmpJsonElement.getAsJsonObject().get("userlist"),
-                    new TypeToken>() {
-                    }.getType()
-            );
+      .fromJson(
+        tmpJsonElement.getAsJsonObject().get("userlist"),
+        new TypeToken>() {
+        }.getType()
+      );
   }
 
   @Override
@@ -404,11 +401,11 @@ public List tagGet() throws WxErrorException {
     String responseContent = get(url, null);
     JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
     return WxCpGsonBuilder.INSTANCE.create()
-            .fromJson(
-                    tmpJsonElement.getAsJsonObject().get("taglist"),
-                    new TypeToken>() {
-                    }.getType()
-            );
+      .fromJson(
+        tmpJsonElement.getAsJsonObject().get("taglist"),
+        new TypeToken>() {
+        }.getType()
+      );
   }
 
   @Override
@@ -417,11 +414,11 @@ public List tagGetUsers(String tagId) throws WxErrorException {
     String responseContent = get(url, null);
     JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
     return WxCpGsonBuilder.INSTANCE.create()
-            .fromJson(
-                    tmpJsonElement.getAsJsonObject().get("userlist"),
-                    new TypeToken>() {
-                    }.getType()
-            );
+      .fromJson(
+        tmpJsonElement.getAsJsonObject().get("userlist"),
+        new TypeToken>() {
+        }.getType()
+      );
   }
 
   @Override
@@ -460,14 +457,14 @@ public void tagRemoveUsers(String tagId, List userIds) throws WxErrorExc
   }
 
   @Override
-	public String oauth2buildAuthorizationUrl(String state) {
-  	return this.oauth2buildAuthorizationUrl(
-this.configStorage.getOauth2redirectUri(),
-  		state
-  	);
-	}
+  public String oauth2buildAuthorizationUrl(String state) {
+    return this.oauth2buildAuthorizationUrl(
+      this.configStorage.getOauth2redirectUri(),
+      state
+    );
+  }
 
-	@Override
+  @Override
   public String oauth2buildAuthorizationUrl(String redirectUri, String state) {
     String url = "https://open.weixin.qq.com/connect/oauth2/authorize?";
     url += "appid=" + this.configStorage.getCorpId();
@@ -489,8 +486,8 @@ public String[] oauth2getUserInfo(String code) throws WxErrorException {
   @Override
   public String[] oauth2getUserInfo(Integer agentId, String code) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?"
-            + "code=" + code
-            + "&agentid=" + agentId;
+      + "code=" + code
+      + "&agentid=" + agentId;
     String responseText = get(url, null);
     JsonElement je = new JsonParser().parse(responseText);
     JsonObject jo = je.getAsJsonObject();
@@ -544,14 +541,14 @@ public  T execute(RequestExecutor executor, String uri, E data) thro
         return executeInternal(executor, uri, data);
       } catch (WxErrorException e) {
         WxError error = e.getError();
-        /**
+        /*
          * -1 系统繁忙, 1000ms后重试
          */
         if (error.getErrorCode() == -1) {
           int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
           try {
             this.log.debug("微信系统繁忙,{}ms 后重试(第{}次)", sleepMillis,
-                retryTimes + 1);
+              retryTimes + 1);
             Thread.sleep(sleepMillis);
           } catch (InterruptedException e1) {
             throw new RuntimeException(e1);
@@ -566,7 +563,7 @@ public  T execute(RequestExecutor executor, String uri, E data) thro
   }
 
   protected synchronized  T executeInternal(RequestExecutor executor, String uri, E data) throws WxErrorException {
-    if (uri.indexOf("access_token=") != -1) {
+    if (uri.contains("access_token=")) {
       throw new IllegalArgumentException("uri参数中不允许有access_token: " + uri);
     }
     String accessToken = getAccessToken(false);
@@ -576,7 +573,7 @@ protected synchronized  T executeInternal(RequestExecutor executor,
 
     try {
       return executor.execute(getHttpclient(), this.httpProxy,
-          uriWithAccessToken, data);
+        uriWithAccessToken, data);
     } catch (WxErrorException e) {
       WxError error = e.getError();
       /*
@@ -593,8 +590,6 @@ protected synchronized  T executeInternal(RequestExecutor executor,
         throw new WxErrorException(error);
       }
       return null;
-    } catch (ClientProtocolException e) {
-      throw new RuntimeException(e);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
@@ -608,15 +603,15 @@ protected CloseableHttpClient getHttpclient() {
   public void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider) {
     this.configStorage = wxConfigProvider;
     ApacheHttpClientBuilder apacheHttpClientBuilder = this.configStorage
-        .getApacheHttpClientBuilder();
+      .getApacheHttpClientBuilder();
     if (null == apacheHttpClientBuilder) {
       apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get();
     }
 
     apacheHttpClientBuilder.httpProxyHost(this.configStorage.getHttpProxyHost())
-        .httpProxyPort(this.configStorage.getHttpProxyPort())
-        .httpProxyUsername(this.configStorage.getHttpProxyUsername())
-        .httpProxyPassword(this.configStorage.getHttpProxyPassword());
+      .httpProxyPort(this.configStorage.getHttpProxyPort())
+      .httpProxyUsername(this.configStorage.getHttpProxyUsername())
+      .httpProxyPassword(this.configStorage.getHttpProxyPassword());
 
     if (this.configStorage.getHttpProxyHost() != null && this.configStorage.getHttpProxyPort() > 0) {
       this.httpProxy = new HttpHost(this.configStorage.getHttpProxyHost(), this.configStorage.getHttpProxyPort());

From 5f20d2c6139da9687b9677d2adfb8939203ea07d Mon Sep 17 00:00:00 2001
From: tanzx 
Date: Mon, 21 Nov 2016 17:03:13 +0800
Subject: [PATCH 25/41] =?UTF-8?q?=E5=9B=BE=E6=96=87=E6=B6=88=E6=81=AF?=
 =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E6=8E=A5=E5=8F=A3=EF=BC=8C=20statDate?=
 =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=94=B9=E4=B8=BAString?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../mp/bean/datacube/WxDataCubeArticleTotalDetail.java      | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotalDetail.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotalDetail.java
index e228a3ccfb..251c5e767c 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotalDetail.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/datacube/WxDataCubeArticleTotalDetail.java
@@ -14,7 +14,7 @@ public class WxDataCubeArticleTotalDetail {
    * 统计的日期,在getarticletotal接口中,ref_date指的是文章群发出日期, 而stat_date是数据统计日期
    */
   @SerializedName("stat_date")
-  private Integer statDate;
+  private String statDate;
 
   /**
    * target_user
@@ -191,11 +191,11 @@ public class WxDataCubeArticleTotalDetail {
   @SerializedName("feed_share_from_other_cnt")
   private Integer feedShareFromOtherCnt;
 
-  public Integer getStatDate() {
+  public String getStatDate() {
     return this.statDate;
   }
 
-  public void setStatDate(Integer statDate) {
+  public void setStatDate(String statDate) {
     this.statDate = statDate;
   }
 

From 0acde0353f15af4e00298fee92546f6ef0fd603c Mon Sep 17 00:00:00 2001
From: "Eric.Tsai" 
Date: Mon, 21 Nov 2016 18:39:26 +0800
Subject: [PATCH 26/41] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=8E=B7=E5=8F=96Token?=
 =?UTF-8?q?/Ticket=E6=97=B6=E7=9A=84=E5=90=8C=E6=AD=A5=E6=9C=BA=E5=88=B6?=
 =?UTF-8?q?=EF=BC=8C=E6=94=B9=E7=94=B1=E9=80=9A=E8=BF=87=E9=94=81=E6=9D=A5?=
 =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E5=90=8C=E6=AD=A5=EF=BC=8C=E7=94=B1WxMpConfi?=
 =?UTF-8?q?gStorage=E6=9D=A5=E6=8F=90=E4=BE=9B=E9=94=81=E5=AE=9E=E7=8E=B0?=
 =?UTF-8?q?=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../weixin/mp/api/WxMpConfigStorage.java      |   7 ++
 .../mp/api/WxMpInMemoryConfigStorage.java     |  21 ++++
 .../mp/api/impl/WxMpCardServiceImpl.java      |  38 +++----
 .../weixin/mp/api/impl/WxMpServiceImpl.java   | 101 +++++++++---------
 4 files changed, 96 insertions(+), 71 deletions(-)

diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
index 67d39a7865..78b1a587d6 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
@@ -5,6 +5,7 @@
 
 import javax.net.ssl.SSLContext;
 import java.io.File;
+import java.util.concurrent.locks.Lock;
 
 /**
  * 微信客户端配置存储
@@ -15,6 +16,8 @@ public interface WxMpConfigStorage {
 
   String getAccessToken();
 
+  Lock getAccessTokenLock();
+
   boolean isAccessTokenExpired();
 
   /**
@@ -37,6 +40,8 @@ public interface WxMpConfigStorage {
 
   String getJsapiTicket();
 
+  Lock getJsapiTicketLock();
+
   boolean isJsapiTicketExpired();
 
   /**
@@ -53,6 +58,8 @@ public interface WxMpConfigStorage {
 
   String getCardApiTicket();
 
+  Lock getCardApiTicketLock();
+
   boolean isCardApiTicketExpired();
 
   /**
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
index bb4a298534..e874e793b8 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
@@ -6,6 +6,8 @@
 
 import javax.net.ssl.SSLContext;
 import java.io.File;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化
@@ -36,6 +38,10 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
   protected volatile String cardApiTicket;
   protected volatile long cardApiTicketExpiresTime;
 
+  protected Lock accessTokenLock = new ReentrantLock();
+  protected Lock jsapiTicketLock = new ReentrantLock();
+  protected Lock cardApiTicketLock = new ReentrantLock();
+
   /**
    * 临时文件目录
    */
@@ -50,6 +56,11 @@ public String getAccessToken() {
     return this.accessToken;
   }
 
+  @Override
+  public Lock getAccessTokenLock() {
+    return this.accessTokenLock;
+  }
+
   @Override
   public boolean isAccessTokenExpired() {
     return System.currentTimeMillis() > this.expiresTime;
@@ -76,6 +87,11 @@ public String getJsapiTicket() {
     return this.jsapiTicket;
   }
 
+  @Override
+  public Lock getJsapiTicketLock() {
+    return this.jsapiTicketLock;
+  }
+
   public void setJsapiTicket(String jsapiTicket) {
     this.jsapiTicket = jsapiTicket;
   }
@@ -113,6 +129,11 @@ public String getCardApiTicket() {
     return this.cardApiTicket;
   }
 
+  @Override
+  public Lock getCardApiTicketLock() {
+    return this.cardApiTicketLock;
+  }
+
   @Override
   public boolean isCardApiTicketExpired() {
     return System.currentTimeMillis() > this.cardApiTicketExpiresTime;
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java
index 63e13809b8..2e6bf88e50 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImpl.java
@@ -1,6 +1,7 @@
 package me.chanjar.weixin.mp.api.impl;
 
 import java.util.Arrays;
+import java.util.concurrent.locks.Lock;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,11 +30,6 @@ public class WxMpCardServiceImpl implements WxMpCardService {
 
   private final Logger log = LoggerFactory.getLogger(WxMpCardServiceImpl.class);
 
-  /**
-   * 全局的是否正在刷新卡券api_ticket的锁
-   */
-  private final Object globalCardApiTicketRefreshLock = new Object();
-
   private WxMpService wxMpService;
 
   public WxMpCardServiceImpl(WxMpService wxMpService) {
@@ -66,21 +62,25 @@ public String getCardApiTicket() throws WxErrorException {
    */
   @Override
   public String getCardApiTicket(boolean forceRefresh) throws WxErrorException {
-    if (forceRefresh) {
-      this.wxMpService.getWxMpConfigStorage().expireCardApiTicket();
-    }
-    if (this.wxMpService.getWxMpConfigStorage().isCardApiTicketExpired()) {
-      synchronized (this.globalCardApiTicketRefreshLock) {
-        if (this.wxMpService.getWxMpConfigStorage().isCardApiTicketExpired()) {
-          String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card";
-          String responseContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null);
-          JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
-          JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
-          String cardApiTicket = tmpJsonObject.get("ticket").getAsString();
-          int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
-          this.wxMpService.getWxMpConfigStorage().updateCardApiTicket(cardApiTicket, expiresInSeconds);
-        }
+    Lock lock = wxMpService.getWxMpConfigStorage().getCardApiTicketLock();
+    try {
+      lock.lock();
+
+      if (forceRefresh) {
+        this.wxMpService.getWxMpConfigStorage().expireCardApiTicket();
+      }
+
+      if (this.wxMpService.getWxMpConfigStorage().isCardApiTicketExpired()) {
+        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card";
+        String responseContent = this.wxMpService.execute(new SimpleGetRequestExecutor(), url, null);
+        JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
+        JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
+        String cardApiTicket = tmpJsonObject.get("ticket").getAsString();
+        int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
+        this.wxMpService.getWxMpConfigStorage().updateCardApiTicket(cardApiTicket, expiresInSeconds);
       }
+    } finally {
+      lock.unlock();
     }
     return this.wxMpService.getWxMpConfigStorage().getCardApiTicket();
   }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
index 6a6044ab1d..3b8bf1137d 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
@@ -28,6 +28,7 @@
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.concurrent.locks.Lock;
 
 public class WxMpServiceImpl implements WxMpService {
 
@@ -35,16 +36,6 @@ public class WxMpServiceImpl implements WxMpService {
 
   protected final Logger log = LoggerFactory.getLogger(this.getClass());
 
-  /**
-   * 全局的是否正在刷新access token的锁
-   */
-  private final Object globalAccessTokenRefreshLock = new Object();
-
-  /**
-   * 全局的是否正在刷新jsapi_ticket的锁
-   */
-  private final Object globalJsapiTicketRefreshLock = new Object();
-
   private WxMpConfigStorage configStorage;
 
   private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this);
@@ -98,39 +89,42 @@ public String getAccessToken() throws WxErrorException {
 
   @Override
   public String getAccessToken(boolean forceRefresh) throws WxErrorException {
-    if (forceRefresh) {
-      this.configStorage.expireAccessToken();
-    }
+    Lock lock = configStorage.getAccessTokenLock();
+    try {
+      lock.lock();
 
-    if (this.configStorage.isAccessTokenExpired()) {
-      synchronized (this.globalAccessTokenRefreshLock) {
-        if (this.configStorage.isAccessTokenExpired()) {
-          String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
-              "&appid=" + this.configStorage.getAppId() + "&secret="
-              + this.configStorage.getSecret();
-          try {
-            HttpGet httpGet = new HttpGet(url);
-            if (this.httpProxy != null) {
-              RequestConfig config = RequestConfig.custom().setProxy(this.httpProxy).build();
-              httpGet.setConfig(config);
-            }
-            try (CloseableHttpResponse response = getHttpclient().execute(httpGet)) {
-              String resultContent = new BasicResponseHandler().handleResponse(response);
-              WxError error = WxError.fromJson(resultContent);
-              if (error.getErrorCode() != 0) {
-                throw new WxErrorException(error);
-              }
-              WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
-              this.configStorage.updateAccessToken(accessToken.getAccessToken(),
-                  accessToken.getExpiresIn());
-            }finally {
-              httpGet.releaseConnection();
+      if (forceRefresh) {
+        this.configStorage.expireAccessToken();
+      }
+
+      if (this.configStorage.isAccessTokenExpired()) {
+        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" +
+            "&appid=" + this.configStorage.getAppId() + "&secret="
+            + this.configStorage.getSecret();
+        try {
+          HttpGet httpGet = new HttpGet(url);
+          if (this.httpProxy != null) {
+            RequestConfig config = RequestConfig.custom().setProxy(this.httpProxy).build();
+            httpGet.setConfig(config);
+          }
+          try (CloseableHttpResponse response = getHttpclient().execute(httpGet)) {
+            String resultContent = new BasicResponseHandler().handleResponse(response);
+            WxError error = WxError.fromJson(resultContent);
+            if (error.getErrorCode() != 0) {
+              throw new WxErrorException(error);
             }
-          } catch (IOException e) {
-            throw new RuntimeException(e);
+            WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
+            this.configStorage.updateAccessToken(accessToken.getAccessToken(),
+                accessToken.getExpiresIn());
+          }finally {
+            httpGet.releaseConnection();
           }
+        } catch (IOException e) {
+          throw new RuntimeException(e);
         }
       }
+    } finally {
+      lock.unlock();
     }
     return this.configStorage.getAccessToken();
   }
@@ -142,22 +136,25 @@ public String getJsapiTicket() throws WxErrorException {
 
   @Override
   public String getJsapiTicket(boolean forceRefresh) throws WxErrorException {
-    if (forceRefresh) {
-      this.configStorage.expireJsapiTicket();
-    }
+    Lock lock = configStorage.getJsapiTicketLock();
+    try {
+      lock.lock();
 
-    if (this.configStorage.isJsapiTicketExpired()) {
-      synchronized (this.globalJsapiTicketRefreshLock) {
-        if (this.configStorage.isJsapiTicketExpired()) {
-          String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
-          String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
-          JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent);
-          JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
-          String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
-          int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
-          this.configStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds);
-        }
+      if (forceRefresh) {
+        this.configStorage.expireJsapiTicket();
+      }
+
+      if (this.configStorage.isJsapiTicketExpired()) {
+        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
+        String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
+        JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent);
+        JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject();
+        String jsapiTicket = tmpJsonObject.get("ticket").getAsString();
+        int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt();
+        this.configStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds);
       }
+    } finally {
+      lock.unlock();
     }
     return this.configStorage.getJsapiTicket();
   }

From b3778d1e31006fdf4197cdda00430f00e0ff95d5 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Tue, 22 Nov 2016 10:34:39 +0800
Subject: [PATCH 27/41] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B4=A1=E7=8C=AE?=
 =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pom.xml | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 05b03233c3..a31ee66782 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,11 @@
       aimilin@yeah.net
       https://github.com/aimilin6688
     
+    
+      Eric.Tsai
+      xiaodong.cai.ks@gmail.com
+      https://github.com/iwareserictsai
+    
   
 
   
@@ -72,7 +77,7 @@
   
     1.7
     1.7
-    
+
     UTF-8
     true
     true

From 077f39c1db13112dcb27a922fa11adb2d1e921b6 Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Tue, 22 Nov 2016 13:45:47 +0800
Subject: [PATCH 28/41] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B4=A1=E7=8C=AE?=
 =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pom.xml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/pom.xml b/pom.xml
index a31ee66782..221d77560a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -59,6 +59,11 @@
       xiaodong.cai.ks@gmail.com
       https://github.com/iwareserictsai
     
+    
+      withinthefog
+      withinthefog@gmail.com
+      https://github.com/withinthefog
+    
   
 
   

From 33ef6ebbe9ebf9e2a631245b0b1ae66101d811bd Mon Sep 17 00:00:00 2001
From: Binary Wang 
Date: Tue, 22 Nov 2016 14:16:13 +0800
Subject: [PATCH 29/41] =?UTF-8?q?=E4=BF=AE=E5=A4=8D#82=E5=BC=95=E5=85=A5?=
 =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=AF=BC=E8=87=B4=E7=9A=84=E5=8D=95=E5=85=83?=
 =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java   | 4 +---
 .../java/me/chanjar/weixin/mp/api/ApiTestModule.java     | 9 +++++----
 .../weixin/mp/api/WxXmlMpInMemoryConfigStorage.java      | 8 ++++++--
 3 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
index 3b8bf1137d..fa7dc7951f 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
@@ -368,9 +368,7 @@ public  T execute(RequestExecutor executor, String uri, E data) thro
         return result;
       } catch (WxErrorException e) {
         WxError error = e.getError();
-        /**
-         * -1 系统繁忙, 1000ms后重试
-         */
+        // -1 系统繁忙, 1000ms后重试
         if (error.getErrorCode() == -1) {
           int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
           try {
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
index 310593c762..2b749b9f09 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java
@@ -1,15 +1,15 @@
 package me.chanjar.weixin.mp.api;
 
-import java.io.IOException;
-import java.io.InputStream;
-
 import com.google.inject.Binder;
 import com.google.inject.Module;
 import com.thoughtworks.xstream.XStream;
-
 import me.chanjar.weixin.common.util.xml.XStreamInitializer;
 import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.locks.ReentrantLock;
+
 public class ApiTestModule implements Module {
 
   @Override
@@ -18,6 +18,7 @@ public void configure(Binder binder) {
         .getSystemResourceAsStream("test-config.xml")) {
       WxXmlMpInMemoryConfigStorage config = this
           .fromXml(WxXmlMpInMemoryConfigStorage.class, is1);
+      config.setAccessTokenLock(new ReentrantLock());
       WxMpService wxService = new WxMpServiceImpl();
       wxService.setWxMpConfigStorage(config);
 
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxXmlMpInMemoryConfigStorage.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxXmlMpInMemoryConfigStorage.java
index c9835a0adc..75494efeaf 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxXmlMpInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxXmlMpInMemoryConfigStorage.java
@@ -1,8 +1,9 @@
 package me.chanjar.weixin.mp.api;
 
+import com.thoughtworks.xstream.annotations.XStreamAlias;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 
-import com.thoughtworks.xstream.annotations.XStreamAlias;
+import java.util.concurrent.locks.Lock;
 
 @XStreamAlias("xml")
 public class WxXmlMpInMemoryConfigStorage
@@ -50,4 +51,7 @@ public void setTemplateId(String templateId) {
     this.templateId = templateId;
   }
 
-}
\ No newline at end of file
+  public void setAccessTokenLock(Lock lock){
+    super.accessTokenLock = lock;
+  }
+}

From a26715cb4cd4f483c962e37b6963cb7d84886e22 Mon Sep 17 00:00:00 2001
From: dongfuqiang 
Date: Thu, 24 Nov 2016 20:03:08 +0800
Subject: [PATCH 30/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9=E5=BE=AE?=
 =?UTF-8?q?=E4=BF=A1=E7=A1=AC=E4=BB=B6=E5=B9=B3=E5=8F=B0=E4=BA=8B=E4=BB=B6?=
 =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=9A=84=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../chanjar/weixin/common/api/WxConsts.java   |   5 +
 .../mp/bean/message/WxMpXmlMessage.java       | 122 +++++++++++++++++-
 2 files changed, 122 insertions(+), 5 deletions(-)

diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
index 3bfd11753f..3553c5f663 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
@@ -18,8 +18,13 @@ public class WxConsts {
   public static final String XML_MSG_LOCATION = "location";
   public static final String XML_MSG_LINK = "link";
   public static final String XML_MSG_EVENT = "event";
+  public static final String XML_MSG_DEVICE_TEXT = "device_text";
+  public static final String XML_MSG_DEVICE_EVENT = "device_event";
+  public static final String XML_MSG_DEVICE_STATUS = "device_status";
+  public static final String XML_MSG_HARDWARE = "hardware";
   public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service";
 
+
   ///////////////////////
   // 主动发送消息(即客服消息)的消息类型
   ///////////////////////
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
index 732c0d54bf..8fa9a6a0fb 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
@@ -263,6 +263,81 @@ public class WxMpXmlMessage implements Serializable {
   @XStreamAlias("FailReason")
   private String failReason;
 
+
+  ///////////////////////////////////////
+  // 微信硬件平台相关事件推送
+  ///////////////////////////////////////
+  /**
+   * 设备类型,目前为"公众账号原始ID"
+   */
+  @XStreamAlias("DeviceType")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String deviceType;
+
+  /**
+   * 设备ID,第三方提供
+   */
+  @XStreamAlias("DeviceId")
+  @XStreamConverter(value = XStreamCDataConverter.class)
+  private String deviceId;
+
+
+  @XStreamAlias("HardWare")
+  private HardWare hardWare = new HardWare();
+
+  /**
+   * 请求类型:0:退订设备状态;1:心跳;(心跳的处理方式跟订阅一样)2:订阅设备状态
+   */
+  @XStreamAlias("OpType")
+  private Integer opType;
+
+  /**
+   * 设备状态:0:未连接;1:已连接
+   */
+  @XStreamAlias("DeviceStatus")
+  private Integer deviceStatus;
+
+  public Integer getOpType() {
+    return opType;
+  }
+
+  public void setOpType(Integer opType) {
+    this.opType = opType;
+  }
+
+  public Integer getDeviceStatus() {
+
+    return deviceStatus;
+  }
+
+  public void setDeviceStatus(Integer deviceStatus) {
+    this.deviceStatus = deviceStatus;
+  }
+
+  public HardWare getHardWare() {
+    return hardWare;
+  }
+
+  public void setHardWare(HardWare hardWare) {
+    this.hardWare = hardWare;
+  }
+
+  public String getDeviceType() {
+    return deviceType;
+  }
+
+  public void setDeviceType(String deviceType) {
+    this.deviceType = deviceType;
+  }
+
+  public String getDeviceId() {
+    return deviceId;
+  }
+
+  public void setDeviceId(String deviceId) {
+    this.deviceId = deviceId;
+  }
+
   public Long getExpiredTime() {
     return this.expiredTime;
   }
@@ -346,7 +421,6 @@ public void setCreateTime(Long createTime) {
    * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_LINK}
    * {@link me.chanjar.weixin.common.api.WxConsts#XML_MSG_EVENT}
    * 
- * */ public String getMsgType() { return this.msgType; @@ -555,8 +629,8 @@ public static WxMpXmlMessage fromXml(InputStream is) { * @param msgSignature */ public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, - WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, - String msgSignature) { + WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, + String msgSignature) { WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, encryptedXml); @@ -564,8 +638,8 @@ public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, } public static WxMpXmlMessage fromEncryptedXml(InputStream is, - WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, - String msgSignature) { + WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, + String msgSignature) { try { return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxMpConfigStorage, timestamp, nonce, msgSignature); @@ -719,6 +793,44 @@ public void setFromKfAccount(String fromKfAccount) { this.fromKfAccount = fromKfAccount; } + @XStreamAlias("HardWare") + public static class HardWare { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + /** + * 消息展示,目前支持myrank(排行榜) + */ + @XStreamAlias("MessageView") + @XStreamConverter(value = XStreamCDataConverter.class) + private String messageView; + + /** + * 消息点击动作,目前支持ranklist(点击跳转排行榜) + */ + @XStreamAlias("MessageAction") + @XStreamConverter(value = XStreamCDataConverter.class) + private String messageAction; + + public String getMessageView() { + return messageView; + } + + public void setMessageView(String messageView) { + this.messageView = messageView; + } + + public String getMessageAction() { + return messageAction; + } + + public void setMessageAction(String messageAction) { + this.messageAction = messageAction; + } + } + @XStreamAlias("ScanCodeInfo") public static class ScanCodeInfo { @Override From 1bc2a5c7b3f55638b1e328ff70c0c51005fdf321 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Thu, 24 Nov 2016 20:08:05 +0800 Subject: [PATCH 31/41] =?UTF-8?q?=E9=83=A8=E5=88=86=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98=E6=9F=A5=E8=AF=A2=E9=80=80?= =?UTF-8?q?=E6=AC=BE=E7=9A=84=E6=8E=A5=E5=8F=A3=20for=20#59?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/mp/api/WxMpPayService.java | 54 +- .../mp/api/impl/WxMpPayServiceImpl.java | 92 ++-- .../pay/request/WxPayRefundQueryRequest.java | 135 +++++ .../pay/result/WxPayRefundQueryResult.java | 488 ++++++++++++++++++ .../mp/api/impl/WxMpPayServiceImplTest.java | 24 +- 5 files changed, 743 insertions(+), 50 deletions(-) create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java index 2fcd4cb929..6cde59507e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java @@ -2,7 +2,6 @@ import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback; -import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderCloseResult; import me.chanjar.weixin.mp.bean.pay.request.WxEntPayRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; @@ -13,8 +12,9 @@ import java.util.Map; /** - * 微信支付相关接口 - * Created by Binary Wang on 2016/7/28. + * 微信支付相关接口 + * Created by Binary Wang on 2016/7/28. + * * @author binarywang (https://github.com/binarywang) */ public interface WxMpPayService { @@ -24,14 +24,15 @@ public interface WxMpPayService { * 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) * 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。 * 需要调用查询接口的情况: - ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; - ◆ 调用支付接口后,返回系统错误或未知交易状态情况; - ◆ 调用被扫支付API,返回USERPAYING的状态; - ◆ 调用关单或撤销接口API之前,需确认支付状态; + * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; + * ◆ 调用支付接口后,返回系统错误或未知交易状态情况; + * ◆ 调用被扫支付API,返回USERPAYING的状态; + * ◆ 调用关单或撤销接口API之前,需确认支付状态; * 接口地址:https://api.mch.weixin.qq.com/pay/orderquery *
+ * * @param transactionId 微信支付分配的商户号 - * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 + * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 * @throws WxErrorException */ WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; @@ -47,6 +48,7 @@ public interface WxMpPayService { * 接口地址:https://api.mch.weixin.qq.com/pay/closeorder * 是否需要证书: 不需要。 *
+ * * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 * @throws WxErrorException */ @@ -56,15 +58,16 @@ public interface WxMpPayService { * 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder - * @throws WxErrorException - * @param request 请求对象 * + * @param request 请求对象 + * @throws WxErrorException */ WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException; /** * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数 * 详见http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN + * * @param request 请求对象 */ Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; @@ -75,16 +78,33 @@ public interface WxMpPayService { * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/refund * + * * @param request 请求对象 - * @param keyFile 证书文件对象 + * @param keyFile 证书文件对象 * @return 退款操作结果 */ WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) throws WxErrorException; + /** + *
+   * 微信支付-查询退款
+   * 应用场景:
+   *  提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
+   * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
+   * 接口链接:https://api.mch.weixin.qq.com/pay/refundquery
+   * 
+ * 以下四个参数四选一 + * @param transactionId 微信订单号 + * @param outTradeNo 商户订单号 + * @param outRefundNo 商户退款单号 + * @param refundId 微信退款单号 + * @return 退款信息 + */ + WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException; + /** * 读取支付结果通知 * 详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 - * */ WxPayJsSDKCallback getJSSDKCallbackData(String xmlData) throws WxErrorException; @@ -93,7 +113,6 @@ public interface WxMpPayService { * 计算Map键值对是否和签名相符, * 按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的 格式(即 key1=value1&key2=value2...)拼接成字符串 * - * */ boolean checkJSSDKCallbackDataSignature(Map kvm, String signature); @@ -104,8 +123,9 @@ public interface WxMpPayService { * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4 * + * * @param request 请求对象 - * @param keyFile 证书文件对象 + * @param keyFile 证书文件对象 */ WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) throws WxErrorException; @@ -118,8 +138,9 @@ public interface WxMpPayService { * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers * + * * @param request 请求对象 - * @param keyFile 证书文件对象 + * @param keyFile 证书文件对象 */ WxEntPayResult entPay(WxEntPayRequest request, File keyFile) throws WxErrorException; @@ -130,8 +151,9 @@ public interface WxMpPayService { * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3 * 接口链接:https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo * + * * @param partnerTradeNo 商户订单号 - * @param keyFile 证书文件对象 + * @param keyFile 证书文件对象 */ WxEntPayQueryResult queryEntPay(String partnerTradeNo, File keyFile) throws WxErrorException; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java index 3273cd2b80..e49891f188 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java @@ -8,7 +8,6 @@ import me.chanjar.weixin.mp.api.WxMpPayService; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.pay.WxPayJsSDKCallback; -import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderCloseResult; import me.chanjar.weixin.mp.bean.pay.request.*; import me.chanjar.weixin.mp.bean.pay.result.*; import org.apache.commons.codec.digest.DigestUtils; @@ -42,13 +41,11 @@ */ public class WxMpPayServiceImpl implements WxMpPayService { - protected final Logger log = LoggerFactory.getLogger(this.getClass()); - private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; - private static final String[] TRADE_TYPES = new String[]{"JSAPI","NATIVE", "APP"}; - private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", + private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; + private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", "REFUND_SOURCE_UNSETTLED_FUNDS"}; - + protected final Logger log = LoggerFactory.getLogger(this.getClass()); private WxMpService wxMpService; public WxMpPayServiceImpl(WxMpService wxMpService) { @@ -57,7 +54,7 @@ public WxMpPayServiceImpl(WxMpService wxMpService) { @Override public WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) - throws WxErrorException { + throws WxErrorException { checkParameters(request); XStream xstream = XStreamInitializer.getInstance(); @@ -67,7 +64,7 @@ public WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId()); String partnerId = this.wxMpService.getWxMpConfigStorage().getPartnerId(); request.setMchId(partnerId); - request.setNonceStr( System.currentTimeMillis() + ""); + request.setNonceStr(System.currentTimeMillis() + ""); request.setOpUserId(partnerId); String sign = this.createSign(BeanUtils.xmlBean2Map(request), this.wxMpService.getWxMpConfigStorage().getPartnerKey()); request.setSign(sign); @@ -79,11 +76,45 @@ public WxPayRefundResult refund(WxPayRefundRequest request, File keyFile) return result; } + @Override + public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException { + if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) || + (StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) { + throw new IllegalArgumentException("transaction_id , out_trade_no,out_refund_no, refund_id 必须四选一"); + } + + XStream xstream = XStreamInitializer.getInstance(); + xstream.processAnnotations(WxPayRefundQueryRequest.class); + xstream.processAnnotations(WxPayRefundQueryResult.class); + + WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); + request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); + request.setTransactionId(StringUtils.trimToNull(transactionId)); + request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); + request.setRefundId(StringUtils.trimToNull(refundId)); + + request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId()); + request.setMchId(this.wxMpService.getWxMpConfigStorage().getPartnerId()); + request.setNonceStr(System.currentTimeMillis() + ""); + + String sign = this.createSign(BeanUtils.xmlBean2Map(request), + this.wxMpService.getWxMpConfigStorage().getPartnerKey()); + request.setSign(sign); + + String url = PAY_BASE_URL + "/pay/refundquery"; + + String responseContent = this.executeRequest(url, xstream.toXML(request)); + WxPayRefundQueryResult result = (WxPayRefundQueryResult) xstream.fromXML(responseContent); + result.composeRefundRecords(responseContent); + this.checkResult(result); + return result; + } + private void checkResult(WxPayBaseResult result) throws WxErrorException { if (!"SUCCESS".equalsIgnoreCase(result.getReturnCode()) || !"SUCCESS".equalsIgnoreCase(result.getResultCode())) { throw new WxErrorException(WxError.newBuilder().setErrorCode(-1) - .setErrorMsg("返回代码:" + result.getReturnCode() + ", 返回信息: " + .setErrorMsg("返回代码: " + result.getReturnCode() + ", 返回信息: " + result.getReturnMsg() + ", 结果代码: " + result.getResultCode() + ", 错误代码: " + result.getErrCode() + ", 错误详情: " + result.getErrCodeDes()) .build()); @@ -94,7 +125,7 @@ private void checkParameters(WxPayRefundRequest request) throws WxErrorException BeanUtils.checkRequiredFields(request); if (StringUtils.isNotBlank(request.getRefundAccount())) { - if(!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())){ + if (!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())) { throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) + "其中之一"); } } @@ -118,14 +149,14 @@ public WxPayJsSDKCallback getJSSDKCallbackData(String xmlData) throws WxErrorExc @Override public boolean checkJSSDKCallbackDataSignature(Map kvm, - String signature) { + String signature) { return signature.equals(this.createSign(kvm, - this.wxMpService.getWxMpConfigStorage().getPartnerKey())); + this.wxMpService.getWxMpConfigStorage().getPartnerKey())); } @Override public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) - throws WxErrorException { + throws WxErrorException { XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(WxPaySendRedpackRequest.class); xstream.processAnnotations(WxPaySendRedpackResult.class); @@ -136,7 +167,7 @@ public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File request.setNonceStr(System.currentTimeMillis() + ""); String sign = this.createSign(BeanUtils.xmlBean2Map(request), - this.wxMpService.getWxMpConfigStorage().getPartnerKey()); + this.wxMpService.getWxMpConfigStorage().getPartnerKey()); request.setSign(sign); String url = PAY_BASE_URL + "/mmpaymkttransfers/sendredpack"; @@ -147,15 +178,16 @@ public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), mchId); WxPaySendRedpackResult result = (WxPaySendRedpackResult) xstream - .fromXML(responseContent); + .fromXML(responseContent); this.checkResult(result); return result; } /** * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) + * * @param packageParams 原始参数 - * @param signKey 加密Key(即 商户Key) + * @param signKey 加密Key(即 商户Key) * @return 签名字符串 */ private String createSign(Map packageParams, String signKey) { @@ -165,7 +197,7 @@ private String createSign(Map packageParams, String signKey) { for (String key : sortedMap.keySet()) { String value = packageParams.get(key); if (null != value && !"".equals(value) && !"sign".equals(key) - && !"key".equals(key)) { + && !"key".equals(key)) { toSign.append(key + "=" + value + "&"); } } @@ -237,7 +269,7 @@ public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorExcepti @Override public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) - throws WxErrorException { + throws WxErrorException { checkParameters(request); XStream xstream = XStreamInitializer.getInstance(); @@ -249,14 +281,14 @@ public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) request.setNonceStr(System.currentTimeMillis() + ""); String sign = this.createSign(BeanUtils.xmlBean2Map(request), - this.wxMpService.getWxMpConfigStorage().getPartnerKey()); + this.wxMpService.getWxMpConfigStorage().getPartnerKey()); request.setSign(sign); String url = PAY_BASE_URL + "/pay/unifiedorder"; String responseContent = this.executeRequest(url, xstream.toXML(request)); WxPayUnifiedOrderResult result = (WxPayUnifiedOrderResult) xstream - .fromXML(responseContent); + .fromXML(responseContent); this.checkResult(result); return result; } @@ -264,7 +296,7 @@ public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) private void checkParameters(WxPayUnifiedOrderRequest request) throws WxErrorException { BeanUtils.checkRequiredFields(request); - if (! ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) { + if (!ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) { throw new IllegalArgumentException("trade_type目前必须为" + Arrays.toString(TRADE_TYPES) + "其中之一"); } @@ -283,7 +315,7 @@ public Map getPayInfo(WxPayUnifiedOrderRequest request) throws W String prepayId = unifiedOrderResult.getPrepayId(); if (StringUtils.isBlank(prepayId)) { throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", - unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); + unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); } Map payInfo = new HashMap<>(); @@ -347,7 +379,7 @@ public WxEntPayQueryResult queryEntPay(String partnerTradeNo, File keyFile) thro return result; } - private String executeRequest( String url, String requestStr) throws WxErrorException { + private String executeRequest(String url, String requestStr) throws WxErrorException { HttpPost httpPost = new HttpPost(url); if (this.wxMpService.getHttpProxy() != null) { httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); @@ -358,25 +390,25 @@ private String executeRequest( String url, String requestStr) throws WxErrorExce try (CloseableHttpResponse response = httpclient.execute(httpPost)) { String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",url, requestStr, result); + this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); return result; } } catch (IOException e) { this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); - }finally { + } finally { httpPost.releaseConnection(); } } - private String executeRequestWithKeyFile( String url, File keyFile, String requestStr, String mchId) throws WxErrorException { + private String executeRequestWithKeyFile(String url, File keyFile, String requestStr, String mchId) throws WxErrorException { try (FileInputStream inputStream = new FileInputStream(keyFile)) { KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(inputStream, mchId.toCharArray()); SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build(); - SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, - new DefaultHostnameVerifier()); + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, + new DefaultHostnameVerifier()); HttpPost httpPost = new HttpPost(url); if (this.wxMpService.getHttpProxy() != null) { @@ -387,10 +419,10 @@ private String executeRequestWithKeyFile( String url, File keyFile, String reque httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); try (CloseableHttpResponse response = httpclient.execute(httpPost)) { String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); - this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}",url, requestStr, result); + this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); return result; } - }finally { + } finally { httpPost.releaseConnection(); } } catch (Exception e) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java new file mode 100644 index 0000000000..373b0917db --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java @@ -0,0 +1,135 @@ +package me.chanjar.weixin.mp.bean.pay.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
+ * Created by Binary Wang on 2016-11-24.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +@XStreamAlias("xml") +public class WxPayRefundQueryRequest extends WxPayBaseRequest { + /** + *
+   * 设备号
+   * device_info
+   * 否
+   * String(32)
+   * 013467007045764
+   * 商户自定义的终端设备号,如门店编号、设备的ID等
+   * 
+ */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
+   * 签名类型
+   * sign_type
+   * 否
+   * String(32)
+   * HMAC-SHA256
+   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
+   * 
+ */ + @XStreamAlias("sign_type") + private String signType; + + //************以下四选一************ + /** + *
+   * 微信订单号
+   * transaction_id
+   * String(32)
+   * 1217752501201407033233368018
+   * 微信订单号
+   * 
+ */ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
+   * 商户订单号
+   * out_trade_no
+   * String(32)
+   * 1217752501201407033233368018
+   * 商户系统内部的订单号
+   * 
+ */ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
+   * 商户退款单号
+   * out_refund_no
+   * String(32)
+   * 1217752501201407033233368018
+   * 商户侧传给微信的退款单号
+   * 
+ */ + @XStreamAlias("out_refund_no") + private String outRefundNo; + + /** + *
+   * 微信退款单号
+   * refund_id
+   * String(28)
+   * 1217752501201407033233368018
+   * 微信生成的退款单号,在申请退款接口有返回
+   * 
+ */ + @XStreamAlias("refund_id") + private String refundId; + + public String getDeviceInfo() { + return deviceInfo; + } + + public void setDeviceInfo(String deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public String getSignType() { + return signType; + } + + public void setSignType(String signType) { + this.signType = signType; + } + + public String getTransactionId() { + return transactionId; + } + + public void setTransactionId(String transactionId) { + this.transactionId = transactionId; + } + + public String getOutTradeNo() { + return outTradeNo; + } + + public void setOutTradeNo(String outTradeNo) { + this.outTradeNo = outTradeNo; + } + + public String getOutRefundNo() { + return outRefundNo; + } + + public void setOutRefundNo(String outRefundNo) { + this.outRefundNo = outRefundNo; + } + + public String getRefundId() { + return refundId; + } + + public void setRefundId(String refundId) { + this.refundId = refundId; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java new file mode 100644 index 0000000000..efc21139b8 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java @@ -0,0 +1,488 @@ +package me.chanjar.weixin.mp.bean.pay.result; + +import com.google.common.collect.Lists; +import com.thoughtworks.xstream.annotations.XStreamAlias; + +import java.util.List; + +/** + *
+ * Created by Binary Wang on 2016-11-24.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +@XStreamAlias("xml") +public class WxPayRefundQueryResult extends WxPayBaseResult { + /** + *
+   * 设备号
+   * device_info
+   * 否
+   * String(32)
+   * 013467007045764
+   * 终端设备号
+   */
+  @XStreamAlias("device_info")
+  private String deviceInfo;
+
+  /**
+   * 
+   * 微信订单号
+   * transaction_id
+   * 是
+   * String(32)
+   * 1217752501201407033233368018
+   * 微信订单号
+   */
+  @XStreamAlias("transaction_id")
+  private String transactionId;
+
+  /**
+   * 
+   * 商户订单号
+   * out_trade_no
+   * 是
+   * String(32)
+   * 1217752501201407033233368018
+   * 商户系统内部的订单号
+   */
+  @XStreamAlias("out_trade_no")
+  private String outTradeNo;
+
+  /**
+   * 
+   * 订单金额
+   * total_fee
+   * 是
+   * Int
+   * 100
+   * 订单总金额,单位为分,只能为整数,详见支付金额
+   */
+  @XStreamAlias("total_fee")
+  private Integer totalFee;
+
+  /**
+   * 
+   * 应结订单金额
+   * settlement_total_fee
+   * 否
+   * Int
+   * 100
+   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
+   */
+  @XStreamAlias("settlement_total_fee")
+  private Integer settlementTotalFee;
+
+  /**
+   * 
+   * 货币种类
+   * fee_type
+   * 否
+   * String(8)
+   * CNY
+   * 订单金额货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
+   */
+  @XStreamAlias("fee_type")
+  private String feeType;
+
+  /**
+   * 
+   * 现金支付金额
+   * cash_fee
+   * 是
+   * Int
+   * 100
+   * 现金支付金额,单位为分,只能为整数,详见支付金额
+   */
+  @XStreamAlias("cash_fee")
+  private Integer cashFee;
+
+  /**
+   * 
+   * 退款笔数
+   * refund_count
+   * 是
+   * Int
+   * 1
+   * 退款记录数
+   */
+  @XStreamAlias("refund_count")
+  private Integer refundCount;
+
+  private List refundRecords;
+
+  public String getDeviceInfo() {
+    return deviceInfo;
+  }
+
+  public void setDeviceInfo(String deviceInfo) {
+    this.deviceInfo = deviceInfo;
+  }
+
+  public String getTransactionId() {
+    return transactionId;
+  }
+
+  public void setTransactionId(String transactionId) {
+    this.transactionId = transactionId;
+  }
+
+  public String getOutTradeNo() {
+    return outTradeNo;
+  }
+
+  public void setOutTradeNo(String outTradeNo) {
+    this.outTradeNo = outTradeNo;
+  }
+
+  public Integer getTotalFee() {
+    return totalFee;
+  }
+
+  public void setTotalFee(Integer totalFee) {
+    this.totalFee = totalFee;
+  }
+
+  public Integer getSettlementTotalFee() {
+    return settlementTotalFee;
+  }
+
+  public void setSettlementTotalFee(Integer settlementTotalFee) {
+    this.settlementTotalFee = settlementTotalFee;
+  }
+
+  public String getFeeType() {
+    return feeType;
+  }
+
+  public void setFeeType(String feeType) {
+    this.feeType = feeType;
+  }
+
+  public Integer getCashFee() {
+    return cashFee;
+  }
+
+  public void setCashFee(Integer cashFee) {
+    this.cashFee = cashFee;
+  }
+
+  public Integer getRefundCount() {
+    return refundCount;
+  }
+
+  public void setRefundCount(Integer refundCount) {
+    this.refundCount = refundCount;
+  }
+
+  public List getRefundRecords() {
+    return refundRecords;
+  }
+
+  public void setRefundRecords(List refundRecords) {
+    this.refundRecords = refundRecords;
+  }
+
+  public static class RefundRecord {
+    /**
+     * 
+     * 商户退款单号
+     * out_refund_no_$n
+     * 是
+     * String(32)
+     * 1217752501201407033233368018
+     * 商户退款单号
+     * 
+ */ + @XStreamAlias("out_refund_no") + private String outRefundNo; + + /** + *
+     * 微信退款单号
+     * refund_id_$n
+     * 是
+     * String(28)
+     * 1217752501201407033233368018
+     * 微信退款单号
+     * 
+ */ + @XStreamAlias("refund_id") + private String refundId; + + /** + *
+     * 退款渠道
+     * refund_channel_$n
+     * 否
+     * String(16)
+     * ORIGINAL
+     * ORIGINAL—原路退款 BALANCE—退回到余额
+     * 
+ */ + @XStreamAlias("refund_channel") + private String refundChannel; + + /** + *
+     * 申请退款金额
+     * refund_fee_$n
+     * 是
+     * Int
+     * 100
+     * 退款总金额,单位为分,可以做部分退款
+     * 
+ */ + @XStreamAlias("refund_fee") + private String refundFee; + + /** + *
+     * 退款金额
+     * settlement_refund_fee_$n
+     * 否
+     * Int
+     * 100
+     * 退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额
+     * 
+ */ + @XStreamAlias("settlement_refund_fee") + private String settlementRefundFee; + + /** + *
+     * 退款资金来源
+     * refund_account
+     * 否
+     * String(30)
+     * REFUND_SOURCE_RECHARGE_FUNDS
+     * REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款/基本账户, REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款
+     * 
+ */ + @XStreamAlias("refund_account") + private String refundAccount; + + /** + *
+     * 代金券类型
+     * coupon_type_$n
+     * 否
+     * Int
+     * CASH
+     * CASH--充值代金券 , NO_CASH---非充值代金券。订单使用代金券时有返回(取值:CASH、NO_CASH)。$n为下标,从0开始编号,举例:coupon_type_$0
+     * 
+ */ + @XStreamAlias("coupon_type") + private String couponType; + + /** + *
+     * 代金券退款金额
+     * coupon_refund_fee_$n
+     * 否
+     * Int
+     * 100
+     * 代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
+     * 
+ */ + @XStreamAlias("coupon_refund_fee") + private String couponRefundFee; + + /** + *
+     * 退款代金券使用数量
+     * coupon_refund_count_$n
+     * 否
+     * Int
+     * 1
+     * 退款代金券使用数量 ,$n为下标,从0开始编号
+     * 
+ */ + @XStreamAlias("coupon_refund_count") + private String couponRefundCount; + + private List refundCoupons; + + /** + *
+     * 退款状态
+     * refund_status_$n
+     * 是
+     * String(16)
+     * SUCCESS
+     * 退款状态:
+     *  SUCCESS—退款成功,
+     *  FAIL—退款失败,
+     *  PROCESSING—退款处理中,
+     *  CHANGE—转入代发,
+     * 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者财付通转账的方式进行退款。
+     * 
+ */ + @XStreamAlias("refund_status") + private String refundStatus; + /** + *
+     * 退款入账账户
+     * refund_recv_accout_$n
+     * 是
+     * String(64)
+     * 招商银行信用卡0403
+     * 取当前退款单的退款入账方,1)退回银行卡:{银行名称}{卡类型}{卡尾号},2)退回支付用户零钱:支付用户零钱
+     * 
+ */ + @XStreamAlias("refund_recv_accout") + private String refundRecvAccout; + + public String getOutRefundNo() { + return outRefundNo; + } + + public void setOutRefundNo(String outRefundNo) { + this.outRefundNo = outRefundNo; + } + + public String getRefundId() { + return refundId; + } + + public void setRefundId(String refundId) { + this.refundId = refundId; + } + + public String getRefundChannel() { + return refundChannel; + } + + public void setRefundChannel(String refundChannel) { + this.refundChannel = refundChannel; + } + + public String getRefundFee() { + return refundFee; + } + + public void setRefundFee(String refundFee) { + this.refundFee = refundFee; + } + + public String getSettlementRefundFee() { + return settlementRefundFee; + } + + public void setSettlementRefundFee(String settlementRefundFee) { + this.settlementRefundFee = settlementRefundFee; + } + + public String getRefundAccount() { + return refundAccount; + } + + public void setRefundAccount(String refundAccount) { + this.refundAccount = refundAccount; + } + + public String getCouponType() { + return couponType; + } + + public void setCouponType(String couponType) { + this.couponType = couponType; + } + + public String getCouponRefundFee() { + return couponRefundFee; + } + + public void setCouponRefundFee(String couponRefundFee) { + this.couponRefundFee = couponRefundFee; + } + + public String getCouponRefundCount() { + return couponRefundCount; + } + + public void setCouponRefundCount(String couponRefundCount) { + this.couponRefundCount = couponRefundCount; + } + + public List getRefundCoupons() { + return refundCoupons; + } + + public void setRefundCoupons(List refundCoupons) { + this.refundCoupons = refundCoupons; + } + + public String getRefundStatus() { + return refundStatus; + } + + public void setRefundStatus(String refundStatus) { + this.refundStatus = refundStatus; + } + + public String getRefundRecvAccout() { + return refundRecvAccout; + } + + public void setRefundRecvAccout(String refundRecvAccout) { + this.refundRecvAccout = refundRecvAccout; + } + + public static class RefundCoupon { + /** + *
+       * 退款代金券批次ID
+       * coupon_refund_batch_id_$n_$m
+       * 否
+       * String(20)
+       * 100
+       * 退款代金券批次ID ,$n为下标,$m为下标,从0开始编号
+       * 
+ */ + @XStreamAlias("coupon_refund_batch_id") + private String couponRefundBatchId; + + /** + *
+       * 退款代金券ID
+       * coupon_refund_id_$n_$m
+       * 否
+       * String(20)
+       * 10000
+       * 退款代金券ID, $n为下标,$m为下标,从0开始编号
+       * 
+ */ + @XStreamAlias("coupon_refund_id") + private String couponRefundId; + + /** + *
+       * 单个退款代金券支付金额
+       * coupon_refund_fee_$n_$m
+       * 否
+       * Int
+       * 100
+       * 单个退款代金券支付金额, $n为下标,$m为下标,从0开始编号
+       * 
+ */ + @XStreamAlias("coupon_refund_fee") + private String couponRefundFee; + + public RefundCoupon(String couponRefundBatchId, String couponRefundId, String couponRefundFee) { + this.couponRefundBatchId = couponRefundBatchId; + this.couponRefundId = couponRefundId; + this.couponRefundFee = couponRefundFee; + } + } + + } + + public void composeRefundRecords(String xmlString){ + if(this.refundCount != null && this.refundCount > 0 ){ + this.refundRecords = Lists.newArrayList(); + //TODO 暂时待实现 + } + } +} + diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java index b4e22f1156..5f155862fd 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java @@ -9,6 +9,7 @@ import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest; import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest; +import me.chanjar.weixin.mp.bean.pay.result.WxPayRefundQueryResult; import me.chanjar.weixin.mp.bean.pay.result.WxPayRefundResult; import me.chanjar.weixin.mp.bean.pay.result.WxPaySendRedpackResult; import me.chanjar.weixin.mp.bean.pay.result.WxPayUnifiedOrderResult; @@ -20,6 +21,7 @@ /** * 测试支付相关接口 * Created by Binary Wang on 2016/7/28. + * * @author binarywang (https://github.com/binarywang) */ @Test @@ -46,6 +48,20 @@ public void testRefund() throws Exception { System.err.println(result); } + @Test + public void testRefundQuery() throws Exception { + WxPayRefundQueryResult result = this.wxService.getPayService().refundQuery("1", "", "", ""); + result = this.wxService.getPayService().refundQuery("", "2", "", ""); + System.err.println(result); + result = this.wxService.getPayService().refundQuery("", "", "3", ""); + System.err.println(result); + result = this.wxService.getPayService().refundQuery("", "", "", "4"); + System.err.println(result); + //测试四个参数都填的情况,应该报异常的 + result = this.wxService.getPayService().refundQuery("1", "2", "3", "4"); + System.err.println(result); + } + @Test public void testCheckJSSDKCallbackDataSignature() throws Exception { @@ -58,7 +74,7 @@ public void testSendRedpack() throws Exception { request.setClientIp("aaa"); request.setMchBillno("aaaa"); request - .setReOpenid(((WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); + .setReOpenid(((WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); File keyFile = new File("E:\\dlt.p12"); WxPaySendRedpackResult redpackResult = this.wxService.getPayService().sendRedpack(request, keyFile); System.err.println(redpackResult); @@ -70,9 +86,9 @@ public void testSendRedpack() throws Exception { @Test public void testUnifiedOrder() throws WxErrorException { WxPayUnifiedOrderResult result = this.wxService.getPayService() - .unifiedOrder(WxPayUnifiedOrderRequest.builder().body("1111111") - .totalFee(1).spbillCreateIp("111111").notifyURL("111111") - .tradeType("JSAPI").openid("122").outTradeNo("111111").build()); + .unifiedOrder(WxPayUnifiedOrderRequest.builder().body("1111111") + .totalFee(1).spbillCreateIp("111111").notifyURL("111111") + .tradeType("JSAPI").openid("122").outTradeNo("111111").build()); System.err.println(result); } From 95821dab2082b75c3f5c861b6f0de7644349fd82 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Fri, 25 Nov 2016 16:31:59 +0800 Subject: [PATCH 32/41] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E8=8F=9C=E5=8D=95=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E7=9A=84=E6=8E=A5=E5=8F=A3=20for=20#70?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/common/bean/menu/WxMenu.java | 12 +- .../weixin/mp/api/WxMpMenuService.java | 17 ++ .../mp/api/impl/WxMpMenuServiceImpl.java | 18 +- .../bean/menu/WxMpGetSelfMenuInfoResult.java | 44 +++ .../weixin/mp/bean/menu/WxMpSelfMenuInfo.java | 288 ++++++++++++++++++ .../mp/api/impl/WxMpMenuServiceImplTest.java | 34 ++- 6 files changed, 389 insertions(+), 24 deletions(-) create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResult.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java index 6b43ed06a6..4e61a46591 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenu.java @@ -1,5 +1,8 @@ package me.chanjar.weixin.common.bean.menu; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + import java.io.InputStream; import java.io.InputStreamReader; import java.io.Serializable; @@ -7,11 +10,8 @@ import java.util.ArrayList; import java.util.List; -import me.chanjar.weixin.common.bean.menu.WxMenuButton; -import me.chanjar.weixin.common.util.json.WxGsonBuilder; - /** - * 企业号菜单 + * 菜单(公众号和企业号共用的) * * @author Daniel Qian */ @@ -61,9 +61,7 @@ public String toJson() { @Override public String toString() { - return "WxMenu{" + - "buttons=" + this.buttons + - '}'; + return ToStringUtils.toSimpleString(this); } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMenuService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMenuService.java index 22217801e0..9435902535 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMenuService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMenuService.java @@ -2,6 +2,7 @@ import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.exception.WxErrorException; +import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; /** * 菜单相关操作接口 @@ -56,4 +57,20 @@ public interface WxMpMenuService { */ WxMenu menuTryMatch(String userid) throws WxErrorException; + /** + *
+   * 获取自定义菜单配置接口
+   * 本接口将会提供公众号当前使用的自定义菜单的配置,如果公众号是通过API调用设置的菜单,则返回菜单的开发配置,而如果公众号是在公众平台官网通过网站功能发布菜单,则本接口返回运营者设置的菜单配置。
+     请注意:
+     1、第三方平台开发者可以通过本接口,在旗下公众号将业务授权给你后,立即通过本接口检测公众号的自定义菜单配置,并通过接口再次给公众号设置好自动回复规则,以提升公众号运营者的业务体验。
+     2、本接口与自定义菜单查询接口的不同之处在于,本接口无论公众号的接口是如何设置的,都能查询到接口,而自定义菜单查询接口则仅能查询到使用API设置的菜单配置。
+     3、认证/未认证的服务号/订阅号,以及接口测试号,均拥有该接口权限。
+     4、从第三方平台的公众号登录授权机制上来说,该接口从属于消息与菜单权限集。
+     5、本接口中返回的图片/语音/视频为临时素材(临时素材每次获取都不同,3天内有效,通过素材管理-获取临时素材接口来获取这些素材),本接口返回的图文消息为永久素材素材(通过素材管理-获取永久素材接口来获取这些素材)。
+   *  接口调用请求说明:
+        http请求方式: GET(请使用https协议)
+        https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=ACCESS_TOKEN
+   *
+ */ + WxMpGetSelfMenuInfoResult getSelfMenuInfo() throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImpl.java index 2f40f709e9..1373c83d19 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImpl.java @@ -1,12 +1,13 @@ package me.chanjar.weixin.mp.api.impl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.mp.api.WxMpMenuService; import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; +import me.chanjar.weixin.mp.bean.menu.WxMpSelfMenuInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Created by Binary Wang on 2016/7/21. @@ -14,7 +15,7 @@ public class WxMpMenuServiceImpl implements WxMpMenuService { private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/menu"; private static Logger log = LoggerFactory - .getLogger(WxMpMenuServiceImpl.class); + .getLogger(WxMpMenuServiceImpl.class); private WxMpService wxMpService; @@ -74,10 +75,17 @@ public WxMenu menuTryMatch(String userid) throws WxErrorException { } catch (WxErrorException e) { // 46003 不存在的菜单数据 46002 不存在的菜单版本 if (e.getError().getErrorCode() == 46003 - || e.getError().getErrorCode() == 46002) { + || e.getError().getErrorCode() == 46002) { return null; } throw e; } } + + @Override + public WxMpGetSelfMenuInfoResult getSelfMenuInfo() throws WxErrorException { + String url = "https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info"; + String resultContent = this.wxMpService.get(url, null); + return WxMpGetSelfMenuInfoResult.fromJson(resultContent); + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResult.java new file mode 100644 index 0000000000..b78d2dc378 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpGetSelfMenuInfoResult.java @@ -0,0 +1,44 @@ +package me.chanjar.weixin.mp.bean.menu; + +import com.google.gson.annotations.SerializedName; +import me.chanjar.weixin.common.util.ToStringUtils; +import me.chanjar.weixin.common.util.json.WxGsonBuilder; + +/** + *
+ * Created by Binary Wang on 2016-11-25.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +public class WxMpGetSelfMenuInfoResult { + @SerializedName("selfmenu_info") + private WxMpSelfMenuInfo selfMenuInfo; + + @SerializedName("is_menu_open") + private Integer isMenuOpen; + + public static WxMpGetSelfMenuInfoResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxMpGetSelfMenuInfoResult.class); + } + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public WxMpSelfMenuInfo getSelfMenuInfo() { + return selfMenuInfo; + } + + public void setSelfMenuInfo(WxMpSelfMenuInfo selfMenuInfo) { + this.selfMenuInfo = selfMenuInfo; + } + + public Integer getIsMenuOpen() { + return isMenuOpen; + } + + public void setIsMenuOpen(Integer isMenuOpen) { + this.isMenuOpen = isMenuOpen; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java new file mode 100644 index 0000000000..f2c0c4ca51 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/menu/WxMpSelfMenuInfo.java @@ -0,0 +1,288 @@ +package me.chanjar.weixin.mp.bean.menu; + +import com.google.gson.annotations.SerializedName; +import me.chanjar.weixin.common.util.ToStringUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + *
+ * Created by Binary Wang on 2016-11-25.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +public class WxMpSelfMenuInfo { + /** + * 菜单按钮 + */ + @SerializedName("button") + private List buttons; + + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + public static class WxMpSelfMenuButton { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + /** + *
+     * 菜单的类型,公众平台官网上能够设置的菜单类型有view(跳转网页)、text(返回文本,下同)、img、photo、video、voice。
+     * 使用API设置的则有8种,详见《自定义菜单创建接口》
+     * 
+ */ + @SerializedName("type") + private String type; + + /** + * 菜单名称 + */ + @SerializedName("name") + private String name; + + /** + *
+     * 对于不同的菜单类型,value的值意义不同。
+     * 官网上设置的自定义菜单:
+     *  
  • Text:保存文字到value; + *
  • Img、voice:保存mediaID到value; + *
  • Video:保存视频下载链接到value; + *
  • News:保存图文消息到news_info,同时保存mediaID到value; + *
  • View:保存链接到url。
  • + * + * 使用API设置的自定义菜单: + *
  • click、scancode_push、scancode_waitmsg、pic_sysphoto、pic_photo_or_album、 pic_weixin、location_select:保存值到key; + *
  • view:保存链接到url + *
  • + */ + @SerializedName("key") + private String key; + + /** + * @see #key + */ + @SerializedName("url") + private String url; + + /** + * @see #key + */ + @SerializedName("value") + private String value; + + /** + * 子菜单信息 + */ + @SerializedName("sub_button") + private SubButtons subButtons; + + public SubButtons getSubButtons() { + return subButtons; + } + + public void setSubButtons(SubButtons subButtons) { + this.subButtons = subButtons; + } + + public static class SubButtons { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + @SerializedName("list") + private List subButtons = new ArrayList<>(); + + public List getSubButtons() { + return subButtons; + } + + public void setSubButtons(List subButtons) { + this.subButtons = subButtons; + } + } + + /** + * 图文消息的信息 + */ + @SerializedName("news_info") + private NewsInfo newsInfo; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public NewsInfo getNewsInfo() { + return newsInfo; + } + + public void setNewsInfo(NewsInfo newsInfo) { + this.newsInfo = newsInfo; + } + + public static class NewsInfo { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + @SerializedName("list") + private List news = new ArrayList<>(); + + public List getNews() { + return news; + } + + public void setNews(List news) { + this.news = news; + } + + public static class NewsInButton { + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + + /** + * 图文消息的标题 + */ + @SerializedName("title") + private String title; + + /** + * 摘要 + */ + @SerializedName("digest") + private String digest; + + /** + * 作者 + */ + @SerializedName("author") + private String author; + + /** + * show_cover + * 是否显示封面,0为不显示,1为显示 + */ + @SerializedName("show_cover") + private Integer showCover; + + /** + * 封面图片的URL + */ + @SerializedName("cover_url") + private String coverUrl; + + /** + * 正文的URL + */ + @SerializedName("content_url") + private String contentUrl; + + /** + * 原文的URL,若置空则无查看原文入口 + */ + @SerializedName("source_url") + private String sourceUrl; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDigest() { + return digest; + } + + public void setDigest(String digest) { + this.digest = digest; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public Integer getShowCover() { + return showCover; + } + + public void setShowCover(Integer showCover) { + this.showCover = showCover; + } + + public String getCoverUrl() { + return coverUrl; + } + + public void setCoverUrl(String coverUrl) { + this.coverUrl = coverUrl; + } + + public String getContentUrl() { + return contentUrl; + } + + public void setContentUrl(String contentUrl) { + this.contentUrl = contentUrl; + } + + public String getSourceUrl() { + return sourceUrl; + } + + public void setSourceUrl(String sourceUrl) { + this.sourceUrl = sourceUrl; + } + } + } + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java index f31a0e4b97..a312b15d6f 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java @@ -1,18 +1,17 @@ package me.chanjar.weixin.mp.api.impl; -import me.chanjar.weixin.mp.api.WxMpService; -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; - import com.google.inject.Inject; - import me.chanjar.weixin.common.api.WxConsts; import me.chanjar.weixin.common.bean.menu.WxMenu; import me.chanjar.weixin.common.bean.menu.WxMenuButton; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.mp.api.ApiTestModule; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; /** * 测试菜单 @@ -28,11 +27,22 @@ public class WxMpMenuServiceImplTest { protected WxMpService wxService; @Test(dataProvider = "menu") - public void testCreateMenu(WxMenu wxMenu) throws WxErrorException { + public void testMenuCreate(WxMenu wxMenu) throws WxErrorException { System.out.println(wxMenu.toJson()); this.wxService.getMenuService().menuCreate(wxMenu); } + @Test + public void testMenuTryMatch() throws Exception { + //TODO + } + + @Test + public void testGetSelfMenuInfo() throws Exception { + WxMpGetSelfMenuInfoResult selfMenuInfo = this.wxService.getMenuService().getSelfMenuInfo(); + System.out.println(selfMenuInfo); + } + @Test public void testCreateMenu2() throws WxErrorException { String a = "{\n" @@ -77,15 +87,15 @@ public void testCreateMenu2() throws WxErrorException { this.wxService.getMenuService().menuCreate(menu); } - @Test(dependsOnMethods = { "testCreateMenu"}) - public void testGetMenu() throws WxErrorException { + @Test(dependsOnMethods = { "testMenuCreate"}) + public void testMenuGet() throws WxErrorException { WxMenu wxMenu = this.wxService.getMenuService().menuGet(); Assert.assertNotNull(wxMenu); System.out.println(wxMenu.toJson()); } - @Test(dependsOnMethods = { "testGetMenu"}) - public void testDeleteMenu() throws WxErrorException { + @Test(dependsOnMethods = { "testMenuGet"}) + public void testMenuDelete() throws WxErrorException { this.wxService.getMenuService().menuDelete(); } From 33de0d8321e2ef2f3e930805ac1e8a1305e31372 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Fri, 25 Nov 2016 16:46:18 +0800 Subject: [PATCH 33/41] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B4=A1=E7=8C=AE?= =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 221d77560a..c2baf0d622 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,11 @@ withinthefog@gmail.com https://github.com/withinthefog + + Keung + dongfuqiang1988@163.com + https://github.com/johnnytung + From 160f608455cac6ffd6ab8049c3b1d81127d53982 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Mon, 28 Nov 2016 17:39:15 +0800 Subject: [PATCH 34/41] test enhancement for template message --- .../weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java index ee975181fc..b37278e236 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpTemplateMsgServiceImplTest.java @@ -39,9 +39,10 @@ public void testSendTemplateMsg() throws WxErrorException { .toUser(configStorage.getOpenid()) .templateId(configStorage.getTemplateId()).build(); templateMessage.addWxMpTemplateData( - new WxMpTemplateData("first", dateFormat.format(new Date()))); + new WxMpTemplateData("first", dateFormat.format(new Date()),"#FF00FF")); templateMessage.addWxMpTemplateData( new WxMpTemplateData("remark", RandomStringUtils.randomAlphanumeric(100), "#FF00FF")); + templateMessage.setUrl(" "); String msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage); Assert.assertNotNull(msgId); System.out.println(msgId); From 5bb6a24590b94eb08d93d8ae37ace65a4934c4e6 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Mon, 28 Nov 2016 17:39:27 +0800 Subject: [PATCH 35/41] reformat code --- .../mp/bean/message/WxMpXmlMessage.java | 127 +++++++++--------- 1 file changed, 62 insertions(+), 65 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java index 8fa9a6a0fb..ab9bc1c321 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java @@ -297,6 +297,43 @@ public class WxMpXmlMessage implements Serializable { @XStreamAlias("DeviceStatus") private Integer deviceStatus; + public static WxMpXmlMessage fromXml(String xml) { + return XStreamTransformer.fromXml(WxMpXmlMessage.class, xml); + } + + public static WxMpXmlMessage fromXml(InputStream is) { + return XStreamTransformer.fromXml(WxMpXmlMessage.class, is); + } + + /** + * 从加密字符串转换 + * + * @param encryptedXml + * @param wxMpConfigStorage + * @param timestamp + * @param nonce + * @param msgSignature + */ + public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, + WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, + String msgSignature) { + WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); + String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, + encryptedXml); + return fromXml(plainText); + } + + public static WxMpXmlMessage fromEncryptedXml(InputStream is, + WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, + String msgSignature) { + try { + return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxMpConfigStorage, + timestamp, nonce, msgSignature); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + public Integer getOpType() { return opType; } @@ -611,43 +648,6 @@ public void setFromUser(String fromUser) { this.fromUser = fromUser; } - public static WxMpXmlMessage fromXml(String xml) { - return XStreamTransformer.fromXml(WxMpXmlMessage.class, xml); - } - - public static WxMpXmlMessage fromXml(InputStream is) { - return XStreamTransformer.fromXml(WxMpXmlMessage.class, is); - } - - /** - * 从加密字符串转换 - * - * @param encryptedXml - * @param wxMpConfigStorage - * @param timestamp - * @param nonce - * @param msgSignature - */ - public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, - WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, - String msgSignature) { - WxMpCryptUtil cryptUtil = new WxMpCryptUtil(wxMpConfigStorage); - String plainText = cryptUtil.decrypt(msgSignature, timestamp, nonce, - encryptedXml); - return fromXml(plainText); - } - - public static WxMpXmlMessage fromEncryptedXml(InputStream is, - WxMpConfigStorage wxMpConfigStorage, String timestamp, String nonce, - String msgSignature) { - try { - return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxMpConfigStorage, - timestamp, nonce, msgSignature); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - public String getStatus() { return this.status; } @@ -757,7 +757,7 @@ public WxMpXmlMessage.SendLocationInfo getSendLocationInfo() { } public void setSendLocationInfo( - WxMpXmlMessage.SendLocationInfo sendLocationInfo) { + WxMpXmlMessage.SendLocationInfo sendLocationInfo) { this.sendLocationInfo = sendLocationInfo; } @@ -793,20 +793,19 @@ public void setFromKfAccount(String fromKfAccount) { this.fromKfAccount = fromKfAccount; } + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + @XStreamAlias("HardWare") public static class HardWare { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - /** * 消息展示,目前支持myrank(排行榜) */ @XStreamAlias("MessageView") @XStreamConverter(value = XStreamCDataConverter.class) private String messageView; - /** * 消息点击动作,目前支持ranklist(点击跳转排行榜) */ @@ -814,6 +813,11 @@ public String toString() { @XStreamConverter(value = XStreamCDataConverter.class) private String messageAction; + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + public String getMessageView() { return messageView; } @@ -833,19 +837,18 @@ public void setMessageAction(String messageAction) { @XStreamAlias("ScanCodeInfo") public static class ScanCodeInfo { - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } - @XStreamAlias("ScanType") @XStreamConverter(value = XStreamCDataConverter.class) private String scanType; - @XStreamAlias("ScanResult") @XStreamConverter(value = XStreamCDataConverter.class) private String scanResult; + @Override + public String toString() { + return ToStringUtils.toSimpleString(this); + } + /** * 扫描类型,一般是qrcode */ @@ -873,17 +876,16 @@ public void setScanResult(String scanResult) { @XStreamAlias("SendPicsInfo") public static class SendPicsInfo { + @XStreamAlias("PicList") + protected final List picList = new ArrayList<>(); + @XStreamAlias("Count") + private Long count; + @Override public String toString() { return ToStringUtils.toSimpleString(this); } - @XStreamAlias("Count") - private Long count; - - @XStreamAlias("PicList") - protected final List picList = new ArrayList<>(); - public Long getCount() { return this.count; } @@ -898,15 +900,15 @@ public List getPicList() { @XStreamAlias("item") public static class Item { + @XStreamAlias("PicMd5Sum") + @XStreamConverter(value = XStreamCDataConverter.class) + private String picMd5Sum; + @Override public String toString() { return ToStringUtils.toSimpleString(this); } - @XStreamAlias("PicMd5Sum") - @XStreamConverter(value = XStreamCDataConverter.class) - private String picMd5Sum; - public String getPicMd5Sum() { return this.picMd5Sum; } @@ -985,9 +987,4 @@ public void setPoiname(String poiname) { this.poiname = poiname; } } - - @Override - public String toString() { - return ToStringUtils.toSimpleString(this); - } } From 9d06a696d0b9feca775f516e78044660bc276766 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Mon, 28 Nov 2016 17:39:42 +0800 Subject: [PATCH 36/41] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=BA=A2=E5=8C=85=E8=AE=B0=E5=BD=95=E7=9A=84=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=20for=20#73?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chanjar/weixin/mp/api/WxMpPayService.java | 15 + .../mp/api/impl/WxMpPayServiceImpl.java | 26 ++ .../pay/request/WxPayRedpackQueryRequest.java | 57 +++ .../pay/request/WxPaySendRedpackRequest.java | 10 +- .../pay/result/WxPayOrderQueryResult.java | 4 +- .../pay/result/WxPayRedpackQueryResult.java | 405 ++++++++++++++++++ .../pay/result/WxPayRefundQueryResult.java | 14 +- .../mp/api/impl/WxMpPayServiceImplTest.java | 27 +- 8 files changed, 539 insertions(+), 19 deletions(-) create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRedpackQueryRequest.java create mode 100644 weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRedpackQueryResult.java diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java index 6cde59507e..06077fc1bf 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java @@ -121,7 +121,9 @@ public interface WxMpPayService { *
        * 文档详见:
        * 发送普通红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3
    +   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack
        * 发送裂变红包 https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_5&index=4
    +   *  接口地址:https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack
        * 
    * * @param request 请求对象 @@ -129,6 +131,19 @@ public interface WxMpPayService { */ WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File keyFile) throws WxErrorException; + /** + *
    +   *   查询红包记录
    +   *   用于商户对已发放的红包进行查询红包的具体信息,可支持普通红包和裂变包。
    +   *   请求Url	https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo
    +   *   是否需要证书	是(证书及使用说明详见商户证书)
    +   *   请求方式	POST
    +   * 
    + * @param mchBillNo 商户发放红包的商户订单号,比如10000098201411111234567890 + * @param keyFile 证书文件对象 + */ + WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException; + /** *
        * 企业付款业务是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,针对部分有开发能力的商户,提供通过API完成企业付款的功能。
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java
    index e49891f188..2483b37930 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java
    +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java
    @@ -183,6 +183,32 @@ public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request, File
         return result;
       }
     
    +  @Override
    +  public WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) throws WxErrorException {
    +    XStream xstream = XStreamInitializer.getInstance();
    +    xstream.processAnnotations(WxPayRedpackQueryRequest.class);
    +    xstream.processAnnotations(WxPayRedpackQueryResult.class);
    +
    +    WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest();
    +    request.setMchBillNo(mchBillNo);
    +    request.setBillType("MCHT");
    +
    +    request.setAppid(this.wxMpService.getWxMpConfigStorage().getAppId());
    +    String mchId = this.wxMpService.getWxMpConfigStorage().getPartnerId();
    +    request.setMchId(mchId);
    +    request.setNonceStr(System.currentTimeMillis() + "");
    +
    +    String sign = this.createSign(BeanUtils.xmlBean2Map(request),
    +      this.wxMpService.getWxMpConfigStorage().getPartnerKey());
    +    request.setSign(sign);
    +
    +    String url = PAY_BASE_URL + "/mmpaymkttransfers/gethbinfo";
    +    String responseContent = this.executeRequestWithKeyFile(url, keyFile, xstream.toXML(request), mchId);
    +    WxPayRedpackQueryResult result = (WxPayRedpackQueryResult) xstream.fromXML(responseContent);
    +    this.checkResult(result);
    +    return result;
    +  }
    +
       /**
        * 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3)
        *
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRedpackQueryRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRedpackQueryRequest.java
    new file mode 100644
    index 0000000000..de7eee932d
    --- /dev/null
    +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRedpackQueryRequest.java
    @@ -0,0 +1,57 @@
    +package me.chanjar.weixin.mp.bean.pay.request;
    +
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +
    +/**
    + * 
    + *   注释中各行对应含义:
    + *   字段名
    + *   字段
    + *   必填
    + *   示例值
    + *   类型
    + *   说明
    + * Created by Binary Wang on 2016-11-28.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayRedpackQueryRequest extends WxPayBaseRequest { + /** + * 商户订单号 + * mch_billno + * 是 + * 10000098201411111234567890 + * String(28) + * 商户发放红包的商户订单号 + */ + @XStreamAlias("mch_billno") + private String mchBillNo; + + /** + * 订单类型 + * bill_type + * 是 + * MCHT + * String(32) + * MCHT:通过商户订单号获取红包信息。 + */ + @XStreamAlias("bill_type") + private String billType; + + public String getBillType() { + return billType; + } + + public void setBillType(String billType) { + this.billType = billType; + } + + public String getMchBillNo() { + return mchBillNo; + } + + public void setMchBillNo(String mchBillNo) { + this.mchBillNo = mchBillNo; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPaySendRedpackRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPaySendRedpackRequest.java index cad2cc5214..535b2941c5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPaySendRedpackRequest.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPaySendRedpackRequest.java @@ -14,7 +14,7 @@ public class WxPaySendRedpackRequest { * 商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 */ @XStreamAlias("mch_billno") - private String mchBillno; + private String mchBillNo; /** * send_name @@ -157,12 +157,12 @@ public class WxPaySendRedpackRequest { @XStreamAlias("consume_mch_id") private String consumeMchId; - public String getMchBillno() { - return this.mchBillno; + public String getMchBillNo() { + return mchBillNo; } - public void setMchBillno(String mchBillno) { - this.mchBillno = mchBillno; + public void setMchBillNo(String mchBillNo) { + this.mchBillNo = mchBillNo; } public String getSendName() { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResult.java index 17b3a412be..373497f37c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResult.java @@ -1,10 +1,10 @@ package me.chanjar.weixin.mp.bean.pay.result; -import java.util.List; - import com.google.common.collect.Lists; import com.thoughtworks.xstream.annotations.XStreamAlias; +import java.util.List; + /** *
      *  查询订单 返回结果对象
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRedpackQueryResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRedpackQueryResult.java
    new file mode 100644
    index 0000000000..faf2df6747
    --- /dev/null
    +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRedpackQueryResult.java
    @@ -0,0 +1,405 @@
    +package me.chanjar.weixin.mp.bean.pay.result;
    +
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +
    +/**
    + * 
    + *   注释中各行对应含义:
    + *   字段名
    + *   字段
    + *   必填
    + *   示例值
    + *   类型
    + *   说明
    + * Created by Binary Wang on 2016-11-28.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +public class WxPayRedpackQueryResult extends WxPayBaseResult { + + /** + *
    +   * 商户订单号
    +   * mch_billno
    +   * 是
    +   * 10000098201411111234567890
    +   * String(28)
    +   * 商户使用查询API填写的商户单号的原路返回
    +   * 
    + */ + @XStreamAlias("mch_billno") + private String mchBillNo; + + /** + *
    +   * 红包单号
    +   * detail_id
    +   * 是
    +   * 1000000000201503283103439304
    +   * String(32)
    +   * 使用API发放现金红包时返回的红包单号
    +   * 
    + */ + @XStreamAlias("detail_id") + private String detailId; + + /** + *
    +   * 红包状态
    +   * status
    +   * 是
    +   * RECEIVED
    +   * string(16)
    +   * SENDING:发放中,
    +     * SENT:已发放待领取,
    +     * FAILED:发放失败,
    +     * RECEIVED:已领取,
    +     * RFUND_ING:退款中,
    +     * REFUND:已退款
    +   * 
    + */ + @XStreamAlias("status") + private String status; + + /** + *
    +   * 发放类型
    +   * send_type
    +   * 是
    +   * API
    +   * String(32)
    +   *  API:通过API接口发放,
    +   *  UPLOAD:通过上传文件方式发放,
    +   *  ACTIVITY:通过活动方式发放
    +   * 
    + */ + @XStreamAlias("send_type") + private String sendType; + + /** + *
    +   * 红包类型
    +   * hb_type
    +   * 是
    +   * GROUP
    +   * String(32)
    +   *  GROUP:裂变红包,
    +   *  NORMAL:普通红包
    +   * 
    + */ + @XStreamAlias("hb_type") + private String hbType; + + /** + *
    +   * 红包个数
    +   * total_num
    +   * 是
    +   * 1
    +   * int
    +   * 红包个数
    +   * 
    + */ + @XStreamAlias("total_num") + private Integer totalNum; + + /** + *
    +   * 红包金额
    +   * total_amount
    +   * 是
    +   * 5000
    +   * int
    +   * 红包总金额(单位分)
    +   * 
    + */ + @XStreamAlias("total_amount") + private Integer totalAmount; + + /** + *
    +   * 失败原因
    +   * reason
    +   * 否
    +   * 余额不足
    +   * String(32)
    +   * 发送失败原因
    +   * 
    + */ + @XStreamAlias("reason") + private String reason; + + /** + *
    +   * 红包发送时间
    +   * send_time
    +   * 是
    +   * 2015-04-21 20:00:00
    +   * String(32)
    +   * 红包的发送时间
    +   * 
    + */ + @XStreamAlias("send_time") + private String sendTime; + + /** + *
    +   * 红包退款时间
    +   * refund_time
    +   * 否
    +   * 2015-04-21 23:03:00
    +   * String(32)
    +   * 红包的退款时间(如果其未领取的退款)
    +   * 
    + */ + @XStreamAlias("refund_time") + private String refundTime; + + /** + *
    +   * 红包退款金额
    +   * refund_amount
    +   * 否
    +   * 8000
    +   * Int
    +   * 红包退款金额
    +   * 
    + */ + @XStreamAlias("refund_amount") + private Integer refundAmount; + + /** + *
    +   * 祝福语
    +   * wishing
    +   * 否
    +   * 新年快乐
    +   * String(128)
    +   * 祝福语
    +   * 
    + */ + @XStreamAlias("wishing") + private String wishing; + + /** + *
    +   * 活动描述
    +   * remark
    +   * 否
    +   * 新年红包
    +   * String(256)
    +   * 活动描述,低版本微信可见
    +   * 
    + */ + @XStreamAlias("remark") + private String remark; + + /** + *
    +   * 活动名称
    +   * act_name
    +   * 否
    +   * 新年红包
    +   * String(32)
    +   * 发红包的活动名称
    +   * 
    + */ + @XStreamAlias("act_name") + private String actName; + + /** + *
    +   * 裂变红包领取列表
    +   * hblist
    +   * 否
    +   *
    +   *
    +   * 裂变红包的领取列表
    +   * 
    + */ + @XStreamAlias("hblist") + private String hblist; + + /** + *
    +   * 领取红包的Openid
    +   * openid
    +   * 是
    +   * ohO4GtzOAAYMp2yapORH3dQB3W18
    +   * String(32)
    +   * 领取红包的openid
    +   * 
    + */ + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 金额
    +   * amount
    +   * 是
    +   * 100
    +   * int
    +   * 领取金额
    +   * 
    + */ + @XStreamAlias("amount") + private Integer amount; + + /** + *
    +   * 接收时间
    +   * rcv_time
    +   * 是
    +   * 2015-04-21 20:00:00
    +   * String(32)
    +   * 领取红包的时间
    +   * 
    + */ + @XStreamAlias("rcv_time") + private String receiveTime; + + public String getMchBillNo() { + return mchBillNo; + } + + public void setMchBillNo(String mchBillNo) { + this.mchBillNo = mchBillNo; + } + + public String getDetailId() { + return detailId; + } + + public void setDetailId(String detailId) { + this.detailId = detailId; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getSendType() { + return sendType; + } + + public void setSendType(String sendType) { + this.sendType = sendType; + } + + public String getHbType() { + return hbType; + } + + public void setHbType(String hbType) { + this.hbType = hbType; + } + + public Integer getTotalNum() { + return totalNum; + } + + public void setTotalNum(Integer totalNum) { + this.totalNum = totalNum; + } + + public Integer getTotalAmount() { + return totalAmount; + } + + public void setTotalAmount(Integer totalAmount) { + this.totalAmount = totalAmount; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public String getSendTime() { + return sendTime; + } + + public void setSendTime(String sendTime) { + this.sendTime = sendTime; + } + + public String getRefundTime() { + return refundTime; + } + + public void setRefundTime(String refundTime) { + this.refundTime = refundTime; + } + + public Integer getRefundAmount() { + return refundAmount; + } + + public void setRefundAmount(Integer refundAmount) { + this.refundAmount = refundAmount; + } + + public String getWishing() { + return wishing; + } + + public void setWishing(String wishing) { + this.wishing = wishing; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getActName() { + return actName; + } + + public void setActName(String actName) { + this.actName = actName; + } + + public String getHblist() { + return hblist; + } + + public void setHblist(String hblist) { + this.hblist = hblist; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public String getReceiveTime() { + return receiveTime; + } + + public void setReceiveTime(String receiveTime) { + this.receiveTime = receiveTime; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java index efc21139b8..430288466a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java @@ -183,6 +183,13 @@ public void setRefundRecords(List refundRecords) { this.refundRecords = refundRecords; } + public void composeRefundRecords(String xmlString) { + if (this.refundCount != null && this.refundCount > 0) { + this.refundRecords = Lists.newArrayList(); + //TODO 暂时待实现 + } + } + public static class RefundRecord { /** *
    @@ -477,12 +484,5 @@ public RefundCoupon(String couponRefundBatchId, String couponRefundId, String co
         }
     
       }
    -
    -  public void composeRefundRecords(String xmlString){
    -    if(this.refundCount != null && this.refundCount > 0 ){
    -      this.refundRecords = Lists.newArrayList();
    -      //TODO 暂时待实现
    -    }
    -  }
     }
     
    diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java
    index 5f155862fd..7b2a9dea4e 100644
    --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java
    +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java
    @@ -9,10 +9,7 @@
     import me.chanjar.weixin.mp.bean.pay.request.WxPayRefundRequest;
     import me.chanjar.weixin.mp.bean.pay.request.WxPaySendRedpackRequest;
     import me.chanjar.weixin.mp.bean.pay.request.WxPayUnifiedOrderRequest;
    -import me.chanjar.weixin.mp.bean.pay.result.WxPayRefundQueryResult;
    -import me.chanjar.weixin.mp.bean.pay.result.WxPayRefundResult;
    -import me.chanjar.weixin.mp.bean.pay.result.WxPaySendRedpackResult;
    -import me.chanjar.weixin.mp.bean.pay.result.WxPayUnifiedOrderResult;
    +import me.chanjar.weixin.mp.bean.pay.result.*;
     import org.testng.annotations.Guice;
     import org.testng.annotations.Test;
     
    @@ -36,6 +33,9 @@ public void testGetPayInfo() throws Exception {
     
       }
     
    +  /**
    +   * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refund(WxPayRefundRequest, File)} .
    +   */
       @Test
       public void testRefund() throws Exception {
         WxPayRefundRequest request = new WxPayRefundRequest();
    @@ -48,6 +48,10 @@ public void testRefund() throws Exception {
         System.err.println(result);
       }
     
    +
    +  /**
    +   * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refundQuery(String, String, String, String)} .
    +   */
       @Test
       public void testRefundQuery() throws Exception {
         WxPayRefundQueryResult result = this.wxService.getPayService().refundQuery("1", "", "", "");
    @@ -67,12 +71,15 @@ public void testCheckJSSDKCallbackDataSignature() throws Exception {
     
       }
     
    +  /**
    +   * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#sendRedpack(WxPaySendRedpackRequest, File)} .
    +   */
       @Test
       public void testSendRedpack() throws Exception {
         WxPaySendRedpackRequest request = new WxPaySendRedpackRequest();
         request.setActName("abc");
         request.setClientIp("aaa");
    -    request.setMchBillno("aaaa");
    +    request.setMchBillNo("aaaa");
         request
           .setReOpenid(((WxXmlMpInMemoryConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid());
         File keyFile = new File("E:\\dlt.p12");
    @@ -80,6 +87,16 @@ public void testSendRedpack() throws Exception {
         System.err.println(redpackResult);
       }
     
    +  /**
    +   * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryRedpack(String, File)}.
    +   */
    +  @Test
    +  public void testQueryRedpack() throws Exception {
    +    File keyFile = new File("E:\\dlt.p12");
    +    WxPayRedpackQueryResult redpackResult = this.wxService.getPayService().queryRedpack("aaaa", keyFile);
    +    System.err.println(redpackResult);
    +  }
    +
       /**
        * Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#unifiedOrder(WxPayUnifiedOrderRequest)}.
        */
    
    From baf92b6535b05edcbc7e3c984575ce772cfa4f4a Mon Sep 17 00:00:00 2001
    From: Binary Wang 
    Date: Tue, 29 Nov 2016 14:02:03 +0800
    Subject: [PATCH 37/41] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    ---
     .../main/java/me/chanjar/weixin/mp/api/WxMpPayService.java   | 5 +++--
     1 file changed, 3 insertions(+), 2 deletions(-)
    
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java
    index 06077fc1bf..e1396afbb3 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java
    +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java
    @@ -59,7 +59,7 @@ public interface WxMpPayService {
        * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
        * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
        *
    -   * @param request 请求对象
    +   * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
        * @throws WxErrorException
        */
       WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException;
    @@ -68,7 +68,8 @@ public interface WxMpPayService {
        * 该接口调用“统一下单”接口,并拼装发起支付请求需要的参数
        * 详见http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
        *
    -   * @param request 请求对象
    +   * @param request 请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
    +   * @throws WxErrorException
        */
       Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException;
     
    
    From eb9c55d39afda60e549cc656e47e14b1dc5fd880 Mon Sep 17 00:00:00 2001
    From: Binary Wang 
    Date: Tue, 29 Nov 2016 15:43:16 +0800
    Subject: [PATCH 38/41] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=83=A8=E5=88=86?=
     =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E7=9A=84=E6=B3=A8=E9=87=8A=E4=B8=AD=E6=96=87?=
     =?UTF-8?q?=E6=A1=A3=E9=93=BE=E6=8E=A5=E5=9C=B0=E5=9D=80=E4=B8=BA=E6=96=B0?=
     =?UTF-8?q?=E7=9A=84=E6=96=87=E6=A1=A3=E5=9C=B0=E5=9D=80?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    ---
     .../me/chanjar/weixin/mp/api/WxMpService.java  | 18 +++++++++---------
     1 file changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
    index a74e718db5..0a2956fe32 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
    +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
    @@ -14,8 +14,8 @@ public interface WxMpService {
     
       /**
        * 
    -   * 验证推送过来的消息的正确性
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性
    +   * 验证消息的确来自微信服务器
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
        * 
    */ boolean checkSignature(String timestamp, String nonce, String signature); @@ -36,7 +36,7 @@ public interface WxMpService { * * 程序员在非必要情况下尽量不要主动调用此方法 * - * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token + * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183&token=&lang=zh_CN *
    * * @param forceRefresh 强制刷新 @@ -55,7 +55,7 @@ public interface WxMpService { * 获得jsapi_ticket * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干 * - * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95 + * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN *
    * * @param forceRefresh 强制刷新 @@ -66,7 +66,7 @@ public interface WxMpService { *
        * 创建调用jsapi时所需要的签名
        *
    -   * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
    +   * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
        * 
    */ WxJsapiSignature createJsapiSignature(String url) throws WxErrorException; @@ -75,7 +75,7 @@ public interface WxMpService { *
        * 上传群发用的图文消息,上传后才能群发图文消息
        *
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
        * 
    * * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) @@ -86,7 +86,7 @@ public interface WxMpService { /** *
        * 上传群发用的视频,上传后才能群发视频消息
    -   * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口
    +   * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN
        * 
    * * @see #massGroupMessageSend(me.chanjar.weixin.mp.bean.WxMpMassTagMessage) @@ -99,7 +99,7 @@ public interface WxMpService { * 分组群发消息 * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送 * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送 - * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口 + * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN *
    */ WxMpMassSendResult massGroupMessageSend(WxMpMassTagMessage message) throws WxErrorException; @@ -109,7 +109,7 @@ public interface WxMpService { * 按openId列表群发消息 * 如果发送图文消息,必须先使用 {@link #massNewsUpload(me.chanjar.weixin.mp.bean.WxMpMassNews)} 获得media_id,然后再发送 * 如果发送视频消息,必须先使用 {@link #massVideoUpload(me.chanjar.weixin.mp.bean.WxMpMassVideo)} 获得media_id,然后再发送 - * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=高级群发接口 + * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN *
    */ WxMpMassSendResult massOpenIdsMessageSend(WxMpMassOpenIdsMessage message) throws WxErrorException; From ef1558046fee4987561658cc895a9426c163fb83 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Tue, 29 Nov 2016 16:58:13 +0800 Subject: [PATCH 39/41] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E5=B0=8F=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../me/chanjar/weixin/mp/api/WxMpService.java | 24 +++++++++---------- .../mp/api/impl/WxMpPayServiceImpl.java | 2 +- .../weixin/mp/api/impl/WxMpServiceImpl.java | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index 0a2956fe32..4fa91badf3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -263,84 +263,84 @@ public interface WxMpService { WxMpConfigStorage getWxMpConfigStorage(); /** - * 返回客服接口方法实现类,以方便调用个其各种接口 + * 返回客服接口方法实现类,以方便调用其各个接口 * * @return WxMpKefuService */ WxMpKefuService getKefuService(); /** - * 返回素材相关接口方法的实现类对象,以方便调用个其各种接口 + * 返回素材相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpMaterialService */ WxMpMaterialService getMaterialService(); /** - * 返回菜单相关接口方法的实现类对象,以方便调用个其各种接口 + * 返回菜单相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpMenuService */ WxMpMenuService getMenuService(); /** - * 返回用户相关接口方法的实现类对象,以方便调用个其各种接口 + * 返回用户相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpUserService */ WxMpUserService getUserService(); /** - * 返回用户标签相关接口方法的实现类对象,以方便调用个其各种接口 + * 返回用户标签相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpUserTagService */ WxMpUserTagService getUserTagService(); /** - * 返回二维码相关接口方法的实现类对象,以方便调用个其各种接口 + * 返回二维码相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpQrcodeService */ WxMpQrcodeService getQrcodeService(); /** - * 返回卡券相关接口方法的实现类对象,以方便调用个其各种接口 + * 返回卡券相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpCardService */ WxMpCardService getCardService(); /** - * 返回微信支付相关接口方法的实现类对象,以方便调用个其各种接口 + * 返回微信支付相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpPayService */ WxMpPayService getPayService(); /** - * 返回数据分析统计相关接口方法的实现类对象,以方便调用个其各种接口 + * 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpDataCubeService */ WxMpDataCubeService getDataCubeService(); /** - * 返回用户黑名单管理相关接口方法的实现类对象,以方便调用其各种接口 + * 返回用户黑名单管理相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpUserBlacklistService */ WxMpUserBlacklistService getBlackListService(); /** - * 返回门店管理相关接口方法的实现类对象,以方便调用其各种接口 + * 返回门店管理相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpStoreService */ WxMpStoreService getStoreService(); /** - * 返回模板消息相关接口方法的实现类对象,以方便调用其各种接口 + * 返回模板消息相关接口方法的实现类对象,以方便调用其各个接口 * * @return WxMpTemplateMsgService */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java index 2483b37930..78e76e0f7c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java @@ -219,7 +219,7 @@ public WxPayRedpackQueryResult queryRedpack(String mchBillNo, File keyFile) thro private String createSign(Map packageParams, String signKey) { SortedMap sortedMap = new TreeMap<>(packageParams); - StringBuffer toSign = new StringBuffer(); + StringBuilder toSign = new StringBuilder(); for (String key : sortedMap.keySet()) { String value = packageParams.get(key); if (null != value && !"".equals(value) && !"sign".equals(key) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java index fa7dc7951f..46fa3157c9 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java @@ -89,7 +89,7 @@ public String getAccessToken() throws WxErrorException { @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { - Lock lock = configStorage.getAccessTokenLock(); + Lock lock = this.configStorage.getAccessTokenLock(); try { lock.lock(); @@ -136,7 +136,7 @@ public String getJsapiTicket() throws WxErrorException { @Override public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { - Lock lock = configStorage.getJsapiTicketLock(); + Lock lock = this.configStorage.getJsapiTicketLock(); try { lock.lock(); From 23f344b5565c6802934fce83b7975cbedf645ba4 Mon Sep 17 00:00:00 2001 From: dongfuqiang Date: Wed, 30 Nov 2016 12:04:42 +0800 Subject: [PATCH 40/41] =?UTF-8?q?=E4=BF=AE=E6=AD=A3WxMpXmlMessage=E4=B8=AD?= =?UTF-8?q?=EF=BC=8C=E8=AE=BE=E5=A4=87id=E7=9A=84XStreamAlias=E4=B8=BADevi?= =?UTF-8?q?ceID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java index 8fa9a6a0fb..5612baceda 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java @@ -277,7 +277,7 @@ public class WxMpXmlMessage implements Serializable { /** * 设备ID,第三方提供 */ - @XStreamAlias("DeviceId") + @XStreamAlias("DeviceID") @XStreamConverter(value = XStreamCDataConverter.class) private String deviceId; From 6ca78bb0ea0beb96c5f407b9a895a7024b24f471 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Wed, 30 Nov 2016 20:54:16 +0800 Subject: [PATCH 41/41] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E4=B8=BA2.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- weixin-java-common/pom.xml | 2 +- weixin-java-cp/pom.xml | 2 +- weixin-java-mp/pom.xml | 2 +- weixin-java-osgi/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index c2baf0d622..e168d12070 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.github.binarywang weixin-java-parent - 2.4.0-SNAPSHOT + 2.4.0 pom WeiXin Java Tools - Parent 微信公众号、企业号上级POM diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index 1f67e0dae1..329da5a297 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.4.0-SNAPSHOT + 2.4.0 weixin-java-common diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 6fa3bde27d..6b7c166535 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.4.0-SNAPSHOT + 2.4.0 weixin-java-cp diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 58b634ec10..ba79d0ca82 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.4.0-SNAPSHOT + 2.4.0 weixin-java-mp WeiXin Java Tools - MP diff --git a/weixin-java-osgi/pom.xml b/weixin-java-osgi/pom.xml index 3833499cf9..ca14e47640 100644 --- a/weixin-java-osgi/pom.xml +++ b/weixin-java-osgi/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.4.0-SNAPSHOT + 2.4.0 weixin-java-osgi