diff --git a/.gitignore b/.gitignore index f50d5eb350..d97b22b768 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ sonar-project.properties !/.mvn/wrapper/maven-wrapper.jar *.versionsBackup + +# STS +.factorypath diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..4ae62709d8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +Please refer to [【代码贡献指南】](contribution.md). diff --git a/contribution.md b/contribution.md index 093614d37c..abf1791fc9 100644 --- a/contribution.md +++ b/contribution.md @@ -8,10 +8,10 @@ - (***暂停此种方式,请使用第一种***)另外一种贡献代码的方式就是加入SDK Developers开发组,前提是对自己的代码足够自信就可以申请加入,加入之后可以随时直接提交代码,但要注意对所做的修改或新增的代码进行单元测试,保证提交代码没有明显问题。 ### PR方式贡献代码步骤 -* 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/weixin-java-tools`,然后 `clone` 到本地,并设置用户信息。 +* 在 GitHub 上 `fork` 到自己的仓库,如 `my_user/WxJava`,然后 `clone` 到本地,并设置用户信息。 ```bash -$ git clone git@github.com:my_user/weixin-java-tools.git +$ git clone git@github.com:my_user/WxJava.git $ cd weixin-java-tools $ git config user.name "yourname" $ git config user.email "your email" @@ -27,7 +27,7 @@ $ git push * 定期使用项目仓库内容更新自己仓库内容。 ```bash -$ git remote add upstream https://github.com/wechat-group/weixin-java-tools +$ git remote add upstream https://github.com/Wechat-Group/WxJava $ git fetch upstream $ git checkout develop $ git rebase upstream/develop diff --git a/demo.md b/demo.md index ec7f4136eb..02601c3e30 100644 --- a/demo.md +++ b/demo.md @@ -12,5 +12,5 @@ 1. 开放平台Demo:[GitHub](http://github.com/Wechat-Group/weixin-java-open-demo)、[码云](http://gitee.com/binary/weixin-java-open-demo) 1. 公众号Demo: - 使用Spring MVC实现的公众号Demo:[GitHub](http://github.com/binarywang/weixin-java-mp-demo-springmvc)、[码云](https://gitee.com/binary/weixin-java-mp-demo) - - 使用Spring Boot实现的公众号Demo:[GitHub](http://github.com/binarywang/weixin-java-mp-demo-springboot)、[码云](http://gitee.com/binary/weixin-java-mp-demo-springboot) - - 含公众号和部分微信支付代码的Demo:[GitHub](http://github.com/Wechat-Group/weixin-java-tools-springmvc)、[码云](http://gitee.com/binary/weixin-java-tools-springmvc) + - 使用Spring Boot实现的公众号Demo(支持多公众号):[GitHub](http://github.com/binarywang/weixin-java-mp-demo-springboot)、[码云](http://gitee.com/binary/weixin-java-mp-demo-springboot) + - 含公众号和部分微信支付代码的Demo:[GitHub](http://github.com/Wechat-Group/weixin-java-springmvc)、[码云](http://gitee.com/binary/weixin-java-tools-springmvc) diff --git a/pom.xml b/pom.xml index ba632060cf..8dc48d8325 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ xmlns="http://maven.apache.org/POM/4.0.0"> 4.0.0 com.github.binarywang - weixin-java-parent - 3.2.4.B + wx-java + 3.3.1.B pom - Weixin Java Tools - Parent + WxJava - Weixin/Wechat Java SDK 微信开发Java SDK - https://github.com/wechat-group/weixin-java-tools + https://github.com/Wechat-Group/WxJava @@ -85,6 +85,11 @@ 007gzs@gmail.com https://github.com/007gzs + + Howard Liu + liuxinghao1988@gmail.com + https://github.com/howardliu-cn + @@ -109,7 +114,7 @@ UTF-8 4.5 - 9.3.0.RC0 + 9.3.24.v20180605 diff --git a/qrcodes/alipay_qrcode.jpg b/qrcodes/alipay_qrcode.jpg index d71e76797f..6cccac8b11 100644 Binary files a/qrcodes/alipay_qrcode.jpg and b/qrcodes/alipay_qrcode.jpg differ diff --git a/qrcodes/wechat_qrcode.jpg b/qrcodes/wechat_qrcode.jpg deleted file mode 100644 index 7edb351058..0000000000 Binary files a/qrcodes/wechat_qrcode.jpg and /dev/null differ diff --git a/readme.md b/readme.md index d577d2af94..630900df9d 100644 --- a/readme.md +++ b/readme.md @@ -1,19 +1,29 @@ -## 全能微信Java开发工具包(SDK) -#### 支持包括微信支付、开放平台、公众号、企业微信/企业号、小程序等微信功能的后端开发。 ---------------------------------- -[![码云Gitee](https://gitee.com/binary/weixin-java-tools/badge/star.svg?theme=blue)](https://gitee.com/binary/weixin-java-tools) -[![Github](https://img.shields.io/badge/Github-10k+-green.svg)](https://github.com/Wechat-Group/weixin-java-tools) -[![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/weixin-java-parent.svg)](http://mvnrepository.com/artifact/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) -[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/idea/) +## WxJava(原weixin-java-tools)微信Java开发工具包(SDK) + +[![码云Gitee](https://gitee.com/binary/weixin-java-tools/badge/star.svg?theme=blue)](https://gitee.com/binary/weixin-java-tools) +[![Github](http://github-svg-buttons.herokuapp.com/star.svg?user=Wechat-Group&repo=WxJava&style=flat&background=1081C1)](https://github.com/Wechat-Group/WxJava) +[![GitHub release](https://img.shields.io/github/release/Wechat-Group/WxJava.svg)](https://github.com/Wechat-Group/WxJava/releases) +[![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](http://mvnrepository.com/artifact/com.github.binarywang/wx-java) +[![Build Status](https://travis-ci.org/Wechat-Group/WxJava.svg?branch=develop)](https://travis-ci.org/Wechat-Group/WxJava) +[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=WxJava-weixin-java-tools) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) --------------------------------- +#### 支持包括微信支付、开放平台、公众号(包括订阅号和服务号)、企业微信/企业号、小程序等微信功能的后端开发。 +--------------------------------- + ### 重要信息 +1. **2018-12-23 项目更名为WxJava,并发布 [【3.3.0正式版】](https://github.com/Wechat-Group/WxJava/releases)**! +1. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;另外微信开发新手请务必阅读[【开发文档 Wiki 首页】](https://github.com/Wechat-Group/WxJava/wiki)的常见问题部分,可以少走很多弯路,节省不少时间。 -1. **2018-09-24 发布 [【3.2.0正式版】](https://github.com/Wechat-Group/weixin-java-tools/releases)**! -1. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;另外微信开发新手请务必阅读[【开发文档 Wiki 首页】](https://github.com/Wechat-Group/weixin-java-tools/wiki)的常见问题部分,可以少走很多弯路,节省不少时间。 -1. [出现`Illegal key size`问题的解决办法](https://github.com/Wechat-Group/weixin-java-tools/wiki/%E5%8A%A0%E8%A7%A3%E5%AF%86%E7%9A%84%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E5%8A%9E%E6%B3%95) (太多人遇到此问题而不知所措,因此特意置顶,希望能引起新手的注意,其他常见问题请查阅 [【开发文档Wiki】](https://github.com/wechat-group/weixin-java-tools/wiki)首页) -1. **更多精彩内容,请扫描以下二维码关注新开通的微信公众号【WX开发助手】,或者加入企业微信,或者[访问此页面扫码](http://www.binarywang.com/article/cp_and_mp) ,也可以在微信中搜索 `weixin-java-tools`或 `WX开发助手` 关注公众号,公众号会及时通知SDK相关更新信息,并不定期分享微信开发相关技术知识。** +--------------------------------- +### 技术交流方式 +1. QQ群:(**注意:目前为付费群,刚入群会有5分钟禁言,稍等片刻即可正常发言**) [![加入QQ群](https://img.shields.io/badge/QQ群-343954419-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=731dc3e7ea31ebe25376cc1a791445468612c63fd0e9e05399b088ec81fd9e15) 或 [![加入QQ群](https://img.shields.io/badge/QQ群-343954419-blue.svg)](http://jq.qq.com/?_wv=1027&k=40lRskK),推荐点击按钮入群,当然如果无法成功操作,请自行搜索群号`343954419`进行添加;由于群容量有限,为了维持运营千人QQ群的所需支付的QQ年费会员费用,故开启付费入群模式,申请者只需支付少量金额即可加入,这样也可以保证只有真实交流需求的人进入,避免闲杂做广告人员的乱入;当然如果确实因为各种原因无法付费入群的,请联系群主说明原因即可入群; +1. 微信群: 请关注公众号后点击相关菜单入群; +1. 钉钉企业群:[请点击链接申请加入](https://h5.dingtalk.com/inviteColleague/index.html#/invite/9ed100cc4a/E1DF918E32E398D191E7FE61FE0552A6) 或者 [用手机钉钉APP扫码](qrcodes/ding_qrcode.jpg) 申请加入。 +1. 新手提问前,请先阅读[【提问的智慧】](http://www.binarywang.com/article/smart-questions),并确保已查阅过 [【开发文档Wiki】](https://github.com/wechat-group/WxJava/wiki); +1. 寻求帮助时需贴代码或大长串异常信息的,请利用http://paste.ubuntu.com +1. **另外,想要得到更多开发交流讨论方式,请扫描以下二维码,关注微信公众号【WxJava】,或者加入企业微信,当然也可以在微信中搜索 `weixin-java-tools`或 `WxJava` 关注公众号,公众号会及时通知SDK相关更新信息,并不定期分享微信Java开发相关技术知识。** ![微信公众号及企业微信](qrcodes/cp_mp_qrcodes.png) @@ -21,45 +31,20 @@ ### 其他说明 1. 本项目Fork自chanjarster/weixin-java-tools,但由于原项目已停止维护,故单独维护和发布,且发布到maven上的groupId也会不同,详细信息见下文。 1. [开源中国本项目的首页](https://www.oschina.net/p/weixin-java-tools-new),欢迎大家积极留言评分 🙂 -1. SDK详细开发文档请查阅 [【开发文档Wiki】](https://github.com/wechat-group/weixin-java-tools/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。 +1. SDK详细开发文档请查阅 [【开发文档Wiki】](https://github.com/wechat-group/WxJava/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。 1. **阅读源码的同学请注意,本SDK为简化代码编译时加入了lombok支持,如果不了解lombok的话,请先学习下相关知识,比如可以阅读[此文章](https://mp.weixin.qq.com/s/cUc-bUcprycADfNepnSwZQ);** -1. 如有新功能需求,发现BUG,或者由于微信官方接口调整导致的代码问题,可以直接在[【Issues】](https://github.com/Wechat-Group/weixin-java-tools/issues)页提出issue,便于讨论追踪问题; +1. 如有新功能需求,发现BUG,或者由于微信官方接口调整导致的代码问题,可以直接在[【Issues】](https://github.com/Wechat-Group/WxJava/issues)页提出issue,便于讨论追踪问题; 1. 如果想贡献代码,请阅读[【代码贡献指南】](contribution.md); 1. **如果本SDK对您有所帮助,欢迎对我们的努力进行肯定,可以扫描[【支付宝付款码】](qrcodes/alipay_qrcode.jpg)或者[【微信支付二维码】](qrcodes/wepay_qrcode.jpg)进行打赏,或者直接前往[【托管于码云的项目首页】](http://gitee.com/binary/weixin-java-tools),在评论区上方可以找到“捐助”按钮。非常感谢各位打赏和捐助的同学!** 1. 各个模块的Javadoc可以在线查看:[weixin-java-miniapp](http://binary.ac.cn/weixin-java-miniapp-javadoc/)、[weixin-java-pay](http://binary.ac.cn/weixin-java-pay-javadoc/)、[weixin-java-mp](http://binary.ac.cn/weixin-java-mp-javadoc/)、[weixin-java-common](http://binary.ac.cn/weixin-java-common-javadoc/)、[weixin-java-cp](http://binary.ac.cn/weixin-java-cp-javadoc/)、[weixin-java-open](http://binary.ac.cn/weixin-java-open-javadoc/) 1. 本SDK要求的最低JDK版本是1.7,还在使用JDK6的用户请参考[【此项目】]( https://github.com/binarywang/weixin-java-tools-for-jdk6) ,而其他更早的JDK版本则需要自己改造实现。 1. 本SDK项目在以下代码托管网站同步更新: * 码云:https://gitee.com/binary/weixin-java-tools -* GitHub:https://github.com/wechat-group/weixin-java-tools - ----------------------------------- -### 使用案例 -1. 开源项目:https://github.com/workcheng/weiya -1. 开源项目:https://github.com/jmdhappy/xxpay-master -1. 微信点餐系统开源项目:http://www.sqmax.top/springboot-project/ -1. 小程序:[喵星人贴吧助手(扫码关注)](http://p98ahz3tg.bkt.clouddn.com/miniappqrcode.jpg) -1. 平台:[小猪餐餐](http://www.xzcancan.com/) -1. 平台:[餐饮系统](http://canyin.daydao.com) -1. 公众号:中国电信上海网厅(sh_189) -1. 公众号:E答平台 -1. 公众号:[全民约跑健身便利店](http://www.oneminsport.com/) -1. 公众号:[洽洽食品](https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQFM8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAycDRPOXBZbVZib2UxMDAwME0wN2gAAgRIu4RbAwQAAAAA)、[洽洽合伙人](https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQFP8jwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyOUpJaU5VcXBlWTAxMDAwME0wN1oAAgSau4RbAwQAAAAA) -1. 公众号和小程序:民医台(可自行搜索) -1. 洽洽企业号 -1. 高善人力资源 -1. 其他更多案例请[【访问这里】](https://github.com/Wechat-Group/weixin-java-tools/issues/729),持续更新中。 - ---------------------------------- -### 技术交流方式 -1. QQ群:(**注意:目前为付费群,刚入群会有5分钟禁言,稍等片刻即可正常发言**) [![加入QQ群](https://img.shields.io/badge/QQ群-343954419-blue.svg)](http://shang.qq.com/wpa/qunwpa?idkey=731dc3e7ea31ebe25376cc1a791445468612c63fd0e9e05399b088ec81fd9e15) 或 [![加入QQ群](https://img.shields.io/badge/QQ群-343954419-blue.svg)](http://jq.qq.com/?_wv=1027&k=40lRskK),推荐点击按钮入群,当然如果无法成功操作,请自行搜索群号`343954419`进行添加;由于群容量有限,为了维持运营千人QQ群的所需支付的QQ年费会员费用,故开启付费入群模式,申请者只需支付少量金额即可加入,这样也可以保证只有真实交流需求的人进入,避免闲杂做广告人员的乱入;当然如果确实因为各种原因无法付费入群的,请联系群主说明原因即可入群; -1. 钉钉企业群:[请点击链接申请加入](https://h5.dingtalk.com/inviteColleague/index.html#/invite/9ed100cc4a/E1DF918E32E398D191E7FE61FE0552A6) 或者 [用手机钉钉APP扫码](qrcodes/ding_qrcode.jpg) 申请加入。 -1. 微信群: 因微信群已达到100人限制,故如有想加入微信群的,可以 [扫码加此微信](qrcodes/wechat_qrcode.jpg) 以便邀请加入(请务必注明“申请加入微信开发群”,否则不予理睬,谢谢配合~); -1. 新手提问前,请先阅读[【提问的智慧】](http://www.binarywang.com/article/smart-questions),并确保已查阅过 [【开发文档Wiki】](https://github.com/wechat-group/weixin-java-tools/wiki); -1. 寻求帮助时需贴代码或大长串异常信息的,请利用http://paste.ubuntu.com +* GitHub:https://github.com/wechat-group/WxJava --------------------------------- -### Maven引用 -注意:最新版本(包括测试版)为 [![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/weixin-java-parent.svg)](http://mvnrepository.com/artifact/com.github.binarywang/weixin-java-parent),以下为最新正式版。 +### SDK使用方式 +注意:最新版本(包括测试版)为 [![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](http://mvnrepository.com/artifact/com.github.binarywang/wx-java),以下为最新正式版。 ```xml @@ -72,16 +57,36 @@ - 微信小程序:`weixin-java-miniapp` - 微信支付:`weixin-java-pay` - 微信开放平台:`weixin-java-open` - - 公众号:`weixin-java-mp` + - 公众号(包括订阅号和服务号):`weixin-java-mp` - 企业号/企业微信:`weixin-java-cp` --------------------------------- ### 版本说明 1. 本项目定为大约每两个月发布一次正式版,版本号格式为X.X.0(如2.1.0,2.2.0等),遇到重大问题需修复会及时提交新版本,欢迎大家随时提交Pull Request; 1. BUG修复和新特性一般会先发布成小版本作为临时测试版本(如3.1.8.B,即尾号不为0,并添加B,以区别于正式版); -1. 目前最新版本号为 [![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/weixin-java-parent.svg)](http://mvnrepository.com/artifact/com.github.binarywang/weixin-java-parent) ,也可以通过访问链接 [【微信支付】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-pay%22) 、[【微信小程序】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-miniapp%22) 、[【公众号】](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)、[【开放平台】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-open%22) +1. 目前最新版本号为 [![Maven Central](https://img.shields.io/maven-central/v/com.github.binarywang/wx-java.svg)](http://mvnrepository.com/artifact/com.github.binarywang/wx-java) ,也可以通过访问链接 [【微信支付】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-pay%22) 、[【微信小程序】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-miniapp%22) 、[【公众号】](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)、[【开放平台】](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.github.binarywang%22%20AND%20a%3A%22weixin-java-open%22) 分别查看所有最新的版本。 +---------------------------------- +### 使用案例 +1. 开源项目:https://github.com/workcheng/weiya +1. 开源项目:https://github.com/jmdhappy/xxpay-master +1. 微信点餐系统开源项目:http://www.sqmax.top/springboot-project/ +1. 小程序:[喵星人贴吧助手(扫码关注)](http://p98ahz3tg.bkt.clouddn.com/miniappqrcode.jpg) +1. 小程序:树懒揽书+ +1. 小程序:广廉快线,鹏城巴士等 +1. 小程序:360考试宝典 +1. 平台:[小猪餐餐](http://www.xzcancan.com/) +1. 平台:[餐饮系统](http://canyin.daydao.com) +1. 公众号:中国电信上海网厅(sh_189) +1. 公众号:E答平台 +1. 公众号:[全民约跑健身便利店](http://www.oneminsport.com/) +1. 公众号:[洽洽食品](https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQFM8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAycDRPOXBZbVZib2UxMDAwME0wN2gAAgRIu4RbAwQAAAAA)、[洽洽合伙人](https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQFP8jwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyOUpJaU5VcXBlWTAxMDAwME0wN1oAAgSau4RbAwQAAAAA) +1. 公众号和小程序:民医台(可自行搜索) +1. 洽洽企业号 +1. 高善人力资源 +1. 其他更多案例请[【访问这里】](https://github.com/Wechat-Group/weixin-java-tools/issues/729),持续更新中。 + ---------------------------------- ### 贡献者列表 特别感谢以下参与贡献的所有同学! @@ -92,34 +97,35 @@ 1. [aimilin6688 (Jonk)](http://github.com/aimilin6688) 1. [kakotor](http://github.com/kakotor) 1. [kareanyi (MillerLin)](http://github.com/kareanyi) -1. [rememberber (周波)](http://github.com/rememberber) 1. [tianmu](http://github.com/tianmu) +1. [rememberber (周波)](http://github.com/rememberber) 1. [charmingoh (Charming)](http://github.com/charmingoh) +1. [gaigeshen (gaigeshen)](http://github.com/gaigeshen) 1. [ukid](http://github.com/ukid) 1. [forfuns (爱因斯唐)](http://github.com/forfuns) +1. [yuanqixun (yuanqixun)](http://github.com/yuanqixun) 1. [zxkane (Meng Xin Zhu)](http://github.com/zxkane) 1. [crskyp (我是木予)](http://github.com/crskyp) -1. [yuanqixun (yuanqixun)](http://github.com/yuanqixun) -1. [gaigeshen (gaigeshen)](http://github.com/gaigeshen) 1. [dylanleung (dylanleung)](http://github.com/dylanleung) 1. [huansinho](http://github.com/huansinho) 1. [codepiano (codepiano)](http://github.com/codepiano) 1. [stvliu (Steven Liu)](http://github.com/stvliu) -1. [ajffdnt](http://github.com/ajffdnt) -1. [fxdfxq (fxdfxq)](http://github.com/fxdfxq) 1. [unlimitedsola (Sola)](http://github.com/unlimitedsola) +1. [fxdfxq (fxdfxq)](http://github.com/fxdfxq) +1. [withinthefog (withinthefog)](http://github.com/withinthefog) 1. [DDLeEHi](http://github.com/DDLeEHi) -1. [Hyseen](http://github.com/Hyseen) 1. [nickwongwong (Nick Wong)](http://github.com/nickwongwong) 1. [jink2005 (Jink2005)](http://github.com/jink2005) -1. [withinthefog (withinthefog)](http://github.com/withinthefog) +1. [ajffdnt](http://github.com/ajffdnt) 1. [iwareserictsai (Eric.Tsai)](http://github.com/iwareserictsai) -1. [lwxian](http://github.com/lwxian) -1. [xusheng1987 (flying)](http://github.com/xusheng1987) -1. [ZhaoxiongTan (xiong)](http://github.com/ZhaoxiongTan) 1. [SimonDolph (Simon Dolph)](http://github.com/SimonDolph) -1. [axeon](http://github.com/axeon) -1. [TonyLuo (Tony)](http://github.com/TonyLuo) +1. [ZhaoxiongTan (xiong)](http://github.com/ZhaoxiongTan) +1. [howardliu-cn (看山)](http://github.com/howardliu-cn) +1. [SunshineTech (SunshineTech Zhang)](http://github.com/SunshineTech) +1. [xusheng1987 (flying)](http://github.com/xusheng1987) +1. [lwxian](http://github.com/lwxian) +1. [aliangsoft (阿亮软件)](http://github.com/aliangsoft) +1. [zhfish (zhfish)](http://github.com/zhfish) 1. [dwandw (dwandw)](http://github.com/dwandw) 1. [alanchenup (alanchen)](http://github.com/alanchenup) 1. [zexpp5 (Lance7in)](http://github.com/zexpp5) @@ -132,8 +138,8 @@ 1. [mog0202 (蘑菇0202)](http://github.com/mog0202) 1. [bobbyguo (bobby_guo)](http://github.com/bobbyguo) 1. [huotaihe (白马度和)](http://github.com/huotaihe) +1. [axeon](http://github.com/axeon) 1. [dxwts (xuewu)](http://github.com/dxwts) -1. [aliangsoft (阿亮软件)](http://github.com/aliangsoft) 1. [Mkluas (Mklaus)](http://github.com/Mkluas) 1. [CodeIdeal (康阳)](http://github.com/CodeIdeal) 1. [leeis (IOMan)](http://github.com/leeis) @@ -145,4 +151,6 @@ 1. [borisbao (Boris)](http://github.com/borisbao) 1. [qsjia (QSJia)](http://github.com/qsjia) 1. [webcreazy (webcreazy)](http://github.com/webcreazy) +1. [TonyLuo (Tony)](http://github.com/TonyLuo) 1. [cwivan (鱼丸Cwivan)](http://github.com/cwivan) +1. [biggates (Xiaoyu Guo)](http://github.com/biggates) diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index 6ebfe97def..ec968d3afa 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -6,12 +6,12 @@ 4.0.0 com.github.binarywang - weixin-java-parent - 3.2.4.B + wx-java + 3.3.1.B weixin-java-common - Weixin Java Tools - Common + WxJava - Common 微信开发Java SDK公共模块 @@ -117,7 +117,7 @@ org.dom4j dom4j - 2.0.0 + 2.1.1 diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java index c0f57c83c4..d7ac36c7c6 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxMessageInMemoryDuplicateChecker.java @@ -76,7 +76,7 @@ public void run() { } } } catch (InterruptedException e) { - e.printStackTrace(); + Thread.currentThread().interrupt(); } } }); diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java index bf2e735872..2472cb44b8 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/session/StandardSessionManager.java @@ -196,6 +196,7 @@ public void run() { Thread.sleep(StandardSessionManager.this.backgroundProcessorDelay * 1000L); backgroundProcess(); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); StandardSessionManager.this.log.error("SessionManagerImpl.backgroundProcess error", e); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java index 73aa6d7a1a..cded0c9846 100755 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/WxCryptUtil.java @@ -37,7 +37,9 @@ public class WxCryptUtil { @Override protected DocumentBuilder initialValue() { try { - return DocumentBuilderFactory.newInstance().newDocumentBuilder(); + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setExpandEntityReferences(false); + return factory.newDocumentBuilder(); } catch (ParserConfigurationException exc) { throw new IllegalArgumentException(exc); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/BaseMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/BaseMediaDownloadRequestExecutor.java index 990e162008..5e344acf9a 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/BaseMediaDownloadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/BaseMediaDownloadRequestExecutor.java @@ -1,13 +1,16 @@ package me.chanjar.weixin.common.util.http; +import java.io.File; +import java.io.IOException; + +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.apache.ApacheMediaDownloadRequestExecutor; import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaDownloadRequestExecutor; import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaDownloadRequestExecutor; -import java.io.File; - /** - * 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File + * 下载媒体文件请求执行器. + * 请求的参数是String, 返回的结果是File * 视频文件不支持下载 * * @author Daniel Qian @@ -21,6 +24,11 @@ public BaseMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpD this.tmpDirFile = tmpDirFile; } + @Override + public void execute(String uri, String data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp, File tmpDirFile) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java index ceaeffbd1e..0d68518849 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/HttpResponseProxy.java @@ -58,7 +58,7 @@ public String getFileName() throws WxErrorException { private String getFileName(CloseableHttpResponse response) throws WxErrorException { Header[] contentDispositionHeader = response.getHeaders("Content-disposition"); if (contentDispositionHeader == null || contentDispositionHeader.length == 0) { - throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").build()); + throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); } return this.extractFileNameFromContentString(contentDispositionHeader[0].getValue()); @@ -76,7 +76,7 @@ private String getFileName(Response response) throws WxErrorException { private String extractFileNameFromContentString(String content) throws WxErrorException { if (content == null || content.length() == 0) { - throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").build()); + throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); } Matcher m = PATTERN.matcher(content); @@ -84,7 +84,7 @@ private String extractFileNameFromContentString(String content) throws WxErrorEx return m.group(1); } - throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").build()); + throw new WxErrorException(WxError.builder().errorMsg("无法获取到文件名").errorCode(99999).build()); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java index 6b883ce7cd..10ef36025d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/MediaUploadRequestExecutor.java @@ -1,14 +1,17 @@ package me.chanjar.weixin.common.util.http; +import java.io.File; +import java.io.IOException; + import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.apache.ApacheMediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.jodd.JoddHttpMediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.okhttp.OkHttpMediaUploadRequestExecutor; -import java.io.File; - /** - * 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String + * 上传媒体文件请求执行器. + * 请求的参数是File, 返回的结果是String * * @author Daniel Qian */ @@ -19,6 +22,11 @@ public MediaUploadRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, File data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java index a5a9423fd0..feef02de6d 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestExecutor.java @@ -1,11 +1,11 @@ package me.chanjar.weixin.common.util.http; -import me.chanjar.weixin.common.error.WxErrorException; - import java.io.IOException; +import me.chanjar.weixin.common.error.WxErrorException; + /** - * http请求执行器 + * http请求执行器. * * @param 返回值类型 * @param 请求参数类型 @@ -14,8 +14,25 @@ public interface RequestExecutor { /** + * 执行http请求. + * * @param uri uri * @param data 数据 + * @return 响应结果 + * @throws WxErrorException 自定义异常 + * @throws IOException io异常 */ T execute(String uri, E data) throws WxErrorException, IOException; + + + /** + * 执行http请求. + * + * @param uri uri + * @param data 数据 + * @param handler http响应处理器 + * @throws WxErrorException 自定义异常 + * @throws IOException io异常 + */ + void execute(String uri, E data, ResponseHandler handler) throws WxErrorException, IOException; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java index efd9f99b9d..b7bc850f8f 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/RequestHttp.java @@ -2,21 +2,30 @@ /** * Created by ecoolper on 2017/4/22. + * + * @author ecoolper */ public interface RequestHttp { /** - * 返回httpClient + * 返回httpClient. * + * @return 返回httpClient */ H getRequestHttpClient(); /** - * 返回httpProxy + * 返回httpProxy. * + * @return 返回httpProxy */ P getRequestHttpProxy(); + /** + * 返回HttpType. + * + * @return HttpType + */ HttpType getRequestType(); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ResponseHandler.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ResponseHandler.java new file mode 100644 index 0000000000..1571764284 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/ResponseHandler.java @@ -0,0 +1,19 @@ +package me.chanjar.weixin.common.util.http; + +/** + *
+ * http请求响应回调处理接口.
+ * Created by Binary Wang on 2018/12/8.
+ * 
+ * + * @param 返回值类型 + * @author Binary Wang + */ +public interface ResponseHandler { + /** + * 响应结果处理. + * + * @param t 要处理的对象 + */ + void handle(T t); +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java index e72acc9084..6d8b52a8eb 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimpleGetRequestExecutor.java @@ -1,11 +1,15 @@ package me.chanjar.weixin.common.util.http; +import java.io.IOException; + +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientSimpleGetRequestExecutor; import me.chanjar.weixin.common.util.http.jodd.JoddHttpSimpleGetRequestExecutor; import me.chanjar.weixin.common.util.http.okhttp.OkHttpSimpleGetRequestExecutor; /** - * 简单的GET请求执行器,请求的参数是String, 返回的结果也是String + * 简单的GET请求执行器. + * 请求的参数是String, 返回的结果也是String * * @author Daniel Qian */ @@ -16,6 +20,11 @@ public SimpleGetRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, String data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java index 2932f24728..97a368eac7 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/SimplePostRequestExecutor.java @@ -1,5 +1,8 @@ package me.chanjar.weixin.common.util.http; +import java.io.IOException; + +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.apache.ApacheSimplePostRequestExecutor; import me.chanjar.weixin.common.util.http.jodd.JoddHttpSimplePostRequestExecutor; import me.chanjar.weixin.common.util.http.okhttp.OkHttpSimplePostRequestExecutor; @@ -17,6 +20,11 @@ public SimplePostRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, String data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientSimpleGetRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientSimpleGetRequestExecutor.java index 658ea4afd0..3951758d62 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientSimpleGetRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpClientSimpleGetRequestExecutor.java @@ -1,16 +1,17 @@ package me.chanjar.weixin.common.util.http.apache; -import me.chanjar.weixin.common.error.WxError; -import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import java.io.IOException; + import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; -import java.io.IOException; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; /** * Created by ecoolper on 2017/5/4. diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java index 033add1aa0..fe5472f3c0 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheHttpDnsClientBuilder.java @@ -1,5 +1,9 @@ package me.chanjar.weixin.common.util.http.apache; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHost; import org.apache.http.annotation.NotThreadSafe; @@ -25,10 +29,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - /** * httpclient 连接管理器 自带DNS解析. *

大部分代码拷贝自:DefaultApacheHttpClientBuilder

@@ -292,6 +292,7 @@ public void run() { } } } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java index 2ce8750822..779a844aa9 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/ApacheMediaDownloadRequestExecutor.java @@ -25,7 +25,6 @@ * Created by ecoolper on 2017/5/5. */ public class ApacheMediaDownloadRequestExecutor extends BaseMediaDownloadRequestExecutor { - public ApacheMediaDownloadRequestExecutor(RequestHttp requestHttp, File tmpDirFile) { super(requestHttp, tmpDirFile); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/InputStreamResponseHandler.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/InputStreamResponseHandler.java index d4cea91da1..5c72744cb0 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/InputStreamResponseHandler.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/InputStreamResponseHandler.java @@ -1,5 +1,8 @@ package me.chanjar.weixin.common.util.http.apache; +import java.io.IOException; +import java.io.InputStream; + import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; @@ -7,18 +10,20 @@ import org.apache.http.client.ResponseHandler; import org.apache.http.util.EntityUtils; -import java.io.IOException; -import java.io.InputStream; - +/** + * 输入流响应处理器. + * + * @author Daniel Qian + */ public class InputStreamResponseHandler implements ResponseHandler { - public static final ResponseHandler INSTANCE = new InputStreamResponseHandler(); + private static final int STATUS_CODE_300 = 300; @Override public InputStream handleResponse(final HttpResponse response) throws IOException { final StatusLine statusLine = response.getStatusLine(); final HttpEntity entity = response.getEntity(); - if (statusLine.getStatusCode() >= 300) { + if (statusLine.getStatusCode() >= STATUS_CODE_300) { EntityUtils.consume(entity); throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } diff --git a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java index b13fee5f5f..06f9d7ba28 100755 --- a/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java +++ b/weixin-java-common/src/test/java/me/chanjar/weixin/common/util/crypto/WxCryptUtilTest.java @@ -1,5 +1,11 @@ package me.chanjar.weixin.common.util.crypto; +import java.io.IOException; +import java.io.StringReader; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + import org.testng.annotations.*; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -7,12 +13,6 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.StringReader; - import static org.testng.Assert.*; @Test @@ -39,6 +39,7 @@ public void testNormal() throws ParserConfigurationException, SAXException, IOEx System.out.println(encryptedXml); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setExpandEntityReferences(false); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document = documentBuilder.parse(new InputSource(new StringReader(encryptedXml))); @@ -81,6 +82,7 @@ public void testValidateSignatureError() throws ParserConfigurationException, SA WxCryptUtil pc = new WxCryptUtil(this.token, this.encodingAesKey, this.appId); String afterEncrpt = pc.encrypt(this.replyMsg); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setExpandEntityReferences(false); DocumentBuilder db = dbf.newDocumentBuilder(); StringReader sr = new StringReader(afterEncrpt); InputSource is = new InputSource(sr); diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 476bf2de68..e7c02c7c14 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -6,13 +6,13 @@ 4.0.0 com.github.binarywang - weixin-java-parent - 3.2.4.B + wx-java + 3.3.1.B weixin-java-cp - Weixin Java Tools - CP - 微信企业号Java SDK + WxJava - CP + 微信企业号/企业微信 Java SDK diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java new file mode 100644 index 0000000000..2c24701dda --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpChatService.java @@ -0,0 +1,48 @@ +package me.chanjar.weixin.cp.api; + +import java.util.List; + +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.cp.bean.WxCpChat; + +/** + * 群聊服务 + * + * @author gaigeshen + */ +public interface WxCpChatService { + + /** + * 创建群聊会话,注意:刚创建的群,如果没有下发消息,在企业微信不会出现该群。 + * + * @param name 群聊名,最多50个utf8字符,超过将截断 + * @param owner 指定群主的id。如果不指定,系统会随机从userlist中选一人作为群主 + * @param users 群成员id列表。至少2人,至多500人 + * @param chatId 群聊的唯一标志,不能与已有的群重复;字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z。如果不填,系统会随机生成群id + * @return 创建群聊会话的结果,群聊的唯一标志 + * @throws WxErrorException 发生异常 + */ + String chatCreate(String name, String owner, List users, String chatId) throws WxErrorException; + + /** + * 修改群聊会话 + * + * @param chatId 群聊id + * @param name 新的群聊名。若不需更新,请忽略此参数(null or empty)。最多50个utf8字符,超过将截断 + * @param owner 新群主的id。若不需更新,请忽略此参数(null or empty) + * @param usersToAdd 添加成员的id列表,若不需要更新,则传递空对象或者空集合 + * @param usersToDelete 踢出成员的id列表,若不需要更新,则传递空对象或者空集合 + * @throws WxErrorException 发生异常 + */ + void chatUpdate(String chatId, String name, String owner, List usersToAdd, List usersToDelete) throws WxErrorException; + + /** + * 获取群聊会话 + * + * @param chatId 群聊编号 + * @return 群聊会话 + * @throws WxErrorException 发生异常 + */ + WxCpChat chatGet(String chatId) throws WxErrorException; + +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java index 8a8ca054d3..82e571ac5b 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpDepartmentService.java @@ -26,7 +26,7 @@ public interface WxCpDepartmentService { * @return 部门id * @throws WxErrorException 异常 */ - Integer create(WxCpDepart depart) throws WxErrorException; + Long create(WxCpDepart depart) throws WxErrorException; /** *
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
index 19e6de4492..31ce6b5991 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
@@ -19,6 +19,7 @@ public interface WxCpMediaService {
   String MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get";
   String MEDIA_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=";
   String IMG_UPLOAD_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/uploadimg";
+  String JSSDK_MEDIA_GET_URL = "https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk";
 
   /**
    * 
@@ -59,6 +60,21 @@ WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputS
    */
   File download(String mediaId) throws WxErrorException;
 
+  /**
+   * 
+   * 获取高清语音素材.
+   * 可以使用本接口获取从JSSDK的uploadVoice接口上传的临时语音素材,格式为speex,16K采样率。该音频比上文的临时素材获取接口(格式为amr,8K采样率)更加清晰,适合用作语音识别等对音质要求较高的业务。
+   * 请求方式:GET(HTTPS)
+   * 请求地址:https://qyapi.weixin.qq.com/cgi-bin/media/get/jssdk?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
+   * 仅企业微信2.4及以上版本支持。
+   * 文档地址:https://work.weixin.qq.com/api/doc#90000/90135/90255
+   * 
+ * + * @param mediaId 媒体id + * @return 保存到本地的临时文件 + */ + File getJssdkFile(String mediaId) throws WxErrorException; + /** *
    * 上传图片.
@@ -69,7 +85,7 @@ WxMediaUploadResult upload(String mediaType, String fileType, InputStream inputS
    * 
* * @param file 上传的文件对象 - * @return 返回图片url + * @return 返回图片url */ String uploadImg(File file) throws WxErrorException; } 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 3e33c4eb55..afda991b55 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 @@ -7,7 +7,8 @@ import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.cp.bean.*; +import me.chanjar.weixin.cp.bean.WxCpMessage; +import me.chanjar.weixin.cp.bean.WxCpMessageSendResult; import me.chanjar.weixin.cp.config.WxCpConfigStorage; /** @@ -241,13 +242,20 @@ public interface WxCpService { * 获取用户相关接口的服务类对象 */ WxCpUserService getUserService(); + + /** + * 获取群聊服务 + * + * @return 群聊服务 + */ + WxCpChatService getChatService(); WxCpAgentService getAgentService(); /** * http请求对象 */ - RequestHttp getRequestHttp(); + RequestHttp getRequestHttp(); void setUserService(WxCpUserService userService); diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java index 9f9317256e..310a1efa5e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpUserService.java @@ -38,7 +38,7 @@ public interface WxCpUserService { * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 */ - List listByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; + List listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException; /** *
@@ -51,7 +51,7 @@ public interface WxCpUserService {
    * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员
    * @param status     非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加
    */
-  List listSimpleByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException;
+  List listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException;
 
   /**
    * 新建用户.
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java
index e4914134c1..3a977b81d8 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentServiceImpl.java
@@ -1,9 +1,11 @@
 package me.chanjar.weixin.cp.api.impl;
 
-import com.google.gson.Gson;
+import java.util.List;
+
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import com.google.gson.reflect.TypeToken;
+
 import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.cp.api.WxCpAgentService;
@@ -11,8 +13,6 @@
 import me.chanjar.weixin.cp.bean.WxCpAgent;
 import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
 
-import java.util.List;
-
 
 /**
  * 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java
new file mode 100644
index 0000000000..b8e894fb9b
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImpl.java
@@ -0,0 +1,83 @@
+package me.chanjar.weixin.cp.api.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.google.gson.JsonParser;
+
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+import me.chanjar.weixin.cp.api.WxCpChatService;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.WxCpChat;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 群聊服务实现
+ *
+ * @author gaigeshen
+ */
+public class WxCpChatServiceImpl implements WxCpChatService { 
+
+  private final WxCpService internalService;
+  
+  /**
+   * 创建群聊服务实现的实例
+   * 
+   * @param internalService 企业微信的服务
+   */
+  public WxCpChatServiceImpl(WxCpService internalService) {
+    this.internalService = internalService;
+  }
+
+  @Override
+  public String chatCreate(String name, String owner, List users, String chatId) throws WxErrorException {
+    Map data = new HashMap<>(4);
+    if (StringUtils.isNotBlank(name)) {
+      data.put("name", name);
+    }
+    if (StringUtils.isNotBlank(owner)) {
+      data.put("owner", owner);
+    }
+    if (users != null) {
+      data.put("userlist", users);
+    }
+    if (StringUtils.isNotBlank(chatId)) {
+      data.put("chatid", chatId);
+    }
+    String result = internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/create", WxGsonBuilder.create().toJson(data));
+    return new JsonParser().parse(result).getAsJsonObject().get("chatid").getAsString();
+  }
+
+  @Override
+  public void chatUpdate(String chatId, String name, String owner, List usersToAdd, List usersToDelete) throws WxErrorException {
+    Map data = new HashMap<>(5);
+    if (StringUtils.isNotBlank(chatId)) {
+      data.put("chatid", chatId);
+    }
+    if (StringUtils.isNotBlank(name)) {
+      data.put("name", name);
+    }
+    if (StringUtils.isNotBlank(owner)) {
+      data.put("owner", owner);
+    }
+    if (usersToAdd != null && !usersToAdd.isEmpty()) {
+      data.put("add_user_list", usersToAdd);
+    }
+    if (usersToDelete != null && !usersToDelete.isEmpty()) {
+      data.put("del_user_list", usersToDelete);
+    }
+    internalService.post("https://qyapi.weixin.qq.com/cgi-bin/appchat/update", WxGsonBuilder.create().toJson(data));
+  }
+
+  @Override
+  public WxCpChat chatGet(String chatId) throws WxErrorException {
+    String result = internalService.get("https://qyapi.weixin.qq.com/cgi-bin/appchat/get?chatid=" + chatId, null);
+    return WxCpGsonBuilder.create().fromJson(
+        new JsonParser().parse(result).getAsJsonObject().getAsJsonObject("chat_info").toString(), WxCpChat.class);
+  }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java
index 5d623b2ad4..481115fa51 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImpl.java
@@ -28,11 +28,11 @@ public WxCpDepartmentServiceImpl(WxCpService mainService) {
   }
 
   @Override
-  public Integer create(WxCpDepart depart) throws WxErrorException {
+  public Long create(WxCpDepart depart) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/department/create";
     String responseContent = this.mainService.post(url, depart.toJson());
     JsonElement tmpJsonElement = new JsonParser().parse(responseContent);
-    return GsonHelper.getAsInteger(tmpJsonElement.getAsJsonObject().get("id"));
+    return GsonHelper.getAsLong(tmpJsonElement.getAsJsonObject().get("id"));
   }
 
   @Override
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
index c3dc477495..7a3dc444cf 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
@@ -48,6 +48,14 @@ public File download(String mediaId) throws WxErrorException {
       MEDIA_GET_URL, "media_id=" + mediaId);
   }
 
+  @Override
+  public File getJssdkFile(String mediaId) throws WxErrorException {
+    return this.mainService.execute(
+      BaseMediaDownloadRequestExecutor.create(this.mainService.getRequestHttp(),
+        this.mainService.getWxCpConfigStorage().getTmpDirFile()),
+      JSSDK_MEDIA_GET_URL, "media_id=" + mediaId);
+  }
+
   @Override
   public String uploadImg(File file) throws WxErrorException {
     final WxMediaUploadResult result = this.mainService
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java
index 0c66e0ef77..f0c15109b9 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpServiceAbstractImpl.java
@@ -1,9 +1,16 @@
 package me.chanjar.weixin.cp.api.impl;
 
+import java.io.File;
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
+
 import me.chanjar.weixin.common.bean.WxJsapiSignature;
 import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
@@ -17,19 +24,24 @@
 import me.chanjar.weixin.common.util.http.RequestHttp;
 import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor;
 import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor;
-import me.chanjar.weixin.cp.api.*;
-import me.chanjar.weixin.cp.bean.*;
+import me.chanjar.weixin.cp.api.WxCpAgentService;
+import me.chanjar.weixin.cp.api.WxCpChatService;
+import me.chanjar.weixin.cp.api.WxCpDepartmentService;
+import me.chanjar.weixin.cp.api.WxCpMediaService;
+import me.chanjar.weixin.cp.api.WxCpMenuService;
+import me.chanjar.weixin.cp.api.WxCpOAuth2Service;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.api.WxCpTagService;
+import me.chanjar.weixin.cp.api.WxCpUserService;
+import me.chanjar.weixin.cp.bean.WxCpMessage;
+import me.chanjar.weixin.cp.bean.WxCpMessageSendResult;
 import me.chanjar.weixin.cp.config.WxCpConfigStorage;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
 
 public abstract class WxCpServiceAbstractImpl implements WxCpService, RequestHttp {
   protected final Logger log = LoggerFactory.getLogger(this.getClass());
 
   private WxCpUserService userService = new WxCpUserServiceImpl(this);
+  private WxCpChatService chatService = new WxCpChatServiceImpl(this);
   private WxCpDepartmentService departmentService = new WxCpDepartmentServiceImpl(this);
   private WxCpMediaService mediaService = new WxCpMediaServiceImpl(this);
   private WxCpMenuService menuService = new WxCpMenuServiceImpl(this);
@@ -184,7 +196,7 @@ public  T execute(RequestExecutor executor, String uri, E data) thro
             this.log.debug("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
             Thread.sleep(sleepMillis);
           } catch (InterruptedException e1) {
-            throw new RuntimeException(e1);
+            Thread.currentThread().interrupt();
           }
         } else {
           throw e;
@@ -334,7 +346,12 @@ public WxCpUserService getUserService() {
   }
 
   @Override
-  public RequestHttp getRequestHttp() {
+  public WxCpChatService getChatService() {
+    return chatService;
+  }
+
+  @Override
+  public RequestHttp getRequestHttp() {
     return this;
   }
 
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java
index 4f6990cc18..84a16f3496 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImpl.java
@@ -76,7 +76,7 @@ public WxCpUser getById(String userid) throws WxErrorException {
   }
 
   @Override
-  public List listByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException {
+  public List listByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list?department_id=" + departId;
     String params = "";
     if (fetchChild != null) {
@@ -98,7 +98,7 @@ public List listByDepartment(Integer departId, Boolean fetchChild, Int
   }
 
   @Override
-  public List listSimpleByDepartment(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException {
+  public List listSimpleByDepartment(Long departId, Boolean fetchChild, Integer status) throws WxErrorException {
     String url = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?department_id=" + departId;
     String params = "";
     if (fetchChild != null) {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java
new file mode 100644
index 0000000000..1f593e4746
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpChat.java
@@ -0,0 +1,20 @@
+package me.chanjar.weixin.cp.bean;
+
+import java.util.List;
+
+import lombok.Data;
+
+/**
+ * 群聊
+ *
+ * @author gaigeshen
+ */
+@Data
+public class WxCpChat {
+  
+  private String id;
+  private String name;
+  private String owner;
+  private List users;
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java
index cab4970b18..90d67b07bc 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpXmlMessage.java
@@ -3,8 +3,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.io.IOUtils;
 
@@ -13,6 +15,7 @@
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.util.XmlUtils;
 import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
 import me.chanjar.weixin.cp.config.WxCpConfigStorage;
 import me.chanjar.weixin.cp.util.crypto.WxCpCryptUtil;
@@ -35,6 +38,11 @@
 public class WxCpXmlMessage implements Serializable {
   private static final long serialVersionUID = -1042994982179476410L;
 
+  /**
+   * 使用dom4j解析的存放所有xml属性和值的map.
+   */
+  private Map allFieldsMap;
+
   ///////////////////////
   // 以下都是微信推送过来的消息的xml的element所对应的属性
   ///////////////////////
@@ -348,7 +356,9 @@ public class WxCpXmlMessage implements Serializable {
   protected static WxCpXmlMessage fromXml(String xml) {
     //修改微信变态的消息内容格式,方便解析
     xml = xml.replace("", "");
-    return XStreamTransformer.fromXml(WxCpXmlMessage.class, xml);
+    final WxCpXmlMessage xmlMessage = XStreamTransformer.fromXml(WxCpXmlMessage.class, xml);
+    xmlMessage.setAllFieldsMap(XmlUtils.xml2Map(xml));
+    return xmlMessage;
   }
 
   protected static WxCpXmlMessage fromXml(InputStream is) {
@@ -369,7 +379,7 @@ public static WxCpXmlMessage fromEncryptedXml(String encryptedXml, WxCpConfigSto
   public static WxCpXmlMessage fromEncryptedXml(InputStream is, WxCpConfigStorage wxCpConfigStorage,
                                                 String timestamp, String nonce, String msgSignature) {
     try {
-      return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), wxCpConfigStorage, timestamp, nonce, msgSignature);
+      return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxCpConfigStorage, timestamp, nonce, msgSignature);
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java
index 99e4fd4573..6b778be66c 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/message/WxCpMessageRouter.java
@@ -1,5 +1,18 @@
 package me.chanjar.weixin.cp.message;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import me.chanjar.weixin.common.api.WxErrorExceptionHandler;
 import me.chanjar.weixin.common.api.WxMessageDuplicateChecker;
 import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker;
@@ -11,18 +24,6 @@
 import me.chanjar.weixin.cp.api.WxCpService;
 import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
 import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
 
 /**
  * 
@@ -195,6 +196,7 @@ public void run() {
               sessionEndAccess(wxMessage);
             } catch (InterruptedException e) {
               WxCpMessageRouter.this.log.error("Error happened when wait task finish", e);
+              Thread.currentThread().interrupt();
             } catch (ExecutionException e) {
               WxCpMessageRouter.this.log.error("Error happened when wait task finish", e);
             }
@@ -207,12 +209,11 @@ public void run() {
 
 
   /**
-   * 处理微信消息
+   * 处理微信消息.
    *
-   * @param wxMessage
    */
   public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) {
-    return this.route(wxMessage, new HashMap());
+    return this.route(wxMessage, new HashMap(2));
   }
 
   protected boolean isDuplicateMessage(WxCpXmlMessage wxMessage) {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpChatGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpChatGsonAdapter.java
new file mode 100644
index 0000000000..0e97181a3a
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpChatGsonAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * KINGSTAR MEDIA SOLUTIONS Co.,LTD. Copyright c 2005-2013. All rights reserved.
+ *
+ * This source code is the property of KINGSTAR MEDIA SOLUTIONS LTD. It is intended
+ * only for the use of KINGSTAR MEDIA application development. Reengineering, reproduction
+ * arose from modification of the original source, or other redistribution of this source
+ * is not permitted without written permission of the KINGSTAR MEDIA SOLUTIONS LTD.
+ */
+package me.chanjar.weixin.cp.util.json;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+import me.chanjar.weixin.common.util.json.GsonHelper;
+import me.chanjar.weixin.cp.bean.WxCpChat;
+
+/**
+ * 群聊适配器
+ *
+ * @author gaigeshen
+ */
+public class WxCpChatGsonAdapter implements JsonSerializer, JsonDeserializer {
+
+  @Override
+  public JsonElement serialize(WxCpChat chat, Type typeOfSrc, JsonSerializationContext context) {
+    JsonObject json = new JsonObject();
+    if (chat.getId() != null) { 
+      json.addProperty("chatid", chat.getId());
+    }
+    if (chat.getName() != null) {
+      json.addProperty("name", chat.getName());
+    } 
+    if (chat.getOwner() != null) {
+      json.addProperty("owner", chat.getOwner());
+    }
+    if (chat.getUsers() != null) {
+      JsonArray users = new JsonArray();
+      for (String user : chat.getUsers()) {
+        users.add(user);
+      }
+      json.add("userlist", users);
+    }
+    return json;
+  }
+
+  @Override
+  public WxCpChat deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+    JsonObject chatJson = json.getAsJsonObject();
+
+    WxCpChat chat = new WxCpChat();
+    chat.setId(GsonHelper.getAsString(chatJson.get("chatid")));
+    chat.setName(GsonHelper.getAsString(chatJson.get("name")));
+    chat.setOwner(GsonHelper.getAsString(chatJson.get("owner")));
+    
+    JsonArray usersJson = chatJson.getAsJsonArray("userlist");
+    if (usersJson != null) {
+      List users = new ArrayList<>(usersJson.size());
+      chat.setUsers(users);
+      for (JsonElement userJson : usersJson) {
+        users.add(userJson.getAsString());
+      }
+    }
+    
+    return chat;
+  }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
index 6ff49bf0a0..654d55111b 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpGsonBuilder.java
@@ -5,6 +5,7 @@
 import me.chanjar.weixin.common.bean.menu.WxMenu;
 import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.util.json.WxErrorAdapter;
+import me.chanjar.weixin.cp.bean.WxCpChat;
 import me.chanjar.weixin.cp.bean.WxCpDepart;
 import me.chanjar.weixin.cp.bean.WxCpMessage;
 import me.chanjar.weixin.cp.bean.WxCpTag;
@@ -20,6 +21,7 @@ public class WxCpGsonBuilder {
   static {
     INSTANCE.disableHtmlEscaping();
     INSTANCE.registerTypeAdapter(WxCpMessage.class, new WxCpMessageGsonAdapter());
+    INSTANCE.registerTypeAdapter(WxCpChat.class, new WxCpChatGsonAdapter());
     INSTANCE.registerTypeAdapter(WxCpDepart.class, new WxCpDepartGsonAdapter());
     INSTANCE.registerTypeAdapter(WxCpUser.class, new WxCpUserGsonAdapter());
     INSTANCE.registerTypeAdapter(WxError.class, new WxErrorAdapter());
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java
new file mode 100644
index 0000000000..8317a45ebb
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpChatServiceImplTest.java
@@ -0,0 +1,46 @@
+package me.chanjar.weixin.cp.api.impl;
+
+import java.util.Arrays;
+
+import me.chanjar.weixin.cp.bean.WxCpChat;
+import org.testng.Assert;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.google.inject.Inject;
+
+import me.chanjar.weixin.cp.api.ApiTestModule;
+import me.chanjar.weixin.cp.api.WxCpService;
+
+/**
+ * 测试群聊服务
+ *
+ * @author gaigeshen
+ */
+@Guice(modules = ApiTestModule.class)
+public class WxCpChatServiceImplTest {
+
+  @Inject
+  private WxCpService wxCpService;
+  
+  @Test
+  public void create() throws Exception {
+    wxCpService.getChatService().chatCreate("测试群聊", "gaige_shen", Arrays.asList("gaige_shen", "ZhangXiaoMing"), "mychatid");
+  }
+
+  @Test
+  public void get() throws Exception {
+    WxCpChat chat = wxCpService.getChatService().chatGet("mychatid");
+    System.out.println(chat);
+    Assert.assertEquals(chat.getName(), "测试群聊");
+  }
+
+  @Test
+  public void update() throws Exception {
+    wxCpService.getChatService().chatUpdate("mychatid",  "", "", Arrays.asList("ZhengWuYao"), null);
+    WxCpChat chat = wxCpService.getChatService().chatGet("mychatid");
+    System.out.println(chat);
+    Assert.assertEquals(chat.getUsers().size(), 3);
+  }
+
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java
index 097a6ee71f..57957d3fb6 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpDepartmentServiceImplTest.java
@@ -31,7 +31,7 @@ public void testCreate() throws Exception {
     cpDepart.setName("子部门" + System.currentTimeMillis());
     cpDepart.setParentId(1L);
     cpDepart.setOrder(1L);
-    Integer departId = this.wxCpService.getDepartmentService().create(cpDepart);
+    Long departId = this.wxCpService.getDepartmentService().create(cpDepart);
     System.out.println(departId);
   }
 
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java
index d7242a7703..70873c49cf 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java
@@ -36,7 +36,8 @@ public class WxCpMediaServiceImplTest {
   public Object[][] mediaData() {
     return new Object[][]{
       new Object[]{WxConsts.MediaFileType.IMAGE, TestConstants.FILE_JPG, "mm.jpeg"},
-      new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, "mm.mp3"},//{"errcode":301017,"errmsg":"voice file only support amr like myvoice.amr"}
+      //new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_MP3, "mm.mp3"},
+      // {"errcode":301017,"errmsg":"voice file only support amr like myvoice.amr"}
       new Object[]{WxConsts.MediaFileType.VOICE, TestConstants.FILE_AMR, "mm.amr"},
       new Object[]{WxConsts.MediaFileType.VIDEO, TestConstants.FILE_MP4, "mm.mp4"},
       new Object[]{WxConsts.MediaFileType.FILE, TestConstants.FILE_JPG, "mm.jpeg"}
@@ -47,8 +48,9 @@ public Object[][] mediaData() {
   public void testUploadMedia(String mediaType, String fileType, String fileName) throws WxErrorException, IOException {
     try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName)) {
       WxMediaUploadResult res = this.wxService.getMediaService().upload(mediaType, fileType, inputStream);
-      assertNotNull(res.getType());
-      assertNotNull(res.getCreatedAt());
+      assertThat(res).isNotNull();
+      assertThat(res.getType()).isNotEmpty();
+      assertThat(res.getCreatedAt()).isGreaterThan(0);
       assertTrue(res.getMediaId() != null || res.getThumbMediaId() != null);
 
       if (res.getMediaId() != null) {
@@ -70,9 +72,9 @@ public Object[][] downloadMedia() {
   }
 
   @Test(dependsOnMethods = {"testUploadMedia"}, dataProvider = "downloadMedia")
-  public void testDownloadMedia(String media_id) throws WxErrorException {
-    File file = this.wxService.getMediaService().download(media_id);
-    assertNotNull(file);
+  public void testDownload(String mediaId) throws WxErrorException {
+    File file = this.wxService.getMediaService().download(mediaId);
+    assertThat(file).isNotNull();
     System.out.println(file);
   }
 
@@ -82,4 +84,11 @@ public void testUploadImg() throws WxErrorException {
     String res = this.wxService.getMediaService().uploadImg(new File(url.getFile()));
     assertThat(res).isNotEmpty();
   }
+
+  @Test
+  public void testGetJssdkFile() throws WxErrorException {
+    File file = this.wxService.getMediaService().getJssdkFile("....");
+    assertThat(file).isNotNull();
+    System.out.println(file);
+  }
 }
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java
index 6b67112095..f473fe6be7 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpUserServiceImplTest.java
@@ -74,7 +74,7 @@ public void testGetById() throws Exception {
 
   @Test
   public void testListByDepartment() throws Exception {
-    List users = this.wxCpService.getUserService().listByDepartment(1, true, 0);
+    List users = this.wxCpService.getUserService().listByDepartment(1L, true, 0);
     assertNotEquals(users.size(), 0);
     for (WxCpUser user : users) {
       System.out.println(ToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE));
@@ -83,7 +83,7 @@ public void testListByDepartment() throws Exception {
 
   @Test
   public void testListSimpleByDepartment() throws Exception {
-    List users = this.wxCpService.getUserService().listSimpleByDepartment(1, true, 0);
+    List users = this.wxCpService.getUserService().listSimpleByDepartment(1L, true, 0);
     assertNotEquals(users.size(), 0);
     for (WxCpUser user : users) {
       System.out.println(ToStringBuilder.reflectionToString(user, ToStringStyle.MULTI_LINE_STYLE));
diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml
index b8e8d51056..7c0b2ce85c 100644
--- a/weixin-java-miniapp/pom.xml
+++ b/weixin-java-miniapp/pom.xml
@@ -6,11 +6,11 @@
   4.0.0
   
     com.github.binarywang
-    weixin-java-parent
-    3.2.4.B
+    wx-java
+    3.3.1.B
   
   weixin-java-miniapp
-  Weixin Java Tools - MiniApp
+  WxJava - MiniApp
   微信小程序Java SDK
 
   
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java
index 2903737486..f2c9e8f1dc 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaQrcodeService.java
@@ -1,10 +1,10 @@
 package cn.binarywang.wx.miniapp.api;
 
+import java.io.File;
+
 import cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor;
 import me.chanjar.weixin.common.error.WxErrorException;
 
-import java.io.File;
-
 /**
  * 
  * 二维码相关操作接口.
@@ -32,24 +32,87 @@ public interface WxMaQrcodeService {
    *
    * @param path  不能为空,最大长度 128 字节
    * @param width 默认430 二维码的宽度
+   * @return 文件内容字节数组
+   * @throws WxErrorException 异常
+   */
+  byte[] createQrcodeBytes(String path, int width) throws WxErrorException;
+
+  /**
+   * 接口C: 获取小程序页面二维码.
+   * 
+   * 适用于需要的码数量较少的业务场景
+   * 通过该接口,仅能生成已发布的小程序的二维码。
+   * 可以在开发者工具预览时生成开发版的带参二维码。
+   * 带参二维码只有 100000 个,请谨慎调用。
+   * 
+ * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @return 文件对象 + * @throws WxErrorException 异常 */ File createQrcode(String path, int width) throws WxErrorException; + /** + * 接口C: 获取小程序页面二维码. + *
+   * 适用于需要的码数量较少的业务场景
+   * 通过该接口,仅能生成已发布的小程序的二维码。
+   * 可以在开发者工具预览时生成开发版的带参二维码。
+   * 带参二维码只有 100000 个,请谨慎调用。
+   * 
+ * + * @param path 不能为空,最大长度 128 字节 + * @return 文件对象 + * @throws WxErrorException 异常 + */ File createQrcode(String path) throws WxErrorException; /** * 接口A: 获取小程序码. * - * @param path 不能为空,最大长度 128 字节 - * @param width 默认430 二维码的宽度 - * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} - * @param is_hyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 + * @return 文件内容字节数组 + * @throws WxErrorException 异常 + */ + byte[] createWxaCodeBytes(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) + throws WxErrorException; + + /** + * 接口A: 获取小程序码. + * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, isHyaline 为true时,生成透明底色的小程序码 + * @return 文件对象 + * @throws WxErrorException 异常 */ - File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean is_hyaline) throws WxErrorException; + File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) + throws WxErrorException; + /** + * 接口A: 获取小程序码. + * + * @param path 不能为空,最大长度 128 字节 + * @param width 默认430 二维码的宽度 + * @return 文件对象 + * @throws WxErrorException 异常 + */ File createWxaCode(String path, int width) throws WxErrorException; + /** + * 接口A: 获取小程序码. + * + * @param path 不能为空,最大长度 128 字节 + * @return 文件对象 + * @throws WxErrorException 异常 + */ File createWxaCode(String path) throws WxErrorException; /** @@ -61,15 +124,56 @@ public interface WxMaQrcodeService { * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode *
* - * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~,其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @param width 默认false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} + * @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 + * @return 文件内容字节数组 + * @throws WxErrorException 异常 + */ + byte[] createWxaCodeUnlimitBytes(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; + + /** + * 接口B: 获取小程序码(永久有效、数量暂无限制). + *
+   * 通过该接口生成的小程序码,永久有效,数量暂无限制。
+   * 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。
+   * 使用如下代码可以获取到二维码中的 scene 字段的值。
+   * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode
+   * 
+ * + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 * @param width 默认false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 * @param autoColor 默认true 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 * @param lineColor auth_color 为 false 时生效,使用 rgb 设置颜色 例如 {"r":"xxx","g":"xxx","b":"xxx"} * @param isHyaline 是否需要透明底色, is_hyaline 为true时,生成透明底色的小程序码 + * @return 文件对象 + * @throws WxErrorException 异常 */ - File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; + File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException; + /** + * 接口B: 获取小程序码(永久有效、数量暂无限制). + *
+   * 通过该接口生成的小程序码,永久有效,数量暂无限制。
+   * 用户扫描该码进入小程序后,将统一打开首页,开发者需在对应页面根据获取的码中 scene 字段的值,再做处理逻辑。
+   * 使用如下代码可以获取到二维码中的 scene 字段的值。
+   * 调试阶段可以使用开发工具的条件编译自定义参数 scene=xxxx 进行模拟,开发工具模拟时的 scene 的参数值需要进行 urlencode
+   * 
+ * + * @param scene 最大32个可见字符,只支持数字,大小写英文以及部分特殊字符:!#$&'()*+,/:;=?@-._~, + * 其它字符请自行编码为合法字符(因不支持%,中文无法使用 urlencode 处理,请使用其他编码方式) + * @param page 必须是已经发布的小程序页面,例如 "pages/index/index" ,如果不填写这个字段,默认跳主页面 + * @return 文件对象 + * @throws WxErrorException 异常 + */ File createWxaCodeUnlimit(String scene, String page) throws WxErrorException; } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java new file mode 100644 index 0000000000..842d0662fe --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaSecCheckService.java @@ -0,0 +1,43 @@ +package cn.binarywang.wx.miniapp.api; + +import java.io.File; + +import me.chanjar.weixin.common.error.WxErrorException; + +/** + *
+ * 内容安全相关接口.
+ * Created by Binary Wang on 2018/11/24.
+ * 
+ * + * @author Binary Wang + */ +public interface WxMaSecCheckService { + + String IMG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/img_sec_check"; + + String MSG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/msg_sec_check"; + + /** + *
+   * 校验一张图片是否含有违法违规内容.
+   * 应用场景举例:
+   * 1)图片智能鉴黄:涉及拍照的工具类应用(如美拍,识图类应用)用户拍照上传检测;电商类商品上架图片检测;媒体类用户文章里的图片检测等;
+   * 2)敏感人脸识别:用户头像;媒体类用户文章里的图片检测;社交类用户上传的图片检测等。频率限制:单个 appId 调用上限为 1000 次/分钟,100,000 次/天
+   * 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/imgSecCheck.html
+   * 
+ */ + boolean checkImage(File file) throws WxErrorException; + + /** + *
+   * 检查一段文本是否含有违法违规内容。
+   * 应用场景举例:
+   * 用户个人资料违规文字检测;
+   * 媒体新闻类用户发表文章,评论内容检测;
+   * 游戏类用户编辑上传的素材(如答题类小游戏用户上传的问题及答案)检测等。 频率限制:单个 appId 调用上限为 4000 次/分钟,2,000,000 次/天*
+   * 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/open-api/sec-check/msgSecCheck.html
+   * 
+ */ + boolean checkMessage(String msgString); +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java index 8d5fbc3c2b..efa53939a8 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java @@ -1,7 +1,5 @@ package cn.binarywang.wx.miniapp.api; -import java.io.File; - import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; import cn.binarywang.wx.miniapp.config.WxMaConfig; import me.chanjar.weixin.common.error.WxErrorException; @@ -20,17 +18,6 @@ public interface WxMaService { String JSCODE_TO_SESSION_URL = "https://api.weixin.qq.com/sns/jscode2session"; - String IMG_SEC_CHECK_URL = "https://api.weixin.qq.com/wxa/img_sec_check"; - - /** - *
-   * 校验一张图片是否含有违法违规内容.
-   * 应用场景举例:1)图片智能鉴黄:涉及拍照的工具类应用(如美拍,识图类应用)用户拍照上传检测;电商类商品上架图片检测;媒体类用户文章里的图片检测等;2)敏感人脸识别:用户头像;媒体类用户文章里的图片检测;社交类用户上传的图片检测等。频率限制:单个 appId 调用上限为 1000 次/分钟,100,000 次/天
-   * 详情请见: https://developers.weixin.qq.com/miniprogram/dev/api/imgSecCheck.html
-   * 
- */ - boolean imgSecCheck(File file) throws WxErrorException; - /** * 获取登录后的session信息. * @@ -186,11 +173,17 @@ public interface WxMaService { WxMaShareService getShareService(); /** - * 返回维新运动相关接口服务对象. + * 返回微信运动相关接口服务对象. * @return WxMaShareService */ WxMaRunService getRunService(); + /** + * 返回内容安全相关接口服务对象. + * @return WxMaShareService + */ + WxMaSecCheckService getSecCheckService(); + /** * 初始化http请求对象. */ diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java index 277418e752..8e7d6b170b 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImpl.java @@ -1,16 +1,17 @@ package cn.binarywang.wx.miniapp.api.impl; +import java.io.File; + import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.bean.WxMaCodeLineColor; import cn.binarywang.wx.miniapp.bean.WxMaQrcode; -import cn.binarywang.wx.miniapp.bean.WxMaWxcode; +import cn.binarywang.wx.miniapp.bean.WxaCode; import cn.binarywang.wx.miniapp.bean.WxaCodeUnlimit; -import cn.binarywang.wx.miniapp.util.http.QrCodeRequestExecutor; +import cn.binarywang.wx.miniapp.util.QrcodeBytesRequestExecutor; +import cn.binarywang.wx.miniapp.util.QrcodeRequestExecutor; import me.chanjar.weixin.common.error.WxErrorException; -import java.io.File; - /** * @author Binary Wang */ @@ -21,10 +22,16 @@ public WxMaQrcodeServiceImpl(WxMaService wxMaService) { this.wxMaService = wxMaService; } + @Override + public byte[] createQrcodeBytes(String path, int width) throws WxErrorException { + final QrcodeBytesRequestExecutor executor = new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()); + return this.wxMaService.execute(executor, CREATE_QRCODE_URL, new WxMaQrcode(path, width)); + } + @Override public File createQrcode(String path, int width) throws WxErrorException { - return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), - CREATE_QRCODE_URL, new WxMaQrcode(path, width)); + final QrcodeRequestExecutor executor = new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()); + return this.wxMaService.execute(executor, CREATE_QRCODE_URL, new WxMaQrcode(path, width)); } @Override @@ -33,15 +40,29 @@ public File createQrcode(String path) throws WxErrorException { } @Override - public File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException { - WxMaWxcode wxMaWxcode = new WxMaWxcode(); - wxMaWxcode.setPath(path); - wxMaWxcode.setWidth(width); - wxMaWxcode.setAutoColor(autoColor); - wxMaWxcode.setLineColor(lineColor); - wxMaWxcode.setHyaline(isHyaline); - return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), - GET_WXACODE_URL, wxMaWxcode); + public byte[] createWxaCodeBytes(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) + throws WxErrorException { + final QrcodeBytesRequestExecutor executor = new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()); + return this.wxMaService.execute(executor, GET_WXACODE_URL, WxaCode.builder() + .path(path) + .width(width) + .autoColor(autoColor) + .lineColor(lineColor) + .isHyaline(isHyaline) + .build()); + } + + @Override + public File createWxaCode(String path, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) + throws WxErrorException { + final QrcodeRequestExecutor executor = new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()); + return this.wxMaService.execute(executor, GET_WXACODE_URL, WxaCode.builder() + .path(path) + .width(width) + .autoColor(autoColor) + .lineColor(lineColor) + .isHyaline(isHyaline) + .build()); } @Override @@ -51,12 +72,27 @@ public File createWxaCode(String path, int width) throws WxErrorException { @Override public File createWxaCode(String path) throws WxErrorException { - return this.createWxaCode(path, 430, true, null, false); + return this.createWxaCode(path, 430); } @Override - public File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, WxMaCodeLineColor lineColor, boolean isHyaline) - throws WxErrorException { + public byte[] createWxaCodeUnlimitBytes(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException { + return this.wxMaService.execute(new QrcodeBytesRequestExecutor(this.wxMaService.getRequestHttp()), + GET_WXACODE_UNLIMIT_URL, + this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline)); + } + + @Override + public File createWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) throws WxErrorException { + return this.wxMaService.execute(new QrcodeRequestExecutor(this.wxMaService.getRequestHttp()), + GET_WXACODE_UNLIMIT_URL, + this.buildWxaCodeUnlimit(scene, page, width, autoColor, lineColor, isHyaline)); + } + + private WxaCodeUnlimit buildWxaCodeUnlimit(String scene, String page, int width, boolean autoColor, + WxMaCodeLineColor lineColor, boolean isHyaline) { WxaCodeUnlimit wxaCodeUnlimit = new WxaCodeUnlimit(); wxaCodeUnlimit.setScene(scene); wxaCodeUnlimit.setPage(page); @@ -64,8 +100,8 @@ public File createWxaCodeUnlimit(String scene, String page, int width, boolean a wxaCodeUnlimit.setAutoColor(autoColor); wxaCodeUnlimit.setLineColor(lineColor); wxaCodeUnlimit.setHyaline(isHyaline); - return this.wxMaService.execute(new QrCodeRequestExecutor(this.wxMaService.getRequestHttp()), - GET_WXACODE_UNLIMIT_URL, wxaCodeUnlimit); + + return wxaCodeUnlimit; } @Override diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java new file mode 100644 index 0000000000..1dab0a82bb --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImpl.java @@ -0,0 +1,48 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import java.io.File; + +import cn.binarywang.wx.miniapp.api.WxMaSecCheckService; +import cn.binarywang.wx.miniapp.api.WxMaService; +import com.google.gson.JsonObject; +import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; + +/** + *
+ *
+ * Created by Binary Wang on 2018/11/24.
+ * 
+ * + * @author Binary Wang + */ +public class WxMaSecCheckServiceImpl implements WxMaSecCheckService { + private WxMaService service; + + public WxMaSecCheckServiceImpl(WxMaService service) { + this.service = service; + } + + @Override + public boolean checkImage(File file) throws WxErrorException { + //这里只是借用MediaUploadRequestExecutor,并不使用其返回值WxMediaUploadResult + WxMediaUploadResult result = this.service.execute(MediaUploadRequestExecutor + .create(this.service.getRequestHttp()), IMG_SEC_CHECK_URL, file); + return result != null; + } + + @Override + public boolean checkMessage(String msgString) { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("content", msgString); + try { + this.service.post(MSG_SEC_CHECK_URL, jsonObject.toString()); + } catch (WxErrorException e) { + return false; + } + + return true; + } + +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java index 82265b9bfc..280a052e4a 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImpl.java @@ -1,6 +1,5 @@ package cn.binarywang.wx.miniapp.api.impl; -import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -12,8 +11,6 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.CloseableHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import cn.binarywang.wx.miniapp.api.WxMaAnalysisService; import cn.binarywang.wx.miniapp.api.WxMaCodeService; @@ -22,6 +19,7 @@ import cn.binarywang.wx.miniapp.api.WxMaMsgService; import cn.binarywang.wx.miniapp.api.WxMaQrcodeService; import cn.binarywang.wx.miniapp.api.WxMaRunService; +import cn.binarywang.wx.miniapp.api.WxMaSecCheckService; import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.api.WxMaSettingService; import cn.binarywang.wx.miniapp.api.WxMaShareService; @@ -31,14 +29,13 @@ import cn.binarywang.wx.miniapp.config.WxMaConfig; import com.google.common.base.Joiner; import com.google.gson.Gson; +import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.bean.WxAccessToken; -import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.DataUtils; import me.chanjar.weixin.common.util.crypto.SHA1; import me.chanjar.weixin.common.util.http.HttpType; -import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; @@ -51,9 +48,8 @@ /** * @author Binary Wang */ +@Slf4j public class WxMaServiceImpl implements WxMaService, RequestHttp { - private final Logger log = LoggerFactory.getLogger(this.getClass()); - private CloseableHttpClient httpClient; private HttpHost httpProxy; private WxMaConfig wxMaConfig; @@ -69,6 +65,7 @@ public class WxMaServiceImpl implements WxMaService, RequestHttp T execute(RequestExecutor executor, String uri, E data) thro return this.executeInternal(executor, uri, data); } catch (WxErrorException e) { if (retryTimes + 1 > this.maxRetryTimes) { - this.log.warn("重试达到最大次数【{}】", maxRetryTimes); + log.warn("重试达到最大次数【{}】", maxRetryTimes); //最后一次重试失败后,直接抛出异常,不再等待 throw new RuntimeException("微信服务端异常,超出重试次数"); } @@ -219,10 +209,10 @@ public T execute(RequestExecutor executor, String uri, E data) thro if (error.getErrorCode() == -1) { int sleepMillis = this.retrySleepMillis * (1 << retryTimes); try { - this.log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); + log.warn("微信系统繁忙,{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1); Thread.sleep(sleepMillis); } catch (InterruptedException e1) { - throw new RuntimeException(e1); + Thread.currentThread().interrupt(); } } else { throw e; @@ -230,7 +220,7 @@ public T execute(RequestExecutor executor, String uri, E data) thro } } while (retryTimes++ < this.maxRetryTimes); - this.log.warn("重试达到最大次数【{}】", this.maxRetryTimes); + log.warn("重试达到最大次数【{}】", this.maxRetryTimes); throw new RuntimeException("微信服务端异常,超出重试次数"); } @@ -246,7 +236,7 @@ private T executeInternal(RequestExecutor executor, String uri, E d try { T result = executor.execute(uriWithAccessToken, data); - this.log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); + log.debug("\n【请求地址】: {}\n【请求参数】:{}\n【响应数据】:{}", uriWithAccessToken, dataForLog, result); return result; } catch (WxErrorException e) { WxError error = e.getError(); @@ -264,12 +254,12 @@ private T executeInternal(RequestExecutor executor, String uri, E d } if (error.getErrorCode() != 0) { - this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【错误信息】:{}", uriWithAccessToken, dataForLog, error); throw new WxErrorException(error, e); } return null; } catch (IOException e) { - this.log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); + log.error("\n【请求地址】: {}\n【请求参数】:{}\n【异常信息】:{}", uriWithAccessToken, dataForLog, e.getMessage()); throw new RuntimeException(e); } } @@ -349,4 +339,9 @@ public WxMaShareService getShareService() { public WxMaRunService getRunService() { return this.runService; } + + @Override + public WxMaSecCheckService getSecCheckService() { + return this.secCheckService; + } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcode.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java similarity index 62% rename from weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcode.java rename to weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java index e538a1bb83..2d94f05cea 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaWxcode.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxaCode.java @@ -1,36 +1,48 @@ package cn.binarywang.wx.miniapp.bean; +import java.io.Serializable; + import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder; import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; - -import java.io.Serializable; +import lombok.NoArgsConstructor; /** + * 小程序码. * * @author Element * @date 2017/7/27 */ @Data @EqualsAndHashCode(callSuper = false) -public class WxMaWxcode extends AbstractWxMaQrcodeWrapper implements Serializable { +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class WxaCode extends AbstractWxMaQrcodeWrapper implements Serializable { private static final long serialVersionUID = 1287399621649210322L; private String path; + + @Builder.Default private int width = 430; @SerializedName("auto_color") + @Builder.Default private boolean autoColor = true; @SerializedName("is_hyaline") + @Builder.Default private boolean isHyaline = false; @SerializedName("line_color") + @Builder.Default private WxMaCodeLineColor lineColor = new WxMaCodeLineColor("0", "0", "0"); - public static WxMaWxcode fromJson(String json) { - return WxMaGsonBuilder.create().fromJson(json, WxMaWxcode.class); + public static WxaCode fromJson(String json) { + return WxMaGsonBuilder.create().fromJson(json, WxaCode.class); } } diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java index 9a26890d27..49a80cc423 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/config/WxMaConfig.java @@ -1,10 +1,10 @@ package cn.binarywang.wx.miniapp.config; +import java.util.concurrent.locks.Lock; + import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; -import java.util.concurrent.locks.Lock; - /** * 小程序配置 * @@ -57,6 +57,9 @@ public interface WxMaConfig { */ void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); + /** + * 卡券api_ticket. + */ String getCardApiTicket(); Lock getCardApiTicketLock(); @@ -64,14 +67,14 @@ public interface WxMaConfig { boolean isCardApiTicketExpired(); /** - * 强制将卡券api ticket过期掉 + * 强制将卡券api ticket过期掉. */ void expireCardApiTicket(); /** - * 应该是线程安全的 + * 应该是线程安全的. * - * @param 卡券apiTicket 新的卡券api ticket值 + * @param apiTicket 新的卡券api ticket值 * @param expiresInSeconds 过期时间,以秒为单位 */ void updateCardApiTicket(String apiTicket, int expiresInSeconds); diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeBytesRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeBytesRequestExecutor.java new file mode 100644 index 0000000000..5421aea316 --- /dev/null +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeBytesRequestExecutor.java @@ -0,0 +1,66 @@ +package cn.binarywang.wx.miniapp.util; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; + +import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; + +/** + * @author Binary Wang + */ +public class QrcodeBytesRequestExecutor implements RequestExecutor { + protected RequestHttp requestHttp; + + public QrcodeBytesRequestExecutor(RequestHttp requestHttp) { + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + + @Override + public byte[] execute(String uri, AbstractWxMaQrcodeWrapper qrcodeWrapper) throws WxErrorException, IOException { + HttpPost httpPost = new HttpPost(uri); + if (requestHttp.getRequestHttpProxy() != null) { + httpPost.setConfig( + RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() + ); + } + + httpPost.setEntity(new StringEntity(qrcodeWrapper.toJson())); + + try (final CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); + final InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { + Header[] contentTypeHeader = response.getHeaders("Content-Type"); + if (contentTypeHeader != null && contentTypeHeader.length > 0 + && ContentType.APPLICATION_JSON.getMimeType() + .equals(ContentType.parse(contentTypeHeader[0].getValue()).getMimeType())) { + String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response); + throw new WxErrorException(WxError.fromJson(responseContent)); + } + + return IOUtils.toByteArray(inputStream); + } finally { + httpPost.releaseConnection(); + } + } +} diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/http/QrCodeRequestExecutor.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeRequestExecutor.java similarity index 71% rename from weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/http/QrCodeRequestExecutor.java rename to weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeRequestExecutor.java index cb8097db53..672f97a0b4 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/http/QrCodeRequestExecutor.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/util/QrcodeRequestExecutor.java @@ -1,13 +1,10 @@ -package cn.binarywang.wx.miniapp.util.http; +package cn.binarywang.wx.miniapp.util; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; -import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; -import me.chanjar.weixin.common.error.WxError; -import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.common.util.fs.FileUtils; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.RequestHttp; -import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; -import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; @@ -17,33 +14,44 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.UUID; +import cn.binarywang.wx.miniapp.bean.AbstractWxMaQrcodeWrapper; +import me.chanjar.weixin.common.error.WxError; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.fs.FileUtils; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.common.util.http.apache.InputStreamResponseHandler; +import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler; /** * @author Binary Wang */ -public class QrCodeRequestExecutor implements RequestExecutor { +public class QrcodeRequestExecutor implements RequestExecutor { protected RequestHttp requestHttp; - public QrCodeRequestExecutor(RequestHttp requestHttp) { + public QrcodeRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } @Override - public File execute(String uri, AbstractWxMaQrcodeWrapper ticket) throws WxErrorException, IOException { + public void execute(String uri, AbstractWxMaQrcodeWrapper data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + + @Override + public File execute(String uri, AbstractWxMaQrcodeWrapper qrcodeWrapper) throws WxErrorException, IOException { HttpPost httpPost = new HttpPost(uri); if (requestHttp.getRequestHttpProxy() != null) { httpPost.setConfig( RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build() ); } - httpPost.setEntity(new StringEntity(ticket.toJson())); - try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); - InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response);) { + httpPost.setEntity(new StringEntity(qrcodeWrapper.toJson())); + + try (final CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost); + final InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) { Header[] contentTypeHeader = response.getHeaders("Content-Type"); if (contentTypeHeader != null && contentTypeHeader.length > 0 && ContentType.APPLICATION_JSON.getMimeType() diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java index ae2e8d8fed..e73fec4270 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaQrcodeServiceImplTest.java @@ -7,6 +7,9 @@ import cn.binarywang.wx.miniapp.api.WxMaService; import cn.binarywang.wx.miniapp.test.ApiTestModule; import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Binary Wang @@ -18,21 +21,38 @@ public class WxMaQrcodeServiceImplTest { private WxMaService wxService; @Test - public void testCreateQrCode() throws Exception { + public void testCreateQrcode() throws Exception { final File qrCode = this.wxService.getQrcodeService().createQrcode("111", 122); - System.out.println(qrCode); + assertThat(qrCode).isNotNull(); } @Test public void testCreateWxaCode() throws Exception { final File wxCode = this.wxService.getQrcodeService().createWxaCode("111", 122); - System.out.println(wxCode); + assertThat(wxCode).isNotNull(); } @Test public void testCreateWxaCodeUnlimit() throws Exception { final File wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimit("111", null); - System.out.println(wxCode); + assertThat(wxCode).isNotNull(); } + @Test + public void testCreateQrcodeBytes() throws WxErrorException { + final byte[] qrCode = this.wxService.getQrcodeService().createQrcodeBytes("111", 122); + assertThat(qrCode).isNotNull(); + } + + @Test + public void testCreateWxaCodeBytes() throws WxErrorException { + final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeBytes("111", 122, true, null, false); + assertThat(wxCode).isNotNull(); + } + + @Test + public void testCreateWxaCodeUnlimitBytes() throws WxErrorException { + final byte[] wxCode = this.wxService.getQrcodeService().createWxaCodeUnlimitBytes("111", null, 122, true, null, false); + assertThat(wxCode).isNotNull(); + } } diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImplTest.java new file mode 100644 index 0000000000..df9ccaa823 --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSecCheckServiceImplTest.java @@ -0,0 +1,51 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import java.io.File; + +import org.testng.annotations.*; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.test.ApiTestModule; +import com.google.inject.Inject; +import me.chanjar.weixin.common.error.WxErrorException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.*; + +/** + *
+ *
+ * Created by Binary Wang on 2018/11/24.
+ * 
+ * + * @author Binary Wang + */ +@Test +@Guice(modules = ApiTestModule.class) +public class WxMaSecCheckServiceImplTest { + @Inject + private WxMaService wxService; + + @Test + public void testCheckImage() throws WxErrorException { + boolean result = this.wxService.getSecCheckService() + .checkImage(new File(ClassLoader.getSystemResource("tmp.png").getFile())); + assertTrue(result); + } + + @DataProvider + public Object[][] secData() { + return new Object[][]{ + {"特3456书yuuo莞6543李zxcz蒜7782法fgnv级", false}, + {"完2347全dfji试3726测asad感3847知qwez到", false}, + {"hello world!", true} + }; + } + + @Test(dataProvider = "secData") + public void testCheckMessage(String msg, boolean result) { + assertThat(this.wxService.getSecCheckService() + .checkMessage(msg)) + .isEqualTo(result); + } +} diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java index dcefc68c72..1f87c7e4ee 100644 --- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaServiceImplTest.java @@ -33,9 +33,4 @@ public void testRefreshAccessToken() throws WxErrorException { assertTrue(StringUtils.isNotBlank(after)); } - @Test - public void testImgSecCheck() throws WxErrorException { - boolean result = this.wxService.imgSecCheck(new File(ClassLoader.getSystemResource("tmp.png").getFile())); - assertTrue(result); - } } diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index ea8bf09377..5c50e83a5b 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -6,11 +6,11 @@ 4.0.0 com.github.binarywang - weixin-java-parent - 3.2.4.B + wx-java + 3.3.1.B weixin-java-mp - Weixin Java Tools - MP + WxJava - MP 微信公众号Java SDK @@ -36,6 +36,11 @@ testng test + + org.mockito + mockito-all + test + com.google.inject guice diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java index 1e9c8e02f8..74b905d683 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpAiOpenService.java @@ -1,7 +1,7 @@ package me.chanjar.weixin.mp.api; import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.AiLangType; +import me.chanjar.weixin.mp.enums.AiLangType; import java.io.File; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java index 1ad01eea55..d0f06c13b8 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpCardService.java @@ -2,9 +2,7 @@ import me.chanjar.weixin.common.bean.WxCardApiSignature; import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateRequest; -import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateResult; -import me.chanjar.weixin.mp.bean.card.WxMpCardQrcodeCreateResult; +import me.chanjar.weixin.mp.bean.card.*; import me.chanjar.weixin.mp.bean.result.WxMpCardResult; /** @@ -14,6 +12,7 @@ * @author yuanqixun 2018-08-29 */ public interface WxMpCardService { + String CARD_CREATE = "https://api.weixin.qq.com/card/create"; String CARD_GET = "https://api.weixin.qq.com/card/get"; String CARD_GET_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card"; String CARD_CODE_DECRYPT = "https://api.weixin.qq.com/card/code/decrypt"; @@ -21,8 +20,8 @@ public interface WxMpCardService { String CARD_CODE_CONSUME = "https://api.weixin.qq.com/card/code/consume"; String CARD_CODE_MARK = "https://api.weixin.qq.com/card/code/mark"; String CARD_TEST_WHITELIST = "https://api.weixin.qq.com/card/testwhitelist/set"; - String CARD_QRCODE_CREAET = "https://api.weixin.qq.com/card/qrcode/create"; - String CARD_LANDING_PAGE_CREAET = "https://api.weixin.qq.com/card/landingpage/create"; + String CARD_QRCODE_CREATE = "https://api.weixin.qq.com/card/qrcode/create"; + String CARD_LANDING_PAGE_CREATE = "https://api.weixin.qq.com/card/landingpage/create"; /** * 将用户的卡券设置为失效状态 */ @@ -142,6 +141,14 @@ void markCardCode(String code, String cardId, String openId, boolean isMark) thr */ String addTestWhiteList(String openid) throws WxErrorException; + /** + * + * @param cardCreateMessage + * @return + * @throws WxErrorException + */ + WxMpCardCreateResult createCard(WxMpCardCreateMessage cardCreateMessage) throws WxErrorException; + /** * 创建卡券二维码 * 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 ea21c80b76..d51ea88b8e 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 @@ -1,13 +1,14 @@ package me.chanjar.weixin.mp.api; -import me.chanjar.weixin.common.bean.WxAccessToken; -import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; - import java.io.File; import java.util.concurrent.locks.Lock; +import me.chanjar.weixin.common.bean.WxAccessToken; +import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.mp.enums.TicketType; + /** - * 微信客户端配置存储 + * 微信客户端配置存储. * * @author chanjarster */ @@ -20,62 +21,45 @@ public interface WxMpConfigStorage { boolean isAccessTokenExpired(); /** - * 强制将access token过期掉 + * 强制将access token过期掉. */ void expireAccessToken(); /** - * 应该是线程安全的 + * 应该是线程安全的. * * @param accessToken 要更新的WxAccessToken对象 */ void updateAccessToken(WxAccessToken accessToken); /** - * 应该是线程安全的 + * 应该是线程安全的. * * @param accessToken 新的accessToken值 * @param expiresInSeconds 过期时间,以秒为单位 */ void updateAccessToken(String accessToken, int expiresInSeconds); - String getJsapiTicket(); - - Lock getJsapiTicketLock(); - - boolean isJsapiTicketExpired(); - - /** - * 强制将jsapi ticket过期掉 - */ - void expireJsapiTicket(); - - /** - * 应该是线程安全的 - * - * @param jsapiTicket 新的jsapi ticket值 - * @param expiresInSeconds 过期时间,以秒为单位 - */ - void updateJsapiTicket(String jsapiTicket, int expiresInSeconds); - - String getCardApiTicket(); + String getTicket(TicketType type); - Lock getCardApiTicketLock(); + Lock getTicketLock(TicketType type); - boolean isCardApiTicketExpired(); + boolean isTicketExpired(TicketType type); /** - * 强制将卡券api ticket过期掉 + * 强制将ticket过期掉. */ - void expireCardApiTicket(); + void expireTicket(TicketType type); /** + * 更新ticket. * 应该是线程安全的 * - * @param cardApiTicket 新的cardApi ticket值 + * @param type ticket类型 + * @param ticket 新的ticket值 * @param expiresInSeconds 过期时间,以秒为单位 */ - void updateCardApiTicket(String cardApiTicket, int expiresInSeconds); + void updateTicket(TicketType type, String ticket, int expiresInSeconds); String getAppId(); @@ -102,14 +86,14 @@ public interface WxMpConfigStorage { File getTmpDirFile(); /** - * http client builder + * http client builder. * * @return ApacheHttpClientBuilder */ ApacheHttpClientBuilder getApacheHttpClientBuilder(); /** - * 是否自动刷新token + * 是否自动刷新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 e4937ec01d..4f24b1671a 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 @@ -1,19 +1,24 @@ package me.chanjar.weixin.mp.api; import java.io.File; +import java.io.Serializable; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import lombok.Data; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder; +import me.chanjar.weixin.mp.enums.TicketType; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** - * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化 + * 基于内存的微信配置provider,在实际生产环境中应该将这些配置持久化. * * @author chanjarster */ -public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { +@Data +public class WxMpInMemoryConfigStorage implements WxMpConfigStorage, Serializable { + private static final long serialVersionUID = -6646519023303395185L; protected volatile String appId; protected volatile String secret; @@ -33,34 +38,21 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { protected volatile String jsapiTicket; protected volatile long jsapiTicketExpiresTime; + protected volatile String sdkTicket; + protected volatile long sdkTicketExpiresTime; + protected volatile String cardApiTicket; protected volatile long cardApiTicketExpiresTime; protected Lock accessTokenLock = new ReentrantLock(); protected Lock jsapiTicketLock = new ReentrantLock(); + protected Lock sdkTicketLock = new ReentrantLock(); protected Lock cardApiTicketLock = new ReentrantLock(); - /** - * 临时文件目录 - */ protected volatile File tmpDirFile; protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder; - @Override - public String getAccessToken() { - return this.accessToken; - } - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - @Override - public Lock getAccessTokenLock() { - return this.accessTokenLock; - } - @Override public boolean isAccessTokenExpired() { return System.currentTimeMillis() > this.expiresTime; @@ -83,167 +75,98 @@ public void expireAccessToken() { } @Override - public String getJsapiTicket() { - return this.jsapiTicket; - } - - public void setJsapiTicket(String jsapiTicket) { - this.jsapiTicket = jsapiTicket; - } - - @Override - public Lock getJsapiTicketLock() { - return this.jsapiTicketLock; - } - - @Override - public boolean isJsapiTicketExpired() { - return System.currentTimeMillis() > this.jsapiTicketExpiresTime; - } - - @Override - public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { - this.jsapiTicket = jsapiTicket; - // 预留200秒的时间 - this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; - } - - @Override - public void expireJsapiTicket() { - this.jsapiTicketExpiresTime = 0; - } - - /** - * 卡券api_ticket - */ - @Override - public String getCardApiTicket() { - return this.cardApiTicket; - } - - public void setCardApiTicket(String cardApiTicket) { - this.cardApiTicket = cardApiTicket; - } - - @Override - public Lock getCardApiTicketLock() { - return this.cardApiTicketLock; - } - - @Override - public boolean isCardApiTicketExpired() { - return System.currentTimeMillis() > this.cardApiTicketExpiresTime; - } - - @Override - public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { - this.cardApiTicket = cardApiTicket; - // 预留200秒的时间 - this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; - } - - @Override - public void expireCardApiTicket() { - this.cardApiTicketExpiresTime = 0; - } - - @Override - public String getAppId() { - return this.appId; - } - - public void setAppId(String appId) { - this.appId = appId; - } - - @Override - public String getSecret() { - return this.secret; - } - - public void setSecret(String secret) { - this.secret = secret; - } - - @Override - public String getToken() { - return this.token; - } - - public void setToken(String token) { - this.token = token; - } - - @Override - public String getTemplateId() { - return this.templateId; - } - - public void setTemplateId(String templateId) { - this.templateId = templateId; - } - - @Override - public long getExpiresTime() { - return this.expiresTime; - } - - public void setExpiresTime(long expiresTime) { - this.expiresTime = expiresTime; - } - - @Override - public String getAesKey() { - return this.aesKey; - } - - public void setAesKey(String aesKey) { - this.aesKey = aesKey; - } - - @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; - } - - public void setHttpProxyUsername(String httpProxyUsername) { - this.httpProxyUsername = httpProxyUsername; - } - - @Override - public String getHttpProxyPassword() { - return this.httpProxyPassword; - } - - public void setHttpProxyPassword(String httpProxyPassword) { - this.httpProxyPassword = httpProxyPassword; + public String getTicket(TicketType type) { + switch (type) { + case SDK: + return this.sdkTicket; + case JSAPI: + return this.jsapiTicket; + case WX_CARD: + return this.cardApiTicket; + default: + return null; + } + } + + public void setTicket(TicketType type, String ticket) { + switch (type) { + case JSAPI: + this.jsapiTicket = ticket; + break; + case WX_CARD: + this.cardApiTicket = ticket; + break; + case SDK: + this.sdkTicket = ticket; + break; + default: + } + } + + @Override + public Lock getTicketLock(TicketType type) { + switch (type) { + case SDK: + return this.sdkTicketLock; + case JSAPI: + return this.jsapiTicketLock; + case WX_CARD: + return this.cardApiTicketLock; + default: + return null; + } + } + + @Override + public boolean isTicketExpired(TicketType type) { + switch (type) { + case SDK: + return System.currentTimeMillis() > this.sdkTicketExpiresTime; + case JSAPI: + return System.currentTimeMillis() > this.jsapiTicketExpiresTime; + case WX_CARD: + return System.currentTimeMillis() > this.cardApiTicketExpiresTime; + default: + return false; + } + } + + @Override + public synchronized void updateTicket(TicketType type, String ticket, int expiresInSeconds) { + switch (type) { + case JSAPI: + this.jsapiTicket = ticket; + // 预留200秒的时间 + this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + break; + case WX_CARD: + this.cardApiTicket = ticket; + // 预留200秒的时间 + this.cardApiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + break; + case SDK: + this.sdkTicket = ticket; + // 预留200秒的时间 + this.sdkTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L; + break; + default: + } + } + + @Override + public void expireTicket(TicketType type) { + switch (type) { + case JSAPI: + this.jsapiTicketExpiresTime = 0; + break; + case WX_CARD: + this.cardApiTicketExpiresTime = 0; + break; + case SDK: + this.sdkTicketExpiresTime = 0; + break; + default: + } } @Override @@ -251,40 +174,6 @@ public String toString() { return WxMpGsonBuilder.create().toJson(this); } - @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; - } - - public long getJsapiTicketExpiresTime() { - return this.jsapiTicketExpiresTime; - } - - public void setJsapiTicketExpiresTime(long jsapiTicketExpiresTime) { - this.jsapiTicketExpiresTime = jsapiTicketExpiresTime; - } - - public long getCardApiTicketExpiresTime() { - return this.cardApiTicketExpiresTime; - } - - public void setCardApiTicketExpiresTime(long cardApiTicketExpiresTime) { - this.cardApiTicketExpiresTime = cardApiTicketExpiresTime; - } - @Override public boolean autoRefreshToken() { return true; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java index e20d1e3f1f..b1ff3f1f87 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java @@ -1,51 +1,45 @@ package me.chanjar.weixin.mp.api; +import me.chanjar.weixin.mp.enums.TicketType; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; /** - * 基于Redis的微信配置provider + * 基于Redis的微信配置provider. + * *
  *    使用说明:本实现仅供参考,并不完整,
  *    比如为减少项目依赖,未加入redis分布式锁的实现,如有需要请自行实现。
  * 
+ * * @author nickwong */ @SuppressWarnings("hiding") public class WxMpInRedisConfigStorage extends WxMpInMemoryConfigStorage { - - private final static String ACCESS_TOKEN_KEY = "wechat_access_token_"; - - private final static String JSAPI_TICKET_KEY = "wechat_jsapi_ticket_"; - - private final static String CARDAPI_TICKET_KEY = "wechat_cardapi_ticket_"; + private static final String ACCESS_TOKEN_KEY = "wx:access_token:"; /** - * 使用连接池保证线程安全 + * 使用连接池保证线程安全. */ protected final JedisPool jedisPool; private String accessTokenKey; - private String jsapiTicketKey; - - private String cardapiTicketKey; - public WxMpInRedisConfigStorage(JedisPool jedisPool) { this.jedisPool = jedisPool; } /** - * 每个公众号生成独有的存储key - * - * @param appId + * 每个公众号生成独有的存储key. */ @Override public void setAppId(String appId) { super.setAppId(appId); this.accessTokenKey = ACCESS_TOKEN_KEY.concat(appId); - this.jsapiTicketKey = JSAPI_TICKET_KEY.concat(appId); - this.cardapiTicketKey = CARDAPI_TICKET_KEY.concat(appId); + } + + private String getTicketRedisKey(TicketType type) { + return String.format("wx:ticket:key:%s:%s", this.appId, type.getCode()); } @Override @@ -77,58 +71,31 @@ public void expireAccessToken() { } @Override - public String getJsapiTicket() { - try (Jedis jedis = this.jedisPool.getResource()) { - return jedis.get(this.jsapiTicketKey); - } - } - - @Override - public boolean isJsapiTicketExpired() { - try (Jedis jedis = this.jedisPool.getResource()) { - return jedis.ttl(this.jsapiTicketKey) < 2; - } - } - - @Override - public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { + public String getTicket(TicketType type) { try (Jedis jedis = this.jedisPool.getResource()) { - jedis.setex(this.jsapiTicketKey, expiresInSeconds - 200, jsapiTicket); + return jedis.get(this.getTicketRedisKey(type)); } } @Override - public void expireJsapiTicket() { + public boolean isTicketExpired(TicketType type) { try (Jedis jedis = this.jedisPool.getResource()) { - jedis.expire(this.jsapiTicketKey, 0); + return jedis.ttl(this.getTicketRedisKey(type)) < 2; } } @Override - public String getCardApiTicket() { + public synchronized void updateTicket(TicketType type, String jsapiTicket, int expiresInSeconds) { try (Jedis jedis = this.jedisPool.getResource()) { - return jedis.get(this.cardapiTicketKey); + jedis.setex(this.getTicketRedisKey(type), expiresInSeconds - 200, jsapiTicket); } } @Override - public boolean isCardApiTicketExpired() { + public void expireTicket(TicketType type) { try (Jedis jedis = this.jedisPool.getResource()) { - return jedis.ttl(this.cardapiTicketKey) < 2; + jedis.expire(this.getTicketRedisKey(type), 0); } } - @Override - public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { - try (Jedis jedis = this.jedisPool.getResource()) { - jedis.setex(this.cardapiTicketKey, expiresInSeconds - 200, cardApiTicket); - } - } - - @Override - public void expireCardApiTicket() { - try (Jedis jedis = this.jedisPool.getResource()) { - jedis.expire(this.cardapiTicketKey, 0); - } - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java index 2bdf631bb3..b071980480 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMemberCardService.java @@ -1,11 +1,20 @@ package me.chanjar.weixin.mp.api; import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.bean.card.*; -import me.chanjar.weixin.mp.bean.membercard.*; +import me.chanjar.weixin.mp.bean.card.CardUpdateResult; +import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormRequest; +import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormResult; +import me.chanjar.weixin.mp.bean.card.MemberCardUpdateRequest; +import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult; +import me.chanjar.weixin.mp.bean.membercard.ActivatePluginParam; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardActivatedMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardCreateMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateMessage; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUpdateResult; +import me.chanjar.weixin.mp.bean.membercard.WxMpMemberCardUserInfoResult; /** - * 会员卡相关接口 + * 会员卡相关接口. * * @author YuJian(mgcnrx11 @ gmail.com) * @author yuanqixun @@ -18,46 +27,38 @@ public interface WxMpMemberCardService { String MEMBER_CARD_USER_INFO_GET = "https://api.weixin.qq.com/card/membercard/userinfo/get"; String MEMBER_CARD_UPDATE_USER = "https://api.weixin.qq.com/card/membercard/updateuser"; /** - * 会员卡激活之微信开卡接口(wx_activate=true情况调用) + * 会员卡激活之微信开卡接口(wx_activate=true情况调用). */ String MEMBER_CARD_ACTIVATEUSERFORM = "https://api.weixin.qq.com/card/membercard/activateuserform/set"; /** - * 获取会员卡开卡插件参数 + * 获取会员卡开卡插件参数. */ String MEMBER_CARD_ACTIVATE_URL = "https://api.weixin.qq.com/card/membercard/activate/geturl"; /** - * 会员卡信息更新 + * 会员卡信息更新. */ String MEMBER_CARD_UPDATE = "https://api.weixin.qq.com/card/update"; /** - * 得到WxMpService + * 得到WxMpService. */ WxMpService getWxMpService(); /** - * 会员卡创建接口 - * - * @param createJson - * @return - * @throws WxErrorException + * 会员卡创建接口. */ WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException; /** - * 会员卡创建接口 - * - * @param createMessageMessage - * @return WxMpCardCreateResult - * @throws WxErrorException + * 会员卡创建接口. */ WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException; /** - * 会员卡激活接口 + * 会员卡激活接口. * * @param activatedMessage 激活所需参数 * @return 返回json字符串 @@ -66,7 +67,7 @@ public interface WxMpMemberCardService { String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException; /** - * 拉取会员信息接口 + * 拉取会员信息接口. * * @param cardId 会员卡的CardId,微信分配 * @param code 领取会员的会员卡Code @@ -76,10 +77,10 @@ public interface WxMpMemberCardService { WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException; /** - * 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 - *

- * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时 - * add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 + * 当会员持卡消费后,支持开发者调用该接口更新会员信息. + * 会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。 + * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。 + * 同时传入add_bonus和bonus时 add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 * * @param updateUserMessage 更新会员信息所需字段消息 @@ -89,29 +90,17 @@ public interface WxMpMemberCardService { WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) throws WxErrorException; /** - * 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要) - * - * @param userFormRequest - * @return - * @throws WxErrorException + * 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要). */ MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException; /** - * 获取会员卡开卡插件参数(跳转型开卡组件需要参数) - * - * @param cardId - * @param outStr - * @return - * @throws WxErrorException + * 获取会员卡开卡插件参数(跳转型开卡组件需要参数). */ ActivatePluginParam getActivatePluginParam(String cardId, String outStr) throws WxErrorException; /** - * 更新会员卡信息 - * @param memberCardUpdateRequest - * @return - * @throws WxErrorException + * 更新会员卡信息. */ CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateRequest) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java index 8350593aa2..e85a2b65cb 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java @@ -1,5 +1,18 @@ package me.chanjar.weixin.mp.api; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import me.chanjar.weixin.common.api.WxErrorExceptionHandler; import me.chanjar.weixin.common.api.WxMessageDuplicateChecker; import me.chanjar.weixin.common.api.WxMessageInMemoryDuplicateChecker; @@ -10,18 +23,6 @@ import me.chanjar.weixin.common.util.LogExceptionHandler; import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; /** *

@@ -155,11 +156,12 @@ public WxMpMessageRouterRule rule() {
   public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map context) {
     return route(wxMessage, context, null);
   }
+
   /**
    * 处理微信消息
    */
   public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage, final Map context, WxMpService wxMpService) {
-    if(wxMpService == null){
+    if (wxMpService == null) {
       wxMpService = this.wxMpService;
     }
     final WxMpService mpService = wxMpService;
@@ -214,7 +216,10 @@ public void run() {
               WxMpMessageRouter.this.log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser());
               // 异步操作结束,session访问结束
               sessionEndAccess(wxMessage);
-            } catch (InterruptedException | ExecutionException e) {
+            } catch (InterruptedException e) {
+              WxMpMessageRouter.this.log.error("Error happened when wait task finish", e);
+              Thread.currentThread().interrupt();
+            } catch (ExecutionException e) {
               WxMpMessageRouter.this.log.error("Error happened when wait task finish", e);
             }
           }
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 c4212affc2..fead289a36 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
@@ -10,76 +10,79 @@
 import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
 import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult;
 import me.chanjar.weixin.mp.bean.result.WxMpUser;
+import me.chanjar.weixin.mp.enums.TicketType;
 
 /**
- * 微信API的Service
+ * 微信公众号API的Service.
+ *
+ * @author chanjarster
  */
 public interface WxMpService {
   /**
-   * 获取access_token
+   * 获取access_token.
    */
   String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
   /**
-   * 获得jsapi_ticket
+   * 获得各种类型的ticket.
    */
-  String GET_JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
+  String GET_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=";
   /**
-   * 长链接转短链接接口
+   * 长链接转短链接接口.
    */
   String SHORTURL_API_URL = "https://api.weixin.qq.com/cgi-bin/shorturl";
   /**
-   * 语义查询接口
+   * 语义查询接口.
    */
   String SEMANTIC_SEMPROXY_SEARCH_URL = "https://api.weixin.qq.com/semantic/semproxy/search";
   /**
-   * 用code换取oauth2的access token
+   * 用code换取oauth2的access token.
    */
   String OAUTH2_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
   /**
-   * 刷新oauth2的access token
+   * 刷新oauth2的access token.
    */
   String OAUTH2_REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s";
   /**
-   * 用oauth2获取用户信息
+   * 用oauth2获取用户信息.
    */
   String OAUTH2_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=%s";
   /**
-   * 验证oauth2的access token是否有效
+   * 验证oauth2的access token是否有效.
    */
   String OAUTH2_VALIDATE_TOKEN_URL = "https://api.weixin.qq.com/sns/auth?access_token=%s&openid=%s";
   /**
-   * 获取微信服务器IP地址
+   * 获取微信服务器IP地址.
    */
   String GET_CALLBACK_IP_URL = "https://api.weixin.qq.com/cgi-bin/getcallbackip";
   /**
-   * 第三方使用网站应用授权登录的url
+   * 第三方使用网站应用授权登录的url.
    */
   String QRCONNECT_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect";
   /**
-   * oauth2授权的url连接
+   * oauth2授权的url连接.
    */
   String CONNECT_OAUTH2_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s&connect_redirect=1#wechat_redirect";
 
   /**
-   * 获取公众号的自动回复规则
+   * 获取公众号的自动回复规则.
    */
   String GET_CURRENT_AUTOREPLY_INFO_URL = "https://api.weixin.qq.com/cgi-bin/get_current_autoreply_info";
 
   /**
-   * 公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零
+   * 公众号调用或第三方平台帮公众号调用对公众号的所有api调用(包括第三方帮其调用)次数进行清零.
    */
   String CLEAR_QUOTA_URL = "https://api.weixin.qq.com/cgi-bin/clear_quota";
 
   /**
    * 
-   * 验证消息的确来自微信服务器
+   * 验证消息的确来自微信服务器.
    * 详情请见: http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319&token=&lang=zh_CN
    * 
*/ boolean checkSignature(String timestamp, String nonce, String signature); /** - * 获取access_token, 不强制刷新access_token + * 获取access_token, 不强制刷新access_token. * * @see #getAccessToken(boolean) */ @@ -87,7 +90,7 @@ public interface WxMpService { /** *
-   * 获取access_token,本方法线程安全
+   * 获取access_token,本方法线程安全.
    * 且在多线程同时刷新时只刷新一次,避免超出2000次/日的调用次数上限
    *
    * 另:本service的所有方法都会在access_token过期时调用此方法
@@ -102,7 +105,24 @@ public interface WxMpService {
   String getAccessToken(boolean forceRefresh) throws WxErrorException;
 
   /**
-   * 获得jsapi_ticket,不强制刷新jsapi_ticket
+   * 获得ticket,不强制刷新ticket.
+   *
+   * @see #getTicket(TicketType, boolean)
+   */
+  String getTicket(TicketType type) throws WxErrorException;
+
+  /**
+   * 
+   * 获得ticket.
+   * 获得时会检查 Token是否过期,如果过期了,那么就刷新一下,否则就什么都不干
+   * 
+ * + * @param forceRefresh 强制刷新 + */ + String getTicket(TicketType type, boolean forceRefresh) throws WxErrorException; + + /** + * 获得jsapi_ticket,不强制刷新jsapi_ticket. * * @see #getJsapiTicket(boolean) */ @@ -110,7 +130,7 @@ public interface WxMpService { /** *
-   * 获得jsapi_ticket
+   * 获得jsapi_ticket.
    * 获得时会检查jsapiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
    *
    * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
@@ -122,7 +142,7 @@ public interface WxMpService {
 
   /**
    * 
-   * 创建调用jsapi时所需要的签名
+   * 创建调用jsapi时所需要的签名.
    *
    * 详情请见:http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
    * 
@@ -131,7 +151,7 @@ public interface WxMpService { /** *
-   * 长链接转短链接接口
+   * 长链接转短链接接口.
    * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=长链接转短链接接口
    * 
*/ @@ -139,7 +159,7 @@ public interface WxMpService { /** *
-   * 语义查询接口
+   * 语义查询接口.
    * 详情请见:http://mp.weixin.qq.com/wiki/index.php?title=语义理解
    * 
*/ @@ -147,7 +167,7 @@ public interface WxMpService { /** *
-   * 构造第三方使用网站应用授权登录的url
+   * 构造第三方使用网站应用授权登录的url.
    * 详情请见: 网站应用微信登录开发指南
    * URL格式为:https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
    * 
@@ -161,7 +181,7 @@ public interface WxMpService { /** *
-   * 构造oauth2授权的url连接
+   * 构造oauth2授权的url连接.
    * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息
    * 
* @@ -172,7 +192,7 @@ public interface WxMpService { /** *
-   * 用code换取oauth2的access token
+   * 用code换取oauth2的access token.
    * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=网页授权获取用户基本信息
    * 
*/ @@ -180,14 +200,14 @@ public interface WxMpService { /** *
-   * 刷新oauth2的access token
+   * 刷新oauth2的access token.
    * 
*/ WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException; /** *
-   * 用oauth2获取用户信息, 当前面引导授权时的scope是snsapi_userinfo的时候才可以
+   * 用oauth2获取用户信息, 当前面引导授权时的scope是snsapi_userinfo的时候才可以.
    * 
* * @param lang zh_CN, zh_TW, en @@ -196,7 +216,7 @@ public interface WxMpService { /** *
-   * 验证oauth2的access token是否有效
+   * 验证oauth2的access token是否有效.
    * 
*/ boolean oauth2validateAccessToken(WxMpOAuth2AccessToken oAuth2AccessToken); @@ -211,7 +231,7 @@ public interface WxMpService { /** *
-   * 获取公众号的自动回复规则
+   * 获取公众号的自动回复规则.
    * http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751299&token=&lang=zh_CN
    * 开发者可以通过该接口,获取公众号当前使用的自动回复规则,包括关注后自动回复、消息自动回复(60分钟内触发一次)、关键词自动回复。
    * 请注意:
@@ -240,12 +260,12 @@ public interface WxMpService {
   void clearQuota(String appid) throws WxErrorException;
 
   /**
-   * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求
+   * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求.
    */
   String get(String url, String queryParam) throws WxErrorException;
 
   /**
-   * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求
+   * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求.
    */
   String post(String url, String postData) throws WxErrorException;
 
@@ -260,7 +280,7 @@ public interface WxMpService {
 
   /**
    * 
-   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试
+   * 设置当微信系统响应系统繁忙时,要等待多少 retrySleepMillis(ms) * 2^(重试次数 - 1) 再发起重试.
    * @param retrySleepMillis 默认:1000ms
    * 
*/ @@ -268,131 +288,131 @@ public interface WxMpService { /** *
-   * 设置当微信系统响应系统繁忙时,最大重试次数
+   * 设置当微信系统响应系统繁忙时,最大重试次数.
    * 默认:5次
    * 
*/ void setMaxRetryTimes(int maxRetryTimes); /** - * 获取WxMpConfigStorage 对象 + * 获取WxMpConfigStorage 对象. * * @return WxMpConfigStorage */ WxMpConfigStorage getWxMpConfigStorage(); /** - * 注入 {@link WxMpConfigStorage} 的实现 + * 注入 {@link WxMpConfigStorage} 的实现. */ void setWxMpConfigStorage(WxMpConfigStorage wxConfigProvider); /** - * 返回客服接口方法实现类,以方便调用其各个接口 + * 返回客服接口方法实现类,以方便调用其各个接口. * * @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 WxMpDataCubeService */ WxMpDataCubeService getDataCubeService(); /** - * 返回用户黑名单管理相关接口方法的实现类对象,以方便调用其各个接口 + * 返回用户黑名单管理相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpUserBlacklistService */ WxMpUserBlacklistService getBlackListService(); /** - * 返回门店管理相关接口方法的实现类对象,以方便调用其各个接口 + * 返回门店管理相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpStoreService */ WxMpStoreService getStoreService(); /** - * 返回模板消息相关接口方法的实现类对象,以方便调用其各个接口 + * 返回模板消息相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpTemplateMsgService */ WxMpTemplateMsgService getTemplateMsgService(); /** - * 返回一次性订阅消息相关接口方法的实现类对象,以方便调用其各个接口 + * 返回一次性订阅消息相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpSubscribeMsgService */ WxMpSubscribeMsgService getSubscribeMsgService(); /** - * 返回硬件平台相关接口方法的实现类对象,以方便调用其各个接口 + * 返回硬件平台相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpDeviceService */ WxMpDeviceService getDeviceService(); /** - * 返回摇一摇周边相关接口方法的实现类对象,以方便调用其各个接口 + * 返回摇一摇周边相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpShakeService */ WxMpShakeService getShakeService(); /** - * 返回会员卡相关接口方法的实现类对象,以方便调用其各个接口 + * 返回会员卡相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpMemberCardService */ WxMpMemberCardService getMemberCardService(); /** - * 初始化http请求对象 + * 初始化http请求对象. */ void initHttp(); @@ -402,21 +422,21 @@ public interface WxMpService { RequestHttp getRequestHttp(); /** - * 返回群发消息相关接口方法的实现类对象,以方便调用其各个接口 + * 返回群发消息相关接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpMassMessageService */ WxMpMassMessageService getMassMessageService(); /** - * 返回AI开放接口方法的实现类对象,以方便调用其各个接口 + * 返回AI开放接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpAiOpenService */ WxMpAiOpenService getAiOpenService(); /** - * 返回WIFI接口方法的实现类对象,以方便调用其各个接口 + * 返回WIFI接口方法的实现类对象,以方便调用其各个接口. * * @return WxMpWifiService */ diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java index 7523a9f39e..71b6b17092 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpUserService.java @@ -1,18 +1,24 @@ package me.chanjar.weixin.mp.api; +import java.util.List; + import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.bean.WxMpUserQuery; +import me.chanjar.weixin.mp.bean.result.WxMpChangeOpenid; import me.chanjar.weixin.mp.bean.result.WxMpUser; import me.chanjar.weixin.mp.bean.result.WxMpUserList; -import java.util.List; - /** - * 用户管理相关操作接口 + * 用户管理相关操作接口. * * @author Binary Wang */ public interface WxMpUserService { + String USER_INFO_BATCH_GET_URL = "https://api.weixin.qq.com/cgi-bin/user/info/batchget"; + String USER_GET_URL = "https://api.weixin.qq.com/cgi-bin/user/get"; + String USER_INFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info"; + String USER_INFO_UPDATE_REMARK_URL = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark"; + String USER_CHANGE_OPENID_URL = "http://api.weixin.qq.com/cgi-bin/changeopenid"; /** *
@@ -61,9 +67,9 @@ public interface WxMpUserService {
    * 接口地址:https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN
    * 
* - * @param openids 用户openid列表 + * @param openidList 用户openid列表 */ - List userInfoList(List openids) throws WxErrorException; + List userInfoList(List openidList) throws WxErrorException; /** *
@@ -90,4 +96,17 @@ public interface WxMpUserService {
    * @param nextOpenid 可选,第一个拉取的OPENID,null为从头开始拉取
    */
   WxMpUserList userList(String nextOpenid) throws WxErrorException;
+
+  /**
+   * 
+   * 微信公众号主体变更迁移用户 openid
+   * 详情请见: http://kf.qq.com/faq/170221aUnmmU170221eUZJNf.html
+   * http请求方式: POST
+   * 接口地址:https://api.weixin.qq.com/cgi-bin/changeopenid?access_token=ACCESS_TOKEN
+   * 
+ * + * @param fromAppid 原公众号的 appid + * @param openidList 需要转换的openid,这些必须是旧账号目前关注的才行,否则会出错;一次最多100个 + */ + List changeOpenid(String fromAppid, List openidList) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java index 09d21bacff..f720461652 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java @@ -1,5 +1,12 @@ package me.chanjar.weixin.mp.api.impl; +import java.io.IOException; +import java.util.concurrent.locks.Lock; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -12,21 +19,41 @@ import me.chanjar.weixin.common.util.DataUtils; import me.chanjar.weixin.common.util.RandomUtils; import me.chanjar.weixin.common.util.crypto.SHA1; -import me.chanjar.weixin.common.util.http.*; -import me.chanjar.weixin.mp.api.*; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; +import me.chanjar.weixin.common.util.http.SimplePostRequestExecutor; +import me.chanjar.weixin.common.util.http.URIUtil; +import me.chanjar.weixin.mp.api.WxMpAiOpenService; +import me.chanjar.weixin.mp.api.WxMpCardService; +import me.chanjar.weixin.mp.api.WxMpConfigStorage; +import me.chanjar.weixin.mp.api.WxMpDataCubeService; +import me.chanjar.weixin.mp.api.WxMpDeviceService; +import me.chanjar.weixin.mp.api.WxMpKefuService; +import me.chanjar.weixin.mp.api.WxMpMassMessageService; +import me.chanjar.weixin.mp.api.WxMpMaterialService; +import me.chanjar.weixin.mp.api.WxMpMemberCardService; +import me.chanjar.weixin.mp.api.WxMpMenuService; +import me.chanjar.weixin.mp.api.WxMpQrcodeService; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.WxMpShakeService; +import me.chanjar.weixin.mp.api.WxMpStoreService; +import me.chanjar.weixin.mp.api.WxMpSubscribeMsgService; +import me.chanjar.weixin.mp.api.WxMpTemplateMsgService; +import me.chanjar.weixin.mp.api.WxMpUserBlacklistService; +import me.chanjar.weixin.mp.api.WxMpUserService; +import me.chanjar.weixin.mp.api.WxMpUserTagService; +import me.chanjar.weixin.mp.api.WxMpWifiService; import me.chanjar.weixin.mp.bean.WxMpSemanticQuery; import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo; import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken; import me.chanjar.weixin.mp.bean.result.WxMpSemanticQueryResult; import me.chanjar.weixin.mp.bean.result.WxMpUser; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.concurrent.locks.Lock; +import me.chanjar.weixin.mp.enums.TicketType; /** + * 基础实现类. + * * @author someone */ public abstract class BaseWxMpServiceImpl implements WxMpService, RequestHttp { @@ -71,31 +98,42 @@ public boolean checkSignature(String timestamp, String nonce, String signature) } @Override - public String getJsapiTicket() throws WxErrorException { - return getJsapiTicket(false); + public String getTicket(TicketType type) throws WxErrorException { + return this.getTicket(type, false); } @Override - public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { - Lock lock = this.getWxMpConfigStorage().getJsapiTicketLock(); + public String getTicket(TicketType type, boolean forceRefresh) throws WxErrorException { + Lock lock = this.getWxMpConfigStorage().getTicketLock(type); try { lock.lock(); if (forceRefresh) { - this.getWxMpConfigStorage().expireJsapiTicket(); + this.getWxMpConfigStorage().expireTicket(type); } - if (this.getWxMpConfigStorage().isJsapiTicketExpired()) { - String responseContent = execute(SimpleGetRequestExecutor.create(this), WxMpService.GET_JSAPI_TICKET_URL, null); - JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); - JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); + if (this.getWxMpConfigStorage().isTicketExpired(type)) { + String responseContent = execute(SimpleGetRequestExecutor.create(this), + WxMpService.GET_TICKET_URL + type.getCode(), null); + JsonObject tmpJsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject(); String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); - this.getWxMpConfigStorage().updateJsapiTicket(jsapiTicket, expiresInSeconds); + this.getWxMpConfigStorage().updateTicket(type, jsapiTicket, expiresInSeconds); } } finally { lock.unlock(); } - return this.getWxMpConfigStorage().getJsapiTicket(); + + return this.getWxMpConfigStorage().getTicket(type); + } + + @Override + public String getJsapiTicket() throws WxErrorException { + return this.getJsapiTicket(false); + } + + @Override + public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { + return this.getTicket(TicketType.JSAPI, forceRefresh); } @Override diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java index f492e4670d..b97da0a8a2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImpl.java @@ -5,7 +5,7 @@ import me.chanjar.weixin.common.WxType; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.AiLangType; +import me.chanjar.weixin.mp.enums.AiLangType; import me.chanjar.weixin.mp.api.WxMpAiOpenService; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.util.requestexecuter.voice.VoiceUploadRequestExecutor; 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 51c87fdcc9..5237fd6d2a 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 @@ -3,6 +3,7 @@ import java.util.Arrays; import java.util.concurrent.locks.Lock; +import me.chanjar.weixin.mp.bean.card.*; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,17 +23,16 @@ import me.chanjar.weixin.common.util.http.SimpleGetRequestExecutor; import me.chanjar.weixin.mp.api.WxMpCardService; import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateRequest; -import me.chanjar.weixin.mp.bean.card.WxMpCardLandingPageCreateResult; -import me.chanjar.weixin.mp.bean.card.WxMpCardQrcodeCreateResult; import me.chanjar.weixin.mp.bean.result.WxMpCardResult; +import me.chanjar.weixin.mp.enums.TicketType; import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** * Created by Binary Wang on 2016/7/27. + * + * @author BinaryWang */ public class WxMpCardServiceImpl implements WxMpCardService { - private final Logger log = LoggerFactory.getLogger(WxMpCardServiceImpl.class); private WxMpService wxMpService; @@ -49,7 +49,7 @@ public WxMpService getWxMpService() { } /** - * 获得卡券api_ticket,不强制刷新卡券api_ticket + * 获得卡券api_ticket,不强制刷新卡券api_ticket. * * @return 卡券api_ticket * @see #getCardApiTicket(boolean) @@ -61,7 +61,7 @@ public String getCardApiTicket() throws WxErrorException { /** *
-   * 获得卡券api_ticket
+   * 获得卡券api_ticket.
    * 获得时会检查卡券apiToken是否过期,如果过期了,那么就刷新一下,否则就什么都不干
    *
    * 详情请见:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#.E9.99.84.E5.BD
@@ -74,26 +74,28 @@ public String getCardApiTicket() throws WxErrorException {
    */
   @Override
   public String getCardApiTicket(boolean forceRefresh) throws WxErrorException {
-    Lock lock = getWxMpService().getWxMpConfigStorage().getCardApiTicketLock();
+    final TicketType type = TicketType.WX_CARD;
+    Lock lock = getWxMpService().getWxMpConfigStorage().getTicketLock(type);
     try {
       lock.lock();
 
       if (forceRefresh) {
-        this.getWxMpService().getWxMpConfigStorage().expireCardApiTicket();
+        this.getWxMpService().getWxMpConfigStorage().expireTicket(type);
       }
 
-      if (this.getWxMpService().getWxMpConfigStorage().isCardApiTicketExpired()) {
-        String responseContent = this.wxMpService.execute(SimpleGetRequestExecutor.create(this.getWxMpService().getRequestHttp()), CARD_GET_TICKET, null);
+      if (this.getWxMpService().getWxMpConfigStorage().isTicketExpired(type)) {
+        String responseContent = this.wxMpService.execute(SimpleGetRequestExecutor
+          .create(this.getWxMpService().getRequestHttp()), CARD_GET_TICKET, 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.getWxMpService().getWxMpConfigStorage().updateCardApiTicket(cardApiTicket, expiresInSeconds);
+        this.getWxMpService().getWxMpConfigStorage().updateTicket(type, cardApiTicket, expiresInSeconds);
       }
     } finally {
       lock.unlock();
     }
-    return this.getWxMpService().getWxMpConfigStorage().getCardApiTicket();
+    return this.getWxMpService().getWxMpConfigStorage().getTicket(type);
   }
 
   /**
@@ -147,7 +149,7 @@ public String decryptCardCode(String encryptCode) throws WxErrorException {
   }
 
   /**
-   * 卡券Code查询
+   * 卡券Code查询.
    *
    * @param cardId       卡券ID代表一类卡券
    * @param code         单张卡券的唯一标准
@@ -247,11 +249,11 @@ public String getCardDetail(String cardId) throws WxErrorException {
   }
 
   /**
-   * 添加测试白名单
+   * 添加测试白名单.
    *
    * @param openid 用户的openid
-   * @return
    */
+  @Override
   public String addTestWhiteList(String openid) throws WxErrorException {
     JsonArray array = new JsonArray();
     array.add(openid);
@@ -261,26 +263,29 @@ public String addTestWhiteList(String openid) throws WxErrorException {
     return respone;
   }
 
+  @Override
+  public WxMpCardCreateResult createCard(WxMpCardCreateMessage cardCreateMessage) throws WxErrorException {
+
+    String response = this.wxMpService.post(CARD_CREATE, GSON.toJson(cardCreateMessage));
+    return WxMpCardCreateResult.fromJson(response);
+  }
+
   /**
-   * 创建卡券二维码
-   *
-   * @param cardId
-   * @param outerStr
-   * @return
+   * 创建卡券二维码.
    */
+  @Override
   public WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr) throws WxErrorException {
     return createQrcodeCard(cardId, outerStr, 0);
   }
 
   /**
-   * 创建卡券二维码
+   * 创建卡券二维码.
    *
    * @param cardId    卡券编号
    * @param outerStr  二维码标识
    * @param expiresIn 失效时间,单位秒,不填默认365天
-   * @return
-   * @throws WxErrorException
    */
+  @Override
   public WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerStr, int expiresIn) throws WxErrorException {
     JsonObject jsonObject = new JsonObject();
     jsonObject.addProperty("action_name", "QR_CARD");
@@ -293,42 +298,35 @@ public WxMpCardQrcodeCreateResult createQrcodeCard(String cardId, String outerSt
     cardJson.addProperty("outer_str", outerStr);
     actionInfoJson.add("card", cardJson);
     jsonObject.add("action_info", actionInfoJson);
-    String response = this.wxMpService.post(CARD_QRCODE_CREAET, GSON.toJson(jsonObject));
-    return WxMpCardQrcodeCreateResult.fromJson(response);
+    return WxMpCardQrcodeCreateResult.fromJson(this.wxMpService.post(CARD_QRCODE_CREATE, GSON.toJson(jsonObject)));
   }
 
   /**
-   * 创建卡券货架接口
-   *
-   * @param request
-   * @return
-   * @throws WxErrorException
+   * 创建卡券货架接口.
    */
   @Override
   public WxMpCardLandingPageCreateResult createLandingPage(WxMpCardLandingPageCreateRequest request) throws WxErrorException {
-    String response = this.wxMpService.post(CARD_LANDING_PAGE_CREAET, GSON.toJson(request));
+    String response = this.wxMpService.post(CARD_LANDING_PAGE_CREATE, GSON.toJson(request));
     return WxMpCardLandingPageCreateResult.fromJson(response);
   }
 
   /**
-   * 将用户的卡券设置为失效状态
+   * 将用户的卡券设置为失效状态.
    * 详见:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1451025272&anchor=9
    *
    * @param cardId 卡券编号
    * @param code   用户会员卡号
    * @param reason 设置为失效的原因
-   * @return
-   * @throws WxErrorException
    */
   @Override
   public String unavailableCardCode(String cardId, String code, String reason) throws WxErrorException {
-    if (StringUtils.isAnyBlank(cardId, code, reason))
+    if (StringUtils.isAnyBlank(cardId, code, reason)) {
       throw new WxErrorException(WxError.builder().errorCode(41012).errorMsg("参数不完整").build());
+    }
     JsonObject jsonRequest = new JsonObject();
     jsonRequest.addProperty("card_id", cardId);
     jsonRequest.addProperty("code", code);
     jsonRequest.addProperty("reason", reason);
-    String response = this.wxMpService.post(CARD_CODE_UNAVAILABLE, GSON.toJson(jsonRequest));
-    return response;
+    return this.wxMpService.post(CARD_CODE_UNAVAILABLE, GSON.toJson(jsonRequest));
   }
 }
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java
index 0db3632217..9bdc11b365 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpMemberCardServiceImpl.java
@@ -5,20 +5,28 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import me.chanjar.weixin.mp.bean.card.*;
 import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 import com.google.gson.reflect.TypeToken;
+import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 import me.chanjar.weixin.mp.api.WxMpMemberCardService;
 import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.bean.card.AdvancedInfo;
+import me.chanjar.weixin.mp.bean.card.BaseInfo;
+import me.chanjar.weixin.mp.bean.card.CardUpdateResult;
+import me.chanjar.weixin.mp.bean.card.DateInfo;
+import me.chanjar.weixin.mp.bean.card.MemberCard;
+import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormRequest;
+import me.chanjar.weixin.mp.bean.card.MemberCardActivateUserFormResult;
+import me.chanjar.weixin.mp.bean.card.MemberCardCreateRequest;
+import me.chanjar.weixin.mp.bean.card.MemberCardUpdateRequest;
+import me.chanjar.weixin.mp.bean.card.WxMpCardCreateResult;
 import me.chanjar.weixin.mp.bean.card.enums.BusinessServiceType;
 import me.chanjar.weixin.mp.bean.card.enums.CardColor;
 import me.chanjar.weixin.mp.bean.card.enums.DateInfoType;
@@ -37,10 +45,8 @@
  * @author YuJian(mgcnrx11 @ gmail.com)
  * @version 2017/7/8
  */
+@Slf4j
 public class WxMpMemberCardServiceImpl implements WxMpMemberCardService {
-
-  private final Logger log = LoggerFactory.getLogger(WxMpMemberCardServiceImpl.class);
-
   private WxMpService wxMpService;
 
   private static final Gson GSON = WxMpGsonBuilder.create();
@@ -49,50 +55,37 @@ public class WxMpMemberCardServiceImpl implements WxMpMemberCardService {
     this.wxMpService = wxMpService;
   }
 
-  /**
-   * 得到WxMpService
-   */
   @Override
   public WxMpService getWxMpService() {
     return this.wxMpService;
   }
 
-  /**
-   * 会员卡创建接口
-   *
-   * @param createJson 创建json
-   * @return 调用返回的JSON字符串。
-   * @throws WxErrorException 接口调用失败抛出的异常
-   */
   @Override
   public WxMpCardCreateResult createMemberCard(String createJson) throws WxErrorException {
-    WxMpMemberCardCreateMessage createMessage = WxGsonBuilder.create().fromJson(createJson, WxMpMemberCardCreateMessage.class);
+    WxMpMemberCardCreateMessage createMessage = WxGsonBuilder.create()
+      .fromJson(createJson, WxMpMemberCardCreateMessage.class);
     return createMemberCard(createMessage);
   }
 
-  /**
-   * 会员卡创建接口
-   *
-   * @param createMessageMessage 创建所需参数
-   * @return WxMpCardCreateResult。
-   * @throws WxErrorException 接口调用失败抛出的异常
-   */
   @Override
-  public WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException {
+  public WxMpCardCreateResult createMemberCard(WxMpMemberCardCreateMessage createMessageMessage)
+    throws WxErrorException {
     //校验请求对象合法性
     WxMpCardCreateResult validResult = validCheck(createMessageMessage);
-    if (!validResult.isSuccess())
+    if (!validResult.isSuccess()) {
       return validResult;
+    }
+
     String response = this.wxMpService.post(MEMBER_CARD_CREAET, GSON.toJson(createMessageMessage));
     return WxMpCardCreateResult.fromJson(response);
   }
 
-  private WxMpCardCreateResult validCheck(WxMpMemberCardCreateMessage createMessageMessage) throws WxErrorException {
+  private WxMpCardCreateResult validCheck(WxMpMemberCardCreateMessage createMessageMessage) {
     if (createMessageMessage == null) {
       return WxMpCardCreateResult.failure("对象不能为空");
     }
     MemberCardCreateRequest cardCreateRequest = createMessageMessage.getCardCreateRequest();
-    if (createMessageMessage == null) {
+    if (cardCreateRequest == null) {
       return WxMpCardCreateResult.failure("会员卡对象不能为空");
     }
     String cardType = cardCreateRequest.getCardType();
@@ -105,17 +98,11 @@ private WxMpCardCreateResult validCheck(WxMpMemberCardCreateMessage createMessag
       return WxMpCardCreateResult.failure("会员卡特权说明不能为空:prerogative");
     }
     //卡片激活规则
-    if (!memberCard.isAutoActivate() && !memberCard.isWxActivate() && StringUtils.isEmpty(memberCard.getActivateUrl())) {
+    if (!memberCard.isAutoActivate() && !memberCard.isWxActivate()
+      && StringUtils.isEmpty(memberCard.getActivateUrl())) {
       return WxMpCardCreateResult.failure("会员卡激活方式为接口激活,activate_url不能为空");
     }
 
-    //积分支持
-//    if(memberCard.isSupplyBonus() && StringUtils.isEmpty(memberCard.getBonusUrl())){
-//      return WxMpCardCreateResult.failure("会员卡支持积分,bonus_url不能为空");
-//    }
-//    if(memberCard.isSupplyBonus() && memberCard.getBonusRule() == null){
-//      return WxMpCardCreateResult.failure("会员卡支持积分,bonus_rule不能为空");
-//    }
     BaseInfo baseInfo = memberCard.getBaseInfo();
     if (baseInfo == null) {
       return WxMpCardCreateResult.failure("会员卡基本信息对象base_info不能为空");
@@ -188,16 +175,24 @@ private WxMpCardCreateResult validCheck(WxMpMemberCardCreateMessage createMessag
     }
 
     //固定时长
-    if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TERM && (dateInfo.getFixedTerm() == null || dateInfo.getFixedBeginTerm() == null)) {
-      return WxMpCardCreateResult.failure("会员卡基本信息的使用日期为:" + dateInfoType.getDescription() + ",fixedTerm和fixedBeginTerm不能为空");
+    if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TERM
+      && (dateInfo.getFixedTerm() == null || dateInfo.getFixedBeginTerm() == null)) {
+      return WxMpCardCreateResult.failure(String.format("会员卡基本信息的使用日期为:%s,fixedTerm和fixedBeginTerm不能为空",
+        dateInfoType.getDescription()));
     }
 
     //固定期限
-    if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TIME_RANGE && (dateInfo.getBeginTimestamp() == null || dateInfo.getEndTimestamp() == null)) {
-      return WxMpCardCreateResult.failure("会员卡基本信息的使用日期为:" + dateInfoType.getDescription() + ",beginTimestamp 和 endTimestamp 不能为空");
+    if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TIME_RANGE
+      && (dateInfo.getBeginTimestamp() == null || dateInfo.getEndTimestamp() == null)) {
+      return WxMpCardCreateResult.failure(String.format("会员卡基本信息的使用日期为:%s,beginTimestamp 和 endTimestamp 不能为空",
+        dateInfoType.getDescription()));
     }
-    if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TIME_RANGE && (dateInfo.getBeginTimestamp() < System.currentTimeMillis() || dateInfo.getEndTimestamp() < System.currentTimeMillis() || dateInfo.getBeginTimestamp() > dateInfo.getEndTimestamp())) {
-      return WxMpCardCreateResult.failure("会员卡基本信息的使用日期为:" + dateInfoType.getDescription() + ",beginTimestamp和endTimestamp的值不合法,请检查");
+    if (dateInfoType == DateInfoType.DATE_TYPE_FIX_TIME_RANGE
+      && (dateInfo.getBeginTimestamp() * 1000 < System.currentTimeMillis()
+      || dateInfo.getEndTimestamp() * 1000 < System.currentTimeMillis()
+      || dateInfo.getBeginTimestamp() > dateInfo.getEndTimestamp())) {
+      return WxMpCardCreateResult.failure(String.format("会员卡基本信息的使用日期为:%s,beginTimestamp和endTimestamp的值不合法,请检查",
+        dateInfoType.getDescription()));
     }
 
     if (!baseInfo.isUseAllLocations() && StringUtils.isBlank(baseInfo.getLocationIdList())) {
@@ -209,9 +204,8 @@ private WxMpCardCreateResult validCheck(WxMpMemberCardCreateMessage createMessag
     if (advancedInfo != null) {
       if (advancedInfo.getBusinessServiceList() != null) {
         for (String bs : advancedInfo.getBusinessServiceList()) {
-          BusinessServiceType businessServiceType = null;
           try {
-            businessServiceType = BusinessServiceType.valueOf(bs);
+            BusinessServiceType.valueOf(bs);
           } catch (IllegalArgumentException ex) {
             return WxMpCardCreateResult.failure("会员卡高级信息的商户服务:" + bs + " 不合法");
           }
@@ -222,26 +216,11 @@ private WxMpCardCreateResult validCheck(WxMpMemberCardCreateMessage createMessag
     return WxMpCardCreateResult.success();
   }
 
-  /**
-   * 会员卡激活接口
-   *
-   * @param activatedMessage 激活所需参数
-   * @return WxMpCardCreateResult。
-   * @throws WxErrorException 接口调用失败抛出的异常
-   */
   @Override
   public String activateMemberCard(WxMpMemberCardActivatedMessage activatedMessage) throws WxErrorException {
     return this.wxMpService.post(MEMBER_CARD_ACTIVATE, GSON.toJson(activatedMessage));
   }
 
-  /**
-   * 拉取会员信息接口
-   *
-   * @param cardId 会员卡的CardId,微信分配
-   * @param code   领取会员的会员卡Code
-   * @return 会员信息的结果对象
-   * @throws WxErrorException 接口调用失败抛出的异常
-   */
   @Override
   public WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) throws WxErrorException {
     JsonObject jsonObject = new JsonObject();
@@ -256,17 +235,6 @@ public WxMpMemberCardUserInfoResult getUserInfo(String cardId, String code) thro
       }.getType());
   }
 
-  /**
-   * 当会员持卡消费后,支持开发者调用该接口更新会员信息。会员卡交易后的每次信息变更需通过该接口通知微信,便于后续消息通知及其他扩展功能。
-   * 

- * 1.开发者可以同时传入add_bonus和bonus解决由于同步失败带来的幂等性问题。同时传入add_bonus和bonus时 - * add_bonus作为积分变动消息中的变量值,而bonus作为卡面上的总积分额度显示。余额变动同理。 - * 2.开发者可以传入is_notify_bonus控制特殊的积分对账变动不发送消息,余额变动同理。 - * - * @param updateUserMessage 更新会员信息所需字段消息 - * @return 调用返回的JSON字符串。 - * @throws WxErrorException 接口调用失败抛出的异常 - */ @Override public WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessage updateUserMessage) throws WxErrorException { @@ -279,26 +247,13 @@ public WxMpMemberCardUpdateResult updateUserMemberCard(WxMpMemberCardUpdateMessa }.getType()); } - /** - * 设置会员卡激活的字段(会员卡设置:wx_activate=true 时需要) - * - * @param userFormRequest - * @return - * @throws WxErrorException - */ @Override public MemberCardActivateUserFormResult setActivateUserForm(MemberCardActivateUserFormRequest userFormRequest) throws WxErrorException { String responseContent = this.getWxMpService().post(MEMBER_CARD_ACTIVATEUSERFORM, GSON.toJson(userFormRequest)); return MemberCardActivateUserFormResult.fromJson(responseContent); } - /** - * 获取会员卡开卡插件参数(跳转型开卡组件需要参数) - * - * @param outStr - * @return - * @throws WxErrorException - */ + @Override public ActivatePluginParam getActivatePluginParam(String cardId, String outStr) throws WxErrorException { JsonObject params = new JsonObject(); params.addProperty("card_id", cardId); @@ -313,7 +268,7 @@ public ActivatePluginParam getActivatePluginParam(String cardId, String outStr) ActivatePluginParam activatePluginParam = new ActivatePluginParam(); activatePluginParam.setEncryptCardId(resultMap.get("encrypt_card_id")); activatePluginParam.setOuterStr(resultMap.get("outer_str")); - activatePluginParam.setBiz(resultMap.get("biz")+"=="); + activatePluginParam.setBiz(resultMap.get("biz") + "=="); return activatePluginParam; } catch (UnsupportedEncodingException e) { e.printStackTrace(); @@ -322,13 +277,6 @@ public ActivatePluginParam getActivatePluginParam(String cardId, String outStr) return null; } - /** - * 更新会员卡信息 - * - * @param memberCardUpdateRequest - * @return - * @throws WxErrorException - */ @Override public CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateRequest) throws WxErrorException { String response = this.wxMpService.post(MEMBER_CARD_UPDATE, GSON.toJson(memberCardUpdateRequest)); @@ -336,15 +284,9 @@ public CardUpdateResult updateCardInfo(MemberCardUpdateRequest memberCardUpdateR return result; } - /** - * 去掉url中的路径,留下请求参数部分 - * - * @param strURL url地址 - * @return url请求参数部分 - */ private static String truncateUrlPage(String strURL) { String strAllParam = null; - String[] arrSplit = null; + String[] arrSplit; arrSplit = strURL.split("[?]"); if (strURL.length() > 1) { if (arrSplit.length > 1) { @@ -357,25 +299,18 @@ private static String truncateUrlPage(String strURL) { return strAllParam; } - /** - * 解析出url参数中的键值对 - * 如 "index.jsp?Action=del&id=123",解析出Action:del,id:123存入map中 - * - * @param URL url地址 - * @return url请求参数部分 - */ - public static Map parseRequestUrl(String URL) { - Map mapRequest = new HashMap(); + public static Map parseRequestUrl(String url) { + Map mapRequest = new HashMap<>(16); - String[] arrSplit = null; + String[] arrSplit; - String strUrlParam = truncateUrlPage(URL); + String strUrlParam = truncateUrlPage(url); if (strUrlParam == null) { return mapRequest; } arrSplit = strUrlParam.split("[&]"); for (String strSplit : arrSplit) { - String[] arrSplitEqual = null; + String[] arrSplitEqual; arrSplitEqual = strSplit.split("[=]"); //解析出键值 @@ -384,7 +319,7 @@ public static Map parseRequestUrl(String URL) { mapRequest.put(arrSplitEqual[0], arrSplitEqual[1]); } else { - if (arrSplitEqual[0] != "") { + if (!"".equals(arrSplitEqual[0])) { //只有参数没有值,不加入 mapRequest.put(arrSplitEqual[0], ""); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImpl.java index def801e4e9..d8c8943d36 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImpl.java @@ -1,20 +1,26 @@ package me.chanjar.weixin.mp.api.impl; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.WxMpUserService; import me.chanjar.weixin.mp.bean.WxMpUserQuery; +import me.chanjar.weixin.mp.bean.result.WxMpChangeOpenid; import me.chanjar.weixin.mp.bean.result.WxMpUser; import me.chanjar.weixin.mp.bean.result.WxMpUserList; - -import java.util.List; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** * Created by Binary Wang on 2016/7/21. + * + * @author BinaryWang */ public class WxMpUserServiceImpl implements WxMpUserService { - private static final String API_URL_PREFIX = "https://api.weixin.qq.com/cgi-bin/user"; private WxMpService wxMpService; public WxMpUserServiceImpl(WxMpService wxMpService) { @@ -23,11 +29,10 @@ public WxMpUserServiceImpl(WxMpService wxMpService) { @Override public void userUpdateRemark(String openid, String remark) throws WxErrorException { - String url = API_URL_PREFIX + "/info/updateremark"; JsonObject json = new JsonObject(); json.addProperty("openid", openid); json.addProperty("remark", remark); - this.wxMpService.post(url, json.toString()); + this.wxMpService.post(USER_INFO_UPDATE_REMARK_URL, json.toString()); } @Override @@ -37,32 +42,38 @@ public WxMpUser userInfo(String openid) throws WxErrorException { @Override public WxMpUser userInfo(String openid, String lang) throws WxErrorException { - String url = API_URL_PREFIX + "/info"; lang = lang == null ? "zh_CN" : lang; - String responseContent = this.wxMpService.get(url, + String responseContent = this.wxMpService.get(USER_INFO_URL, "openid=" + openid + "&lang=" + lang); return WxMpUser.fromJson(responseContent); } @Override - public WxMpUserList userList(String next_openid) throws WxErrorException { - String url = API_URL_PREFIX + "/get"; - String responseContent = this.wxMpService.get(url, - next_openid == null ? null : "next_openid=" + next_openid); + public WxMpUserList userList(String nextOpenid) throws WxErrorException { + String responseContent = this.wxMpService.get(USER_GET_URL, + nextOpenid == null ? null : "next_openid=" + nextOpenid); return WxMpUserList.fromJson(responseContent); } @Override - public List userInfoList(List openids) + public List changeOpenid(String fromAppid, List openidList) throws WxErrorException { + Map map = new HashMap<>(); + map.put("from_appid", fromAppid); + map.put("openid_list", openidList); + String responseContent = this.wxMpService.post(USER_CHANGE_OPENID_URL, WxMpGsonBuilder.create().toJson(map)); + + return WxMpChangeOpenid.fromJsonList(responseContent); + } + + @Override + public List userInfoList(List openidList) throws WxErrorException { - return this.userInfoList(new WxMpUserQuery(openids)); + return this.userInfoList(new WxMpUserQuery(openidList)); } @Override public List userInfoList(WxMpUserQuery userQuery) throws WxErrorException { - String url = API_URL_PREFIX + "/info/batchget"; - String responseContent = this.wxMpService.post(url, - userQuery.toJsonString()); + String responseContent = this.wxMpService.post(USER_INFO_BATCH_GET_URL, userQuery.toJsonString()); return WxMpUser.fromJsonList(responseContent); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java new file mode 100644 index 0000000000..c97f88f2fc --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/Card.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public class Card implements Serializable { + + private static final long serialVersionUID = -3697110761983756780L; + + /** + * 基本信息. + */ + @SerializedName("base_info") + private BaseInfo baseInfo; + + /** + * 创建优惠券特有的高级字段. + */ + @SerializedName("advanced_info") + private AdvancedInfo advancedInfo; + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardCreateRequest.java new file mode 100644 index 0000000000..8cb16bc7b2 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CardCreateRequest.java @@ -0,0 +1,13 @@ +package me.chanjar.weixin.mp.bean.card; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @Author leeis + * @Date 2018/12/29 + */ +@Data +public class CardCreateRequest implements Serializable { +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java new file mode 100644 index 0000000000..3d4de40c0b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCard.java @@ -0,0 +1,39 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public final class CashCard extends Card implements Serializable { + + private static final long serialVersionUID = 6965491956462769745L; + + /** + * 代金券专用,表示起用金额(单位为分),如果无起用门槛则填0 + */ + @SerializedName("least_cost") + private int leastCost; + + /** + * 代金券专用,表示减免金额。(单位为分) + */ + @SerializedName("reduce_cost") + private int reduceCost; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static CashCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, CashCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java new file mode 100644 index 0000000000..dcefab0e29 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/CashCardCreateRequest.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public class CashCardCreateRequest extends CardCreateRequest implements Serializable { + @SerializedName("card_type") + private String cardType = "CASH"; + + private CashCard cash; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java new file mode 100644 index 0000000000..b2c3a13ffc --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCard.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public final class DiscountCard extends Card implements Serializable { + + private static final long serialVersionUID = 1704610082472315077L; + + /** + * 折扣券专用,表示打折额度(百分比)。填30就是七折。 + */ + private int discount; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static DiscountCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, DiscountCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java new file mode 100644 index 0000000000..869c487c92 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/DiscountCardCreateRequest.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public class DiscountCardCreateRequest extends CardCreateRequest implements Serializable { + @SerializedName("card_type") + private String cardType = "DISCOUNT"; + + private DiscountCard discount; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCard.java new file mode 100644 index 0000000000..dcfba74da7 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCard.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public final class GeneralCard extends Card implements Serializable { + + private static final long serialVersionUID = -1577656733441132585L; + + /** + * 兑换券专用,填写兑换内容的名称。 + */ + @SerializedName("default_detail") + private String defaultDetail; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static GeneralCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, GeneralCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCardCreateRequest.java new file mode 100644 index 0000000000..8a9c3d1801 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GeneralCardCreateRequest.java @@ -0,0 +1,26 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public class GeneralCardCreateRequest extends CardCreateRequest implements Serializable { + @SerializedName("card_type") + private String cardType = "GENERAL_COUPON"; + + @SerializedName("general_coupon") + private GeneralCard generalCoupon; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java new file mode 100644 index 0000000000..0bfb5359ba --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCard.java @@ -0,0 +1,32 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public final class GiftCard extends Card implements Serializable { + + private static final long serialVersionUID = -6168739707511792266L; + + /** + * 兑换券专用,填写兑换内容的名称。 + */ + private String gift; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static GiftCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, GiftCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java new file mode 100644 index 0000000000..8791b70a9d --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GiftCardCreateRequest.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public class GiftCardCreateRequest extends CardCreateRequest implements Serializable { + @SerializedName("card_type") + private String cardType = "GIFT"; + + private GiftCard gift; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java new file mode 100644 index 0000000000..b0df7262bb --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCard.java @@ -0,0 +1,33 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public final class GrouponCard extends Card implements Serializable { + + private static final long serialVersionUID = 3221312561666697005L; + + /** + * 团购券专用,团购详情 + */ + @SerializedName("deal_detail") + private String dealDetail; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static GrouponCard fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, GrouponCard.class); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java new file mode 100644 index 0000000000..48b1e3e7da --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/GrouponCardCreateRequest.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +/** + * . + * @author leeis + * @Date 2018/12/29 + */ +@Data +public class GrouponCardCreateRequest extends CardCreateRequest implements Serializable { + @SerializedName("card_type") + private String cardType = "GROUPON"; + + private GrouponCard groupon; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateMessage.java new file mode 100644 index 0000000000..760002cb52 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/card/WxMpCardCreateMessage.java @@ -0,0 +1,23 @@ +package me.chanjar.weixin.mp.bean.card; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; + +@Data +public final class WxMpCardCreateMessage implements Serializable { + + @SerializedName("card") + private CardCreateRequest cardCreateRequest; + + @Override + public String toString() { + return WxMpGsonBuilder.create().toJson(this); + } + + public static WxMpCardCreateMessage fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpCardCreateMessage.class); + } +} 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 cbd6970597..368f70e383 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 @@ -3,6 +3,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.nio.charset.StandardCharsets; import java.util.Map; import org.apache.commons.io.IOUtils; @@ -445,6 +446,8 @@ public class WxMpXmlMessage implements Serializable { /** * 审核结果,成功succ 或失败fail. + * + * 在商品审核结果推送时,verify_ok表示审核通过,verify_not_pass表示审核未通过。 */ @XStreamAlias("Result") private String result; @@ -566,6 +569,68 @@ public class WxMpXmlMessage implements Serializable { @XStreamAlias("Reason") private String reason; + /////////////////////////////////////// + // 扫一扫事件推送 + /////////////////////////////////////// + /** + * 商品编码标准 + */ + @XStreamAlias("KeyStandard") + private String keyStandard; + /** + * 商品编码内容 + */ + @XStreamAlias("KeyStr") + private String keyStr; + + /** + * 用户在微信内设置的国家 + */ + @XStreamAlias("Country") + private String country; + + /** + * 用户在微信内设置的省份 + */ + @XStreamAlias("Province") + private String province; + + /** + * 用户在微信内设置的城市 + */ + @XStreamAlias("City") + private String city; + + /** + * 用户的性别,1为男性,2为女性,0代表未知 + */ + @XStreamAlias("Sex") + private String sex; + + /** + * 打开商品主页的场景,1为扫码,2为其他打开场景(如会话、收藏或朋友圈) + */ + @XStreamAlias("Scene") + private String scene; + + /** + * 调用“获取商品二维码接口”时传入的extinfo,为标识参数 + */ + @XStreamAlias("ExtInfo") + private String extInfo; + + /** + * 用户的实时地理位置信息(目前只精确到省一级),可在国家统计局网站查到对应明细: http://www.stats.gov.cn/tjsj/tjbz/xzqhdm/201504/t20150415_712722.html + */ + @XStreamAlias("RegionCode") + private String regionCode; + + /** + * 审核未通过的原因。 + */ + @XStreamAlias("ReasonMsg") + private String reasonMsg; + public static WxMpXmlMessage fromXml(String xml) { //修改微信变态的消息内容格式,方便解析 xml = xml.replace("", ""); @@ -598,7 +663,7 @@ public static WxMpXmlMessage fromEncryptedXml(String encryptedXml, WxMpConfigSto 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); + return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxMpConfigStorage, timestamp, nonce, msgSignature); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpChangeOpenid.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpChangeOpenid.java new file mode 100644 index 0000000000..203aa97f5b --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpChangeOpenid.java @@ -0,0 +1,34 @@ +package me.chanjar.weixin.mp.bean.result; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import lombok.Data; +import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.List; + +/** + * 主体变更迁移用户 openid 返回. + * + * @author 007gzs + */ +@Data +public class WxMpChangeOpenid implements Serializable { + private static final long serialVersionUID = -8132023284876534743L; + private String oriOpenid; + private String newOpenid; + private String errMsg; + public static WxMpChangeOpenid fromJson(String json) { + return WxMpGsonBuilder.create().fromJson(json, WxMpChangeOpenid.class); + } + public static List fromJsonList(String json) { + Type collectionType = new TypeToken>() { + }.getType(); + Gson gson = WxMpGsonBuilder.create(); + JsonObject jsonObject = gson.fromJson(json, JsonObject.class); + return gson.fromJson(jsonObject.get("result_list"), collectionType); + } +} 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 3923374366..f04f8c91a8 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 @@ -55,6 +55,7 @@ public class WxMpTemplateMessage implements Serializable { /** * 模板数据. */ + @Builder.Default private List data = new ArrayList<>(); public WxMpTemplateMessage addData(WxMpTemplateData datum) { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/AiLangType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/AiLangType.java similarity index 84% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/AiLangType.java rename to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/AiLangType.java index 54b61b1e51..b37772b01a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/AiLangType.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/AiLangType.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.mp; +package me.chanjar.weixin.mp.enums; import lombok.Getter; @@ -13,11 +13,11 @@ @Getter public enum AiLangType { /** - * 中文 汉语 + * 中文 汉语. */ zh_CN("zh_CN"), /** - * 英文 英语 + * 英文 英语. */ en_US("en_US"); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/TicketType.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/TicketType.java new file mode 100644 index 0000000000..02de06f6d1 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/enums/TicketType.java @@ -0,0 +1,35 @@ +package me.chanjar.weixin.mp.enums; + +import lombok.Getter; + +/** + *

+ * ticket类型枚举
+ * Created by Binary Wang on 2018/11/18.
+ * 
+ * + * @author Binary Wang + */ +@Getter +public enum TicketType { + /** + * jsapi + */ + JSAPI("jsapi"), + /** + * sdk + */ + SDK("2"), + /** + * 微信卡券 + */ + WX_CARD("wx_card"); + /** + * type代码 + */ + private String code; + + TicketType(String code) { + this.code = code; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpChangeOpenidGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpChangeOpenidGsonAdapter.java new file mode 100644 index 0000000000..430726ca49 --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpChangeOpenidGsonAdapter.java @@ -0,0 +1,25 @@ +package me.chanjar.weixin.mp.util.json; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import me.chanjar.weixin.common.util.json.GsonHelper; +import me.chanjar.weixin.mp.bean.result.WxMpChangeOpenid; + +import java.lang.reflect.Type; + +public class WxMpChangeOpenidGsonAdapter implements JsonDeserializer { + + @Override + public WxMpChangeOpenid deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject o = json.getAsJsonObject(); + WxMpChangeOpenid changeOpenid = new WxMpChangeOpenid(); + changeOpenid.setOriOpenid(GsonHelper.getString(o, "ori_openid")); + changeOpenid.setNewOpenid(GsonHelper.getString(o, "new_openid")); + changeOpenid.setErrMsg(GsonHelper.getString(o, "err_msg")); + return changeOpenid; + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java index e34128eaf5..a4357e9117 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpGsonBuilder.java @@ -25,6 +25,7 @@ public class WxMpGsonBuilder { INSTANCE.registerTypeAdapter(WxMpMassTagMessage.class, new WxMpMassTagMessageGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMassOpenIdsMessage.class, new WxMpMassOpenIdsMessageGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpUser.class, new WxMpUserGsonAdapter()); + INSTANCE.registerTypeAdapter(WxMpChangeOpenid.class, new WxMpChangeOpenidGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpUserList.class, new WxUserListGsonAdapter()); INSTANCE.registerTypeAdapter(WxMpMassVideo.class, new WxMpMassVideoAdapter()); INSTANCE.registerTypeAdapter(WxMpMassSendResult.class, new WxMpMassSendResultAdapter()); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteOkhttpRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteOkhttpRequestExecutor.java index 1635793e95..2e4a37ec42 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteOkhttpRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteOkhttpRequestExecutor.java @@ -1,15 +1,21 @@ package me.chanjar.weixin.mp.util.requestexecuter.material; +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import me.chanjar.weixin.common.WxType; import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo; -import okhttp3.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; +import okhttp3.FormBody; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; /** * Created by ecoolper on 2017/5/5. @@ -17,11 +23,15 @@ public class MaterialDeleteOkhttpRequestExecutor extends MaterialDeleteRequestExecutor { private final Logger logger = LoggerFactory.getLogger(this.getClass()); - public MaterialDeleteOkhttpRequestExecutor(RequestHttp requestHttp) { super(requestHttp); } + @Override + public void execute(String uri, String data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + @Override public Boolean execute(String uri, String materialId) throws WxErrorException, IOException { logger.debug("MaterialDeleteOkhttpRequestExecutor is running"); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteRequestExecutor.java index a185b54d75..b1fd2ca90b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialDeleteRequestExecutor.java @@ -1,7 +1,11 @@ package me.chanjar.weixin.mp.util.requestexecuter.material; +import java.io.IOException; + +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; public abstract class MaterialDeleteRequestExecutor implements RequestExecutor { protected RequestHttp requestHttp; @@ -10,6 +14,11 @@ public MaterialDeleteRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, String data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoRequestExecutor.java index 62ef709aca..a636ed2696 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialNewsInfoRequestExecutor.java @@ -1,7 +1,11 @@ package me.chanjar.weixin.mp.util.requestexecuter.material; +import java.io.IOException; + +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews; public abstract class MaterialNewsInfoRequestExecutor implements RequestExecutor { @@ -11,6 +15,11 @@ public MaterialNewsInfoRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, String data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadRequestExecutor.java index c279be3213..4e518669b1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialUploadRequestExecutor.java @@ -1,7 +1,11 @@ package me.chanjar.weixin.mp.util.requestexecuter.material; +import java.io.IOException; + +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; import me.chanjar.weixin.mp.bean.material.WxMpMaterial; import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult; @@ -15,6 +19,11 @@ public MaterialUploadRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, WxMpMaterial data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoRequestExecutor.java index b09fd75ae2..ee0575929b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVideoInfoRequestExecutor.java @@ -1,30 +1,37 @@ package me.chanjar.weixin.mp.util.requestexecuter.material; - import me.chanjar.weixin.common.util.http.RequestExecutor; - import me.chanjar.weixin.common.util.http.RequestHttp; - - import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; +import java.io.IOException; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; +import me.chanjar.weixin.mp.bean.material.WxMpMaterialVideoInfoResult; public abstract class MaterialVideoInfoRequestExecutor implements RequestExecutor { protected RequestHttp requestHttp; public MaterialVideoInfoRequestExecutor(RequestHttp requestHttp) { - this.requestHttp = requestHttp; - } - - public static RequestExecutor create(RequestHttp requestHttp) { - switch (requestHttp.getRequestType()) { - case APACHE_HTTP: - return new MaterialVideoInfoApacheHttpRequestExecutor(requestHttp); - case JODD_HTTP: - return new MaterialVideoInfoJoddHttpRequestExecutor(requestHttp); - case OK_HTTP: - return new MaterialVideoInfoOkhttpRequestExecutor(requestHttp); - default: - return null; - } - } - - } + this.requestHttp = requestHttp; + } + + @Override + public void execute(String uri, String data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + + public static RequestExecutor create(RequestHttp requestHttp) { + switch (requestHttp.getRequestType()) { + case APACHE_HTTP: + return new MaterialVideoInfoApacheHttpRequestExecutor(requestHttp); + case JODD_HTTP: + return new MaterialVideoInfoJoddHttpRequestExecutor(requestHttp); + case OK_HTTP: + return new MaterialVideoInfoOkhttpRequestExecutor(requestHttp); + default: + return null; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadRequestExecutor.java index c11c41cce0..c00ac148e4 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/material/MaterialVoiceAndImageDownloadRequestExecutor.java @@ -1,13 +1,15 @@ package me.chanjar.weixin.mp.util.requestexecuter.material; -import me.chanjar.weixin.common.util.http.RequestExecutor; -import me.chanjar.weixin.common.util.http.RequestHttp; - import java.io.File; +import java.io.IOException; import java.io.InputStream; -public abstract class MaterialVoiceAndImageDownloadRequestExecutor - implements RequestExecutor { +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.util.http.RequestExecutor; +import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; + +public abstract class MaterialVoiceAndImageDownloadRequestExecutor implements RequestExecutor { protected RequestHttp requestHttp; protected File tmpDirFile; @@ -16,6 +18,11 @@ public MaterialVoiceAndImageDownloadRequestExecutor(RequestHttp requestHttp, Fil this.tmpDirFile = tmpDirFile; } + @Override + public void execute(String uri, String data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp, File tmpDirFile) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadRequestExecutor.java index c937fbe51a..18678f87f5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/media/MediaImgUploadRequestExecutor.java @@ -1,11 +1,14 @@ package me.chanjar.weixin.mp.util.requestexecuter.media; +import java.io.File; +import java.io.IOException; + +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; import me.chanjar.weixin.mp.bean.material.WxMediaImgUploadResult; -import java.io.File; - /** * @author miller */ @@ -16,6 +19,11 @@ public MediaImgUploadRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, File data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeRequestExecutor.java index 7c666cd0d5..ca4f6e6d4c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/qrcode/QrCodeRequestExecutor.java @@ -1,15 +1,17 @@ package me.chanjar.weixin.mp.util.requestexecuter.qrcode; +import java.io.File; +import java.io.IOException; + import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; -import java.io.File; - /** - * 获得QrCode图片 请求执行器 + * 获得QrCode图片 请求执行器. * * @author chanjarster */ @@ -20,6 +22,11 @@ public QrCodeRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, WxMpQrCodeTicket data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) throws WxErrorException { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadRequestExecutor.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadRequestExecutor.java index 34c7ae2108..32bab9f60e 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadRequestExecutor.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/requestexecuter/voice/VoiceUploadRequestExecutor.java @@ -1,9 +1,12 @@ package me.chanjar.weixin.mp.util.requestexecuter.voice; +import java.io.File; +import java.io.IOException; + +import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; - -import java.io.File; +import me.chanjar.weixin.common.util.http.ResponseHandler; /** *
@@ -19,6 +22,11 @@ public VoiceUploadRequestExecutor(RequestHttp requestHttp) {
     this.requestHttp = requestHttp;
   }
 
+  @Override
+  public void execute(String uri, File data, ResponseHandler handler) throws WxErrorException, IOException {
+    handler.handle(this.execute(uri, data));
+  }
+
   public static RequestExecutor create(RequestHttp requestHttp) {
     switch (requestHttp.getRequestType()) {
       case APACHE_HTTP:
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java
deleted file mode 100644
index ffcd232cf4..0000000000
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package me.chanjar.weixin.mp.api;
-
-import com.google.inject.Inject;
-import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.mp.api.test.ApiTestModule;
-import org.apache.commons.lang3.StringUtils;
-import org.testng.*;
-import org.testng.annotations.*;
-
-/**
- * 基础API测试
- *
- * @author chanjarster
- */
-@Test(groups = "baseAPI")
-@Guice(modules = ApiTestModule.class)
-public class WxMpBaseAPITest {
-
-  @Inject
-  protected WxMpService wxService;
-
-  public void testRefreshAccessToken() throws WxErrorException {
-    WxMpConfigStorage configStorage = this.wxService.getWxMpConfigStorage();
-    String before = configStorage.getAccessToken();
-    this.wxService.getAccessToken(false);
-
-    String after = configStorage.getAccessToken();
-    Assert.assertNotEquals(before, after);
-    Assert.assertTrue(StringUtils.isNotBlank(after));
-  }
-
-  public void testJsapiTicket() throws WxErrorException {
-    String jsapiTicket = this.wxService.getJsapiTicket(false);
-    System.out.println(jsapiTicket);
-    Assert.assertNotNull(jsapiTicket);
-  }
-
-}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java
index f10f988866..1e61c1faa8 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpAiOpenServiceImplTest.java
@@ -2,7 +2,7 @@
 
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.error.WxErrorException;
-import me.chanjar.weixin.mp.AiLangType;
+import me.chanjar.weixin.mp.enums.AiLangType;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.api.test.ApiTestModule;
 import org.testng.annotations.Guice;
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java
index 06945d9a08..5623299394 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpCardServiceImplTest.java
@@ -2,8 +2,10 @@
 
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.bean.WxCardApiSignature;
+import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.api.test.ApiTestModule;
+import me.chanjar.weixin.mp.bean.card.*;
 import me.chanjar.weixin.mp.bean.result.WxMpCardResult;
 import org.testng.annotations.*;
 
@@ -100,4 +102,98 @@ public void testUnavailableCardCode() throws Exception {
     assertNotNull(result);
     System.out.println(result);
   }
+
+  @Test
+  public void testCreateGrouponCard() throws WxErrorException {
+
+    BaseInfo base = new BaseInfo();
+    base.setLogoUrl("http://mmbiz.qpic.cn/mmbiz/iaL1LJM1mF9aRKPZJkmG8xXhiaHqkKSVMMWeN3hLut7X7hicFNjakmxibMLGWpXrEXB33367o7zHN0CwngnQY7zb7g/0");
+    base.setBrandName("测试优惠券");
+    base.setCodeType("CODE_TYPE_QRCODE");
+    base.setTitle("测试标题");
+    base.setColor("Color010");
+    base.setNotice("测试Notice");
+    base.setServicePhone("020-88888888");
+    base.setDescription("不可与其他优惠同享\\n如需团购券发票,请在消费时向商户提出\\n店内均可使用,仅限堂食");
+    DateInfo info = new DateInfo();
+    info.setType("DATE_TYPE_FIX_TERM");
+    info.setFixedBeginTerm(0);
+    info.setFixedTerm(30);
+    base.setDateInfo(info);
+    Sku sku = new Sku();
+    sku.setQuantity(100);
+    base.setSku(sku);
+    base.setGetLimit(1);
+    base.setCanShare(true);
+    base.setCanGiveFriend(true);
+    base.setUseAllLocations(true);
+    base.setCenterTitle("顶部居中按钮");
+    base.setCenterSubTitle("按钮下方的wording");
+    base.setCenterUrl("www.qq.com");
+    base.setCustomUrl("http://www.qq.com");
+    base.setCustomUrlName("立即使用");
+    base.setCustomUrlSubTitle("副标题tip");
+    base.setPromotionUrlName("更多优惠");
+    base.setPromotionUrl("http://www.qq.com");
+    base.setLocationIdList("1234");
+
+    //团购券
+    WxMpCardCreateMessage grouponMessage = new WxMpCardCreateMessage();
+    GrouponCardCreateRequest grouponCardCreateRequest = new GrouponCardCreateRequest();
+    GrouponCard grouponCard = new GrouponCard();
+    grouponCard.setBaseInfo(base);
+    grouponCard.setDealDetail("deal detail");
+
+    grouponCardCreateRequest.setGroupon(grouponCard);
+    grouponMessage.setCardCreateRequest(grouponCardCreateRequest);
+
+    System.out.println(this.wxService.getCardService().createCard(grouponMessage));
+
+    //现金券
+    WxMpCardCreateMessage cashMessage = new WxMpCardCreateMessage();
+    CashCardCreateRequest cashCardCreateRequest = new CashCardCreateRequest();
+    CashCard cashCard = new CashCard();
+    cashCard.setBaseInfo(base);
+    cashCard.setLeastCost(1000);
+    cashCard.setReduceCost(100);
+
+    cashCardCreateRequest.setCash(cashCard);
+    cashMessage.setCardCreateRequest(cashCardCreateRequest);
+
+    System.out.println(this.wxService.getCardService().createCard(cashMessage));
+
+    //折扣券
+    WxMpCardCreateMessage discountMessage = new WxMpCardCreateMessage();
+    DiscountCardCreateRequest discountCardCreateRequest = new DiscountCardCreateRequest();
+    DiscountCard discountCard = new DiscountCard();
+    discountCard.setBaseInfo(base);
+    discountCard.setDiscount(30);
+
+    discountCardCreateRequest.setDiscount(discountCard);
+    discountMessage.setCardCreateRequest(discountCardCreateRequest);
+
+    System.out.println(this.wxService.getCardService().createCard(discountMessage));
+
+    //兑换券
+    WxMpCardCreateMessage giftMessage = new WxMpCardCreateMessage();
+    GiftCardCreateRequest giftCardCreateRequest = new GiftCardCreateRequest();
+    GiftCard giftCard = new GiftCard();
+    giftCard.setBaseInfo(base);
+    giftCard.setGift("星巴克雪瑞纳咖啡大杯");
+
+    giftCardCreateRequest.setGift(giftCard);
+    giftMessage.setCardCreateRequest(giftCardCreateRequest);
+    System.out.println(this.wxService.getCardService().createCard(giftMessage));
+
+    //普通兑换券
+    WxMpCardCreateMessage generalMessage = new WxMpCardCreateMessage();
+    GeneralCardCreateRequest generalCardCreateRequest = new GeneralCardCreateRequest();
+    GeneralCard generalCard = new GeneralCard();
+    generalCard.setBaseInfo(base);
+    generalCard.setDefaultDetail("音乐木盒");
+
+    generalCardCreateRequest.setGeneralCoupon(generalCard);
+    generalMessage.setCardCreateRequest(generalCardCreateRequest);
+    System.out.println(this.wxService.getCardService().createCard(generalMessage));
+  }
 }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java
index e9999f577d..7c36930b0a 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImplTest.java
@@ -1,14 +1,18 @@
 package me.chanjar.weixin.mp.api.impl;
 
+import org.apache.commons.lang3.StringUtils;
+import org.testng.*;
+import org.testng.annotations.*;
+
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.api.WxConsts;
 import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpConfigStorage;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.api.test.ApiTestModule;
 import me.chanjar.weixin.mp.api.test.TestConfigStorage;
 import me.chanjar.weixin.mp.bean.result.WxMpCurrentAutoReplyInfo;
-import org.testng.*;
-import org.testng.annotations.*;
+import me.chanjar.weixin.mp.enums.TicketType;
 
 import static org.testng.Assert.*;
 
@@ -40,4 +44,19 @@ public void testBuildQrConnectUrl() {
     System.out.println(qrConnectUrl);
   }
 
+  public void testGetTicket() throws WxErrorException {
+    String ticket = this.wxService.getTicket(TicketType.SDK, false);
+    System.out.println(ticket);
+    Assert.assertNotNull(ticket);
+  }
+
+  public void testRefreshAccessToken() throws WxErrorException {
+    WxMpConfigStorage configStorage = this.wxService.getWxMpConfigStorage();
+    String before = configStorage.getAccessToken();
+    this.wxService.getAccessToken(false);
+
+    String after = configStorage.getAccessToken();
+    Assert.assertNotEquals(before, after);
+    Assert.assertTrue(StringUtils.isNotBlank(after));
+  }
 }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java
index 0a4cd21a17..fa73b8f4d8 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java
@@ -1,5 +1,16 @@
 package me.chanjar.weixin.mp.api.impl;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import me.chanjar.weixin.mp.api.WxMpUserService;
+import me.chanjar.weixin.mp.bean.result.WxMpChangeOpenid;
+import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;
+import org.testng.*;
+import org.testng.annotations.*;
+
 import com.google.inject.Inject;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.mp.api.WxMpService;
@@ -8,11 +19,9 @@
 import me.chanjar.weixin.mp.bean.WxMpUserQuery;
 import me.chanjar.weixin.mp.bean.result.WxMpUser;
 import me.chanjar.weixin.mp.bean.result.WxMpUserList;
-import org.testng.*;
-import org.testng.annotations.*;
 
-import java.util.ArrayList;
-import java.util.List;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 /**
  * 测试用户相关的接口
@@ -20,7 +29,7 @@
  * @author chanjarster
  * @author Binary Wang
  */
-@Test(groups = "userAPI")
+@Test
 @Guice(modules = ApiTestModule.class)
 public class WxMpUserServiceImplTest {
 
@@ -68,10 +77,56 @@ public void testUserInfoListByWxMpUserQuery() throws WxErrorException {
   public void testUserList() throws WxErrorException {
     WxMpUserList wxMpUserList = this.wxService.getUserService().userList(null);
     Assert.assertNotNull(wxMpUserList);
-    Assert.assertFalse(wxMpUserList.getCount() == -1);
-    Assert.assertFalse(wxMpUserList.getTotal() == -1);
-    Assert.assertFalse(wxMpUserList.getOpenids().size() == -1);
+    Assert.assertNotEquals(-1, wxMpUserList.getCount());
+    Assert.assertNotEquals(-1, wxMpUserList.getTotal());
+    Assert.assertNotEquals(-1, wxMpUserList.getOpenids().size());
     System.out.println(wxMpUserList);
   }
 
+  public void testChangeOpenid() throws WxErrorException {
+    List openids = new ArrayList<>();
+    openids.add(this.configProvider.getOpenid());
+    List wxMpChangeOpenidList = this.wxService.getUserService()
+      .changeOpenid("原公众号appid", openids);
+    Assert.assertNotNull(wxMpChangeOpenidList);
+    Assert.assertEquals(1, wxMpChangeOpenidList.size());
+    WxMpChangeOpenid wxMpChangeOpenid = wxMpChangeOpenidList.get(0);
+    Assert.assertNotNull(wxMpChangeOpenid);
+    Assert.assertEquals(this.configProvider.getOpenid(), wxMpChangeOpenid.getOriOpenid());
+    System.out.println(wxMpChangeOpenid);
+  }
+
+  public static class MockTest {
+    private WxMpService wxService = mock(WxMpService.class);
+    @Test
+    public void testMockChangeOpenid() throws WxErrorException {
+      List openids = new ArrayList<>();
+      openids.add("oEmYbwN-n24jxvk4Sox81qedINkQ");
+      openids.add("oEmYbwH9uVd4RKJk7ZZg6SzL6tTo");
+      String fromAppid = "old_appid";
+      Map map = new HashMap<>();
+      map.put("from_appid", fromAppid);
+      map.put("openid_list", openids);
+
+      String returnJson = "{\"errcode\": 0,\"errmsg\": \"ok\",\"result_list\": [{\"ori_openid\": \"oEmYbwN-n24jxvk4Sox81qedINkQ\",\"new_openid\": \"o2FwqwI9xCsVadFah_HtpPfaR-X4\",\"err_msg\": \"ok\"},{\"ori_openid\": \"oEmYbwH9uVd4RKJk7ZZg6SzL6tTo\",\"err_msg\": \"ori_openid error\"}]}";
+      when(wxService.post(WxMpUserService.USER_CHANGE_OPENID_URL, WxMpGsonBuilder.create().toJson(map))).thenReturn(returnJson);
+      List wxMpChangeOpenidList = this.wxService.getUserService()
+        .changeOpenid(fromAppid, openids);
+      Assert.assertNotNull(wxMpChangeOpenidList);
+      Assert.assertEquals(2, wxMpChangeOpenidList.size());
+      WxMpChangeOpenid wxMpChangeOpenid = wxMpChangeOpenidList.get(0);
+      Assert.assertNotNull(wxMpChangeOpenid);
+      Assert.assertEquals("oEmYbwN-n24jxvk4Sox81qedINkQ", wxMpChangeOpenid.getOriOpenid());
+      Assert.assertEquals("o2FwqwI9xCsVadFah_HtpPfaR-X4", wxMpChangeOpenid.getNewOpenid());
+      Assert.assertEquals("ok", wxMpChangeOpenid.getErrMsg());
+      wxMpChangeOpenid = wxMpChangeOpenidList.get(1);
+      Assert.assertNotNull(wxMpChangeOpenid);
+      Assert.assertEquals("oEmYbwH9uVd4RKJk7ZZg6SzL6tTo", wxMpChangeOpenid.getOriOpenid());
+      Assert.assertNull(wxMpChangeOpenid.getNewOpenid());
+      Assert.assertEquals("ori_openid error", wxMpChangeOpenid.getErrMsg());
+      System.out.println(wxMpChangeOpenid);
+    }
+
+  }
+
 }
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java
index e741e93108..50bb0ac09d 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessageTest.java
@@ -1,14 +1,13 @@
 package me.chanjar.weixin.mp.bean.message;
 
+import me.chanjar.weixin.common.api.WxConsts;
+import org.testng.annotations.Test;
+
 import java.util.List;
 import java.util.Map;
 
-import org.testng.annotations.*;
-
-import me.chanjar.weixin.common.api.WxConsts;
-
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.testng.Assert.*;
+import static org.testng.Assert.assertEquals;
 
 @Test
 public class WxMpXmlMessageTest {
@@ -44,7 +43,7 @@ public void testFromXml() {
       + " "
       + ""
       + ""
-      + " 1\n"
+      + " 1"
       + " "
       + "  "
       + "   "
@@ -52,12 +51,22 @@ public void testFromXml() {
       + " "
       + ""
       + ""
-      + "  \n"
-      + "  \n"
-      + "  \n"
-      + "  \n"
-      + "  \n"
+      + "  "
+      + "  "
+      + "  "
+      + "  "
+      + "  "
       + ""
+      + ""
+      + ""
+      + ""
+      + ""
+      + ""
+      + "1"
+      + "2"
+      + ""
+      + ""
+      + ""
       + "";
     WxMpXmlMessage wxMessage = WxMpXmlMessage.fromXml(xml);
     assertEquals(wxMessage.getToUser(), "toUser");
@@ -92,6 +101,16 @@ public void testFromXml() {
     assertEquals(wxMessage.getSendLocationInfo().getScale(), "15");
     assertEquals(wxMessage.getSendLocationInfo().getLabel(), " 广州市海珠区客村艺苑路 106号");
     assertEquals(wxMessage.getSendLocationInfo().getPoiName(), "wo de poi");
+    assertEquals(wxMessage.getKeyStandard(), "ean13");
+    assertEquals(wxMessage.getKeyStr(), "6901481811083");
+    assertEquals(wxMessage.getCountry(), "中国");
+    assertEquals(wxMessage.getProvince(), "广东");
+    assertEquals(wxMessage.getCity(), "揭阳");
+    assertEquals(wxMessage.getSex(), "1");
+    assertEquals(wxMessage.getScene(), "2");
+    assertEquals(wxMessage.getExtInfo(), "123");
+    assertEquals(wxMessage.getRegionCode(), "440105");
+    assertEquals(wxMessage.getReasonMsg(), "");
   }
 
   public void testFromXml2() {
@@ -232,7 +251,8 @@ public void testFromXml_MASSSENDJOBFINISH() {
     final Map allFields = wxMessage.getAllFieldsMap();
     assertThat(allFields).isNotNull();
     final Map copyrightCheckResult = (Map) allFields.get("CopyrightCheckResult");
-    List> resultList = (List>) ((Map) copyrightCheckResult.get("ResultList")).get("item");
+    List> resultList = (List>) ((Map) copyrightCheckResult
+      .get("ResultList")).get("item");
     assertThat(copyrightCheckResult).isNotNull();
 
     assertThat(copyrightCheckResult.get("Count")).isEqualTo("2");
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
index 257e533205..30d36920bd 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java
@@ -1,18 +1,19 @@
 package me.chanjar.weixin.mp.demo;
 
+import java.io.InputStream;
+import java.util.concurrent.locks.ReentrantLock;
+
 import com.thoughtworks.xstream.XStream;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import me.chanjar.weixin.common.util.xml.XStreamInitializer;
 import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
 
-import java.io.InputStream;
-import java.util.concurrent.locks.ReentrantLock;
-
 /**
  * @author Daniel Qian
  */
 @XStreamAlias("xml")
 class WxMpDemoInMemoryConfigStorage extends WxMpInMemoryConfigStorage {
+  private static final long serialVersionUID = -3706236839197109704L;
 
   public static WxMpDemoInMemoryConfigStorage fromXml(InputStream is) {
     XStream xstream = XStreamInitializer.getInstance();
@@ -24,10 +25,4 @@ public static WxMpDemoInMemoryConfigStorage fromXml(InputStream is) {
     return wxMpDemoInMemoryConfigStorage;
   }
 
-  @Override
-  public String toString() {
-    return "SimpleWxConfigProvider [appId=" + this.appId + ", secret=" + this.secret + ", accessToken=" + this.accessToken
-      + ", expiresTime=" + this.expiresTime + ", token=" + this.token + ", aesKey=" + this.aesKey + ", templateId=" + this.templateId + "]";
-  }
-
 }
diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml
index 68734bb1dc..b15ef92834 100644
--- a/weixin-java-open/pom.xml
+++ b/weixin-java-open/pom.xml
@@ -7,11 +7,11 @@
   4.0.0
   
     com.github.binarywang
-    weixin-java-parent
-    3.2.4.B
+    wx-java
+    3.3.1.B
   
   weixin-java-open
-  Weixin Java Tools - Open
+  WxJava - Open
   微信开放平台Java SDK
   
     
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java
index 69a3aeba56..0ae803fccf 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/WxOpenComponentService.java
@@ -4,6 +4,7 @@
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
+import me.chanjar.weixin.open.bean.WxOpenCreateResult;
 import me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate;
 import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
 import me.chanjar.weixin.open.bean.result.WxOpenAuthorizerInfoResult;
@@ -42,6 +43,8 @@ public interface WxOpenComponentService {
 
   String MINIAPP_JSCODE_2_SESSION = "https://api.weixin.qq.com/sns/component/jscode2session?appid=%s&js_code=%s&grant_type=authorization_code&component_appid=%s";
 
+  String CREATE_OPEN_URL= "https://api.weixin.qq.com/cgi-bin/open/create";
+
   WxMpService getWxMpServiceByAppid(String appid);
 
   /**
@@ -168,4 +171,15 @@ public interface WxOpenComponentService {
    * @see #getTemplateList
    */
   void deleteTemplate(long templateId) throws WxErrorException;
+
+  /**
+   * https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1498704199_1bcax&token=6df5e3650041eff2cd3ec3662425ad8d7beec8d9&lang=zh_CN
+   * 创建 开放平台帐号并绑定公众号/小程序
+   *
+   * https://api.weixin.qq.com/cgi-bin/open/create
+   *
+   * @param appId 公众号/小程序的appId
+   * @return
+   */
+  WxOpenCreateResult createOpenAccount(String appId) throws WxErrorException;
 }
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java
index 93155690c9..4c2a0ee275 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenComponentServiceImpl.java
@@ -17,6 +17,7 @@
 import me.chanjar.weixin.open.api.WxOpenService;
 import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
 import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
+import me.chanjar.weixin.open.bean.WxOpenCreateResult;
 import me.chanjar.weixin.open.bean.WxOpenMaCodeTemplate;
 import me.chanjar.weixin.open.bean.auth.WxOpenAuthorizationInfo;
 import me.chanjar.weixin.open.bean.message.WxOpenXmlMessage;
@@ -387,4 +388,14 @@ public void deleteTemplate(long templateId) throws WxErrorException {
     param.addProperty("template_id", templateId);
     post(DELETE_TEMPLATE_URL, param.toString(), "access_token");
   }
+
+  @Override
+  public WxOpenCreateResult createOpenAccount(String appId) throws WxErrorException {
+    JsonObject param = new JsonObject();
+    param.addProperty("appid", appId);
+
+    String json = post(CREATE_OPEN_URL, param.toString(), "access_token");
+
+    return WxOpenCreateResult.fromJson(json);
+  }
 }
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java
index ce04293c29..570721f1fd 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenInMemoryConfigStorage.java
@@ -11,6 +11,7 @@
 import me.chanjar.weixin.common.bean.WxAccessToken;
 import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
 import me.chanjar.weixin.mp.api.WxMpConfigStorage;
+import me.chanjar.weixin.mp.enums.TicketType;
 import me.chanjar.weixin.open.api.WxOpenConfigStorage;
 import me.chanjar.weixin.open.bean.WxOpenAuthorizerAccessToken;
 import me.chanjar.weixin.open.bean.WxOpenComponentAccessToken;
@@ -322,6 +323,90 @@ public synchronized void updateAccessToken(String accessToken, int expiresInSeco
       wxOpenConfigStorage.updateAuthorizerAccessToken(appId, accessToken, expiresInSeconds);
     }
 
+    @Override
+    public String getTicket(TicketType type) {
+      switch (type) {
+        case JSAPI: {
+          return wxOpenConfigStorage.getJsapiTicket(appId);
+        }
+        case WX_CARD: {
+          return wxOpenConfigStorage.getCardApiTicket(appId);
+        }
+        default: {
+          // do nothing
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public Lock getTicketLock(TicketType type) {
+      switch (type) {
+        case JSAPI: {
+          return this.jsapiTicketLock;
+        }
+        case WX_CARD: {
+          return this.cardApiTicketLock;
+        }
+        default: {
+          // do nothing
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public boolean isTicketExpired(TicketType type) {
+      switch (type) {
+        case JSAPI: {
+          return wxOpenConfigStorage.isJsapiTicketExpired(appId);
+        }
+        case WX_CARD: {
+          return wxOpenConfigStorage.isCardApiTicketExpired(appId);
+        }
+        default: {
+          // do nothing
+        }
+      }
+
+      return false;
+    }
+
+    @Override
+    public void expireTicket(TicketType type) {
+      switch (type) {
+        case JSAPI: {
+          wxOpenConfigStorage.expireJsapiTicket(appId);
+          break;
+        }
+        case WX_CARD: {
+          wxOpenConfigStorage.expireCardApiTicket(appId);
+          break;
+        }
+        default: {
+          // do nothing
+        }
+      }
+    }
+
+    @Override
+    public void updateTicket(TicketType type, String ticket, int expiresInSeconds) {
+      switch (type) {
+        case JSAPI: {
+          wxOpenConfigStorage.updateJsapiTicket(appId, ticket, expiresInSeconds);
+          break;
+        }
+        case WX_CARD: {
+          wxOpenConfigStorage.updateCardApiTicket(appId, ticket, expiresInSeconds);
+          break;
+        }
+        default: {
+          // do nothing
+        }
+      }
+
+    }
+
     @Override
     public String getAppid() {
       return this.appId;
@@ -357,9 +442,6 @@ public void expireJsapiTicket() {
       wxOpenConfigStorage.expireJsapiTicket(appId);
     }
 
-    /**
-     * 卡券api_ticket
-     */
     @Override
     public String getCardApiTicket() {
       return wxOpenConfigStorage.getCardApiTicket(appId);
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java
index 4949726402..65dfef6e35 100644
--- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/api/impl/WxOpenServiceAbstractImpl.java
@@ -1,5 +1,10 @@
 package me.chanjar.weixin.open.api.impl;
 
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import me.chanjar.weixin.common.error.WxError;
 import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.common.util.http.RequestExecutor;
@@ -7,17 +12,13 @@
 import me.chanjar.weixin.open.api.WxOpenComponentService;
 import me.chanjar.weixin.open.api.WxOpenConfigStorage;
 import me.chanjar.weixin.open.api.WxOpenService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
 
 /**
  * @author 007
  */
 public abstract class WxOpenServiceAbstractImpl implements WxOpenService, RequestHttp {
-  protected final Logger log = LoggerFactory.getLogger(this.getClass());
-  protected WxOpenComponentService wxOpenComponentService = new WxOpenComponentServiceImpl(this);
+  private final Logger log = LoggerFactory.getLogger(this.getClass());
+  private WxOpenComponentService wxOpenComponentService = new WxOpenComponentServiceImpl(this);
   private WxOpenConfigStorage wxOpenConfigStorage;
 
   @Override
@@ -37,7 +38,7 @@ public void setWxOpenConfigStorage(WxOpenConfigStorage wxOpenConfigStorage) {
   }
 
   /**
-   * 初始化 RequestHttp
+   * 初始化 RequestHttp.
    */
   public abstract void initHttp();
 
diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java
new file mode 100644
index 0000000000..6e06a16d4a
--- /dev/null
+++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/WxOpenCreateResult.java
@@ -0,0 +1,36 @@
+package me.chanjar.weixin.open.bean;
+
+import cn.binarywang.wx.miniapp.util.json.WxMaGsonBuilder;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 
+ * code换取session_key接口的响应
+ * 文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxloginobject
+ * 微信返回报文:{"session_key":"nzoqhc3OnwHzeTxJs+inbQ==","openid":"oVBkZ0aYgDMDIywRdgPW8-joxXc4"}
+ * 
+ * @author Binary Wang + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class WxOpenCreateResult implements Serializable { + + @SerializedName("open_appid") + private String openAppid; + + @SerializedName("errcode") + private String errcode; + + @SerializedName("errmsg") + private String errmsg; + + public static WxOpenCreateResult fromJson(String json) { + return WxGsonBuilder.create().fromJson(json, WxOpenCreateResult.class); + } + +} diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java index f38194529d..a6ba9945d3 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/bean/message/WxOpenXmlMessage.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; @@ -99,7 +100,7 @@ public static WxMpXmlMessage fromEncryptedMpXml(String encryptedXml, WxOpenConfi public static WxOpenXmlMessage fromEncryptedXml(InputStream is, WxOpenConfigStorage wxOpenConfigStorage, String timestamp, String nonce, String msgSignature) { try { - return fromEncryptedXml(IOUtils.toString(is, "UTF-8"), + return fromEncryptedXml(IOUtils.toString(is, StandardCharsets.UTF_8), wxOpenConfigStorage, timestamp, nonce, msgSignature); } catch (IOException e) { throw new RuntimeException(e); diff --git a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeRequestExecutor.java b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeRequestExecutor.java index 55766d9952..3c6b842dac 100644 --- a/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeRequestExecutor.java +++ b/weixin-java-open/src/main/java/me/chanjar/weixin/open/util/requestexecuter/ma/MaQrCodeRequestExecutor.java @@ -1,15 +1,17 @@ package me.chanjar.weixin.open.util.requestexecuter.ma; +import java.io.File; +import java.io.IOException; + import me.chanjar.weixin.common.error.WxError; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.common.util.http.RequestExecutor; import me.chanjar.weixin.common.util.http.RequestHttp; +import me.chanjar.weixin.common.util.http.ResponseHandler; import me.chanjar.weixin.open.bean.ma.WxMaQrcodeParam; -import java.io.File; - /** - * 获得小程序体验QrCode图片 请求执行器 + * 获得小程序体验QrCode图片 请求执行器. * * @author yqx * @date 2018-09-13 @@ -21,6 +23,11 @@ public MaQrCodeRequestExecutor(RequestHttp requestHttp) { this.requestHttp = requestHttp; } + @Override + public void execute(String uri, WxMaQrcodeParam data, ResponseHandler handler) throws WxErrorException, IOException { + handler.handle(this.execute(uri, data)); + } + public static RequestExecutor create(RequestHttp requestHttp) throws WxErrorException { switch (requestHttp.getRequestType()) { case APACHE_HTTP: diff --git a/weixin-java-osgi/pom.xml b/weixin-java-osgi/pom.xml index 693d530946..107a78b4a2 100644 --- a/weixin-java-osgi/pom.xml +++ b/weixin-java-osgi/pom.xml @@ -5,14 +5,14 @@ 4.0.0 com.github.binarywang - weixin-java-parent + wx-java 2.6.0 weixin-java-osgi bundle - Weixin Java Tools - OSGI - 微信公众号Java SDK OSGI + WxJava - OSGI + 微信Java开发SDK OSGI模块 diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml index 8ab154fae8..bb354af507 100644 --- a/weixin-java-pay/pom.xml +++ b/weixin-java-pay/pom.xml @@ -3,14 +3,14 @@ xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - weixin-java-parent com.github.binarywang - 3.2.4.B + wx-java + 3.3.1.B 4.0.0 weixin-java-pay - Weixin Java Tools - PAY + WxJava - PAY 微信支付 Java SDK diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java index 90bafc7819..cab25062af 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponInfoQueryResult.java @@ -74,15 +74,15 @@ public class WxPayCouponInfoQueryResult extends BaseWxPayResult { /** *
    * 字段名:代金券使用门槛.
-   * 变量名:coupon_mininum
+   * 变量名:coupon_minimum 微信文档有误
    * 是否必填:是
    * 示例值:10
    * 类型:Unsinged int
    * 说明:代金券使用最低限额,单位是分
    * 
*/ - @XStreamAlias("coupon_mininum") - private Integer couponMininum; + @XStreamAlias("coupon_minimum") + private Integer couponMinimum; /** *
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java
index c4bbc7053b..f3e91d030b 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/coupon/WxPayCouponStockQueryResult.java
@@ -9,7 +9,7 @@
 
 /**
  * 
- * 查询代金券批次响应结果类
+ * 查询代金券批次响应结果类.
  * Created by Binary Wang on 2017-7-15.
  * 
* @@ -21,9 +21,10 @@ @AllArgsConstructor @XStreamAlias("xml") public class WxPayCouponStockQueryResult extends BaseWxPayResult { + private static final long serialVersionUID = 4644274730788451926L; /** *
-   * 字段名:设备号
+   * 字段名:设备号.
    * 变量名:device_info
    * 是否必填:否
    * 示例值:123456sb
@@ -36,7 +37,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:代金券批次ID
+   * 字段名:代金券批次ID.
    * 变量名:coupon_stock_id
    * 是否必填:是
    * 示例值:1757
@@ -49,7 +50,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:代金券名称
+   * 字段名:代金券名称.
    * 变量名:coupon_name
    * 是否必填:否
    * 示例值:测试代金券
@@ -62,7 +63,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:代金券面额
+   * 字段名:代金券面额.
    * 变量名:coupon_value
    * 是否必填:是
    * 示例值:5
@@ -75,7 +76,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:代金券使用最低限额
+   * 字段名:代金券使用最低限额.
    * 变量名:coupon_mininumn
    * 是否必填:否
    * 示例值:10
@@ -84,11 +85,11 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
    * 
*/ @XStreamAlias("coupon_mininumn") - private Integer couponMininumn; + private Integer couponMinimum; /** *
-   * 字段名:代金券批次状态
+   * 字段名:代金券批次状态.
    * 变量名:coupon_stock_status
    * 是否必填:是
    * 示例值:4
@@ -101,7 +102,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:代金券数量
+   * 字段名:代金券数量.
    * 变量名:coupon_total
    * 是否必填:是
    * 示例值:100
@@ -114,7 +115,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:代金券最大领取数量
+   * 字段名:代金券最大领取数量.
    * 变量名:max_quota
    * 是否必填:否
    * 示例值:1
@@ -127,7 +128,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:代金券已经发送的数量
+   * 字段名:代金券已经发送的数量.
    * 变量名:is_send_num
    * 是否必填:否
    * 示例值:0
@@ -140,7 +141,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:生效开始时间
+   * 字段名:生效开始时间.
    * 变量名:begin_time
    * 是否必填:是
    * 示例值:1943787483
@@ -153,7 +154,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:生效结束时间
+   * 字段名:生效结束时间.
    * 变量名:end_time
    * 是否必填:是
    * 示例值:1943787490
@@ -166,7 +167,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:创建时间
+   * 字段名:创建时间.
    * 变量名:create_time
    * 是否必填:是
    * 示例值:1943787420
@@ -179,7 +180,7 @@ public class WxPayCouponStockQueryResult extends BaseWxPayResult {
 
   /**
    * 
-   * 字段名:代金券预算额度
+   * 字段名:代金券预算额度.
    * 变量名:coupon_budget
    * 是否必填:否
    * 示例值:500
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java
index 9e405aa0f9..164c30b3c5 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayAppOrderResult.java
@@ -19,6 +19,9 @@ public class WxPayAppOrderResult {
   private String prepayId;
   private String partnerId;
   private String appId;
+  /**
+   * 由于package为java保留关键字,因此改为packageValue. 前端使用时记得要更改为package
+   */
   private String packageValue;
   private String timeStamp;
   private String nonceStr;
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java
index 12f8e43bc2..04cf893feb 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/order/WxPayMpOrderResult.java
@@ -20,7 +20,7 @@ public class WxPayMpOrderResult {
   private String timeStamp;
   private String nonceStr;
   /**
-   * 由于package为java保留关键字,因此改为packageValue.
+   * 由于package为java保留关键字,因此改为packageValue. 前端使用时记得要更改为package
    */
   @XStreamAlias("package")
   private String packageValue;
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java
index 6b81aeed48..5ff1c76a04 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayQueryCommentRequest.java
@@ -84,8 +84,4 @@ public class WxPayQueryCommentRequest extends BaseWxPayRequest {
   protected void checkConstraints() throws WxPayException {
   }
 
-  @Override
-  protected String[] getIgnoredParamsForSign() {
-    return new String[]{"limit","sign_type"};
-  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
index 494ac1f325..3e5364c5ce 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
@@ -25,9 +25,11 @@
 @AllArgsConstructor
 @XStreamAlias("xml")
 public class WxPayReportRequest extends BaseWxPayRequest {
+  private static final long serialVersionUID = 2667579776376527663L;
+
   /**
    * 
-   * 设备号
+   * 设备号.
    * device_info
    * 否
    * String(32)
@@ -40,7 +42,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
 
   /**
    * 
-   * 接口URL
+   * 接口URL.
    * interface_url
    * 是
    * String(127)
@@ -57,7 +59,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private String interfaceUrl;
   /**
    * 
-   * 接口耗时
+   * 接口耗时.
    * execute_time
    * 是
    * Int
@@ -70,7 +72,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private Integer executeTime;
   /**
    * 
-   * 返回状态码
+   * 返回状态码.
    * return_code
    * 是
    * String(16)
@@ -83,7 +85,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private String returnCode;
   /**
    * 
-   * 返回信息
+   * 返回信息.
    * return_msg
    * 否
    * String(128)
@@ -95,7 +97,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private String returnMsg;
   /**
    * 
-   * 业务结果
+   * 业务结果.
    * result_code
    * 是
    * String(16)
@@ -108,7 +110,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private String resultCode;
   /**
    * 
-   * 错误代码
+   * 错误代码.
    * err_code
    * 否
    * String(32)
@@ -120,7 +122,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private String errCode;
   /**
    * 
-   * 错误代码描述
+   * 错误代码描述.
    * err_code_des
    * 否
    * String(128)
@@ -132,7 +134,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private String errCodeDes;
   /**
    * 
-   * 商户订单号
+   * 商户订单号.
    * out_trade_no
    * 否
    * String(32)
@@ -144,7 +146,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private String outTradeNo;
   /**
    * 
-   * 访问接口IP
+   * 访问接口IP.
    * user_ip
    * 是
    * String(16)
@@ -157,7 +159,7 @@ public class WxPayReportRequest extends BaseWxPayRequest {
   private String userIp;
   /**
    * 
-   * 商户上报时间
+   * 商户上报时间.
    * time
    * 否
    * String(14)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java
index dda0c121b4..b0233ded4d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/BaseWxPayResult.java
@@ -1,14 +1,12 @@
 package com.github.binarywang.wxpay.bean.result;
 
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Map;
 import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFactory;
@@ -18,7 +16,6 @@
 import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
 
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -27,7 +24,6 @@
 import com.google.common.base.Joiner;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.gson.GsonBuilder;
 import com.thoughtworks.xstream.XStream;
 import com.thoughtworks.xstream.annotations.XStreamAlias;
 import lombok.Data;
@@ -191,13 +187,13 @@ private Document getXmlDoc() {
     }
 
     try {
-      this.xmlDoc = DocumentBuilderFactory
-        .newInstance()
-        .newDocumentBuilder()
+      final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+      factory.setExpandEntityReferences(false);
+      this.xmlDoc = factory.newDocumentBuilder()
         .parse(new ByteArrayInputStream(this.xmlString.getBytes(StandardCharsets.UTF_8)));
       return xmlDoc;
-    } catch (SAXException | IOException | ParserConfigurationException e) {
-      throw new RuntimeException("非法的xml文本内容:" + this.xmlString);
+    } catch (Exception e) {
+      throw new RuntimeException("非法的xml文本内容:\n" + this.xmlString, e);
     }
 
   }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillInfo.java
similarity index 82%
rename from weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillBaseResult.java
rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillInfo.java
index a747b98d8a..f77d49e788 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillBaseResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillInfo.java
@@ -15,7 +15,7 @@
  */
 @Data
 @NoArgsConstructor
-public class WxPayBillBaseResult implements Serializable {
+public class WxPayBillInfo implements Serializable {
   private static final long serialVersionUID = 2226245109137435453L;
 
   @Override
@@ -36,7 +36,7 @@ public String toString() {
    */
   private String mchId;
   /**
-   * 子商户号.
+   * 特约商户号.
    */
   private String subMchId;
   /**
@@ -72,11 +72,11 @@ public String toString() {
    */
   private String feeType;
   /**
-   * 总金额.
+   * 应结订单金额.
    */
   private String totalFee;
   /**
-   * 企业红包金额.
+   * 代金券金额.
    */
   private String couponFee;
   /**
@@ -92,7 +92,7 @@ public String toString() {
    */
   private String settlementRefundFee;
   /**
-   * 企业红包退款金额.
+   * 充值券退款金额.
    */
   private String couponRefundFee;
   /**
@@ -119,5 +119,24 @@ public String toString() {
    * 费率.
    */
   private String poundageRate;
-
+  /**
+   * 订单金额.
+   */
+  private String totalAmount;
+  /**
+   * 申请退款金额.
+   */
+  private String appliedRefundAmount;
+  /**
+   * 费率备注.
+   */
+  private String feeRemark;
+  /**
+   * 退款申请时间
+   */
+  private String refundTime;
+  /**
+   * 退款成功时间
+   */
+  private String refundSuccessTime;
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java
index cf2616b5b8..e568a77d14 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBillResult.java
@@ -1,6 +1,7 @@
 package com.github.binarywang.wxpay.bean.result;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
 
 import lombok.Data;
@@ -8,13 +9,14 @@
 import me.chanjar.weixin.common.util.json.WxGsonBuilder;
 
 /**
- * The type Wx pay bill result.
+ * 微信对账单结果类.
  *
- * @author BinaryWang
+ * @author DDLeEHi
  */
 @Data
 @NoArgsConstructor
 public class WxPayBillResult implements Serializable {
+  private static final String TOTAL_DEAL_COUNT = "总交易单数";
   private static final long serialVersionUID = -7687458652694204070L;
 
   @Override
@@ -23,28 +25,356 @@ public String toString() {
   }
 
   /**
-   * 对账返回对象.
+   * 对账明细列表.
    */
-  private List wxPayBillBaseResultLst;
+  private List billInfoList;
   /**
    * 总交易单数.
    */
   private String totalRecord;
   /**
-   * 总交易额.
+   * 应结订单总金额.
    */
   private String totalFee;
   /**
-   * 总退款金额.
+   * 退款总金额.
    */
   private String totalRefundFee;
   /**
-   * 总代金券或立减优惠退款金额.
+   * 充值券退款总金额.
    */
   private String totalCouponFee;
   /**
    * 手续费总金额.
    */
   private String totalPoundageFee;
+  /**
+   * 订单总金额.
+   */
+  private String totalAmount;
+  /**
+   * 申请退款总金额.
+   */
+  private String totalAppliedRefundFee;
+
+  /**
+   * 根据账单类型,从原始对账单字符串里构造出WxPayBillResult对象
+   *
+   * @param responseContent 原始对账单字符串
+   * @param billType        账单类型
+   * @return WxPayBillResult对象
+   */
+  public static WxPayBillResult fromRawBillResultString(String responseContent, String billType) {
+    switch (billType) {
+      case "ALL":{
+        return fromRawBillResultString(responseContent);
+      }
+      case "SUCCESS":{
+        return fromRawBillResultStringToSuccess(responseContent);
+      }
+      case "REFUND" :{
+        return fromRawBillResultStringToRefund(responseContent);
+      }
+      case "RECHARGE_REFUND" :{
+        return fromRawBillResultStringToRechargeRefund(responseContent);
+      }
+      default: {
+        return null;
+      }
+    }
+  }
+
+  /**
+   * 从原始对账单字符串里构造出WxPayBillResult对象,用于构建当日所有订单信息
+   */
+  private static WxPayBillResult fromRawBillResultString(String responseContent) {
+    String listStr = "";
+    String objStr = "";
+    if (responseContent.contains(TOTAL_DEAL_COUNT)) {
+      listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT));
+      objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT));
+    }
 
+    List results = new ArrayList<>();
+    // 去空格
+    String newStr = listStr.replaceAll(",", " ");
+    // 数据分组
+    String[] tempStr = newStr.split("`");
+    // 分组标题
+    String[] t = tempStr[0].split(" ");
+    // 计算循环次数
+    int j = tempStr.length / t.length;
+    // 纪录数组下标
+    int k = 1;
+    for (int i = 0; i < j; i++) {
+      WxPayBillInfo result = new WxPayBillInfo();
+      result.setTradeTime(tempStr[k].trim());
+      result.setAppId(tempStr[k + 1].trim());
+      result.setMchId(tempStr[k + 2].trim());
+      result.setSubMchId(tempStr[k + 3].trim());
+      result.setDeviceInfo(tempStr[k + 4].trim());
+      result.setTransactionId(tempStr[k + 5].trim());
+      result.setOutTradeNo(tempStr[k + 6].trim());
+      result.setOpenId(tempStr[k + 7].trim());
+      result.setTradeType(tempStr[k + 8].trim());
+      result.setTradeState(tempStr[k + 9].trim());
+      result.setBankType(tempStr[k + 10].trim());
+      result.setFeeType(tempStr[k + 11].trim());
+      result.setTotalFee(tempStr[k + 12].trim());
+      result.setCouponFee(tempStr[k + 13].trim());
+      result.setRefundId(tempStr[k + 14].trim());
+      result.setOutRefundNo(tempStr[k + 15].trim());
+      result.setSettlementRefundFee(tempStr[k + 16].trim());
+      result.setCouponRefundFee(tempStr[k + 17].trim());
+      result.setRefundChannel(tempStr[k + 18].trim());
+      result.setRefundState(tempStr[k + 19].trim());
+      result.setBody(tempStr[k + 20].trim());
+      result.setAttach(tempStr[k + 21].trim());
+      result.setPoundage(tempStr[k + 22].trim());
+      result.setPoundageRate(tempStr[k + 23].trim());
+      results.add(result);
+      k += t.length;
+    }
+
+    WxPayBillResult billResult = new WxPayBillResult();
+    billResult.setBillInfoList(results);
+
+    /*
+     * 总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额 `2,`0.02,`0.0,`0.0,`0
+     * 参考以上格式进行取值
+     */
+    String[] totalTempStr = objStr.replaceAll(",", " ").split("`");
+    billResult.setTotalRecord(totalTempStr[1].trim());
+    billResult.setTotalFee(totalTempStr[2].trim());
+    billResult.setTotalRefundFee(totalTempStr[3].trim());
+    billResult.setTotalCouponFee(totalTempStr[4].trim());
+    billResult.setTotalPoundageFee(totalTempStr[5].trim());
+    return billResult;
+  }
+
+  /**
+   * 从原始对账单字符串里构造出WxPayBillResult对象,用于构建当日成功支付的订单
+   */
+  private static WxPayBillResult fromRawBillResultStringToSuccess(String responseContent) {
+    String listStr = "";
+    String objStr = "";
+    if (responseContent.contains(TOTAL_DEAL_COUNT)) {
+      listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT));
+      objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT));
+    }
+
+    List results = new ArrayList<>();
+    // 去空格
+    String newStr = listStr.replaceAll(",", " ");
+    // 数据分组
+    String[] tempStr = newStr.split("`");
+    // 分组标题
+    String[] t = tempStr[0].split(" ");
+    // 计算循环次数
+    int j = tempStr.length / t.length;
+    // 纪录数组下标
+    int k = 1;
+    // 交易时间,公众账号ID,商户号,子商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,总金额,代金券或立减优惠金额,商品名称,商户数据包,手续费,费率
+    for (int i = 0; i < j; i++) {
+      WxPayBillInfo result = new WxPayBillInfo();
+      result.setTradeTime(tempStr[k].trim());
+      result.setAppId(tempStr[k + 1].trim());
+      result.setMchId(tempStr[k + 2].trim());
+      result.setSubMchId(tempStr[k + 3].trim());
+      result.setDeviceInfo(tempStr[k + 4].trim());
+      result.setTransactionId(tempStr[k + 5].trim());
+      result.setOutTradeNo(tempStr[k + 6].trim());
+      result.setOpenId(tempStr[k + 7].trim());
+      result.setTradeType(tempStr[k + 8].trim());
+      result.setTradeState(tempStr[k + 9].trim());
+      result.setBankType(tempStr[k + 10].trim());
+      result.setFeeType(tempStr[k + 11].trim());
+      result.setTotalFee(tempStr[k + 12].trim());
+      result.setCouponFee(tempStr[k + 13].trim());
+      result.setBody(tempStr[k + 14].trim());
+      result.setAttach(tempStr[k + 15].trim());
+      result.setPoundage(tempStr[k + 16].trim());
+      result.setPoundageRate(tempStr[k + 17].trim());
+      results.add(result);
+      k += t.length;
+    }
+
+    WxPayBillResult billResult = new WxPayBillResult();
+    billResult.setBillInfoList(results);
+
+    /*
+     * 总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额 `2,`0.02,`0.0,`0.0,`0
+     * 参考以上格式进行取值
+     */
+    String[] totalTempStr = objStr.replaceAll(",", " ").split("`");
+    billResult.setTotalRecord(totalTempStr[1].trim());
+    billResult.setTotalFee(totalTempStr[2].trim());
+    billResult.setTotalRefundFee(totalTempStr[3].trim());
+    billResult.setTotalCouponFee(totalTempStr[4].trim());
+    billResult.setTotalPoundageFee(totalTempStr[5].trim());
+    return billResult;
+  }
+
+  /**
+   * 从原始对账单字符串里构造出WxPayBillResult对象,用于构建当日退款的订单
+   */
+  private static WxPayBillResult fromRawBillResultStringToRefund(String responseContent) {
+    String listStr = "";
+    String objStr = "";
+    if (responseContent.contains(TOTAL_DEAL_COUNT)) {
+      listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT));
+      objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT));
+    }
+
+    List results = new ArrayList<>();
+    // 去空格
+    String newStr = listStr.replaceAll(",", " ");
+    // 数据分组
+    String[] tempStr = newStr.split("`");
+    // 分组标题
+    String[] t = tempStr[0].split(" ");
+    // 计算循环次数
+    int j = tempStr.length / t.length;
+    // 纪录数组下标
+    int k = 1;
+    // 交易时间,公众账号ID,商户号,子商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,总金额,代金券或立减优惠金额,
+    // 退款申请时间,退款成功时间,微信退款单号,商户退款单号,退款金额,代金券或立减优惠退款金额,退款类型,退款状态,商品名称,商户数据包,手续费,费率
+    for (int i = 0; i < j; i++) {
+      WxPayBillInfo result = new WxPayBillInfo();
+      result.setTradeTime(tempStr[k].trim());
+      result.setAppId(tempStr[k + 1].trim());
+      result.setMchId(tempStr[k + 2].trim());
+      result.setSubMchId(tempStr[k + 3].trim());
+      result.setDeviceInfo(tempStr[k + 4].trim());
+      result.setTransactionId(tempStr[k + 5].trim());
+      result.setOutTradeNo(tempStr[k + 6].trim());
+      result.setOpenId(tempStr[k + 7].trim());
+      result.setTradeType(tempStr[k + 8].trim());
+      result.setTradeState(tempStr[k + 9].trim());
+      result.setBankType(tempStr[k + 10].trim());
+      result.setFeeType(tempStr[k + 11].trim());
+      result.setTotalFee(tempStr[k + 12].trim());
+      result.setCouponFee(tempStr[k + 13].trim());
+      result.setRefundTime(tempStr[k + 14].trim());
+      result.setRefundSuccessTime(tempStr[k + 15].trim());
+      result.setRefundId(tempStr[k + 16].trim());
+      result.setOutRefundNo(tempStr[k + 17].trim());
+      result.setSettlementRefundFee(tempStr[k + 18].trim());
+      result.setCouponRefundFee(tempStr[k + 19].trim());
+      result.setRefundChannel(tempStr[k + 20].trim());
+      result.setRefundState(tempStr[k + 21].trim());
+      result.setBody(tempStr[k + 22].trim());
+      result.setAttach(tempStr[k + 23].trim());
+      result.setPoundage(tempStr[k + 24].trim());
+      result.setPoundageRate(tempStr[k + 25].trim());
+      results.add(result);
+      k += t.length;
+    }
+
+    WxPayBillResult billResult = new WxPayBillResult();
+    billResult.setBillInfoList(results);
+
+    /*
+     * 总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额 `2,`0.02,`0.0,`0.0,`0
+     * 参考以上格式进行取值
+     */
+    String[] totalTempStr = objStr.replaceAll(",", " ").split("`");
+    billResult.setTotalRecord(totalTempStr[1].trim());
+    billResult.setTotalFee(totalTempStr[2].trim());
+    billResult.setTotalRefundFee(totalTempStr[3].trim());
+    billResult.setTotalCouponFee(totalTempStr[4].trim());
+    billResult.setTotalPoundageFee(totalTempStr[5].trim());
+    billResult.setTotalAmount(get(totalTempStr, 6));
+    billResult.setTotalAppliedRefundFee(get(totalTempStr, 7));
+
+    return billResult;
+  }
+
+  /**
+   * 从原始对账单字符串里构造出WxPayBillResult对象,用于构建当日充值退款订单
+   */
+  private static WxPayBillResult fromRawBillResultStringToRechargeRefund(String responseContent) {
+    String listStr = "";
+    String objStr = "";
+    if (responseContent.contains(TOTAL_DEAL_COUNT)) {
+      listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT));
+      objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT));
+    }
+
+    List results = new ArrayList<>();
+    // 去空格
+    String newStr = listStr.replaceAll(",", " ");
+    // 数据分组
+    String[] tempStr = newStr.split("`");
+    // 分组标题
+    String[] t = tempStr[0].split(" ");
+    // 计算循环次数
+    int j = tempStr.length / t.length;
+    // 纪录数组下标
+    int k = 1;
+    // 交易时间,公众账号ID,商户号,子商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,
+    // 退款申请时间,退款成功时间,微信退款单号,商户退款单号,退款金额,充值券退款金额,退款类型,退款状态,商品名称,商户数据包,返还手续费,费率,订单金额,申请退款金额
+    for (int i = 0; i < j; i++) {
+      WxPayBillInfo result = new WxPayBillInfo();
+      result.setTradeTime(tempStr[k].trim());
+      result.setAppId(tempStr[k + 1].trim());
+      result.setMchId(tempStr[k + 2].trim());
+      result.setSubMchId(tempStr[k + 3].trim());
+      result.setDeviceInfo(tempStr[k + 4].trim());
+      result.setTransactionId(tempStr[k + 5].trim());
+      result.setOutTradeNo(tempStr[k + 6].trim());
+      result.setOpenId(tempStr[k + 7].trim());
+      result.setTradeType(tempStr[k + 8].trim());
+      result.setTradeState(tempStr[k + 9].trim());
+      result.setBankType(tempStr[k + 10].trim());
+      result.setFeeType(tempStr[k + 11].trim());
+      result.setTotalFee(tempStr[k + 12].trim());
+      result.setCouponFee(tempStr[k + 13].trim());
+      result.setRefundTime(tempStr[k + 14].trim());
+      result.setRefundSuccessTime(tempStr[k + 15].trim());
+      result.setRefundId(tempStr[k + 16].trim());
+      result.setOutRefundNo(tempStr[k + 17].trim());
+      result.setSettlementRefundFee(tempStr[k + 18].trim());
+      result.setCouponRefundFee(tempStr[k + 19].trim());
+      result.setRefundChannel(tempStr[k + 20].trim());
+      result.setRefundState(tempStr[k + 21].trim());
+      result.setBody(tempStr[k + 22].trim());
+      result.setAttach(tempStr[k + 23].trim());
+      result.setPoundage(tempStr[k + 24].trim());
+      result.setPoundageRate(tempStr[k + 25].trim());
+      result.setTotalAmount(get(tempStr, k + 26, t.length));
+      result.setAppliedRefundAmount(get(tempStr, k + 27, t.length));
+      results.add(result);
+      k += t.length;
+    }
+
+    WxPayBillResult billResult = new WxPayBillResult();
+    billResult.setBillInfoList(results);
+
+    /*
+     * 总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额 `2,`0.02,`0.0,`0.0,`0
+     * 参考以上格式进行取值
+     */
+    String[] totalTempStr = objStr.replaceAll(",", " ").split("`");
+    billResult.setTotalRecord(totalTempStr[1].trim());
+    billResult.setTotalFee(totalTempStr[2].trim());
+    billResult.setTotalRefundFee(totalTempStr[3].trim());
+    billResult.setTotalCouponFee(totalTempStr[4].trim());
+    billResult.setTotalPoundageFee(totalTempStr[5].trim());
+    billResult.setTotalAmount(get(totalTempStr, 6));
+    billResult.setTotalAppliedRefundFee(get(totalTempStr, 7));
+
+    return billResult;
+  }
+
+  private static String get(String[] array, int idx) {
+    return get(array, idx, array.length);
+  }
+
+  private static String get(String[] array, int idx, int length) {
+    if (length > idx) {
+      return array[idx].trim();
+    }
+    return null;
+  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
index 87aad5f436..36f910be63 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
@@ -18,9 +18,11 @@
 @NoArgsConstructor
 @XStreamAlias("xml")
 public class WxPayMicropayResult extends BaseWxPayResult {
+  private static final long serialVersionUID = 529670965722059189L;
+
   /**
    * 
-   * 用户标识
+   * 用户标识.
    * openid
    * 是
    * String(128)
@@ -33,7 +35,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 是否关注公众账号
+   * 是否关注公众账号.
    * is_subscribe
    * 是
    * String(1)
@@ -46,7 +48,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 交易类型
+   * 交易类型.
    * trade_type
    * 是
    * String(16)
@@ -59,7 +61,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 付款银行
+   * 付款银行.
    * bank_type
    * 是
    * String(32)
@@ -72,7 +74,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 货币类型
+   * 货币类型.
    * fee_type
    * 否
    * String(16)
@@ -85,7 +87,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 订单金额
+   * 订单金额.
    * total_fee
    * 是
    * Int
@@ -94,11 +96,11 @@ public class WxPayMicropayResult extends BaseWxPayResult {
    * 
**/ @XStreamAlias("total_fee") - private String totalFee; + private Integer totalFee; /** *
-   * 应结订单金额
+   * 应结订单金额.
    * settlement_total_fee
    * 否
    * Int
@@ -111,7 +113,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 代金券金额
+   * 代金券金额.
    * coupon_fee
    * 否
    * Int
@@ -124,7 +126,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 现金支付货币类型
+   * 现金支付货币类型.
    * cash_fee_type
    * 否
    * String(16)
@@ -137,7 +139,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 现金支付金额
+   * 现金支付金额.
    * cash_fee
    * 是
    * Int
@@ -150,7 +152,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 微信支付订单号
+   * 微信支付订单号.
    * transaction_id
    * 是
    * String(32)
@@ -163,7 +165,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 商户订单号
+   * 商户订单号.
    * out_trade_no
    * 是
    * String(32)
@@ -176,7 +178,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 商家数据包
+   * 商家数据包.
    * attach
    * 否
    * String(128)
@@ -189,7 +191,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 支付完成时间
+   * 支付完成时间.
    * time_end
    * 是
    * String(14)
@@ -202,7 +204,7 @@ public class WxPayMicropayResult extends BaseWxPayResult {
 
   /**
    * 
-   * 营销详情
+   * 营销详情.
    * promotion_detail
    * 否
    * String(6000)
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
index 01753c0e51..76f823ccfc 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
@@ -1,11 +1,12 @@
 package com.github.binarywang.wxpay.constant;
 
-import com.google.common.collect.Lists;
-import org.apache.commons.lang3.time.FastDateFormat;
-
 import java.text.Format;
 import java.util.List;
 
+import org.apache.commons.lang3.time.FastDateFormat;
+
+import com.google.common.collect.Lists;
+
 /**
  * 
  * 微信支付常量类
@@ -19,7 +20,7 @@ public class WxPayConstants {
   /**
    * 拉取订单评价数据接口的参数中日期格式.
    */
-  public static final Format QUERY_COMMENT_DATE_FORMAT =  FastDateFormat.getInstance("yyyyMMddHHmmss");
+  public static final Format QUERY_COMMENT_DATE_FORMAT = FastDateFormat.getInstance("yyyyMMddHHmmss");
 
   /**
    * 校验用户姓名选项,企业付款时使用.
@@ -89,7 +90,7 @@ public static class TradeType {
     public static final String APP = "APP";
 
     /**
-     * 公众号支付.
+     * 公众号支付/小程序支付.
      */
     public static final String JSAPI = "JSAPI";
 
@@ -108,7 +109,7 @@ public static class TradeType {
   /**
    * 账户类型
    */
-  public static class AccountType{
+  public static class AccountType {
     /**
      * 基本账户
      */
@@ -272,49 +273,4 @@ public static class RefundStatus {
      */
     public static final String CHANGE = "CHANGE";
   }
-
-  /**
-   * 关闭订单结果错误代码.
-   */
-  public static class OrderCloseResultErrorCode {
-    /**
-     * 订单已支付.
-     */
-    public static final String ORDER_PAID = "ORDERPAID";
-
-    /**
-     * 系统错误.
-     */
-    public static final String SYSTEM_ERROR = "SYSTEMERROR";
-
-    /**
-     * 订单不存在.
-     */
-    public static final String ORDER_NOT_EXIST = "ORDERNOTEXIST";
-
-    /**
-     * 订单已关闭.
-     */
-    public static final String ORDER_CLOSED = "ORDERCLOSED";
-
-    /**
-     * 签名错误.
-     */
-    public static final String SIGN_ERROR = "SIGNERROR";
-
-    /**
-     * 未使用POST传递参数.
-     */
-    public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD";
-
-    /**
-     * XML格式错误.
-     */
-    public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR";
-
-    /**
-     * 订单状态错误.
-     */
-    public static final String TRADE_STATE_ERROR = "TRADE_STATE_ERROR";
-  }
 }
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayErrorCode.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayErrorCode.java
new file mode 100644
index 0000000000..0cb7568fed
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayErrorCode.java
@@ -0,0 +1,495 @@
+package com.github.binarywang.wxpay.constant;
+
+/**
+ * 
+ * 微信支付错误码
+ * Created by Binary Wang on 2018/11/18.
+ * 
+ * + * @author Binary Wang + */ +public class WxPayErrorCode { + /** + * 统一下单接口的错误码. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1 + */ + public static class UnifiedOrder { + /** + *
+     * 描述:商户无此接口权限.
+     * 原因:商户未开通此接口权限
+     * 解决方案:请商户前往申请此接口权限
+     * 
+ */ + public static final String NOAUTH = "NOAUTH"; + /** + *
+     * 描述:余额不足.
+     * 原因:用户帐号余额不足
+     * 解决方案:用户帐号余额不足,请用户充值或更换支付卡后再支付
+     * 
+ */ + public static final String NOTENOUGH = "NOTENOUGH"; + /** + *
+     * 描述:商户订单已支付.
+     * 原因:商户订单已支付,无需重复操作
+     * 解决方案:商户订单已支付,无需更多操作
+     * 
+ */ + public static final String ORDERPAID = "ORDERPAID"; + /** + *
+     * 描述:订单已关闭.
+     * 原因:当前订单已关闭,无法支付
+     * 解决方案:当前订单已关闭,请重新下单
+     * 
+ */ + public static final String ORDERCLOSED = "ORDERCLOSED"; + /** + *
+     * 描述:系统错误.
+     * 原因:系统超时
+     * 解决方案:系统异常,请用相同参数重新调用
+     * 
+ */ + public static final String SYSTEMERROR = "SYSTEMERROR"; + /** + *
+     * 描述:APPID不存在.
+     * 原因:参数中缺少APPID
+     * 解决方案:请检查APPID是否正确
+     * 
+ */ + public static final String APPID_NOT_EXIST = "APPID_NOT_EXIST"; + /** + *
+     * 描述:MCHID不存在.
+     * 原因:参数中缺少MCHID
+     * 解决方案:请检查MCHID是否正确
+     * 
+ */ + public static final String MCHID_NOT_EXIST = "MCHID_NOT_EXIST"; + /** + *
+     * 描述:appid和mch_id不匹配.
+     * 原因:appid和mch_id不匹配
+     * 解决方案:请确认appid和mch_id是否匹配
+     * 
+ */ + public static final String APPID_MCHID_NOT_MATCH = "APPID_MCHID_NOT_MATCH"; + /** + *
+     * 描述:缺少参数.
+     * 原因:缺少必要的请求参数
+     * 解决方案:请检查参数是否齐全
+     * 
+ */ + public static final String LACK_PARAMS = "LACK_PARAMS"; + /** + *
+     * 描述:商户订单号重复.
+     * 原因:同一笔交易不能多次提交
+     * 解决方案:请核实商户订单号是否重复提交
+     * 
+ */ + public static final String OUT_TRADE_NO_USED = "OUT_TRADE_NO_USED"; + /** + *
+     * 描述:签名错误.
+     * 原因:参数签名结果不正确
+     * 解决方案:请检查签名参数和方法是否都符合签名算法要求
+     * 
+ */ + public static final String SIGNERROR = "SIGNERROR"; + /** + *
+     * 描述:XML格式错误.
+     * 原因:XML格式错误
+     * 解决方案:请检查XML参数格式是否正确
+     * 
+ */ + public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR"; + /** + *
+     * 描述:请使用post方法.
+     * 原因:未使用post传递参数
+     * 解决方案:请检查请求参数是否通过post方法提交
+     * 
+ */ + public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD"; + /** + *
+     * 描述:post数据为空.
+     * 原因:post数据不能为空
+     * 解决方案:请检查post数据是否为空
+     * 
+ */ + public static final String POST_DATA_EMPTY = "POST_DATA_EMPTY"; + /** + *
+     * 描述:编码格式错误.
+     * 原因:未使用指定编码格式
+     * 解决方案:请使用UTF-8编码格式
+     * 
+ */ + public static final String NOT_UTF8 = "NOT_UTF8"; + } + + /** + * 关闭订单. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_3&index=5 + */ + public static class OrderClose { + /** + * 订单已支付. + */ + public static final String ORDER_PAID = "ORDERPAID"; + + /** + * 系统错误. + */ + public static final String SYSTEM_ERROR = "SYSTEMERROR"; + + /** + * 订单不存在. + */ + public static final String ORDER_NOT_EXIST = "ORDERNOTEXIST"; + + /** + * 订单已关闭. + */ + public static final String ORDER_CLOSED = "ORDERCLOSED"; + + /** + * 签名错误. + */ + public static final String SIGN_ERROR = "SIGNERROR"; + + /** + * 未使用POST传递参数. + */ + public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD"; + + /** + * XML格式错误. + */ + public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR"; + + /** + * 订单状态错误. + */ + public static final String TRADE_STATE_ERROR = "TRADE_STATE_ERROR"; + } + + /** + * 退款申请. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=6 + */ + public static class Refund { + /** + *
+     * 描述:接口返回错误.
+     * 原因:系统超时等
+     * 解决方案:请不要更换商户退款单号,请使用相同参数再次调用API。
+     * 
+ */ + public static final String SYSTEMERROR = "SYSTEMERROR"; + /** + *
+     * 描述:退款业务流程错误,需要商户触发重试来解决.
+     * 原因:并发情况下,业务被拒绝,商户重试即可解决
+     * 解决方案:请不要更换商户退款单号,请使用相同参数再次调用API。
+     * 
+ */ + public static final String BIZERR_NEED_RETRY = "BIZERR_NEED_RETRY"; + /** + *
+     * 描述:订单已经超过退款期限.
+     * 原因:订单已经超过可退款的最大期限(支付后一年内可退款)
+     * 解决方案:请选择其他方式自行退款
+     * 
+ */ + public static final String TRADE_OVERDUE = "TRADE_OVERDUE"; + /** + *
+     * 描述:业务错误.
+     * 原因:申请退款业务发生错误
+     * 解决方案:该错误都会返回具体的错误原因,请根据实际返回做相应处理。
+     * 
+ */ + public static final String ERROR = "ERROR"; + /** + *
+     * 描述:退款请求失败.
+     * 原因:用户帐号注销
+     * 解决方案:此状态代表退款申请失败,商户可自行处理退款。
+     * 
+ */ + public static final String USER_ACCOUNT_ABNORMAL = "USER_ACCOUNT_ABNORMAL"; + /** + *
+     * 描述:无效请求过多.
+     * 原因:连续错误请求数过多被系统短暂屏蔽
+     * 解决方案:请检查业务是否正常,确认业务正常后请在1分钟后再来重试
+     * 
+ */ + public static final String INVALID_REQ_TOO_MUCH = "INVALID_REQ_TOO_MUCH"; + /** + *
+     * 描述:余额不足.
+     * 原因:商户可用退款余额不足
+     * 解决方案:此状态代表退款申请失败,商户可根据具体的错误提示做相应的处理。
+     * 
+ */ + public static final String NOTENOUGH = "NOTENOUGH"; + /** + *
+     * 描述:无效transaction_id.
+     * 原因:请求参数未按指引进行填写
+     * 解决方案:请求参数错误,检查原交易号是否存在或发起支付交易接口返回失败
+     * 
+ */ + public static final String INVALID_TRANSACTIONID = "INVALID_TRANSACTIONID"; + /** + *
+     * 描述:参数错误.
+     * 原因:请求参数未按指引进行填写
+     * 解决方案:请求参数错误,请重新检查再调用退款申请
+     * 
+ */ + public static final String PARAM_ERROR = "PARAM_ERROR"; + /** + *
+     * 描述:APPID不存在.
+     * 原因:参数中缺少APPID
+     * 解决方案:请检查APPID是否正确
+     * 
+ */ + public static final String APPID_NOT_EXIST = "APPID_NOT_EXIST"; + /** + *
+     * 描述:MCHID不存在.
+     * 原因:参数中缺少MCHID
+     * 解决方案:请检查MCHID是否正确
+     * 
+ */ + public static final String MCHID_NOT_EXIST = "MCHID_NOT_EXIST"; + /** + *
+     * 描述:订单号不存在.
+     * 原因:缺少有效的订单号
+     * 解决方案:请检查你的订单号是否正确且是否已支付,未支付的订单不能发起退款
+     * 
+ */ + public static final String ORDERNOTEXIST = "ORDERNOTEXIST"; + /** + *
+     * 描述:请使用post方法.
+     * 原因:未使用post传递参数
+     * 解决方案:请检查请求参数是否通过post方法提交
+     * 
+ */ + public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD"; + /** + *
+     * 描述:签名错误.
+     * 原因:参数签名结果不正确
+     * 解决方案:请检查签名参数和方法是否都符合签名算法要求
+     * 
+ */ + public static final String SIGNERROR = "SIGNERROR"; + /** + *
+     * 描述:XML格式错误.
+     * 原因:XML格式错误
+     * 解决方案:请检查XML参数格式是否正确
+     * 
+ */ + public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR"; + /** + *
+     * 描述:频率限制.
+     * 原因:2个月之前的订单申请退款有频率限制
+     * 解决方案:该笔退款未受理,请降低频率后重试
+     * 
+ */ + public static final String FREQUENCY_LIMITED = "FREQUENCY_LIMITED"; + } + + /** + * 退款查询. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=7 + */ + public static class RefundQuery { + /** + *
+     * 描述:接口返回错误.
+     * 原因:系统超时
+     * 解决方案:请尝试再次掉调用API。
+     * 
+ */ + public static final String SYSTEMERROR = "SYSTEMERROR"; + + /** + *
+     * 描述:退款订单查询失败.
+     * 原因:订单号错误或订单状态不正确
+     * 解决方案:请检查订单号是否有误以及订单状态是否正确,如:未支付、已支付未退款
+     * 
+ */ + public static final String REFUNDNOTEXIST = "REFUNDNOTEXIST"; + + /** + *
+     * 描述:无效transaction_id.
+     * 原因:请求参数未按指引进行填写
+     * 解决方案:请求参数错误,检查原交易号是否存在或发起支付交易接口返回失败
+     * 
+ */ + public static final String INVALID_TRANSACTIONID = "INVALID_TRANSACTIONID"; + + /** + *
+     * 描述:参数错误.
+     * 原因:请求参数未按指引进行填写
+     * 解决方案:请求参数错误,请检查参数再调用退款申请
+     * 
+ */ + public static final String PARAM_ERROR = "PARAM_ERROR"; + + /** + *
+     * 描述:APPID不存在.
+     * 原因:参数中缺少APPID
+     * 解决方案:请检查APPID是否正确
+     * 
+ */ + public static final String APPID_NOT_EXIST = "APPID_NOT_EXIST"; + + /** + *
+     * 描述:MCHID不存在.
+     * 原因:参数中缺少MCHID
+     * 解决方案:请检查MCHID是否正确
+     * 
+ */ + public static final String MCHID_NOT_EXIST = "MCHID_NOT_EXIST"; + + /** + *
+     * 描述:请使用post方法.
+     * 原因:未使用post传递参数
+     * 解决方案:请检查请求参数是否通过post方法提交
+     * 
+ */ + public static final String REQUIRE_POST_METHOD = "REQUIRE_POST_METHOD"; + + /** + *
+     * 描述:签名错误.
+     * 原因:参数签名结果不正确
+     * 解决方案:请检查签名参数和方法是否都符合签名算法要求
+     * 
+ */ + public static final String SIGNERROR = "SIGNERROR"; + + /** + *
+     * 描述:XML格式错误.
+     * 原因:XML格式错误
+     * 解决方案:请检查XML参数格式是否正确
+     * 
+ */ + public static final String XML_FORMAT_ERROR = "XML_FORMAT_ERROR"; + } + + /** + * 下载对账单. + * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=8 + */ + public static class DownloadBill { + /** + *
+     * 描述:下载失败.
+     * 原因:系统超时
+     * 解决方案:请尝试再次查询。
+     * 
+ */ + public static final String SYSTEMERROR = "SYSTEMERROR"; + + /** + *
+     * 描述:参数错误.
+     * 原因:请求参数未按指引进行填写
+     * 解决方案:参数错误,请重新检查
+     * 
+ */ + public static final String INVALID_BILL_TYPE = "invalid bill_type"; + + /** + *
+     * 描述:参数错误.
+     * 原因:请求参数未按指引进行填写
+     * 解决方案:参数错误,请重新检查
+     * 
+ */ + public static final String DATA_FORMAT_ERROR = "data format error"; + + /** + *
+     * 描述:参数错误.
+     * 原因:请求参数未按指引进行填写
+     * 解决方案:参数错误,请重新检查
+     * 
+ */ + public static final String MISSING_PARAMETER = "missing parameter"; + + /** + *
+     * 描述:参数错误.
+     * 原因:请求参数未按指引进行填写
+     * 解决方案:参数错误,请重新检查
+     * 
+ */ + public static final String SIGN_ERROR = "SIGN ERROR"; + + /** + *
+     * 描述:账单不存在.
+     * 原因:当前商户号没有已成交的订单,不生成对账单
+     * 解决方案:请检查当前商户号在指定日期内是否有成功的交易。
+     * 
+ */ + public static final String NO_Bill_Exist = "NO Bill Exist"; + + /** + *
+     * 描述:账单未生成.
+     * 原因:当前商户号没有已成交的订单或对账单尚未生成
+     * 解决方案:请先检查当前商户号在指定日期内是否有成功的交易,如指定日期有交易则表示账单正在生成中,请在上午10点以后再下载。
+     * 
+ */ + public static final String BILL_CREATING = "Bill Creating"; + + /** + *
+     * 描述:账单压缩失败.
+     * 原因:账单压缩失败,请稍后重试
+     * 解决方案:账单压缩失败,请稍后重试
+     * 
+ */ + public static final String COMPRESSG_ZIP_ERROR = "CompressGZip Error"; + + /** + *
+     * 描述:账单解压失败.
+     * 原因:账单解压失败,请稍后重试
+     * 解决方案:账单解压失败,请稍后重试
+     * 
+ */ + public static final String UN_COMPRESSG_ZIP_ERROR = "UnCompressGZip Error"; + + + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java index 0b9dc45875..ec57e745f9 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -49,7 +49,6 @@ import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; import com.github.binarywang.wxpay.bean.result.WxPayAuthcode2OpenidResult; -import com.github.binarywang.wxpay.bean.result.WxPayBillBaseResult; import com.github.binarywang.wxpay.bean.result.WxPayBillResult; import com.github.binarywang.wxpay.bean.result.WxPayCommonResult; import com.github.binarywang.wxpay.bean.result.WxPayFundFlowBaseResult; @@ -91,7 +90,6 @@ public abstract class BaseWxPayServiceImpl implements WxPayService { private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; private static final String TOTAL_FUND_COUNT = "资金流水总笔数"; - private static final String TOTAL_DEAL_COUNT = "总交易单数"; /** * The Log. @@ -344,7 +342,7 @@ public T createOrder(WxPayUnifiedOrderRequest request) throws WxPayException configMap.put("appid", appId); final WxPayAppOrderResult result = WxPayAppOrderResult.builder() - .sign(SignUtils.createSign(configMap, null, this.getConfig().getMchKey(), null)) + .sign(SignUtils.createSign(configMap, request.getSignType(), this.getConfig().getMchKey(), null)) .prepayId(prepayId) .partnerId(partnerId) .appId(appId) @@ -421,7 +419,7 @@ public Map getPayInfo(WxPayUnifiedOrderRequest request) throws W configMap.put("noncestr", nonceStr); configMap.put("appid", appId); // 此map用于客户端与微信服务器交互 - payInfo.put("sign", SignUtils.createSign(configMap, null, this.getConfig().getMchKey(), null)); + payInfo.put("sign", SignUtils.createSign(configMap, request.getSignType(), this.getConfig().getMchKey(), null)); payInfo.put("prepayId", prepayId); payInfo.put("partnerId", partnerId); payInfo.put("appId", appId); @@ -434,8 +432,8 @@ public Map getPayInfo(WxPayUnifiedOrderRequest request) throws W payInfo.put("timeStamp", timestamp); payInfo.put("nonceStr", nonceStr); payInfo.put("package", "prepay_id=" + prepayId); - payInfo.put("signType", SignType.MD5); - payInfo.put("paySign", SignUtils.createSign(payInfo, null, this.getConfig().getMchKey(), null)); + payInfo.put("signType", request.getSignType()); + payInfo.put("paySign", SignUtils.createSign(payInfo, request.getSignType(), this.getConfig().getMchKey(), null)); } return payInfo; @@ -459,7 +457,7 @@ public String createScanPayQrcodeMode1(String productId) { params.put("time_stamp", String.valueOf(System.currentTimeMillis() / 1000)); params.put("nonce_str", String.valueOf(System.currentTimeMillis())); - String sign = SignUtils.createSign(params, null, this.getConfig().getMchKey(), null); + String sign = SignUtils.createSign(params, SignType.MD5, this.getConfig().getMchKey(), null); params.put("sign", sign); for (String key : params.keySet()) { @@ -507,11 +505,7 @@ public WxPayBillResult downloadBill(String billDate, String billType, String tar } private WxPayDownloadBillRequest buildDownloadBillRequest(String billDate, String billType, String tarType, - String deviceInfo) throws WxPayException { - if (!BillType.ALL.equals(billType)) { - throw new WxPayException("目前仅支持ALL类型的对账单下载"); - } - + String deviceInfo) { WxPayDownloadBillRequest request = new WxPayDownloadBillRequest(); request.setBillType(billType); request.setBillDate(billDate); @@ -550,14 +544,10 @@ public String downloadRawBill(WxPayDownloadBillRequest request) throws WxPayExce } private WxPayBillResult handleBill(String billType, String responseContent) { - if (!BillType.ALL.equals(billType)) { - return null; - } - - return this.handleAllBill(responseContent); + return WxPayBillResult.fromRawBillResultString(responseContent, billType); } - private String handleGzipBill(String url, String requestStr) throws WxPayException { + private String handleGzipBill(String url, String requestStr) { try { byte[] responseBytes = this.postForBytes(url, requestStr, false); Path tempDirectory = Files.createTempDirectory("bill"); @@ -581,80 +571,6 @@ private String handleGzipBill(String url, String requestStr) throws WxPayExcepti return null; } - private WxPayBillResult handleAllBill(String responseContent) { - WxPayBillResult wxPayBillResult = new WxPayBillResult(); - - String listStr = ""; - String objStr = ""; - if (responseContent.contains(TOTAL_DEAL_COUNT)) { - listStr = responseContent.substring(0, responseContent.indexOf(TOTAL_DEAL_COUNT)); - objStr = responseContent.substring(responseContent.indexOf(TOTAL_DEAL_COUNT)); - } - - /* - * 交易时间:2017-04-06 01:00:02 公众账号ID: 商户号: 子商户号:0 设备号:WEB 微信订单号: 商户订单号:2017040519091071873216 用户标识: 交易类型:NATIVE - * 交易状态:REFUND 付款银行:CFT 货币种类:CNY 总金额:0.00 企业红包金额:0.00 微信退款单号: 商户退款单号:20170406010000933 退款金额:0.01 企业红包退款金额:0.00 - * 退款类型:ORIGINAL 退款状态:SUCCESS 商品名称: 商户数据包: 手续费:0.00000 费率 :0.60% - * 参考以上格式进行取值 - */ - List wxPayBillBaseResultLst = new LinkedList<>(); - // 去空格 - String newStr = listStr.replaceAll(",", " "); - // 数据分组 - String[] tempStr = newStr.split("`"); - // 分组标题 - String[] t = tempStr[0].split(" "); - // 计算循环次数 - int j = tempStr.length / t.length; - // 纪录数组下标 - int k = 1; - for (int i = 0; i < j; i++) { - WxPayBillBaseResult wxPayBillBaseResult = new WxPayBillBaseResult(); - - wxPayBillBaseResult.setTradeTime(tempStr[k].trim()); - wxPayBillBaseResult.setAppId(tempStr[k + 1].trim()); - wxPayBillBaseResult.setMchId(tempStr[k + 2].trim()); - wxPayBillBaseResult.setSubMchId(tempStr[k + 3].trim()); - wxPayBillBaseResult.setDeviceInfo(tempStr[k + 4].trim()); - wxPayBillBaseResult.setTransactionId(tempStr[k + 5].trim()); - wxPayBillBaseResult.setOutTradeNo(tempStr[k + 6].trim()); - wxPayBillBaseResult.setOpenId(tempStr[k + 7].trim()); - wxPayBillBaseResult.setTradeType(tempStr[k + 8].trim()); - wxPayBillBaseResult.setTradeState(tempStr[k + 9].trim()); - wxPayBillBaseResult.setBankType(tempStr[k + 10].trim()); - wxPayBillBaseResult.setFeeType(tempStr[k + 11].trim()); - wxPayBillBaseResult.setTotalFee(tempStr[k + 12].trim()); - wxPayBillBaseResult.setCouponFee(tempStr[k + 13].trim()); - wxPayBillBaseResult.setRefundId(tempStr[k + 14].trim()); - wxPayBillBaseResult.setOutRefundNo(tempStr[k + 15].trim()); - wxPayBillBaseResult.setSettlementRefundFee(tempStr[k + 16].trim()); - wxPayBillBaseResult.setCouponRefundFee(tempStr[k + 17].trim()); - wxPayBillBaseResult.setRefundChannel(tempStr[k + 18].trim()); - wxPayBillBaseResult.setRefundState(tempStr[k + 19].trim()); - wxPayBillBaseResult.setBody(tempStr[k + 20].trim()); - wxPayBillBaseResult.setAttach(tempStr[k + 21].trim()); - wxPayBillBaseResult.setPoundage(tempStr[k + 22].trim()); - wxPayBillBaseResult.setPoundageRate(tempStr[k + 23].trim()); - wxPayBillBaseResultLst.add(wxPayBillBaseResult); - k += t.length; - } - wxPayBillResult.setWxPayBillBaseResultLst(wxPayBillBaseResultLst); - - /* - * 总交易单数,总交易额,总退款金额,总代金券或立减优惠退款金额,手续费总金额 `2,`0.02,`0.0,`0.0,`0 - * 参考以上格式进行取值 - */ - String totalStr = objStr.replaceAll(",", " "); - String[] totalTempStr = totalStr.split("`"); - wxPayBillResult.setTotalRecord(totalTempStr[1]); - wxPayBillResult.setTotalFee(totalTempStr[2]); - wxPayBillResult.setTotalRefundFee(totalTempStr[3]); - wxPayBillResult.setTotalCouponFee(totalTempStr[4]); - wxPayBillResult.setTotalPoundageFee(totalTempStr[5]); - - return wxPayBillResult; - } - @Override public WxPayFundFlowResult downloadFundFlow(String billDate, String accountType, String tarType) throws WxPayException { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java index 2883c8b1a0..4712987249 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java @@ -52,7 +52,7 @@ public static String createSign(Object xmlBean, String signKey) { */ @Deprecated public static String createSign(Map params, String signKey) { - return createSign(params, null, signKey, new String[0]); + return createSign(params, SignType.MD5, signKey, new String[0]); } /**