@@ -2,7 +2,7 @@ allprojects { | |||||
apply plugin: 'maven' | apply plugin: 'maven' | ||||
group = 'com.github.binarywang' | group = 'com.github.binarywang' | ||||
version = '2.5.0' | |||||
version = '2.6.0' | |||||
} | } | ||||
subprojects { | subprojects { | ||||
@@ -27,7 +27,6 @@ subprojects { | |||||
compile group: 'commons-codec', name: 'commons-codec', version:'1.10' | compile group: 'commons-codec', name: 'commons-codec', version:'1.10' | ||||
compile group: 'commons-io', name: 'commons-io', version:'2.5' | compile group: 'commons-io', name: 'commons-io', version:'2.5' | ||||
compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.4' | compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.4' | ||||
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version:'2.8.0' | |||||
compile group: 'redis.clients', name: 'jedis', version:'2.9.0' | compile group: 'redis.clients', name: 'jedis', version:'2.9.0' | ||||
testCompile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2' | testCompile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2' | ||||
} | } | ||||
@@ -5,7 +5,7 @@ | |||||
<modelVersion>4.0.0</modelVersion> | <modelVersion>4.0.0</modelVersion> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>2.5.0</version> | |||||
<version>2.6.0</version> | |||||
<packaging>pom</packaging> | <packaging>pom</packaging> | ||||
<name>WeiXin Java Tools - Parent</name> | <name>WeiXin Java Tools - Parent</name> | ||||
<description>微信公众号、企业号上级POM</description> | <description>微信公众号、企业号上级POM</description> | ||||
@@ -86,7 +86,8 @@ | |||||
<module>weixin-java-common</module> | <module>weixin-java-common</module> | ||||
<module>weixin-java-cp</module> | <module>weixin-java-cp</module> | ||||
<module>weixin-java-mp</module> | <module>weixin-java-mp</module> | ||||
<module>weixin-java-osgi</module> | |||||
<module>weixin-java-pay</module> | |||||
<!--module>weixin-java-osgi</module--> | |||||
</modules> | </modules> | ||||
<properties> | <properties> | ||||
@@ -105,6 +106,7 @@ | |||||
<commons-io.version>2.5</commons-io.version> | <commons-io.version>2.5</commons-io.version> | ||||
<commons-codec.version>1.10</commons-codec.version> | <commons-codec.version>1.10</commons-codec.version> | ||||
<jetty.version>9.3.0.RC0</jetty.version> | <jetty.version>9.3.0.RC0</jetty.version> | ||||
<jedis.version>2.9.0</jedis.version> | |||||
</properties> | </properties> | ||||
<dependencies> | <dependencies> | ||||
@@ -113,6 +115,11 @@ | |||||
<artifactId>slf4j-api</artifactId> | <artifactId>slf4j-api</artifactId> | ||||
<version>${slf4j.version}</version> | <version>${slf4j.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>com.thoughtworks.xstream</groupId> | |||||
<artifactId>xstream</artifactId> | |||||
<version>1.4.9</version> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>ch.qos.logback</groupId> | <groupId>ch.qos.logback</groupId> | ||||
<artifactId>logback-classic</artifactId> | <artifactId>logback-classic</artifactId> | ||||
@@ -152,17 +159,12 @@ | |||||
<dependency> | <dependency> | ||||
<groupId>com.google.guava</groupId> | <groupId>com.google.guava</groupId> | ||||
<artifactId>guava</artifactId> | <artifactId>guava</artifactId> | ||||
<version>${guava.version}</version> | |||||
</dependency> | </dependency> | ||||
</dependencies> | </dependencies> | ||||
<dependencyManagement> | <dependencyManagement> | ||||
<dependencies> | <dependencies> | ||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<version>4.11</version> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>com.google.inject</groupId> | <groupId>com.google.inject</groupId> | ||||
<artifactId>guice</artifactId> | <artifactId>guice</artifactId> | ||||
@@ -194,9 +196,9 @@ | |||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
<groupId>com.google.guava</groupId> | |||||
<artifactId>guava</artifactId> | |||||
<version>${guava.version}</version> | |||||
<groupId>redis.clients</groupId> | |||||
<artifactId>jedis</artifactId> | |||||
<version>${jedis.version}</version> | |||||
</dependency> | </dependency> | ||||
</dependencies> | </dependencies> | ||||
</dependencyManagement> | </dependencyManagement> | ||||
@@ -6,14 +6,15 @@ | |||||
### 注意事项: | ### 注意事项: | ||||
1. 声明: ***本项目Fork自chanjarster/weixin-java-tools,但由于原项目已停止维护,故单独维护和发布,且发布到maven上的groupId也会不同,详细信息见下文。*** | 1. 声明: ***本项目Fork自chanjarster/weixin-java-tools,但由于原项目已停止维护,故单独维护和发布,且发布到maven上的groupId也会不同,详细信息见下文。*** | ||||
1. **新手请注意,本项目仅是一个开发工具包(即SDK),未提供Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种功能,详情可参考下文中提到的Demo项目或本项目中的部分单元测试代码;如果没有贡献代码的意愿,不建议下载项目的源码自行编译,因为如果想看源码使用maven也是可以下载源码的**; | 1. **新手请注意,本项目仅是一个开发工具包(即SDK),未提供Web实现,建议使用maven或gradle引用本项目即可使用本SDK提供的各种功能,详情可参考下文中提到的Demo项目或本项目中的部分单元测试代码;如果没有贡献代码的意愿,不建议下载项目的源码自行编译,因为如果想看源码使用maven也是可以下载源码的**; | ||||
1. 最新更新:**2017-2-12 发布[【2.5.0正式版】](https://github.com/Wechat-Group/weixin-java-tools/releases)**! | |||||
1. 最新更新:**2017-4-13 发布[【2.6.0正式版】](https://github.com/Wechat-Group/weixin-java-tools/releases)**! | |||||
1. 开源中国网站的本项目介绍的首页链接地址:https://www.oschina.net/p/weixin-java-tools-new | 1. 开源中国网站的本项目介绍的首页链接地址:https://www.oschina.net/p/weixin-java-tools-new | ||||
1. 自2.0.0版本以来,公众号的接口调整比较大,主要是为了解决主接口类过于庞大不方便管理的问题,将接口实现代码按模块进行拆分。 | 1. 自2.0.0版本以来,公众号的接口调整比较大,主要是为了解决主接口类过于庞大不方便管理的问题,将接口实现代码按模块进行拆分。 | ||||
1. 自2.6.0版本开始,微信支付相关功能抽出独立为一个模块,详细使用方式请参考相关demo; | |||||
1. SDK详细开发文档请查阅 [【Wiki】](https://github.com/wechat-group/weixin-java-tools/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。***另外微信开发新手请务必阅读wiki首页的常见问题部分,可以少走很多弯路,节省不少时间。*** | 1. SDK详细开发文档请查阅 [【Wiki】](https://github.com/wechat-group/weixin-java-tools/wiki),部分文档可能未能及时更新,如有发现,可以及时上报或者自行修改。***另外微信开发新手请务必阅读wiki首页的常见问题部分,可以少走很多弯路,节省不少时间。*** | ||||
1. 各个模块的Javadoc可以在线查看(有可能是最新的测试版本的,请注意观察版本号):[weixin-java-pay](https://binarywang.github.io/weixin-java-pay-javadoc/)、[weixin-java-mp](https://binarywang.github.io/weixin-java-mp-javadoc/)、[weixin-java-common](https://binarywang.github.io/weixin-java-common-javadoc/)、[weixin-java-cp](https://binarywang.github.io/weixin-java-cp-javadoc/) | 1. 各个模块的Javadoc可以在线查看(有可能是最新的测试版本的,请注意观察版本号):[weixin-java-pay](https://binarywang.github.io/weixin-java-pay-javadoc/)、[weixin-java-mp](https://binarywang.github.io/weixin-java-mp-javadoc/)、[weixin-java-common](https://binarywang.github.io/weixin-java-common-javadoc/)、[weixin-java-cp](https://binarywang.github.io/weixin-java-cp-javadoc/) | ||||
1. 本SDK要求的最低JDK版本是7,还在使用JDK6的用户请参考[【此项目】]( https://github.com/binarywang/weixin-java-tools-for-jdk6) ,而其他更早的JDK版本则需要自己改造实现。 | 1. 本SDK要求的最低JDK版本是7,还在使用JDK6的用户请参考[【此项目】]( https://github.com/binarywang/weixin-java-tools-for-jdk6) ,而其他更早的JDK版本则需要自己改造实现。 | ||||
1. 如有新功能需求,发现BUG,或者由于微信官方接口调整导致的代码问题,可以直接在[【Issues】](https://github.com/Wechat-Group/weixin-java-tools/issues)页提出issue,便于讨论追踪问题; | 1. 如有新功能需求,发现BUG,或者由于微信官方接口调整导致的代码问题,可以直接在[【Issues】](https://github.com/Wechat-Group/weixin-java-tools/issues)页提出issue,便于讨论追踪问题; | ||||
1. 如果想贡献代码,请阅读[【代码贡献指南】](CONTRIBUTION.md); | |||||
1. 如果想贡献代码,请阅读[【代码贡献指南】](contribution.md); | |||||
1. 捐助渠道已开通,如有意向请前往托管于码云的项目首页(具体地址见下文)的页面评论区上方,可以找到“捐助”按钮,非常感谢各位捐助的同学! | 1. 捐助渠道已开通,如有意向请前往托管于码云的项目首页(具体地址见下文)的页面评论区上方,可以找到“捐助”按钮,非常感谢各位捐助的同学! | ||||
--------------------------------- | --------------------------------- | ||||
@@ -58,12 +59,12 @@ maven: | |||||
<dependency> | <dependency> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-pay</artifactId> | <artifactId>weixin-java-pay</artifactId> | ||||
<version>2.5.7.BETA</version> | |||||
<version>2.6.0</version> | |||||
</dependency> | </dependency> | ||||
``` | ``` | ||||
gradle: | gradle: | ||||
```groovy | ```groovy | ||||
compile 'com.github.binarywang:weixin-java-pay:2.5.7.BETA' | |||||
compile 'com.github.binarywang:weixin-java-pay:2.6.0' | |||||
``` | ``` | ||||
* 公众号(订阅号及服务号): | * 公众号(订阅号及服务号): | ||||
@@ -73,12 +74,12 @@ maven: | |||||
<dependency> | <dependency> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
<version>2.5.0</version> | |||||
<version>2.6.0</version> | |||||
</dependency> | </dependency> | ||||
``` | ``` | ||||
gradle: | gradle: | ||||
```groovy | ```groovy | ||||
compile 'com.github.binarywang:weixin-java-mp:2.5.0' | |||||
compile 'com.github.binarywang:weixin-java-mp:2.6.0' | |||||
``` | ``` | ||||
* 企业号: | * 企业号: | ||||
@@ -88,10 +89,10 @@ maven: | |||||
<dependency> | <dependency> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
<version>2.5.0</version> | |||||
<version>2.6.0</version> | |||||
</dependency> | </dependency> | ||||
``` | ``` | ||||
gradle: | gradle: | ||||
```groovy | ```groovy | ||||
compile 'com.github.binarywang:weixin-java-cp:2.5.0' | |||||
compile 'com.github.binarywang:weixin-java-cp:2.6.0' | |||||
``` | ``` |
@@ -6,7 +6,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>2.5.0</version> | |||||
<version>2.6.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
@@ -14,16 +14,6 @@ | |||||
<description>微信公众号、企业号Java SDK Common</description> | <description>微信公众号、企业号Java SDK Common</description> | ||||
<dependencies> | <dependencies> | ||||
<dependency> | |||||
<groupId>com.thoughtworks.xstream</groupId> | |||||
<artifactId>xstream</artifactId> | |||||
<version>1.4.7</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.testng</groupId> | <groupId>org.testng</groupId> | ||||
<artifactId>testng</artifactId> | <artifactId>testng</artifactId> | ||||
@@ -55,15 +55,15 @@ public class WxConsts { | |||||
/////////////////////// | /////////////////////// | ||||
public static final String MASS_ST_SUCCESS = "send success"; | public static final String MASS_ST_SUCCESS = "send success"; | ||||
public static final String MASS_ST_FAIL = "send fail"; | public static final String MASS_ST_FAIL = "send fail"; | ||||
public static final String MASS_ST_涉嫌广告 = "err(10001)"; | |||||
public static final String MASS_ST_涉嫌政治 = "err(20001)"; | |||||
public static final String MASS_ST_涉嫌社会 = "err(20004)"; | |||||
public static final String MASS_ST_涉嫌色情 = "err(20002)"; | |||||
public static final String MASS_ST_涉嫌违法犯罪 = "err(20006)"; | |||||
public static final String MASS_ST_涉嫌欺诈 = "err(20008)"; | |||||
public static final String MASS_ST_涉嫌版权 = "err(20013)"; | |||||
public static final String MASS_ST_涉嫌互推_互相宣传 = "err(22000)"; | |||||
public static final String MASS_ST_涉嫌其他 = "err(21000)"; | |||||
public static final String MASS_ST_10001 = "err(10001)"; | |||||
public static final String MASS_ST_20001 = "err(20001)"; | |||||
public static final String MASS_ST_20004 = "err(20004)"; | |||||
public static final String MASS_ST_20002 = "err(20002)"; | |||||
public static final String MASS_ST_20006 = "err(20006)"; | |||||
public static final String MASS_ST_20008 = "err(20008)"; | |||||
public static final String MASS_ST_20013 = "err(20013)"; | |||||
public static final String MASS_ST_22000 = "err(22000)"; | |||||
public static final String MASS_ST_21000 = "err(21000)"; | |||||
/** | /** | ||||
* 群发反馈消息代码所对应的文字描述 | * 群发反馈消息代码所对应的文字描述 | ||||
@@ -101,6 +101,7 @@ public class WxConsts { | |||||
public static final String EVT_KF_CLOSE_SESSION = "kf_close_session"; // 客服关闭会话 | public static final String EVT_KF_CLOSE_SESSION = "kf_close_session"; // 客服关闭会话 | ||||
public static final String EVT_KF_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话 | public static final String EVT_KF_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话 | ||||
public static final String EVT_POI_CHECK_NOTIFY = "poi_check_notify"; //门店审核事件推送 | public static final String EVT_POI_CHECK_NOTIFY = "poi_check_notify"; //门店审核事件推送 | ||||
public static final String EVN_SUBMIT_MEMBERCARD_USER_INFO = "submit_membercard_user_info"; //接收会员信息事件推送 | |||||
//以下为微信认证事件 | //以下为微信认证事件 | ||||
/** | /** | ||||
* 资质认证成功 | * 资质认证成功 | ||||
@@ -135,26 +136,23 @@ public class WxConsts { | |||||
public static final String MEDIA_VIDEO = "video"; | public static final String MEDIA_VIDEO = "video"; | ||||
public static final String MEDIA_THUMB = "thumb"; | public static final String MEDIA_THUMB = "thumb"; | ||||
public static final String MEDIA_FILE = "file"; | public static final String MEDIA_FILE = "file"; | ||||
/////////////////////// | /////////////////////// | ||||
// 文件类型 | |||||
// 自定义菜单的按钮类型 | |||||
/////////////////////// | /////////////////////// | ||||
public static final String FILE_JPG = "jpeg"; | |||||
public static final String FILE_MP3 = "mp3"; | |||||
public static final String FILE_AMR = "amr"; | |||||
public static final String FILE_MP4 = "mp4"; | |||||
/** | /** | ||||
* 点击推事件 | * 点击推事件 | ||||
*/ | */ | ||||
public static final String BUTTON_CLICK = "click"; | public static final String BUTTON_CLICK = "click"; | ||||
/////////////////////// | |||||
// 自定义菜单的按钮类型 | |||||
/////////////////////// | |||||
/** | /** | ||||
* 跳转URL | * 跳转URL | ||||
*/ | */ | ||||
public static final String BUTTON_VIEW = "view"; | public static final String BUTTON_VIEW = "view"; | ||||
/** | |||||
* 跳转到小程序 | |||||
*/ | |||||
public static final String BUTTON_MINIPROGRAM = "miniprogram"; | |||||
/** | /** | ||||
* 扫码推事件 | * 扫码推事件 | ||||
*/ | */ | ||||
@@ -187,6 +185,7 @@ public class WxConsts { | |||||
* 跳转图文消息URL | * 跳转图文消息URL | ||||
*/ | */ | ||||
public static final String BUTTON_VIEW_LIMITED = "view_limited"; | public static final String BUTTON_VIEW_LIMITED = "view_limited"; | ||||
/** | /** | ||||
* 不弹出授权页面,直接跳转,只能获取用户openid | * 不弹出授权页面,直接跳转,只能获取用户openid | ||||
*/ | */ | ||||
@@ -216,14 +215,14 @@ public class WxConsts { | |||||
static { | static { | ||||
MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); | MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); | ||||
MASS_ST_2_DESC.put(MASS_ST_FAIL, "发送失败"); | MASS_ST_2_DESC.put(MASS_ST_FAIL, "发送失败"); | ||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌广告, "涉嫌广告"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌政治, "涉嫌政治"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌社会, "涉嫌社会"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌色情, "涉嫌色情"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌违法犯罪, "涉嫌违法犯罪"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌欺诈, "涉嫌欺诈"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌版权, "涉嫌版权"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌互推_互相宣传, "涉嫌互推_互相宣传"); | |||||
MASS_ST_2_DESC.put(MASS_ST_涉嫌其他, "涉嫌其他"); | |||||
MASS_ST_2_DESC.put(MASS_ST_10001, "涉嫌广告"); | |||||
MASS_ST_2_DESC.put(MASS_ST_20001, "涉嫌政治"); | |||||
MASS_ST_2_DESC.put(MASS_ST_20004, "涉嫌社会"); | |||||
MASS_ST_2_DESC.put(MASS_ST_20002, "涉嫌色情"); | |||||
MASS_ST_2_DESC.put(MASS_ST_20006, "涉嫌违法犯罪"); | |||||
MASS_ST_2_DESC.put(MASS_ST_20008, "涉嫌欺诈"); | |||||
MASS_ST_2_DESC.put(MASS_ST_20013, "涉嫌版权"); | |||||
MASS_ST_2_DESC.put(MASS_ST_22000, "涉嫌互推_互相宣传"); | |||||
MASS_ST_2_DESC.put(MASS_ST_21000, "涉嫌其他"); | |||||
} | } | ||||
} | } |
@@ -1,5 +1,6 @@ | |||||
package me.chanjar.weixin.common.bean.menu; | package me.chanjar.weixin.common.bean.menu; | ||||
import com.google.gson.annotations.SerializedName; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | import me.chanjar.weixin.common.util.ToStringUtils; | ||||
import java.io.Serializable; | import java.io.Serializable; | ||||
@@ -9,12 +10,65 @@ import java.util.List; | |||||
public class WxMenuButton implements Serializable { | public class WxMenuButton implements Serializable { | ||||
private static final long serialVersionUID = -1070939403109776555L; | private static final long serialVersionUID = -1070939403109776555L; | ||||
/** | |||||
* <pre> | |||||
* 菜单的响应动作类型: | |||||
* view表示网页类型, | |||||
* click表示点击类型, | |||||
* miniprogram表示小程序类型 | |||||
* </pre> | |||||
*/ | |||||
private String type; | private String type; | ||||
/** | |||||
* 菜单标题,不超过16个字节,子菜单不超过60个字节 | |||||
*/ | |||||
private String name; | private String name; | ||||
/** | |||||
* <pre> | |||||
* 菜单KEY值,用于消息接口推送,不超过128字节 | |||||
* click等点击类型必须 | |||||
* </pre> | |||||
*/ | |||||
private String key; | private String key; | ||||
/** | |||||
* <pre> | |||||
* 网页链接,用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。 | |||||
* view、miniprogram类型必须 | |||||
* </pre> | |||||
*/ | |||||
private String url; | private String url; | ||||
/** | |||||
* <pre> | |||||
* 调用新增永久素材接口返回的合法media_id | |||||
* media_id类型和view_limited类型必须 | |||||
* </pre> | |||||
*/ | |||||
@SerializedName("media_id") | |||||
private String mediaId; | private String mediaId; | ||||
/** | |||||
* <pre> | |||||
* 小程序的appid | |||||
* miniprogram类型必须 | |||||
* </pre> | |||||
*/ | |||||
@SerializedName("appid") | |||||
private String appId; | |||||
/** | |||||
* <pre> | |||||
* 小程序的页面路径 | |||||
* miniprogram类型必须 | |||||
* </pre> | |||||
*/ | |||||
@SerializedName("pagepath") | |||||
private String pagePath; | |||||
@SerializedName("sub_button") | |||||
private List<WxMenuButton> subButtons = new ArrayList<>(); | private List<WxMenuButton> subButtons = new ArrayList<>(); | ||||
@Override | @Override | ||||
@@ -69,4 +123,20 @@ public class WxMenuButton implements Serializable { | |||||
public void setMediaId(String mediaId) { | public void setMediaId(String mediaId) { | ||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
} | } | ||||
public String getAppId() { | |||||
return appId; | |||||
} | |||||
public void setAppId(String appId) { | |||||
this.appId = appId; | |||||
} | |||||
public String getPagePath() { | |||||
return pagePath; | |||||
} | |||||
public void setPagePath(String pagePath) { | |||||
this.pagePath = pagePath; | |||||
} | |||||
} | } |
@@ -9,13 +9,13 @@ | |||||
package me.chanjar.weixin.common.util.json; | package me.chanjar.weixin.common.util.json; | ||||
import java.util.List; | |||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.google.gson.JsonArray; | import com.google.gson.JsonArray; | ||||
import com.google.gson.JsonElement; | import com.google.gson.JsonElement; | ||||
import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||
import java.util.List; | |||||
public class GsonHelper { | public class GsonHelper { | ||||
@@ -130,6 +130,20 @@ public class GsonHelper { | |||||
return result.toArray(new Integer[0]); | return result.toArray(new Integer[0]); | ||||
} | } | ||||
public static Long[] getLongArray(JsonObject o, String string) { | |||||
JsonArray jsonArray = getAsJsonArray(o.getAsJsonArray(string)); | |||||
if (jsonArray == null) { | |||||
return null; | |||||
} | |||||
List<Long> result = Lists.newArrayList(); | |||||
for (int i = 0; i < jsonArray.size(); i++) { | |||||
result.add(jsonArray.get(i).getAsLong()); | |||||
} | |||||
return result.toArray(new Long[0]); | |||||
} | |||||
public static JsonArray getAsJsonArray(JsonElement element) { | public static JsonArray getAsJsonArray(JsonElement element) { | ||||
return element == null ? null : element.getAsJsonArray(); | return element == null ? null : element.getAsJsonArray(); | ||||
} | } | ||||
@@ -46,6 +46,8 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||||
buttonJson.addProperty("key", button.getKey()); | buttonJson.addProperty("key", button.getKey()); | ||||
buttonJson.addProperty("url", button.getUrl()); | buttonJson.addProperty("url", button.getUrl()); | ||||
buttonJson.addProperty("media_id", button.getMediaId()); | buttonJson.addProperty("media_id", button.getMediaId()); | ||||
buttonJson.addProperty("appid", button.getAppId()); | |||||
buttonJson.addProperty("pagepath", button.getPagePath()); | |||||
if (button.getSubButtons() != null && button.getSubButtons().size() > 0) { | if (button.getSubButtons() != null && button.getSubButtons().size() > 0) { | ||||
JsonArray buttonArray = new JsonArray(); | JsonArray buttonArray = new JsonArray(); | ||||
for (WxMenuButton sub_button : button.getSubButtons()) { | for (WxMenuButton sub_button : button.getSubButtons()) { | ||||
@@ -114,6 +116,8 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||||
button.setUrl(GsonHelper.getString(json, "url")); | button.setUrl(GsonHelper.getString(json, "url")); | ||||
button.setType(GsonHelper.getString(json, "type")); | button.setType(GsonHelper.getString(json, "type")); | ||||
button.setMediaId(GsonHelper.getString(json, "media_id")); | button.setMediaId(GsonHelper.getString(json, "media_id")); | ||||
button.setAppId(GsonHelper.getString(json, "appid")); | |||||
button.setPagePath(GsonHelper.getString(json, "pagepath")); | |||||
return button; | return button; | ||||
} | } | ||||
@@ -0,0 +1,139 @@ | |||||
package me.chanjar.weixin.cp.bean.article; | |||||
/** | |||||
* <pre> | |||||
* Created by BinaryWang on 2017/3/27. | |||||
* </pre> | |||||
* @author Binary Wang | |||||
*/ | |||||
public class MpnewsArticle { | |||||
private String title; | |||||
private String thumbMediaId; | |||||
private String author; | |||||
private String contentSourceUrl; | |||||
private String content; | |||||
private String digest; | |||||
private String showCoverPic; | |||||
private MpnewsArticle(Builder builder) { | |||||
setTitle(builder.title); | |||||
setThumbMediaId(builder.thumbMediaId); | |||||
setAuthor(builder.author); | |||||
setContentSourceUrl(builder.contentSourceUrl); | |||||
setContent(builder.content); | |||||
setDigest(builder.digest); | |||||
setShowCoverPic(builder.showCoverPic); | |||||
} | |||||
public static Builder newBuilder() { | |||||
return new Builder(); | |||||
} | |||||
public String getTitle() { | |||||
return title; | |||||
} | |||||
public void setTitle(String title) { | |||||
this.title = title; | |||||
} | |||||
public String getThumbMediaId() { | |||||
return thumbMediaId; | |||||
} | |||||
public void setThumbMediaId(String thumbMediaId) { | |||||
this.thumbMediaId = thumbMediaId; | |||||
} | |||||
public String getAuthor() { | |||||
return author; | |||||
} | |||||
public void setAuthor(String author) { | |||||
this.author = author; | |||||
} | |||||
public String getContentSourceUrl() { | |||||
return contentSourceUrl; | |||||
} | |||||
public void setContentSourceUrl(String contentSourceUrl) { | |||||
this.contentSourceUrl = contentSourceUrl; | |||||
} | |||||
public String getContent() { | |||||
return content; | |||||
} | |||||
public void setContent(String content) { | |||||
this.content = content; | |||||
} | |||||
public String getDigest() { | |||||
return digest; | |||||
} | |||||
public void setDigest(String digest) { | |||||
this.digest = digest; | |||||
} | |||||
public String getShowCoverPic() { | |||||
return showCoverPic; | |||||
} | |||||
public void setShowCoverPic(String showCoverPic) { | |||||
this.showCoverPic = showCoverPic; | |||||
} | |||||
public static final class Builder { | |||||
private String title; | |||||
private String thumbMediaId; | |||||
private String author; | |||||
private String contentSourceUrl; | |||||
private String content; | |||||
private String digest; | |||||
private String showCoverPic; | |||||
private Builder() { | |||||
} | |||||
public Builder title(String title) { | |||||
this.title = title; | |||||
return this; | |||||
} | |||||
public Builder thumbMediaId(String thumbMediaId) { | |||||
this.thumbMediaId = thumbMediaId; | |||||
return this; | |||||
} | |||||
public Builder author(String author) { | |||||
this.author = author; | |||||
return this; | |||||
} | |||||
public Builder contentSourceUrl(String contentSourceUrl) { | |||||
this.contentSourceUrl = contentSourceUrl; | |||||
return this; | |||||
} | |||||
public Builder content(String content) { | |||||
this.content = content; | |||||
return this; | |||||
} | |||||
public Builder digest(String digest) { | |||||
this.digest = digest; | |||||
return this; | |||||
} | |||||
public Builder showCoverPic(String showCoverPic) { | |||||
this.showCoverPic = showCoverPic; | |||||
return this; | |||||
} | |||||
public MpnewsArticle build() { | |||||
return new MpnewsArticle(this); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,49 @@ | |||||
package me.chanjar.weixin.cp.bean.article; | |||||
/** | |||||
* <pre> | |||||
* Created by BinaryWang on 2017/3/27. | |||||
* </pre> | |||||
* | |||||
* @author Binary Wang | |||||
*/ | |||||
public class NewArticle { | |||||
private String title; | |||||
private String description; | |||||
private String url; | |||||
private String picUrl; | |||||
public String getTitle() { | |||||
return this.title; | |||||
} | |||||
public void setTitle(String title) { | |||||
this.title = title; | |||||
} | |||||
public String getDescription() { | |||||
return this.description; | |||||
} | |||||
public void setDescription(String description) { | |||||
this.description = description; | |||||
} | |||||
public String getUrl() { | |||||
return this.url; | |||||
} | |||||
public void setUrl(String url) { | |||||
this.url = url; | |||||
} | |||||
public String getPicUrl() { | |||||
return this.picUrl; | |||||
} | |||||
public void setPicUrl(String picUrl) { | |||||
this.picUrl = picUrl; | |||||
} | |||||
} |
@@ -6,7 +6,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>2.5.0</version> | |||||
<version>2.6.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-cp</artifactId> | <artifactId>weixin-java-cp</artifactId> | ||||
@@ -20,9 +20,8 @@ | |||||
<version>${project.version}</version> | <version>${project.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
<groupId>redis.clients</groupId> | |||||
<artifactId>jedis</artifactId> | |||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
<groupId>org.testng</groupId> | <groupId>org.testng</groupId> | ||||
@@ -49,12 +48,6 @@ | |||||
<artifactId>jetty-servlet</artifactId> | <artifactId>jetty-servlet</artifactId> | ||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>redis.clients</groupId> | |||||
<artifactId>jedis</artifactId> | |||||
<version>2.9.0</version> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
<build> | <build> | ||||
@@ -28,9 +28,9 @@ public interface WxCpService { | |||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性 | * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param msgSignature | |||||
* @param timestamp | |||||
* @param nonce | |||||
* @param msgSignature 消息签名 | |||||
* @param timestamp 时间戳 | |||||
* @param nonce 随机数 | |||||
* @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息 | * @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息 | ||||
*/ | */ | ||||
boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); | boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); | ||||
@@ -41,14 +41,13 @@ public interface WxCpService { | |||||
* 企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。 | * 企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param userId | |||||
* @param userId 用户id | |||||
*/ | */ | ||||
void userAuthenticated(String userId) throws WxErrorException; | void userAuthenticated(String userId) throws WxErrorException; | ||||
/** | /** | ||||
* 获取access_token, 不强制刷新access_token | * 获取access_token, 不强制刷新access_token | ||||
* | * | ||||
* @throws WxErrorException | |||||
* @see #getAccessToken(boolean) | * @see #getAccessToken(boolean) | ||||
*/ | */ | ||||
String getAccessToken() throws WxErrorException; | String getAccessToken() throws WxErrorException; | ||||
@@ -63,14 +62,12 @@ public interface WxCpService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param forceRefresh 强制刷新 | * @param forceRefresh 强制刷新 | ||||
* @throws me.chanjar.weixin.common.exception.WxErrorException | |||||
*/ | */ | ||||
String getAccessToken(boolean forceRefresh) throws WxErrorException; | String getAccessToken(boolean forceRefresh) throws WxErrorException; | ||||
/** | /** | ||||
* 获得jsapi_ticket,不强制刷新jsapi_ticket | * 获得jsapi_ticket,不强制刷新jsapi_ticket | ||||
* | * | ||||
* @throws WxErrorException | |||||
* @see #getJsapiTicket(boolean) | * @see #getJsapiTicket(boolean) | ||||
*/ | */ | ||||
String getJsapiTicket() throws WxErrorException; | String getJsapiTicket() throws WxErrorException; | ||||
@@ -84,7 +81,6 @@ public interface WxCpService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param forceRefresh 强制刷新 | * @param forceRefresh 强制刷新 | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
String getJsapiTicket(boolean forceRefresh) throws WxErrorException; | String getJsapiTicket(boolean forceRefresh) throws WxErrorException; | ||||
@@ -113,15 +109,13 @@ public interface WxCpService { | |||||
* @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} | * @param mediaType 媒体类型, 请看{@link me.chanjar.weixin.common.api.WxConsts} | ||||
* @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} | * @param fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} | ||||
* @param inputStream 输入流 | * @param inputStream 输入流 | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) | ||||
throws WxErrorException, IOException; | |||||
throws WxErrorException, IOException; | |||||
/** | /** | ||||
* @param mediaType | |||||
* @param file | |||||
* @throws WxErrorException | |||||
* @param mediaType 媒体类型 | |||||
* @param file 文件对象 | |||||
* @see #mediaUpload(String, String, InputStream) | * @see #mediaUpload(String, String, InputStream) | ||||
*/ | */ | ||||
WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; | WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; | ||||
@@ -133,20 +127,18 @@ public interface WxCpService { | |||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 | * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param media_id | |||||
* @param mediaId 媒体id | |||||
* @return 保存到本地的临时文件 | * @return 保存到本地的临时文件 | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
File mediaDownload(String media_id) throws WxErrorException; | |||||
File mediaDownload(String mediaId) throws WxErrorException; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 发送消息 | * 发送消息 | ||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=发送消息 | |||||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param message | |||||
* @throws WxErrorException | |||||
* @param message 要发送的消息对象 | |||||
*/ | */ | ||||
void messageSend(WxCpMessage message) throws WxErrorException; | void messageSend(WxCpMessage message) throws WxErrorException; | ||||
@@ -158,8 +150,7 @@ public interface WxCpService { | |||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | * 注意: 这个方法使用WxCpConfigStorage里的agentId | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param menu | |||||
* @throws WxErrorException | |||||
* @param menu 菜单对象 | |||||
* @see #menuCreate(Integer, WxMenu) | * @see #menuCreate(Integer, WxMenu) | ||||
*/ | */ | ||||
void menuCreate(WxMenu menu) throws WxErrorException; | void menuCreate(WxMenu menu) throws WxErrorException; | ||||
@@ -173,8 +164,7 @@ public interface WxCpService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @param menu | |||||
* @throws WxErrorException | |||||
* @param menu 菜单对象 | |||||
* @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu) | * @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu) | ||||
*/ | */ | ||||
void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException; | void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException; | ||||
@@ -187,7 +177,6 @@ public interface WxCpService { | |||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | * 注意: 这个方法使用WxCpConfigStorage里的agentId | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @throws WxErrorException | |||||
* @see #menuDelete(Integer) | * @see #menuDelete(Integer) | ||||
*/ | */ | ||||
void menuDelete() throws WxErrorException; | void menuDelete() throws WxErrorException; | ||||
@@ -201,7 +190,6 @@ public interface WxCpService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @throws WxErrorException | |||||
* @see #menuDelete() | * @see #menuDelete() | ||||
*/ | */ | ||||
void menuDelete(Integer agentId) throws WxErrorException; | void menuDelete(Integer agentId) throws WxErrorException; | ||||
@@ -214,7 +202,6 @@ public interface WxCpService { | |||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | * 注意: 这个方法使用WxCpConfigStorage里的agentId | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @throws WxErrorException | |||||
* @see #menuGet(Integer) | * @see #menuGet(Integer) | ||||
*/ | */ | ||||
WxMenu menuGet() throws WxErrorException; | WxMenu menuGet() throws WxErrorException; | ||||
@@ -228,7 +215,6 @@ public interface WxCpService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @throws WxErrorException | |||||
* @see #menuGet() | * @see #menuGet() | ||||
*/ | */ | ||||
WxMenu menuGet(Integer agentId) throws WxErrorException; | WxMenu menuGet(Integer agentId) throws WxErrorException; | ||||
@@ -242,7 +228,6 @@ public interface WxCpService { | |||||
* | * | ||||
* @param depart 部门 | * @param depart 部门 | ||||
* @return 部门id | * @return 部门id | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
Integer departCreate(WxCpDepart depart) throws WxErrorException; | Integer departCreate(WxCpDepart depart) throws WxErrorException; | ||||
@@ -251,8 +236,6 @@ public interface WxCpService { | |||||
* 部门管理接口 - 查询所有部门 | * 部门管理接口 - 查询所有部门 | ||||
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 | ||||
* </pre> | * </pre> | ||||
* | |||||
* @throws WxErrorException | |||||
*/ | */ | ||||
List<WxCpDepart> departGet() throws WxErrorException; | List<WxCpDepart> departGet() throws WxErrorException; | ||||
@@ -264,7 +247,6 @@ public interface WxCpService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param group 要更新的group,group的id,name必须设置 | * @param group 要更新的group,group的id,name必须设置 | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
void departUpdate(WxCpDepart group) throws WxErrorException; | void departUpdate(WxCpDepart group) throws WxErrorException; | ||||
@@ -273,8 +255,7 @@ public interface WxCpService { | |||||
* 部门管理接口 - 删除部门 | * 部门管理接口 - 删除部门 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param departId | |||||
* @throws WxErrorException | |||||
* @param departId 部门id | |||||
*/ | */ | ||||
void departDelete(Integer departId) throws WxErrorException; | void departDelete(Integer departId) throws WxErrorException; | ||||
@@ -288,7 +269,6 @@ public interface WxCpService { | |||||
* @param departId 必填。部门id | * @param departId 必填。部门id | ||||
* @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | ||||
* @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | List<WxCpUser> userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | ||||
@@ -302,73 +282,66 @@ public interface WxCpService { | |||||
* @param departId 必填。部门id | * @param departId 必填。部门id | ||||
* @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 | ||||
* @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | List<WxCpUser> departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; | ||||
/** | /** | ||||
* 新建用户 | * 新建用户 | ||||
* | * | ||||
* @param user | |||||
* @throws WxErrorException | |||||
* @param user 用户对象 | |||||
*/ | */ | ||||
void userCreate(WxCpUser user) throws WxErrorException; | void userCreate(WxCpUser user) throws WxErrorException; | ||||
/** | /** | ||||
* 更新用户 | * 更新用户 | ||||
* | * | ||||
* @param user | |||||
* @throws WxErrorException | |||||
* @param user 用户对象 | |||||
*/ | */ | ||||
void userUpdate(WxCpUser user) throws WxErrorException; | void userUpdate(WxCpUser user) throws WxErrorException; | ||||
/** | /** | ||||
* 删除用户 | * 删除用户 | ||||
* | * | ||||
* @param userid | |||||
* @throws WxErrorException | |||||
* @param userid 用户id | |||||
*/ | */ | ||||
void userDelete(String userid) throws WxErrorException; | void userDelete(String userid) throws WxErrorException; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 批量删除成员 | * 批量删除成员 | ||||
* | |||||
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98 | * http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param userids 员工UserID列表。对应管理端的帐号 | * @param userids 员工UserID列表。对应管理端的帐号 | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
void userDelete(String[] userids) throws WxErrorException; | void userDelete(String[] userids) throws WxErrorException; | ||||
/** | /** | ||||
* 获取用户 | * 获取用户 | ||||
* | * | ||||
* @param userid | |||||
* @throws WxErrorException | |||||
* @param userid 用户id | |||||
*/ | */ | ||||
WxCpUser userGet(String userid) throws WxErrorException; | WxCpUser userGet(String userid) throws WxErrorException; | ||||
/** | /** | ||||
* 创建标签 | * 创建标签 | ||||
* | * | ||||
* @param tagName | |||||
* @param tagName 标签名 | |||||
*/ | */ | ||||
String tagCreate(String tagName) throws WxErrorException; | String tagCreate(String tagName) throws WxErrorException; | ||||
/** | /** | ||||
* 更新标签 | * 更新标签 | ||||
* | * | ||||
* @param tagId | |||||
* @param tagName | |||||
* @param tagId 标签id | |||||
* @param tagName 标签名 | |||||
*/ | */ | ||||
void tagUpdate(String tagId, String tagName) throws WxErrorException; | void tagUpdate(String tagId, String tagName) throws WxErrorException; | ||||
/** | /** | ||||
* 删除标签 | * 删除标签 | ||||
* | * | ||||
* @param tagId | |||||
* @param tagId 标签id | |||||
*/ | */ | ||||
void tagDelete(String tagId) throws WxErrorException; | void tagDelete(String tagId) throws WxErrorException; | ||||
@@ -380,24 +353,24 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 获取标签成员 | * 获取标签成员 | ||||
* | * | ||||
* @param tagId | |||||
* @param tagId 标签ID | |||||
*/ | */ | ||||
List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException; | List<WxCpUser> tagGetUsers(String tagId) throws WxErrorException; | ||||
/** | /** | ||||
* 增加标签成员 | * 增加标签成员 | ||||
* | * | ||||
* @param tagId | |||||
* @param userIds | |||||
* @param tagId 标签id | |||||
* @param userIds 用户ID 列表 | |||||
*/ | */ | ||||
void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException; | void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException; | ||||
/** | /** | ||||
* <pre> | |||||
* <pre> | |||||
* 构造oauth2授权的url连接 | * 构造oauth2授权的url连接 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param state | |||||
* @param state 状态码 | |||||
* @return url | * @return url | ||||
*/ | */ | ||||
String oauth2buildAuthorizationUrl(String state); | String oauth2buildAuthorizationUrl(String state); | ||||
@@ -408,8 +381,8 @@ public interface WxCpService { | |||||
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param redirectUri | |||||
* @param state | |||||
* @param redirectUri 跳转链接地址 | |||||
* @param state 状态码 | |||||
* @return url | * @return url | ||||
*/ | */ | ||||
String oauth2buildAuthorizationUrl(String redirectUri, String state); | String oauth2buildAuthorizationUrl(String redirectUri, String state); | ||||
@@ -423,7 +396,7 @@ public interface WxCpService { | |||||
* 注意: 这个方法使用WxCpConfigStorage里的agentId | * 注意: 这个方法使用WxCpConfigStorage里的agentId | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param code | |||||
* @param code 微信oauth授权返回的代码 | |||||
* @return [userid, deviceid] | * @return [userid, deviceid] | ||||
* @see #oauth2getUserInfo(Integer, String) | * @see #oauth2getUserInfo(Integer, String) | ||||
*/ | */ | ||||
@@ -439,7 +412,7 @@ public interface WxCpService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param agentId 企业号应用的id | * @param agentId 企业号应用的id | ||||
* @param code | |||||
* @param code 微信oauth授权返回的代码 | |||||
* @return [userid, deviceid] | * @return [userid, deviceid] | ||||
* @see #oauth2getUserInfo(String) | * @see #oauth2getUserInfo(String) | ||||
*/ | */ | ||||
@@ -449,8 +422,8 @@ public interface WxCpService { | |||||
/** | /** | ||||
* 移除标签成员 | * 移除标签成员 | ||||
* | * | ||||
* @param tagId | |||||
* @param userIds | |||||
* @param tagId 标签id | |||||
* @param userIds 用户id列表 | |||||
*/ | */ | ||||
void tagRemoveUsers(String tagId, List<String> userIds) throws WxErrorException; | void tagRemoveUsers(String tagId, List<String> userIds) throws WxErrorException; | ||||
@@ -463,7 +436,6 @@ public interface WxCpService { | |||||
* @param userId 用户的userid | * @param userId 用户的userid | ||||
* @param inviteTips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 | * @param inviteTips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 | ||||
* @return 1:微信邀请 2.邮件邀请 | * @return 1:微信邀请 2.邮件邀请 | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
int invite(String userId, String inviteTips) throws WxErrorException; | int invite(String userId, String inviteTips) throws WxErrorException; | ||||
@@ -474,25 +446,22 @@ public interface WxCpService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @return { "ip_list": ["101.226.103.*", "101.226.62.*"] } | * @return { "ip_list": ["101.226.103.*", "101.226.62.*"] } | ||||
* @throws WxErrorException | |||||
*/ | */ | ||||
String[] getCallbackIp() throws WxErrorException; | String[] getCallbackIp() throws WxErrorException; | ||||
/** | /** | ||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 | ||||
* | * | ||||
* @param url | |||||
* @param queryParam | |||||
* @throws WxErrorException | |||||
* @param url 接口地址 | |||||
* @param queryParam 请求参数 | |||||
*/ | */ | ||||
String get(String url, String queryParam) throws WxErrorException; | String get(String url, String queryParam) throws WxErrorException; | ||||
/** | /** | ||||
* 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 | * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 | ||||
* | * | ||||
* @param url | |||||
* @param postData | |||||
* @throws WxErrorException | |||||
* @param url 接口地址 | |||||
* @param postData 请求body字符串 | |||||
*/ | */ | ||||
String post(String url, String postData) throws WxErrorException; | String post(String url, String postData) throws WxErrorException; | ||||
@@ -503,19 +472,18 @@ public interface WxCpService { | |||||
* 可以参考,{@link me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor}的实现方法 | * 可以参考,{@link me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor}的实现方法 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param executor | |||||
* @param uri | |||||
* @param data | |||||
* @param <T> | |||||
* @param <E> | |||||
* @throws WxErrorException | |||||
* @param executor 执行器 | |||||
* @param uri 请求地址 | |||||
* @param data 参数 | |||||
* @param <T> 请求值类型 | |||||
* @param <E> 返回值类型 | |||||
*/ | */ | ||||
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException; | <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException; | ||||
/** | /** | ||||
* 注入 {@link WxCpConfigStorage} 的实现 | * 注入 {@link WxCpConfigStorage} 的实现 | ||||
* | * | ||||
* @param wxConfigProvider | |||||
* @param wxConfigProvider 配置对象 | |||||
*/ | */ | ||||
void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider); | void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider); | ||||
@@ -525,7 +493,7 @@ public interface WxCpService { | |||||
* 默认:1000ms | * 默认:1000ms | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param retrySleepMillis | |||||
* @param retrySleepMillis 重试休息时间 | |||||
*/ | */ | ||||
void setRetrySleepMillis(int retrySleepMillis); | void setRetrySleepMillis(int retrySleepMillis); | ||||
@@ -535,7 +503,7 @@ public interface WxCpService { | |||||
* 默认:5次 | * 默认:5次 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param maxRetryTimes | |||||
* @param maxRetryTimes 最大重试次数 | |||||
*/ | */ | ||||
void setMaxRetryTimes(int maxRetryTimes); | void setMaxRetryTimes(int maxRetryTimes); | ||||
@@ -550,7 +518,7 @@ public interface WxCpService { | |||||
* 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 | * 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 | ||||
* | * | ||||
* @param id id可以为任意字符串,建议使用FromUserName作为id | * @param id id可以为任意字符串,建议使用FromUserName作为id | ||||
* @param create | |||||
* @param create 是否新建 | |||||
*/ | */ | ||||
WxSession getSession(String id, boolean create); | WxSession getSession(String id, boolean create); | ||||
@@ -560,31 +528,26 @@ public interface WxCpService { | |||||
* WxCpService默认使用的是{@link me.chanjar.weixin.common.session.StandardSessionManager} | * WxCpService默认使用的是{@link me.chanjar.weixin.common.session.StandardSessionManager} | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param sessionManager | |||||
* @param sessionManager 会话管理器 | |||||
*/ | */ | ||||
void setSessionManager(WxSessionManager sessionManager); | void setSessionManager(WxSessionManager sessionManager); | ||||
/** | /** | ||||
* 上传部门列表覆盖企业号上的部门信息 | * 上传部门列表覆盖企业号上的部门信息 | ||||
* | * | ||||
* @param mediaId | |||||
* @throws WxErrorException | |||||
* @param mediaId 媒体id | |||||
*/ | */ | ||||
String replaceParty(String mediaId) throws WxErrorException; | String replaceParty(String mediaId) throws WxErrorException; | ||||
/** | /** | ||||
* 上传用户列表覆盖企业号上的用户信息 | * 上传用户列表覆盖企业号上的用户信息 | ||||
* | * | ||||
* @param mediaId | |||||
* @throws WxErrorException | |||||
* @param mediaId 媒体id | |||||
*/ | */ | ||||
String replaceUser(String mediaId) throws WxErrorException; | String replaceUser(String mediaId) throws WxErrorException; | ||||
/** | /** | ||||
* 获取异步任务结果 | * 获取异步任务结果 | ||||
* | |||||
* @param joinId | |||||
* @throws WxErrorException | |||||
*/ | */ | ||||
String getTaskResult(String joinId) throws WxErrorException; | String getTaskResult(String joinId) throws WxErrorException; | ||||
} | } |
@@ -238,12 +238,12 @@ public class WxCpServiceImpl implements WxCpService { | |||||
} | } | ||||
@Override | @Override | ||||
public File mediaDownload(String media_id) throws WxErrorException { | |||||
public File mediaDownload(String mediaId) throws WxErrorException { | |||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; | String url = "https://qyapi.weixin.qq.com/cgi-bin/media/get"; | ||||
return execute( | return execute( | ||||
new MediaDownloadRequestExecutor( | new MediaDownloadRequestExecutor( | ||||
this.configStorage.getTmpDirFile()), | this.configStorage.getTmpDirFile()), | ||||
url, "media_id=" + media_id); | |||||
url, "media_id=" + mediaId); | |||||
} | } | ||||
@@ -1,5 +1,7 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||||
import me.chanjar.weixin.cp.bean.messagebuilder.*; | import me.chanjar.weixin.cp.bean.messagebuilder.*; | ||||
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder; | ||||
@@ -28,7 +30,16 @@ public class WxCpMessage implements Serializable { | |||||
private String musicUrl; | private String musicUrl; | ||||
private String hqMusicUrl; | private String hqMusicUrl; | ||||
private String safe; | private String safe; | ||||
private List<WxArticle> articles = new ArrayList<>(); | |||||
private List<NewArticle> articles = new ArrayList<>(); | |||||
private List<MpnewsArticle> mpnewsArticles = new ArrayList<>(); | |||||
public List<MpnewsArticle> getMpnewsArticles() { | |||||
return mpnewsArticles; | |||||
} | |||||
public void setMpnewsArticles(List<MpnewsArticle> mpnewsArticles) { | |||||
this.mpnewsArticles = mpnewsArticles; | |||||
} | |||||
/** | /** | ||||
* 获得文本消息builder | * 获得文本消息builder | ||||
@@ -65,6 +76,13 @@ public class WxCpMessage implements Serializable { | |||||
return new NewsBuilder(); | return new NewsBuilder(); | ||||
} | } | ||||
/** | |||||
* 获得mpnews图文消息builder | |||||
*/ | |||||
public static MpnewsBuilder MPNEWS() { | |||||
return new MpnewsBuilder(); | |||||
} | |||||
/** | /** | ||||
* 获得文件消息builder | * 获得文件消息builder | ||||
*/ | */ | ||||
@@ -117,9 +135,10 @@ public class WxCpMessage implements Serializable { | |||||
* {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MUSIC} | * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MUSIC} | ||||
* {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VIDEO} | * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_VIDEO} | ||||
* {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_NEWS} | * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_NEWS} | ||||
* {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MPNEWS} | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param msgType | |||||
* @param msgType 消息类型 | |||||
*/ | */ | ||||
public void setMsgType(String msgType) { | public void setMsgType(String msgType) { | ||||
this.msgType = msgType; | this.msgType = msgType; | ||||
@@ -189,11 +208,11 @@ public class WxCpMessage implements Serializable { | |||||
this.hqMusicUrl = hqMusicUrl; | this.hqMusicUrl = hqMusicUrl; | ||||
} | } | ||||
public List<WxArticle> getArticles() { | |||||
public List<NewArticle> getArticles() { | |||||
return this.articles; | return this.articles; | ||||
} | } | ||||
public void setArticles(List<WxArticle> articles) { | |||||
public void setArticles(List<NewArticle> articles) { | |||||
this.articles = articles; | this.articles = articles; | ||||
} | } | ||||
@@ -201,45 +220,4 @@ public class WxCpMessage implements Serializable { | |||||
return WxCpGsonBuilder.INSTANCE.create().toJson(this); | return WxCpGsonBuilder.INSTANCE.create().toJson(this); | ||||
} | } | ||||
public static class WxArticle { | |||||
private String title; | |||||
private String description; | |||||
private String url; | |||||
private String picUrl; | |||||
public String getTitle() { | |||||
return this.title; | |||||
} | |||||
public void setTitle(String title) { | |||||
this.title = title; | |||||
} | |||||
public String getDescription() { | |||||
return this.description; | |||||
} | |||||
public void setDescription(String description) { | |||||
this.description = description; | |||||
} | |||||
public String getUrl() { | |||||
return this.url; | |||||
} | |||||
public void setUrl(String url) { | |||||
this.url = url; | |||||
} | |||||
public String getPicUrl() { | |||||
return this.picUrl; | |||||
} | |||||
public void setPicUrl(String picUrl) { | |||||
this.picUrl = picUrl; | |||||
} | |||||
} | |||||
} | } |
@@ -0,0 +1,48 @@ | |||||
package me.chanjar.weixin.cp.bean.messagebuilder; | |||||
import me.chanjar.weixin.common.api.WxConsts; | |||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | |||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | |||||
* mpnews类型的图文消息builder | |||||
* <pre> | |||||
* 用法: | |||||
* WxCustomMessage m = WxCustomMessage.MPNEWS().addArticle(article).toUser(...).build(); | |||||
* </pre> | |||||
* | |||||
* @author Binary Wang | |||||
*/ | |||||
public final class MpnewsBuilder extends BaseBuilder<MpnewsBuilder> { | |||||
private List<MpnewsArticle> articles = new ArrayList<>(); | |||||
private String mediaId; | |||||
public MpnewsBuilder() { | |||||
this.msgType = WxConsts.CUSTOM_MSG_MPNEWS; | |||||
} | |||||
public MpnewsBuilder mediaId(String mediaId) { | |||||
this.mediaId = mediaId; | |||||
return this; | |||||
} | |||||
public MpnewsBuilder addArticle(MpnewsArticle article) { | |||||
this.articles.add(article); | |||||
return this; | |||||
} | |||||
@Override | |||||
public WxCpMessage build() { | |||||
WxCpMessage m = super.build(); | |||||
m.setMpnewsArticles(this.articles); | |||||
if (this.mediaId != null) { | |||||
m.setMediaId(this.mediaId); | |||||
} | |||||
return m; | |||||
} | |||||
} |
@@ -2,6 +2,7 @@ package me.chanjar.weixin.cp.bean.messagebuilder; | |||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | import me.chanjar.weixin.cp.bean.WxCpMessage; | ||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
@@ -17,13 +18,13 @@ import java.util.List; | |||||
*/ | */ | ||||
public final class NewsBuilder extends BaseBuilder<NewsBuilder> { | public final class NewsBuilder extends BaseBuilder<NewsBuilder> { | ||||
private List<WxCpMessage.WxArticle> articles = new ArrayList<>(); | |||||
private List<NewArticle> articles = new ArrayList<>(); | |||||
public NewsBuilder() { | public NewsBuilder() { | ||||
this.msgType = WxConsts.CUSTOM_MSG_NEWS; | this.msgType = WxConsts.CUSTOM_MSG_NEWS; | ||||
} | } | ||||
public NewsBuilder addArticle(WxCpMessage.WxArticle article) { | |||||
public NewsBuilder addArticle(NewArticle article) { | |||||
this.articles.add(article); | this.articles.add(article); | ||||
return this; | return this; | ||||
} | } | ||||
@@ -11,6 +11,8 @@ package me.chanjar.weixin.cp.util.json; | |||||
import com.google.gson.*; | import com.google.gson.*; | ||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessage; | import me.chanjar.weixin.cp.bean.WxCpMessage; | ||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import java.lang.reflect.Type; | import java.lang.reflect.Type; | ||||
@@ -71,7 +73,7 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||||
if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { | if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { | ||||
JsonObject newsJsonObject = new JsonObject(); | JsonObject newsJsonObject = new JsonObject(); | ||||
JsonArray articleJsonArray = new JsonArray(); | JsonArray articleJsonArray = new JsonArray(); | ||||
for (WxCpMessage.WxArticle article : message.getArticles()) { | |||||
for (NewArticle article : message.getArticles()) { | |||||
JsonObject articleJson = new JsonObject(); | JsonObject articleJson = new JsonObject(); | ||||
articleJson.addProperty("title", article.getTitle()); | articleJson.addProperty("title", article.getTitle()); | ||||
articleJson.addProperty("description", article.getDescription()); | articleJson.addProperty("description", article.getDescription()); | ||||
@@ -83,6 +85,29 @@ public class WxCpMessageGsonAdapter implements JsonSerializer<WxCpMessage> { | |||||
messageJson.add("news", newsJsonObject); | messageJson.add("news", newsJsonObject); | ||||
} | } | ||||
if (WxConsts.CUSTOM_MSG_MPNEWS.equals(message.getMsgType())) { | |||||
JsonObject newsJsonObject = new JsonObject(); | |||||
if (message.getMediaId() != null) { | |||||
newsJsonObject.addProperty("media_id", message.getMediaId()); | |||||
}else { | |||||
JsonArray articleJsonArray = new JsonArray(); | |||||
for (MpnewsArticle article : message.getMpnewsArticles()) { | |||||
JsonObject articleJson = new JsonObject(); | |||||
articleJson.addProperty("title", article.getTitle()); | |||||
articleJson.addProperty("thumb_media_id", article.getThumbMediaId()); | |||||
articleJson.addProperty("author", article.getAuthor()); | |||||
articleJson.addProperty("content_source_url", article.getContentSourceUrl()); | |||||
articleJson.addProperty("content", article.getContent()); | |||||
articleJson.addProperty("digest", article.getDigest()); | |||||
articleJson.addProperty("show_cover_pic", article.getShowCoverPic()); | |||||
articleJsonArray.add(articleJson); | |||||
} | |||||
newsJsonObject.add("articles", articleJsonArray); | |||||
} | |||||
messageJson.add("mpnews", newsJsonObject); | |||||
} | |||||
return messageJson; | return messageJson; | ||||
} | } | ||||
@@ -0,0 +1,17 @@ | |||||
package me.chanjar.weixin.cp.api; | |||||
/** | |||||
* <pre> | |||||
* 仅供测试使用的一些常量 | |||||
* Created by Binary Wang on 2017-3-9. | |||||
* </pre> | |||||
*/ | |||||
public class TestConstants { | |||||
/////////////////////// | |||||
// 文件类型 | |||||
/////////////////////// | |||||
public static final String FILE_JPG = "jpeg"; | |||||
public static final String FILE_MP3 = "mp3"; | |||||
public static final String FILE_AMR = "amr"; | |||||
public static final String FILE_MP4 = "mp4"; | |||||
} |
@@ -54,10 +54,10 @@ public class WxCpMediaAPITest { | |||||
@DataProvider | @DataProvider | ||||
public Object[][] uploadMedia() { | public Object[][] uploadMedia() { | ||||
return new Object[][]{ | return new Object[][]{ | ||||
new Object[]{WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, "mm.jpeg"}, | |||||
new Object[]{WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, "mm.mp3"}, | |||||
new Object[]{WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, "mm.mp4"}, | |||||
new Object[]{WxConsts.MEDIA_FILE, WxConsts.FILE_JPG, "mm.jpeg"} | |||||
new Object[]{WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, | |||||
new Object[]{WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, "mm.mp3"}, | |||||
new Object[]{WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, | |||||
new Object[]{WxConsts.MEDIA_FILE, TestConstants.FILE_JPG, "mm.jpeg"} | |||||
}; | }; | ||||
} | } | ||||
@@ -1,10 +1,13 @@ | |||||
package me.chanjar.weixin.cp.bean; | package me.chanjar.weixin.cp.bean; | ||||
import me.chanjar.weixin.common.api.WxConsts; | import me.chanjar.weixin.common.api.WxConsts; | ||||
import me.chanjar.weixin.cp.bean.WxCpMessage.WxArticle; | |||||
import me.chanjar.weixin.cp.bean.article.NewArticle; | |||||
import me.chanjar.weixin.cp.bean.article.MpnewsArticle; | |||||
import org.testng.Assert; | import org.testng.Assert; | ||||
import org.testng.annotations.Test; | import org.testng.annotations.Test; | ||||
import static org.testng.Assert.assertEquals; | |||||
@Test | @Test | ||||
public class WxCpMessageTest { | public class WxCpMessageTest { | ||||
@@ -13,12 +16,12 @@ public class WxCpMessageTest { | |||||
reply.setToUser("OPENID"); | reply.setToUser("OPENID"); | ||||
reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT); | reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT); | ||||
reply.setContent("sfsfdsdf"); | reply.setContent("sfsfdsdf"); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); | |||||
} | } | ||||
public void testTextBuild() { | public void testTextBuild() { | ||||
WxCpMessage reply = WxCpMessage.TEXT().toUser("OPENID").content("sfsfdsdf").build(); | WxCpMessage reply = WxCpMessage.TEXT().toUser("OPENID").content("sfsfdsdf").build(); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"text\",\"text\":{\"content\":\"sfsfdsdf\"}}"); | |||||
} | } | ||||
public void testImageReply() { | public void testImageReply() { | ||||
@@ -26,12 +29,12 @@ public class WxCpMessageTest { | |||||
reply.setToUser("OPENID"); | reply.setToUser("OPENID"); | ||||
reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE); | reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE); | ||||
reply.setMediaId("MEDIA_ID"); | reply.setMediaId("MEDIA_ID"); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); | |||||
} | } | ||||
public void testImageBuild() { | public void testImageBuild() { | ||||
WxCpMessage reply = WxCpMessage.IMAGE().toUser("OPENID").mediaId("MEDIA_ID").build(); | WxCpMessage reply = WxCpMessage.IMAGE().toUser("OPENID").mediaId("MEDIA_ID").build(); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"image\",\"image\":{\"media_id\":\"MEDIA_ID\"}}"); | |||||
} | } | ||||
public void testVoiceReply() { | public void testVoiceReply() { | ||||
@@ -39,12 +42,12 @@ public class WxCpMessageTest { | |||||
reply.setToUser("OPENID"); | reply.setToUser("OPENID"); | ||||
reply.setMsgType(WxConsts.CUSTOM_MSG_VOICE); | reply.setMsgType(WxConsts.CUSTOM_MSG_VOICE); | ||||
reply.setMediaId("MEDIA_ID"); | reply.setMediaId("MEDIA_ID"); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); | |||||
} | } | ||||
public void testVoiceBuild() { | public void testVoiceBuild() { | ||||
WxCpMessage reply = WxCpMessage.VOICE().toUser("OPENID").mediaId("MEDIA_ID").build(); | WxCpMessage reply = WxCpMessage.VOICE().toUser("OPENID").mediaId("MEDIA_ID").build(); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"voice\",\"voice\":{\"media_id\":\"MEDIA_ID\"}}"); | |||||
} | } | ||||
public void testVideoReply() { | public void testVideoReply() { | ||||
@@ -55,12 +58,12 @@ public class WxCpMessageTest { | |||||
reply.setThumbMediaId("MEDIA_ID"); | reply.setThumbMediaId("MEDIA_ID"); | ||||
reply.setTitle("TITLE"); | reply.setTitle("TITLE"); | ||||
reply.setDescription("DESCRIPTION"); | reply.setDescription("DESCRIPTION"); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); | |||||
} | } | ||||
public void testVideoBuild() { | public void testVideoBuild() { | ||||
WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); | WxCpMessage reply = WxCpMessage.VIDEO().toUser("OPENID").title("TITLE").mediaId("MEDIA_ID").thumbMediaId("MEDIA_ID").description("DESCRIPTION").build(); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"video\",\"video\":{\"media_id\":\"MEDIA_ID\",\"thumb_media_id\":\"MEDIA_ID\",\"title\":\"TITLE\",\"description\":\"DESCRIPTION\"}}"); | |||||
} | } | ||||
public void testNewsReply() { | public void testNewsReply() { | ||||
@@ -68,14 +71,14 @@ public class WxCpMessageTest { | |||||
reply.setToUser("OPENID"); | reply.setToUser("OPENID"); | ||||
reply.setMsgType(WxConsts.CUSTOM_MSG_NEWS); | reply.setMsgType(WxConsts.CUSTOM_MSG_NEWS); | ||||
WxArticle article1 = new WxArticle(); | |||||
NewArticle article1 = new NewArticle(); | |||||
article1.setUrl("URL"); | article1.setUrl("URL"); | ||||
article1.setPicUrl("PIC_URL"); | article1.setPicUrl("PIC_URL"); | ||||
article1.setDescription("Is Really A Happy Day"); | article1.setDescription("Is Really A Happy Day"); | ||||
article1.setTitle("Happy Day"); | article1.setTitle("Happy Day"); | ||||
reply.getArticles().add(article1); | reply.getArticles().add(article1); | ||||
WxArticle article2 = new WxArticle(); | |||||
NewArticle article2 = new NewArticle(); | |||||
article2.setUrl("URL"); | article2.setUrl("URL"); | ||||
article2.setPicUrl("PIC_URL"); | article2.setPicUrl("PIC_URL"); | ||||
article2.setDescription("Is Really A Happy Day"); | article2.setDescription("Is Really A Happy Day"); | ||||
@@ -83,17 +86,17 @@ public class WxCpMessageTest { | |||||
reply.getArticles().add(article2); | reply.getArticles().add(article2); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
} | } | ||||
public void testNewsBuild() { | public void testNewsBuild() { | ||||
WxArticle article1 = new WxArticle(); | |||||
NewArticle article1 = new NewArticle(); | |||||
article1.setUrl("URL"); | article1.setUrl("URL"); | ||||
article1.setPicUrl("PIC_URL"); | article1.setPicUrl("PIC_URL"); | ||||
article1.setDescription("Is Really A Happy Day"); | article1.setDescription("Is Really A Happy Day"); | ||||
article1.setTitle("Happy Day"); | article1.setTitle("Happy Day"); | ||||
WxArticle article2 = new WxArticle(); | |||||
NewArticle article2 = new NewArticle(); | |||||
article2.setUrl("URL"); | article2.setUrl("URL"); | ||||
article2.setPicUrl("PIC_URL"); | article2.setPicUrl("PIC_URL"); | ||||
article2.setDescription("Is Really A Happy Day"); | article2.setDescription("Is Really A Happy Day"); | ||||
@@ -101,7 +104,42 @@ public class WxCpMessageTest { | |||||
WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | WxCpMessage reply = WxCpMessage.NEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | ||||
Assert.assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"news\",\"news\":{\"articles\":[{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"},{\"title\":\"Happy Day\",\"description\":\"Is Really A Happy Day\",\"url\":\"URL\",\"picurl\":\"PIC_URL\"}]}}"); | |||||
} | |||||
public void testMpnewsBuild_with_articles() { | |||||
MpnewsArticle article1 = MpnewsArticle.newBuilder() | |||||
.title("Happy Day") | |||||
.author("aaaaaa") | |||||
.content("hahaha") | |||||
.contentSourceUrl("nice url") | |||||
.digest("digest") | |||||
.showCoverPic("heihei") | |||||
.thumbMediaId("thumb") | |||||
.build(); | |||||
MpnewsArticle article2 = MpnewsArticle.newBuilder() | |||||
.title("Happy Day") | |||||
.author("aaaaaa") | |||||
.content("hahaha") | |||||
.contentSourceUrl("nice url") | |||||
.digest("digest") | |||||
.showCoverPic("heihei") | |||||
.thumbMediaId("thumb") | |||||
.build(); | |||||
WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").addArticle(article1).addArticle(article2).build(); | |||||
assertEquals(reply.toJson(), "{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\"," + | |||||
"\"mpnews\":{\"articles\":[{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}," + | |||||
"{\"title\":\"Happy Day\",\"thumb_media_id\":\"thumb\",\"author\":\"aaaaaa\",\"content_source_url\":\"nice url\",\"content\":\"hahaha\",\"digest\":\"digest\",\"show_cover_pic\":\"heihei\"}]}}"); | |||||
} | |||||
public void testMpnewsBuild_with_media_id() { | |||||
WxCpMessage reply = WxCpMessage.MPNEWS().toUser("OPENID").mediaId("mmm").build(); | |||||
assertEquals(reply.toJson(), | |||||
"{\"touser\":\"OPENID\",\"msgtype\":\"mpnews\",\"mpnews\":{\"media_id\":\"mmm\"}}"); | |||||
} | } | ||||
} | } |
@@ -6,7 +6,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>2.5.0</version> | |||||
<version>2.6.0</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-mp</artifactId> | <artifactId>weixin-java-mp</artifactId> | ||||
<name>WeiXin Java Tools - MP</name> | <name>WeiXin Java Tools - MP</name> | ||||
@@ -18,21 +18,11 @@ | |||||
<artifactId>weixin-java-common</artifactId> | <artifactId>weixin-java-common</artifactId> | ||||
<version>${project.version}</version> | <version>${project.version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.testng</groupId> | <groupId>org.testng</groupId> | ||||
<artifactId>testng</artifactId> | <artifactId>testng</artifactId> | ||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.mockito</groupId> | |||||
<artifactId>mockito-all</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>com.google.inject</groupId> | <groupId>com.google.inject</groupId> | ||||
<artifactId>guice</artifactId> | <artifactId>guice</artifactId> | ||||
@@ -55,14 +45,8 @@ | |||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
<groupId>io.rest-assured</groupId> | |||||
<artifactId>xml-path</artifactId> | |||||
<version>3.0.1</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<artifactId>qrcode-utils</artifactId> | |||||
<version>1.0</version> | |||||
<groupId>redis.clients</groupId> | |||||
<artifactId>jedis</artifactId> | |||||
</dependency> | </dependency> | ||||
</dependencies> | </dependencies> | ||||
@@ -3,7 +3,6 @@ package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | ||||
import javax.net.ssl.SSLContext; | |||||
import java.io.File; | import java.io.File; | ||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
@@ -82,27 +81,6 @@ public interface WxMpConfigStorage { | |||||
String getSecret(); | String getSecret(); | ||||
String getPartnerId(); | |||||
String getPartnerKey(); | |||||
/** | |||||
* 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。 | |||||
* | |||||
* @since 2.5.0 | |||||
*/ | |||||
String getNotifyURL(); | |||||
/** | |||||
* 交易类型 | |||||
* <pre> | |||||
* JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付 | |||||
* </pre> | |||||
* | |||||
* @since 2.5.0 | |||||
*/ | |||||
String getTradeType(); | |||||
String getToken(); | String getToken(); | ||||
String getAesKey(); | String getAesKey(); | ||||
@@ -121,17 +99,6 @@ public interface WxMpConfigStorage { | |||||
File getTmpDirFile(); | File getTmpDirFile(); | ||||
SSLContext getSslContext(); | |||||
void setSslContext(SSLContext sslContext); | |||||
/** | |||||
* 在此之前,必须将partnerId进行赋值 | |||||
* | |||||
* @param filePath apiclient_cert.p12的文件的绝对路径 | |||||
*/ | |||||
void setSslContextFilePath(String filePath) throws Exception; | |||||
/** | /** | ||||
* http client builder | * http client builder | ||||
* | * | ||||
@@ -144,8 +111,4 @@ public interface WxMpConfigStorage { | |||||
*/ | */ | ||||
boolean autoRefreshToken(); | boolean autoRefreshToken(); | ||||
/** | |||||
* 微信支付是否使用仿真测试环境 | |||||
*/ | |||||
boolean useSandboxForWxPay(); | |||||
} | } |
@@ -76,5 +76,24 @@ public interface WxMpDeviceService { | |||||
*/ | */ | ||||
WxDeviceBindResult compelUnbind(WxDeviceBind wxDeviceBind) throws WxErrorException; | WxDeviceBindResult compelUnbind(WxDeviceBind wxDeviceBind) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 通过device type和device id 获取设备主人的openid | |||||
* 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-11 | |||||
* </pre> | |||||
* @param deviceType 设备类型,目前为"公众账号原始ID" | |||||
* @param deviceId 设备ID | |||||
* @return WxDeviceOpenIdResult | |||||
*/ | |||||
WxDeviceOpenIdResult getOpenId(String deviceType,String deviceId) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 通过openid获取用户在当前devicetype下绑定的deviceid列表` | |||||
* 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-12 | |||||
* </pre> | |||||
* @param openId 要查询的用户的openid | |||||
* @return WxDeviceBindDeviceResult | |||||
*/ | |||||
WxDeviceBindDeviceResult getBindDevice(String openId) throws WxErrorException; | |||||
} | } |
@@ -3,12 +3,8 @@ package me.chanjar.weixin.mp.api; | |||||
import me.chanjar.weixin.common.bean.WxAccessToken; | import me.chanjar.weixin.common.bean.WxAccessToken; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | import me.chanjar.weixin.common.util.ToStringUtils; | ||||
import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; | ||||
import org.apache.http.ssl.SSLContexts; | |||||
import javax.net.ssl.SSLContext; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | |||||
import java.security.KeyStore; | |||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
import java.util.concurrent.locks.ReentrantLock; | import java.util.concurrent.locks.ReentrantLock; | ||||
@@ -21,10 +17,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
protected volatile String appId; | protected volatile String appId; | ||||
protected volatile String secret; | protected volatile String secret; | ||||
protected volatile String partnerId; | |||||
protected volatile String partnerKey; | |||||
protected volatile String notifyURL; | |||||
protected volatile String tradeType; | |||||
protected volatile String token; | protected volatile String token; | ||||
protected volatile String accessToken; | protected volatile String accessToken; | ||||
protected volatile String aesKey; | protected volatile String aesKey; | ||||
@@ -52,8 +44,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
*/ | */ | ||||
protected volatile File tmpDirFile; | protected volatile File tmpDirFile; | ||||
protected volatile SSLContext sslContext; | |||||
protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder; | ||||
@Override | @Override | ||||
@@ -61,10 +51,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
return this.accessToken; | return this.accessToken; | ||||
} | } | ||||
public void setAccessToken(String accessToken) { | |||||
this.accessToken = accessToken; | |||||
} | |||||
@Override | @Override | ||||
public Lock getAccessTokenLock() { | public Lock getAccessTokenLock() { | ||||
return this.accessTokenLock; | return this.accessTokenLock; | ||||
@@ -96,23 +82,11 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
return this.jsapiTicket; | return this.jsapiTicket; | ||||
} | } | ||||
public void setJsapiTicket(String jsapiTicket) { | |||||
this.jsapiTicket = jsapiTicket; | |||||
} | |||||
@Override | @Override | ||||
public Lock getJsapiTicketLock() { | public Lock getJsapiTicketLock() { | ||||
return this.jsapiTicketLock; | return this.jsapiTicketLock; | ||||
} | } | ||||
public long getJsapiTicketExpiresTime() { | |||||
return this.jsapiTicketExpiresTime; | |||||
} | |||||
public void setJsapiTicketExpiresTime(long jsapiTicketExpiresTime) { | |||||
this.jsapiTicketExpiresTime = jsapiTicketExpiresTime; | |||||
} | |||||
@Override | @Override | ||||
public boolean isJsapiTicketExpired() { | public boolean isJsapiTicketExpired() { | ||||
return System.currentTimeMillis() > this.jsapiTicketExpiresTime; | return System.currentTimeMillis() > this.jsapiTicketExpiresTime; | ||||
@@ -256,88 +230,49 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
} | } | ||||
@Override | @Override | ||||
public String getPartnerId() { | |||||
return this.partnerId; | |||||
public File getTmpDirFile() { | |||||
return this.tmpDirFile; | |||||
} | } | ||||
public void setPartnerId(String partnerId) { | |||||
this.partnerId = partnerId; | |||||
public void setTmpDirFile(File tmpDirFile) { | |||||
this.tmpDirFile = tmpDirFile; | |||||
} | } | ||||
@Override | @Override | ||||
public String getPartnerKey() { | |||||
return this.partnerKey; | |||||
} | |||||
public void setPartnerKey(String partnerKey) { | |||||
this.partnerKey = partnerKey; | |||||
} | |||||
public String getNotifyURL() { | |||||
return notifyURL; | |||||
} | |||||
public void setNotifyURL(String notifyURL) { | |||||
this.notifyURL = notifyURL; | |||||
} | |||||
public String getTradeType() { | |||||
return tradeType; | |||||
public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||||
return this.apacheHttpClientBuilder; | |||||
} | } | ||||
public void setTradeType(String tradeType) { | |||||
this.tradeType = tradeType; | |||||
public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | |||||
this.apacheHttpClientBuilder = apacheHttpClientBuilder; | |||||
} | } | ||||
@Override | |||||
public File getTmpDirFile() { | |||||
return this.tmpDirFile; | |||||
public void setAccessToken(String accessToken) { | |||||
this.accessToken = accessToken; | |||||
} | } | ||||
public void setTmpDirFile(File tmpDirFile) { | |||||
this.tmpDirFile = tmpDirFile; | |||||
public void setJsapiTicket(String jsapiTicket) { | |||||
this.jsapiTicket = jsapiTicket; | |||||
} | } | ||||
@Override | |||||
public SSLContext getSslContext() { | |||||
return this.sslContext; | |||||
public long getJsapiTicketExpiresTime() { | |||||
return this.jsapiTicketExpiresTime; | |||||
} | } | ||||
@Override | |||||
public void setSslContext(SSLContext context) { | |||||
this.sslContext = context; | |||||
public void setJsapiTicketExpiresTime(long jsapiTicketExpiresTime) { | |||||
this.jsapiTicketExpiresTime = jsapiTicketExpiresTime; | |||||
} | } | ||||
@Override | |||||
public void setSslContextFilePath(String filePath) { | |||||
if (null == partnerId) { | |||||
throw new IllegalArgumentException("请设置partnerId的值"); | |||||
} | |||||
File file = new File(filePath); | |||||
if (!file.exists()) { | |||||
throw new RuntimeException("证书文件:【" + file.getPath() + "】不存在!"); | |||||
} | |||||
try { | |||||
FileInputStream inputStream = new FileInputStream(file); | |||||
KeyStore keystore = KeyStore.getInstance("PKCS12"); | |||||
char[] partnerId2charArray = partnerId.toCharArray(); | |||||
keystore.load(inputStream, partnerId2charArray); | |||||
this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); | |||||
} catch (Exception e) { | |||||
throw new RuntimeException("证书文件有问题,请核实!", e); | |||||
} | |||||
public void setCardApiTicket(String cardApiTicket) { | |||||
this.cardApiTicket = cardApiTicket; | |||||
} | } | ||||
@Override | |||||
public ApacheHttpClientBuilder getApacheHttpClientBuilder() { | |||||
return this.apacheHttpClientBuilder; | |||||
public long getCardApiTicketExpiresTime() { | |||||
return this.cardApiTicketExpiresTime; | |||||
} | } | ||||
public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) { | |||||
this.apacheHttpClientBuilder = apacheHttpClientBuilder; | |||||
public void setCardApiTicketExpiresTime(long cardApiTicketExpiresTime) { | |||||
this.cardApiTicketExpiresTime = cardApiTicketExpiresTime; | |||||
} | } | ||||
@Override | @Override | ||||
@@ -345,8 +280,4 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { | |||||
return true; | return true; | ||||
} | } | ||||
@Override | |||||
public boolean useSandboxForWxPay() { | |||||
return false; | |||||
} | |||||
} | } |
@@ -0,0 +1,90 @@ | |||||
package me.chanjar.weixin.mp.api; | |||||
import redis.clients.jedis.Jedis; | |||||
/** | |||||
* 基于Redis的微信配置provider | |||||
* | |||||
* @author lly835 | |||||
*/ | |||||
@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_"; | |||||
protected Jedis jedis; | |||||
@Override | |||||
public String getAccessToken() { | |||||
return jedis.get(ACCESS_TOKEN_KEY.concat(appId)); | |||||
} | |||||
@Override | |||||
public boolean isAccessTokenExpired() { | |||||
return getAccessToken() == null ? true : false; | |||||
} | |||||
@Override | |||||
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) { | |||||
jedis.set(ACCESS_TOKEN_KEY.concat(appId), accessToken); | |||||
jedis.expire(ACCESS_TOKEN_KEY.concat(appId), expiresInSeconds - 200); | |||||
} | |||||
@Override | |||||
public void expireAccessToken() { | |||||
jedis.expire(ACCESS_TOKEN_KEY.concat(appId), 0); | |||||
} | |||||
@Override | |||||
public String getJsapiTicket() { | |||||
return jedis.get(JSAPI_TICKET_KEY.concat(appId)); | |||||
} | |||||
@Override | |||||
public boolean isJsapiTicketExpired() { | |||||
return getJsapiTicket() == null ? true : false; | |||||
} | |||||
@Override | |||||
public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) { | |||||
jedis.set(JSAPI_TICKET_KEY.concat(appId), jsapiTicket); | |||||
jedis.expire(JSAPI_TICKET_KEY.concat(appId), expiresInSeconds - 200); | |||||
} | |||||
@Override | |||||
public void expireJsapiTicket() { | |||||
jedis.expire(JSAPI_TICKET_KEY.concat(appId), 0); | |||||
} | |||||
/** | |||||
* 卡券api_ticket | |||||
*/ | |||||
@Override | |||||
public String getCardApiTicket() { | |||||
return jedis.get(CARDAPI_TICKET_KEY.concat(appId)); | |||||
} | |||||
@Override | |||||
public boolean isCardApiTicketExpired() { | |||||
return getCardApiTicket() == null ? true : false; | |||||
} | |||||
@Override | |||||
public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) { | |||||
jedis.set(CARDAPI_TICKET_KEY.concat(appId), cardApiTicket); | |||||
jedis.expire(CARDAPI_TICKET_KEY.concat(appId), expiresInSeconds - 200); | |||||
} | |||||
@Override | |||||
public void expireCardApiTicket() { | |||||
jedis.expire(CARDAPI_TICKET_KEY.concat(appId), 0); | |||||
} | |||||
public void setJedis(Jedis jedis) { | |||||
this.jedis = jedis; | |||||
} | |||||
} |
@@ -6,8 +6,10 @@ import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; | |||||
import java.io.File; | import java.io.File; | ||||
/** | /** | ||||
* <pre> | |||||
* 二维码相关操作接口 | * 二维码相关操作接口 | ||||
* 文档地址:https://mp.weixin.qq.com/wiki?action=doc&id=mp1443433542&t=0.9274944716856435 | * 文档地址:https://mp.weixin.qq.com/wiki?action=doc&id=mp1443433542&t=0.9274944716856435 | ||||
* </pre> | |||||
* @author Binary Wang | * @author Binary Wang | ||||
*/ | */ | ||||
public interface WxMpQrcodeService { | public interface WxMpQrcodeService { | ||||
@@ -311,13 +311,6 @@ public interface WxMpService { | |||||
*/ | */ | ||||
WxMpCardService getCardService(); | WxMpCardService getCardService(); | ||||
/** | |||||
* 返回微信支付相关接口方法的实现类对象,以方便调用其各个接口 | |||||
* | |||||
* @return WxMpPayService | |||||
*/ | |||||
WxMpPayService getPayService(); | |||||
/** | /** | ||||
* 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口 | * 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口 | ||||
* | * | ||||
@@ -23,7 +23,7 @@ public class WxMpDeviceServiceImpl implements WxMpDeviceService { | |||||
@Override | @Override | ||||
public TransMsgResp transMsg(WxDeviceMsg msg) throws WxErrorException { | public TransMsgResp transMsg(WxDeviceMsg msg) throws WxErrorException { | ||||
String url = API_URL_PREFIX + "/transmsg"; | String url = API_URL_PREFIX + "/transmsg"; | ||||
String response = this.wxMpService.post(url,msg.toJson()); | |||||
String response = this.wxMpService.post(url, msg.toJson()); | |||||
return TransMsgResp.fromJson(response); | return TransMsgResp.fromJson(response); | ||||
} | } | ||||
@@ -37,21 +37,21 @@ public class WxMpDeviceServiceImpl implements WxMpDeviceService { | |||||
@Override | @Override | ||||
public WxDeviceAuthorizeResult authorize(WxDeviceAuthorize wxDeviceAuthorize) throws WxErrorException { | public WxDeviceAuthorizeResult authorize(WxDeviceAuthorize wxDeviceAuthorize) throws WxErrorException { | ||||
String url = API_URL_PREFIX + "/authorize_device"; | String url = API_URL_PREFIX + "/authorize_device"; | ||||
String response = this.wxMpService.post(url,wxDeviceAuthorize.toJson()); | |||||
String response = this.wxMpService.post(url, wxDeviceAuthorize.toJson()); | |||||
return WxDeviceAuthorizeResult.fromJson(response); | return WxDeviceAuthorizeResult.fromJson(response); | ||||
} | } | ||||
@Override | @Override | ||||
public WxDeviceBindResult bind(WxDeviceBind wxDeviceBind) throws WxErrorException { | public WxDeviceBindResult bind(WxDeviceBind wxDeviceBind) throws WxErrorException { | ||||
String url = API_URL_PREFIX + "/bind"; | String url = API_URL_PREFIX + "/bind"; | ||||
String response = this.wxMpService.post(url,wxDeviceBind.toJson()); | |||||
String response = this.wxMpService.post(url, wxDeviceBind.toJson()); | |||||
return WxDeviceBindResult.fromJson(response); | return WxDeviceBindResult.fromJson(response); | ||||
} | } | ||||
@Override | @Override | ||||
public WxDeviceBindResult compelBind(WxDeviceBind wxDeviceBind) throws WxErrorException { | public WxDeviceBindResult compelBind(WxDeviceBind wxDeviceBind) throws WxErrorException { | ||||
String url = API_URL_PREFIX + "/compel_bind"; | String url = API_URL_PREFIX + "/compel_bind"; | ||||
String response = this.wxMpService.post(url,wxDeviceBind.toJson()); | |||||
String response = this.wxMpService.post(url, wxDeviceBind.toJson()); | |||||
return WxDeviceBindResult.fromJson(response); | return WxDeviceBindResult.fromJson(response); | ||||
} | } | ||||
@@ -68,5 +68,19 @@ public class WxMpDeviceServiceImpl implements WxMpDeviceService { | |||||
String response = this.wxMpService.post(url, wxDeviceBind.toJson()); | String response = this.wxMpService.post(url, wxDeviceBind.toJson()); | ||||
return WxDeviceBindResult.fromJson(response); | return WxDeviceBindResult.fromJson(response); | ||||
} | } | ||||
@Override | |||||
public WxDeviceOpenIdResult getOpenId(String deviceType, String deviceId) throws WxErrorException { | |||||
String url = API_URL_PREFIX + "/get_openid"; | |||||
String response = this.wxMpService.get(url, "device_type=" + deviceType + "&device_id=" + deviceId); | |||||
return WxDeviceOpenIdResult.fromJson(response); | |||||
} | |||||
@Override | |||||
public WxDeviceBindDeviceResult getBindDevice(String openId) throws WxErrorException { | |||||
String url = API_URL_PREFIX+"/get_bind_device"; | |||||
String response = this.wxMpService.get(url,"openid="+openId); | |||||
return WxDeviceBindDeviceResult.fromJson(response); | |||||
} | |||||
} | } | ||||
@@ -1,521 +0,0 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | |||||
import com.github.binarywang.utils.qrcode.QrcodeUtils; | |||||
import com.google.common.collect.Maps; | |||||
import me.chanjar.weixin.common.bean.result.WxError; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import me.chanjar.weixin.common.util.BeanUtils; | |||||
import me.chanjar.weixin.mp.api.WxMpConfigStorage; | |||||
import me.chanjar.weixin.mp.api.WxMpPayService; | |||||
import me.chanjar.weixin.mp.api.WxMpService; | |||||
import me.chanjar.weixin.mp.bean.pay.request.*; | |||||
import me.chanjar.weixin.mp.bean.pay.result.*; | |||||
import org.apache.commons.codec.digest.DigestUtils; | |||||
import org.apache.commons.lang3.ArrayUtils; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.apache.http.Consts; | |||||
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.conn.ssl.DefaultHostnameVerifier; | |||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||||
import org.apache.http.entity.StringEntity; | |||||
import org.apache.http.impl.client.CloseableHttpClient; | |||||
import org.apache.http.impl.client.HttpClients; | |||||
import org.apache.http.util.EntityUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import javax.net.ssl.SSLContext; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.util.*; | |||||
/** | |||||
* Created by Binary Wang on 2016/7/28. | |||||
* | |||||
* @author binarywang (https://github.com/binarywang) | |||||
*/ | |||||
public class WxMpPayServiceImpl implements WxMpPayService { | |||||
private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; | |||||
private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; | |||||
private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", "REFUND_SOURCE_UNSETTLED_FUNDS"}; | |||||
private static final String[] BILL_TYPE = new String[]{"ALL","REFUND","SUCCESS"};; | |||||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
private WxMpService wxMpService; | |||||
public WxMpPayServiceImpl(WxMpService wxMpService) { | |||||
this.wxMpService = wxMpService; | |||||
} | |||||
private WxMpConfigStorage getConfig() { | |||||
return wxMpService.getWxMpConfigStorage(); | |||||
} | |||||
private String getPayBaseUrl(){ | |||||
if(this.getConfig().useSandboxForWxPay()){ | |||||
return PAY_BASE_URL + "/sandboxnew"; | |||||
} | |||||
return PAY_BASE_URL; | |||||
} | |||||
@Override | |||||
public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException { | |||||
this.initRequest(request); | |||||
if (StringUtils.isBlank(request.getOpUserId())) { | |||||
request.setOpUserId(this.getConfig().getPartnerId()); | |||||
} | |||||
this.checkParameters(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/secapi/pay/refund"; | |||||
String responseContent = this.executeWithKey(url, request.toXML()); | |||||
WxPayRefundResult result = WxPayRefundResult.fromXML(responseContent, WxPayRefundResult.class); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException { | |||||
if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) || | |||||
(StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) { | |||||
throw new IllegalArgumentException("transaction_id , out_trade_no,out_refund_no, refund_id 必须四选一"); | |||||
} | |||||
WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); | |||||
this.initRequest(request); | |||||
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||||
request.setTransactionId(StringUtils.trimToNull(transactionId)); | |||||
request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); | |||||
request.setRefundId(StringUtils.trimToNull(refundId)); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/pay/refundquery"; | |||||
String responseContent = this.executeRequest(url, request.toXML()); | |||||
WxPayRefundQueryResult result = WxPayRefundQueryResult.fromXML(responseContent, WxPayRefundQueryResult.class); | |||||
result.composeRefundRecords(); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
private void checkResult(WxPayBaseResult result) throws WxErrorException { | |||||
//校验返回结果签名 | |||||
Map<String, String> map = result.toMap(); | |||||
if (result.getSign() != null &&!this.checkSign(map)) { | |||||
log.debug("校验结果签名失败,参数:{}", map); | |||||
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); | |||||
} | |||||
//校验结果是否成功 | |||||
if (!"SUCCESS".equalsIgnoreCase(result.getReturnCode()) | |||||
|| !"SUCCESS".equalsIgnoreCase(result.getResultCode())) { | |||||
WxError error = WxError.newBuilder().setErrorCode(-1) | |||||
.setErrorMsg("返回代码: " + result.getReturnCode() + ", 返回信息: " | |||||
+ result.getReturnMsg() + ", 结果代码: " + result.getResultCode() + ", 错误代码: " | |||||
+ result.getErrCode() + ", 错误详情: " + result.getErrCodeDes()) | |||||
.build(); | |||||
log.error("结果业务代码异常,参数:{},详细:{}", map, error); | |||||
throw new WxErrorException(error); | |||||
} | |||||
} | |||||
private void checkParameters(WxPayDownloadBillRequest request) throws WxErrorException { | |||||
BeanUtils.checkRequiredFields(request); | |||||
if (StringUtils.isNotBlank(request.getTarType()) && !"GZIP".equals(request.getTarType())) { | |||||
throw new IllegalArgumentException("tar_type值如果存在,只能为GZIP"); | |||||
} | |||||
if ( !ArrayUtils.contains(BILL_TYPE, request.getBillType())) { | |||||
throw new IllegalArgumentException("bill_tpye目前必须为" + Arrays.toString(BILL_TYPE) + "其中之一,实际值:" + request.getBillType()); | |||||
} | |||||
} | |||||
private void checkParameters(WxPayRefundRequest request) throws WxErrorException { | |||||
BeanUtils.checkRequiredFields(request); | |||||
if (StringUtils.isNotBlank(request.getRefundAccount())) { | |||||
if (!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())) { | |||||
throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) + "其中之一,实际值:" + request.getRefundAccount()); | |||||
} | |||||
} | |||||
if (StringUtils.isBlank(request.getOutTradeNo()) && StringUtils.isBlank(request.getTransactionId())) { | |||||
throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个"); | |||||
} | |||||
} | |||||
@Override | |||||
public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException { | |||||
try { | |||||
log.trace("微信支付回调参数详细:{}", xmlData); | |||||
WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); | |||||
log.debug("微信支付回调结果对象:{}", result); | |||||
this.checkResult(result); | |||||
return result; | |||||
} catch (WxErrorException e) { | |||||
log.error(e.getMessage(), e); | |||||
throw e; | |||||
} catch (Exception e) { | |||||
log.error(e.getMessage(), e); | |||||
throw new WxErrorException(WxError.newBuilder().setErrorMsg("发生异常" + e.getMessage()).build()); | |||||
} | |||||
} | |||||
@Override | |||||
public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) | |||||
throws WxErrorException { | |||||
this.initRequest(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack"; | |||||
if (request.getAmtType() != null) { | |||||
//裂变红包 | |||||
url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack"; | |||||
} | |||||
String responseContent = this.executeWithKey(url, request.toXML()); | |||||
WxPaySendRedpackResult result = WxPaySendRedpackResult.fromXML(responseContent, WxPaySendRedpackResult.class); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException { | |||||
WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); | |||||
request.setMchBillNo(mchBillNo); | |||||
request.setBillType("MCHT"); | |||||
initRequest(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; | |||||
String responseContent = this.executeWithKey(url, request.toXML()); | |||||
WxPayRedpackQueryResult result = WxPayRedpackQueryResult.fromXML(responseContent, WxPayRedpackQueryResult.class); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException { | |||||
if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) || | |||||
(StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo))) { | |||||
throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一"); | |||||
} | |||||
WxPayOrderQueryRequest request = new WxPayOrderQueryRequest(); | |||||
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||||
request.setTransactionId(StringUtils.trimToNull(transactionId)); | |||||
initRequest(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/pay/orderquery"; | |||||
String responseContent = this.executeRequest(url, request.toXML()); | |||||
WxPayOrderQueryResult result = WxPayOrderQueryResult.fromXML(responseContent, WxPayOrderQueryResult.class); | |||||
result.composeCoupons(); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException { | |||||
if (StringUtils.isBlank(outTradeNo)) { | |||||
throw new IllegalArgumentException("out_trade_no 不能为空"); | |||||
} | |||||
WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); | |||||
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||||
initRequest(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/pay/closeorder"; | |||||
String responseContent = this.executeRequest(url, request.toXML()); | |||||
WxPayOrderCloseResult result = WxPayOrderCloseResult.fromXML(responseContent, WxPayOrderCloseResult.class); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) | |||||
throws WxErrorException { | |||||
this.initRequest(request); | |||||
if (StringUtils.isBlank(request.getNotifyURL())) { | |||||
request.setNotifyURL(getConfig().getNotifyURL()); | |||||
} | |||||
if (StringUtils.isBlank(request.getTradeType())) { | |||||
request.setTradeType(getConfig().getTradeType()); | |||||
} | |||||
checkParameters(request);//校验参数 | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/pay/unifiedorder"; | |||||
String xmlParam = request.toXML(); | |||||
log.debug("微信统一下单接口,URL:{},参数:{}", url, xmlParam); | |||||
String responseContent = this.executeRequest(url, xmlParam); | |||||
log.debug("微信统一下单接口,URL:{},结果:{}", url, responseContent); | |||||
WxPayUnifiedOrderResult result = WxPayUnifiedOrderResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
private void initRequest(WxPayBaseRequest request) { | |||||
if (StringUtils.isBlank(request.getAppid())) { | |||||
request.setAppid(getConfig().getAppId()); | |||||
} | |||||
if (StringUtils.isBlank(request.getMchId())) { | |||||
request.setMchId(getConfig().getPartnerId()); | |||||
} | |||||
if (StringUtils.isBlank(request.getNonceStr())) { | |||||
request.setNonceStr(String.valueOf(System.currentTimeMillis())); | |||||
} | |||||
} | |||||
private void checkParameters(WxPayUnifiedOrderRequest request) throws WxErrorException { | |||||
BeanUtils.checkRequiredFields(request); | |||||
if (!ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) { | |||||
throw new IllegalArgumentException("trade_type目前必须为" + Arrays.toString(TRADE_TYPES) + "其中之一,实际值:" + request.getTradeType()); | |||||
} | |||||
if ("JSAPI".equals(request.getTradeType()) && request.getOpenid() == null) { | |||||
throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid"); | |||||
} | |||||
if ("NATIVE".equals(request.getTradeType()) && request.getProductId() == null) { | |||||
throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id"); | |||||
} | |||||
} | |||||
@Override | |||||
public Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException { | |||||
WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); | |||||
String prepayId = unifiedOrderResult.getPrepayId(); | |||||
if (StringUtils.isBlank(prepayId)) { | |||||
throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).", | |||||
unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); | |||||
} | |||||
Map<String, String> payInfo = new HashMap<>(); | |||||
payInfo.put("appId", getConfig().getAppId()); | |||||
// 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 | |||||
payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); | |||||
payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis())); | |||||
payInfo.put("package", "prepay_id=" + prepayId); | |||||
payInfo.put("signType", "MD5"); | |||||
if ("NATIVE".equals(request.getTradeType())) { | |||||
payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); | |||||
} | |||||
payInfo.put("paySign", this.createSign(payInfo)); | |||||
return payInfo; | |||||
} | |||||
@Override | |||||
public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException { | |||||
this.initRequest(request); | |||||
BeanUtils.checkRequiredFields(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers"; | |||||
String responseContent = this.executeWithKey(url, request.toXML()); | |||||
WxEntPayResult result = WxEntPayResult.fromXML(responseContent, WxEntPayResult.class); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException { | |||||
WxEntPayQueryRequest request = new WxEntPayQueryRequest(); | |||||
this.initRequest(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; | |||||
String responseContent = this.executeWithKey(url, request.toXML()); | |||||
WxEntPayQueryResult result = WxEntPayQueryResult.fromXML(responseContent, WxEntPayQueryResult.class); | |||||
this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | |||||
public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) { | |||||
//weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX | |||||
StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); | |||||
Map<String, String> params = Maps.newHashMap(); | |||||
params.put("appid", this.getConfig().getAppId()); | |||||
params.put("mch_id", this.getConfig().getPartnerId()); | |||||
params.put("product_id", productId); | |||||
params.put("time_stamp", String.valueOf(System.currentTimeMillis())); | |||||
params.put("nonce_str", String.valueOf(System.currentTimeMillis())); | |||||
String sign = this.createSign(params); | |||||
params.put("sign", sign); | |||||
for (String key : params.keySet()) { | |||||
codeUrl.append(key + "=" + params.get(key) + "&"); | |||||
} | |||||
String content = codeUrl.toString().substring(0, codeUrl.length() - 1); | |||||
if (sideLength == null || sideLength < 1) { | |||||
return QrcodeUtils.createQrcode(content, logoFile); | |||||
} | |||||
return QrcodeUtils.createQrcode(content, sideLength, logoFile); | |||||
} | |||||
@Override | |||||
public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) { | |||||
if (sideLength == null || sideLength < 1) { | |||||
return QrcodeUtils.createQrcode(codeUrl, logoFile); | |||||
} | |||||
return QrcodeUtils.createQrcode(codeUrl, sideLength, logoFile); | |||||
} | |||||
public void report(WxPayReportRequest request) throws WxErrorException { | |||||
BeanUtils.checkRequiredFields(request); | |||||
this.initRequest(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/payitil/report"; | |||||
String responseContent = this.wxMpService.post(url, request.toXML()); | |||||
WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); | |||||
this.checkResult(result); | |||||
} | |||||
@Override | |||||
public File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException { | |||||
WxPayDownloadBillRequest request = new WxPayDownloadBillRequest(); | |||||
this.initRequest(request); | |||||
request.setBillType(billType); | |||||
request.setBillDate(billDate); | |||||
request.setTarType(tarType); | |||||
request.setDeviceInfo(deviceInfo); | |||||
this.checkParameters(request); | |||||
request.setSign(this.createSign(request)); | |||||
String url = this.getPayBaseUrl() + "/pay/downloadbill"; | |||||
//TODO 返回的内容可能是文件流,也有可能是xml,需要区分对待 | |||||
String responseContent = this.wxMpService.post(url, request.toXML()); | |||||
WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); | |||||
this.checkResult(result); | |||||
return null; | |||||
} | |||||
private String executeRequest(String url, String requestStr) throws WxErrorException { | |||||
HttpPost httpPost = new HttpPost(url); | |||||
if (this.wxMpService.getHttpProxy() != null) { | |||||
httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); | |||||
} | |||||
try (CloseableHttpClient httpclient = HttpClients.custom().build()) { | |||||
httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); | |||||
try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | |||||
String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | |||||
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); | |||||
return result; | |||||
} | |||||
} catch (IOException e) { | |||||
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); | |||||
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); | |||||
} finally { | |||||
httpPost.releaseConnection(); | |||||
} | |||||
} | |||||
private String executeWithKey(String url, String requestStr) throws WxErrorException { | |||||
try { | |||||
SSLContext sslContext = getConfig().getSslContext(); | |||||
if (null == sslContext) { | |||||
throw new IllegalArgumentException("请先初始化配置类(即WxMpConfigStorage的实现类)中的SSLContext!"); | |||||
} | |||||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null, | |||||
new DefaultHostnameVerifier()); | |||||
HttpPost httpPost = new HttpPost(url); | |||||
if (this.wxMpService.getHttpProxy() != null) { | |||||
httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build()); | |||||
} | |||||
try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) { | |||||
httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1"))); | |||||
try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | |||||
String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | |||||
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); | |||||
return result; | |||||
} | |||||
} finally { | |||||
httpPost.releaseConnection(); | |||||
} | |||||
} catch (Exception e) { | |||||
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); | |||||
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); | |||||
} | |||||
} | |||||
@Override | |||||
public String createSign(Object xmlBean) { | |||||
return this.createSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | |||||
} | |||||
@Override | |||||
public String createSign(Object xmlBean, String signKey) { | |||||
return this.createSign(BeanUtils.xmlBean2Map(xmlBean), signKey); | |||||
} | |||||
@Override | |||||
public String createSign(Map<String, String> params) { | |||||
return this.createSign(params, this.getConfig().getPartnerKey()); | |||||
} | |||||
@Override | |||||
public String createSign(Map<String, String> params, String signKey) { | |||||
if(this.getConfig().useSandboxForWxPay()){ | |||||
//使用仿真测试环境 | |||||
//TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用这个仿真测试环境 | |||||
signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; | |||||
//return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; | |||||
} | |||||
SortedMap<String, String> sortedMap = new TreeMap<>(params); | |||||
StringBuilder toSign = new StringBuilder(); | |||||
for (String key : sortedMap.keySet()) { | |||||
String value = params.get(key); | |||||
if (StringUtils.isNotEmpty(value) && !"sign".equals(key) && !"key".equals(key)) { | |||||
toSign.append(key + "=" + value + "&"); | |||||
} | |||||
} | |||||
toSign.append("key=" + signKey); | |||||
return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | |||||
} | |||||
@Override | |||||
public boolean checkSign(Object xmlBean) { | |||||
return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey()); | |||||
} | |||||
@Override | |||||
public boolean checkSign(Object xmlBean, String signKey) { | |||||
return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey); | |||||
} | |||||
@Override | |||||
public boolean checkSign(Map<String, String> params) { | |||||
return this.checkSign(params, getConfig().getPartnerKey()); | |||||
} | |||||
@Override | |||||
public boolean checkSign(Map<String, String> params, String signKey) { | |||||
String sign = this.createSign(params, signKey); | |||||
return sign.equals(params.get("sign")); | |||||
} | |||||
} |
@@ -43,9 +43,8 @@ public class WxMpQrcodeServiceImpl implements WxMpQrcodeService { | |||||
String url = API_URL_PREFIX + "/create"; | String url = API_URL_PREFIX + "/create"; | ||||
JsonObject json = new JsonObject(); | JsonObject json = new JsonObject(); | ||||
json.addProperty("action_name", "QR_SCENE"); | json.addProperty("action_name", "QR_SCENE"); | ||||
if (expireSeconds != null) { | |||||
json.addProperty("expire_seconds", expireSeconds); | |||||
} | |||||
json.addProperty("expire_seconds", expireSeconds); | |||||
JsonObject actionInfo = new JsonObject(); | JsonObject actionInfo = new JsonObject(); | ||||
JsonObject scene = new JsonObject(); | JsonObject scene = new JsonObject(); | ||||
scene.addProperty("scene_id", sceneId); | scene.addProperty("scene_id", sceneId); | ||||
@@ -28,9 +28,6 @@ import org.slf4j.LoggerFactory; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.concurrent.locks.Lock; | import java.util.concurrent.locks.Lock; | ||||
//import org.apache.http.conn.ssl.DefaultHostnameVerifier; | |||||
//import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||||
public class WxMpServiceImpl implements WxMpService { | public class WxMpServiceImpl implements WxMpService { | ||||
private static final JsonParser JSON_PARSER = new JsonParser(); | private static final JsonParser JSON_PARSER = new JsonParser(); | ||||
@@ -45,7 +42,6 @@ public class WxMpServiceImpl implements WxMpService { | |||||
private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this); | private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this); | ||||
private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); | private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); | ||||
private WxMpCardService cardService = new WxMpCardServiceImpl(this); | private WxMpCardService cardService = new WxMpCardServiceImpl(this); | ||||
private WxMpPayService payService = new WxMpPayServiceImpl(this); | |||||
private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); | private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); | ||||
private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); | private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); | ||||
private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); | private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); | ||||
@@ -59,7 +55,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
@Override | @Override | ||||
public boolean checkSignature(String timestamp, String nonce, String signature) { | public boolean checkSignature(String timestamp, String nonce, String signature) { | ||||
try { | try { | ||||
return SHA1.gen(this.wxMpConfigStorage.getToken(), timestamp, nonce) | |||||
return SHA1.gen(this.getWxMpConfigStorage().getToken(), timestamp, nonce) | |||||
.equals(signature); | .equals(signature); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
return false; | return false; | ||||
@@ -73,18 +69,18 @@ public class WxMpServiceImpl implements WxMpService { | |||||
@Override | @Override | ||||
public String getAccessToken(boolean forceRefresh) throws WxErrorException { | public String getAccessToken(boolean forceRefresh) throws WxErrorException { | ||||
Lock lock = this.wxMpConfigStorage.getAccessTokenLock(); | |||||
Lock lock = this.getWxMpConfigStorage().getAccessTokenLock(); | |||||
try { | try { | ||||
lock.lock(); | lock.lock(); | ||||
if (forceRefresh) { | if (forceRefresh) { | ||||
this.wxMpConfigStorage.expireAccessToken(); | |||||
this.getWxMpConfigStorage().expireAccessToken(); | |||||
} | } | ||||
if (this.wxMpConfigStorage.isAccessTokenExpired()) { | |||||
if (this.getWxMpConfigStorage().isAccessTokenExpired()) { | |||||
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + | String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + | ||||
"&appid=" + this.wxMpConfigStorage.getAppId() + "&secret=" | |||||
+ this.wxMpConfigStorage.getSecret(); | |||||
"&appid=" + this.getWxMpConfigStorage().getAppId() + "&secret=" | |||||
+ this.getWxMpConfigStorage().getSecret(); | |||||
try { | try { | ||||
HttpGet httpGet = new HttpGet(url); | HttpGet httpGet = new HttpGet(url); | ||||
if (this.httpProxy != null) { | if (this.httpProxy != null) { | ||||
@@ -98,7 +94,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
throw new WxErrorException(error); | throw new WxErrorException(error); | ||||
} | } | ||||
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | ||||
this.wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), | |||||
this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(), | |||||
accessToken.getExpiresIn()); | accessToken.getExpiresIn()); | ||||
} finally { | } finally { | ||||
httpGet.releaseConnection(); | httpGet.releaseConnection(); | ||||
@@ -110,7 +106,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
} finally { | } finally { | ||||
lock.unlock(); | lock.unlock(); | ||||
} | } | ||||
return this.wxMpConfigStorage.getAccessToken(); | |||||
return this.getWxMpConfigStorage().getAccessToken(); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -120,27 +116,27 @@ public class WxMpServiceImpl implements WxMpService { | |||||
@Override | @Override | ||||
public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { | public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { | ||||
Lock lock = this.wxMpConfigStorage.getJsapiTicketLock(); | |||||
Lock lock = this.getWxMpConfigStorage().getJsapiTicketLock(); | |||||
try { | try { | ||||
lock.lock(); | lock.lock(); | ||||
if (forceRefresh) { | if (forceRefresh) { | ||||
this.wxMpConfigStorage.expireJsapiTicket(); | |||||
this.getWxMpConfigStorage().expireJsapiTicket(); | |||||
} | } | ||||
if (this.wxMpConfigStorage.isJsapiTicketExpired()) { | |||||
if (this.getWxMpConfigStorage().isJsapiTicketExpired()) { | |||||
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; | String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi"; | ||||
String responseContent = execute(new SimpleGetRequestExecutor(), url, null); | String responseContent = execute(new SimpleGetRequestExecutor(), url, null); | ||||
JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); | JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); | ||||
JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); | ||||
String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); | ||||
int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); | ||||
this.wxMpConfigStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds); | |||||
this.getWxMpConfigStorage().updateJsapiTicket(jsapiTicket, expiresInSeconds); | |||||
} | } | ||||
} finally { | } finally { | ||||
lock.unlock(); | lock.unlock(); | ||||
} | } | ||||
return this.wxMpConfigStorage.getJsapiTicket(); | |||||
return this.getWxMpConfigStorage().getJsapiTicket(); | |||||
} | } | ||||
@Override | @Override | ||||
@@ -151,7 +147,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, | String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, | ||||
"noncestr=" + noncestr, "timestamp=" + timestamp, "url=" + url); | "noncestr=" + noncestr, "timestamp=" + timestamp, "url=" + url); | ||||
WxJsapiSignature jsapiSignature = new WxJsapiSignature(); | WxJsapiSignature jsapiSignature = new WxJsapiSignature(); | ||||
jsapiSignature.setAppId(this.wxMpConfigStorage.getAppId()); | |||||
jsapiSignature.setAppId(this.getWxMpConfigStorage().getAppId()); | |||||
jsapiSignature.setTimestamp(timestamp); | jsapiSignature.setTimestamp(timestamp); | ||||
jsapiSignature.setNonceStr(noncestr); | jsapiSignature.setNonceStr(noncestr); | ||||
jsapiSignature.setUrl(url); | jsapiSignature.setUrl(url); | ||||
@@ -216,7 +212,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
public String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state) { | public String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state) { | ||||
StringBuilder url = new StringBuilder(); | StringBuilder url = new StringBuilder(); | ||||
url.append("https://open.weixin.qq.com/connect/oauth2/authorize?"); | url.append("https://open.weixin.qq.com/connect/oauth2/authorize?"); | ||||
url.append("appid=").append(this.wxMpConfigStorage.getAppId()); | |||||
url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); | |||||
url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); | url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); | ||||
url.append("&response_type=code"); | url.append("&response_type=code"); | ||||
url.append("&scope=").append(scope); | url.append("&scope=").append(scope); | ||||
@@ -232,7 +228,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
String state) { | String state) { | ||||
StringBuilder url = new StringBuilder(); | StringBuilder url = new StringBuilder(); | ||||
url.append("https://open.weixin.qq.com/connect/qrconnect?"); | url.append("https://open.weixin.qq.com/connect/qrconnect?"); | ||||
url.append("appid=").append(this.wxMpConfigStorage.getAppId()); | |||||
url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); | |||||
url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); | url.append("&redirect_uri=").append(URIUtil.encodeURIComponent(redirectURI)); | ||||
url.append("&response_type=code"); | url.append("&response_type=code"); | ||||
url.append("&scope=").append(scope); | url.append("&scope=").append(scope); | ||||
@@ -258,8 +254,8 @@ public class WxMpServiceImpl implements WxMpService { | |||||
public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { | public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { | ||||
StringBuilder url = new StringBuilder(); | StringBuilder url = new StringBuilder(); | ||||
url.append("https://api.weixin.qq.com/sns/oauth2/access_token?"); | url.append("https://api.weixin.qq.com/sns/oauth2/access_token?"); | ||||
url.append("appid=").append(this.wxMpConfigStorage.getAppId()); | |||||
url.append("&secret=").append(this.wxMpConfigStorage.getSecret()); | |||||
url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); | |||||
url.append("&secret=").append(this.getWxMpConfigStorage().getSecret()); | |||||
url.append("&code=").append(code); | url.append("&code=").append(code); | ||||
url.append("&grant_type=authorization_code"); | url.append("&grant_type=authorization_code"); | ||||
@@ -270,7 +266,7 @@ public class WxMpServiceImpl implements WxMpService { | |||||
public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { | public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { | ||||
StringBuilder url = new StringBuilder(); | StringBuilder url = new StringBuilder(); | ||||
url.append("https://api.weixin.qq.com/sns/oauth2/refresh_token?"); | url.append("https://api.weixin.qq.com/sns/oauth2/refresh_token?"); | ||||
url.append("appid=").append(this.wxMpConfigStorage.getAppId()); | |||||
url.append("appid=").append(this.getWxMpConfigStorage().getAppId()); | |||||
url.append("&grant_type=refresh_token"); | url.append("&grant_type=refresh_token"); | ||||
url.append("&refresh_token=").append(refreshToken); | url.append("&refresh_token=").append(refreshToken); | ||||
@@ -397,8 +393,8 @@ public class WxMpServiceImpl implements WxMpService { | |||||
*/ | */ | ||||
if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | ||||
// 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | // 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | ||||
this.wxMpConfigStorage.expireAccessToken(); | |||||
if (this.wxMpConfigStorage.autoRefreshToken()) { | |||||
this.getWxMpConfigStorage().expireAccessToken(); | |||||
if (this.getWxMpConfigStorage().autoRefreshToken()) { | |||||
return this.execute(executor, uri, data); | return this.execute(executor, uri, data); | ||||
} | } | ||||
} | } | ||||
@@ -424,26 +420,19 @@ public class WxMpServiceImpl implements WxMpService { | |||||
} | } | ||||
private void initHttpClient() { | private void initHttpClient() { | ||||
ApacheHttpClientBuilder apacheHttpClientBuilder = this.wxMpConfigStorage | |||||
.getApacheHttpClientBuilder(); | |||||
WxMpConfigStorage configStorage = this.getWxMpConfigStorage(); | |||||
ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); | |||||
if (null == apacheHttpClientBuilder) { | if (null == apacheHttpClientBuilder) { | ||||
apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | apacheHttpClientBuilder = DefaultApacheHttpClientBuilder.get(); | ||||
} | } | ||||
apacheHttpClientBuilder.httpProxyHost(this.wxMpConfigStorage.getHttpProxyHost()) | |||||
.httpProxyPort(this.wxMpConfigStorage.getHttpProxyPort()) | |||||
.httpProxyUsername(this.wxMpConfigStorage.getHttpProxyUsername()) | |||||
.httpProxyPassword(this.wxMpConfigStorage.getHttpProxyPassword()); | |||||
// if (this.wxMpConfigStorage.getSSLContext() != null) { | |||||
// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( | |||||
// this.wxMpConfigStorage.getSSLContext(), new String[] { "TLSv1" }, null, | |||||
// new DefaultHostnameVerifier()); | |||||
// apacheHttpClientBuilder.sslConnectionSocketFactory(sslsf); | |||||
// } | |||||
apacheHttpClientBuilder.httpProxyHost(configStorage.getHttpProxyHost()) | |||||
.httpProxyPort(configStorage.getHttpProxyPort()) | |||||
.httpProxyUsername(configStorage.getHttpProxyUsername()) | |||||
.httpProxyPassword(configStorage.getHttpProxyPassword()); | |||||
if (this.wxMpConfigStorage.getHttpProxyHost() != null && this.wxMpConfigStorage.getHttpProxyPort() > 0) { | |||||
this.httpProxy = new HttpHost(this.wxMpConfigStorage.getHttpProxyHost(), this.wxMpConfigStorage.getHttpProxyPort()); | |||||
if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) { | |||||
this.httpProxy = new HttpHost(configStorage.getHttpProxyHost(), configStorage.getHttpProxyPort()); | |||||
} | } | ||||
this.httpClient = apacheHttpClientBuilder.build(); | this.httpClient = apacheHttpClientBuilder.build(); | ||||
@@ -505,11 +494,6 @@ public class WxMpServiceImpl implements WxMpService { | |||||
return this.cardService; | return this.cardService; | ||||
} | } | ||||
@Override | |||||
public WxMpPayService getPayService() { | |||||
return this.payService; | |||||
} | |||||
@Override | @Override | ||||
public WxMpDataCubeService getDataCubeService() { | public WxMpDataCubeService getDataCubeService() { | ||||
return this.dataCubeService; | return this.dataCubeService; | ||||
@@ -9,7 +9,8 @@ import java.io.Serializable; | |||||
*/ | */ | ||||
public class WxMpMassPreviewMessage implements Serializable { | public class WxMpMassPreviewMessage implements Serializable { | ||||
private static final long serialVersionUID = 9095211638358424020L; | private static final long serialVersionUID = 9095211638358424020L; | ||||
private String toWxUsername; | |||||
private String toWxUserName; | |||||
private String toWxUserOpenid; | |||||
private String msgType; | private String msgType; | ||||
private String content; | private String content; | ||||
private String mediaId; | private String mediaId; | ||||
@@ -18,12 +19,12 @@ public class WxMpMassPreviewMessage implements Serializable { | |||||
super(); | super(); | ||||
} | } | ||||
public String getToWxUsername() { | |||||
return this.toWxUsername; | |||||
public String getToWxUserName() { | |||||
return this.toWxUserName; | |||||
} | } | ||||
public void setToWxUsername(String toWxUsername) { | |||||
this.toWxUsername = toWxUsername; | |||||
public void setToWxUserName(String toWxUserName) { | |||||
this.toWxUserName = toWxUserName; | |||||
} | } | ||||
public String getMsgType() { | public String getMsgType() { | ||||
@@ -41,7 +42,7 @@ public class WxMpMassPreviewMessage implements Serializable { | |||||
* 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误 | * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param msgType | |||||
* @param msgType 消息类型 | |||||
*/ | */ | ||||
public void setMsgType(String msgType) { | public void setMsgType(String msgType) { | ||||
this.msgType = msgType; | this.msgType = msgType; | ||||
@@ -63,6 +64,14 @@ public class WxMpMassPreviewMessage implements Serializable { | |||||
this.mediaId = mediaId; | this.mediaId = mediaId; | ||||
} | } | ||||
public String getToWxUserOpenid() { | |||||
return this.toWxUserOpenid; | |||||
} | |||||
public void setToWxUserOpenid(String toWxUserOpenid) { | |||||
this.toWxUserOpenid = toWxUserOpenid; | |||||
} | |||||
public String toJson() { | public String toJson() { | ||||
return WxMpGsonBuilder.INSTANCE.create().toJson(this); | return WxMpGsonBuilder.INSTANCE.create().toJson(this); | ||||
} | } | ||||
@@ -38,23 +38,25 @@ public class BaseResp extends AbstractDeviceBean{ | |||||
} | } | ||||
private class BaseInfo { | private class BaseInfo { | ||||
private String device_type; | |||||
private String device_id; | |||||
@SerializedName("device_type") | |||||
private String deviceType; | |||||
@SerializedName("device_id") | |||||
private String deviceId; | |||||
public String getDevice_type() { | |||||
return device_type; | |||||
public String getDeviceType() { | |||||
return deviceType; | |||||
} | } | ||||
public void setDevice_type(String device_type) { | |||||
this.device_type = device_type; | |||||
public void setDeviceType(String deviceType) { | |||||
this.deviceType = deviceType; | |||||
} | } | ||||
public String getDevice_id() { | |||||
return device_id; | |||||
public String getDeviceId() { | |||||
return deviceId; | |||||
} | } | ||||
public void setDevice_id(String device_id) { | |||||
this.device_id = device_id; | |||||
public void setDeviceId(String deviceId) { | |||||
this.deviceId = deviceId; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,46 @@ | |||||
package me.chanjar.weixin.mp.bean.device; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
import java.util.List; | |||||
/** | |||||
* Created by keungtung on 16/12/2016. | |||||
*/ | |||||
public class WxDeviceBindDeviceResult extends AbstractDeviceBean { | |||||
@SerializedName("resp_msg") | |||||
private RespMsg respMsg; | |||||
@SerializedName("openid") | |||||
private String openId; | |||||
@SerializedName("device_list") | |||||
private List<Device> devices; | |||||
public static WxDeviceBindDeviceResult fromJson(String json) { | |||||
return WxMpGsonBuilder.create().fromJson(json, WxDeviceBindDeviceResult.class); | |||||
} | |||||
private class Device { | |||||
@SerializedName("device_type") | |||||
private String deviceType; | |||||
@SerializedName("device_id") | |||||
private String deviceId; | |||||
public String getDeviceType() { | |||||
return deviceType; | |||||
} | |||||
public void setDeviceType(String deviceType) { | |||||
this.deviceType = deviceType; | |||||
} | |||||
public String getDeviceId() { | |||||
return deviceId; | |||||
} | |||||
public void setDeviceId(String deviceId) { | |||||
this.deviceId = deviceId; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,56 @@ | |||||
package me.chanjar.weixin.mp.bean.device; | |||||
import com.google.gson.annotations.SerializedName; | |||||
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
import java.util.List; | |||||
/** | |||||
* Created by keungtung on 16/12/2016. | |||||
*/ | |||||
public class WxDeviceOpenIdResult extends AbstractDeviceBean { | |||||
@SerializedName("errcode") | |||||
private Integer errCode; | |||||
@SerializedName("errmsg") | |||||
private String errMsg; | |||||
@SerializedName("open_id") | |||||
private List<String> openIds; | |||||
@SerializedName("resp_msg") | |||||
private RespMsg respMsg; | |||||
public static WxDeviceOpenIdResult fromJson(String json) { | |||||
return WxMpGsonBuilder.create().fromJson(json, WxDeviceOpenIdResult.class); | |||||
} | |||||
public Integer getErrCode() { | |||||
return errCode; | |||||
} | |||||
public void setErrCode(Integer errCode) { | |||||
this.errCode = errCode; | |||||
} | |||||
public String getErrMsg() { | |||||
return errMsg; | |||||
} | |||||
public void setErrMsg(String errMsg) { | |||||
this.errMsg = errMsg; | |||||
} | |||||
public List<String> getOpenIds() { | |||||
return openIds; | |||||
} | |||||
public void setOpenIds(List<String> openIds) { | |||||
this.openIds = openIds; | |||||
} | |||||
public RespMsg getRespMsg() { | |||||
return respMsg; | |||||
} | |||||
public void setRespMsg(RespMsg respMsg) { | |||||
this.respMsg = respMsg; | |||||
} | |||||
} |
@@ -6,15 +6,15 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; | |||||
/** | /** | ||||
* Created by keungtung on 10/12/2016. | * Created by keungtung on 10/12/2016. | ||||
*/ | */ | ||||
public class WxDeviceQrCodeResult extends AbstractDeviceBean{ | |||||
public class WxDeviceQrCodeResult extends AbstractDeviceBean { | |||||
@SerializedName("deviceid") | @SerializedName("deviceid") | ||||
private String deviceId; | private String deviceId; | ||||
@SerializedName("qrticket") | @SerializedName("qrticket") | ||||
private String qrTicket; | private String qrTicket; | ||||
@SerializedName("devicelicence") | @SerializedName("devicelicence") | ||||
private String deviceLicence; | private String deviceLicence; | ||||
@SerializedName("resp_msg") | |||||
private RespMsg respMsg; | |||||
@SerializedName("base_resp") | |||||
private BaseResp baseResp; | |||||
public static WxDeviceQrCodeResult fromJson(String json) { | public static WxDeviceQrCodeResult fromJson(String json) { | ||||
return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxDeviceQrCodeResult.class); | return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxDeviceQrCodeResult.class); | ||||
@@ -28,12 +28,12 @@ public class WxDeviceQrCodeResult extends AbstractDeviceBean{ | |||||
this.deviceLicence = deviceLicence; | this.deviceLicence = deviceLicence; | ||||
} | } | ||||
public RespMsg getRespMsg() { | |||||
return respMsg; | |||||
public BaseResp getBaseResp() { | |||||
return baseResp; | |||||
} | } | ||||
public void setRespMsg(RespMsg respMsg) { | |||||
this.respMsg = respMsg; | |||||
public void setBaseResp(BaseResp baseResp) { | |||||
this.baseResp = baseResp; | |||||
} | } | ||||
public String getDeviceId() { | public String getDeviceId() { | ||||
@@ -1,235 +0,0 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
import com.google.common.collect.Maps; | |||||
import com.thoughtworks.xstream.XStream; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import io.restassured.internal.path.xml.NodeChildrenImpl; | |||||
import io.restassured.internal.path.xml.NodeImpl; | |||||
import io.restassured.path.xml.XmlPath; | |||||
import io.restassured.path.xml.element.Node; | |||||
import io.restassured.path.xml.exception.XmlPathException; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import java.math.BigDecimal; | |||||
import java.util.Iterator; | |||||
import java.util.Map; | |||||
/** | |||||
* <pre> | |||||
* 微信支付结果共用属性类 | |||||
* Created by Binary Wang on 2016-10-24. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
public abstract class WxPayBaseResult { | |||||
/** | |||||
* 返回状态码 | |||||
*/ | |||||
@XStreamAlias("return_code") | |||||
protected String returnCode; | |||||
/** | |||||
* 返回信息 | |||||
*/ | |||||
@XStreamAlias("return_msg") | |||||
protected String returnMsg; | |||||
private String xmlString; | |||||
/** | |||||
* 业务结果 | |||||
*/ | |||||
@XStreamAlias("result_code") | |||||
private String resultCode; | |||||
/** | |||||
* 错误代码 | |||||
*/ | |||||
@XStreamAlias("err_code") | |||||
private String errCode; | |||||
/** | |||||
* 错误代码描述 | |||||
*/ | |||||
@XStreamAlias("err_code_des") | |||||
private String errCodeDes; | |||||
/** | |||||
* 公众账号ID | |||||
*/ | |||||
@XStreamAlias("appid") | |||||
private String appid; | |||||
/** | |||||
* 商户号 | |||||
*/ | |||||
@XStreamAlias("mch_id") | |||||
private String mchId; | |||||
/** | |||||
* 随机字符串 | |||||
*/ | |||||
@XStreamAlias("nonce_str") | |||||
private String nonceStr; | |||||
/** | |||||
* 签名 | |||||
*/ | |||||
@XStreamAlias("sign") | |||||
private String sign; | |||||
/** | |||||
* 将单位分转换成单位圆 | |||||
* | |||||
* @param fee 将要被转换为元的分的数值 | |||||
*/ | |||||
public static String feeToYuan(Integer fee) { | |||||
return new BigDecimal(Double.valueOf(fee) / 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString(); | |||||
} | |||||
/** | |||||
* 从xml字符串创建bean对象 | |||||
*/ | |||||
public static <T extends WxPayBaseResult> T fromXML(String xmlString, Class<T> clz) { | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.processAnnotations(clz); | |||||
T result = (T) xstream.fromXML(xmlString); | |||||
result.setXmlString(xmlString); | |||||
return result; | |||||
} | |||||
public String getXmlString() { | |||||
return this.xmlString; | |||||
} | |||||
public void setXmlString(String xmlString) { | |||||
this.xmlString = xmlString; | |||||
} | |||||
protected Logger getLogger() { | |||||
return LoggerFactory.getLogger(this.getClass()); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return ToStringUtils.toSimpleString(this); | |||||
} | |||||
public String getReturnCode() { | |||||
return this.returnCode; | |||||
} | |||||
public void setReturnCode(String returnCode) { | |||||
this.returnCode = returnCode; | |||||
} | |||||
public String getReturnMsg() { | |||||
return this.returnMsg; | |||||
} | |||||
public void setReturnMsg(String returnMsg) { | |||||
this.returnMsg = returnMsg; | |||||
} | |||||
public String getResultCode() { | |||||
return this.resultCode; | |||||
} | |||||
public void setResultCode(String resultCode) { | |||||
this.resultCode = resultCode; | |||||
} | |||||
public String getErrCode() { | |||||
return this.errCode; | |||||
} | |||||
public void setErrCode(String errCode) { | |||||
this.errCode = errCode; | |||||
} | |||||
public String getErrCodeDes() { | |||||
return this.errCodeDes; | |||||
} | |||||
public void setErrCodeDes(String errCodeDes) { | |||||
this.errCodeDes = errCodeDes; | |||||
} | |||||
public String getAppid() { | |||||
return this.appid; | |||||
} | |||||
public void setAppid(String appid) { | |||||
this.appid = appid; | |||||
} | |||||
public String getMchId() { | |||||
return this.mchId; | |||||
} | |||||
public void setMchId(String mchId) { | |||||
this.mchId = mchId; | |||||
} | |||||
public String getNonceStr() { | |||||
return this.nonceStr; | |||||
} | |||||
public void setNonceStr(String nonceStr) { | |||||
this.nonceStr = nonceStr; | |||||
} | |||||
public String getSign() { | |||||
return this.sign; | |||||
} | |||||
public void setSign(String sign) { | |||||
this.sign = sign; | |||||
} | |||||
/** | |||||
* 将bean通过保存的xml字符串转换成map | |||||
*/ | |||||
public Map<String, String> toMap() { | |||||
Map<String, String> result = Maps.newHashMap(); | |||||
XmlPath xmlPath = new XmlPath(this.xmlString); | |||||
NodeImpl rootNode = null; | |||||
try { | |||||
rootNode = xmlPath.get("xml"); | |||||
} catch (XmlPathException e) { | |||||
throw new RuntimeException("xml数据有问题,请核实!"); | |||||
} | |||||
if (rootNode == null) { | |||||
throw new RuntimeException("xml数据有问题,请核实!"); | |||||
} | |||||
Iterator<Node> iterator = rootNode.children().nodeIterator(); | |||||
while (iterator.hasNext()) { | |||||
Node node = iterator.next(); | |||||
result.put(node.name(), node.value()); | |||||
} | |||||
return result; | |||||
} | |||||
private String getXmlValue(XmlPath xmlPath, String path) { | |||||
if (xmlPath.get(path) instanceof NodeChildrenImpl) { | |||||
if (((NodeChildrenImpl) xmlPath.get(path)).size() == 0) { | |||||
return null; | |||||
} | |||||
} | |||||
return xmlPath.getString(path); | |||||
} | |||||
protected <T> T getXmlValue(XmlPath xmlPath, String path, Class<T> clz) { | |||||
String value = this.getXmlValue(xmlPath, path); | |||||
if (value == null) { | |||||
return null; | |||||
} | |||||
switch (clz.getSimpleName()) { | |||||
case "String": | |||||
return (T) value; | |||||
case "Integer": | |||||
return (T) Integer.valueOf(value); | |||||
} | |||||
throw new UnsupportedOperationException("暂时不支持此种类型的数据"); | |||||
} | |||||
} |
@@ -1,392 +0,0 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
import com.thoughtworks.xstream.XStream; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import me.chanjar.weixin.common.util.BeanUtils; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||||
import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyCoupon; | |||||
import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyResultConverter; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | |||||
* 支付结果通用通知 ,文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | |||||
* @author aimilin6688 | |||||
* @since 2.5.0 | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializable { | |||||
private static final long serialVersionUID = 5389718115223345496L; | |||||
/** | |||||
* <pre> | |||||
* 设备号 | |||||
* device_info | |||||
* 否 | |||||
* String(32) | |||||
* 013467007045764 | |||||
* 微信支付分配的终端设备号, | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("device_info") | |||||
private String deviceInfo; | |||||
/** | |||||
* <pre> | |||||
* 用户标识 | |||||
* openid | |||||
* 是 | |||||
* String(128) | |||||
* wxd930ea5d5a258f4f | |||||
* 用户在商户appid下的唯一标识 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("openid") | |||||
private String openid; | |||||
/** | |||||
* <pre> | |||||
* 是否关注公众账号 | |||||
* is_subscribe | |||||
* 否 | |||||
* String(1) | |||||
* Y | |||||
* 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("is_subscribe") | |||||
private String isSubscribe; | |||||
/** | |||||
* <pre> | |||||
* 交易类型 | |||||
* trade_type | |||||
* 是 | |||||
* String(16) | |||||
* JSAPI JSAPI、NATIVE、APP | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("trade_type") | |||||
private String tradeType; | |||||
/** | |||||
* <pre> | |||||
* 付款银行 | |||||
* bank_type | |||||
* 是 | |||||
* String(16) | |||||
* CMC | |||||
* 银行类型,采用字符串类型的银行标识,银行类型见银行列表 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("bank_type") | |||||
private String bankType; | |||||
/** | |||||
* <pre> | |||||
* 订单金额 | |||||
* total_fee | |||||
* 是 | |||||
* Int | |||||
* 100 | |||||
* 订单总金额,单位为分 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("total_fee") | |||||
private Integer totalFee; | |||||
/** | |||||
* <pre> | |||||
* 应结订单金额 | |||||
* settlement_total_fee | |||||
* 否 | |||||
* Int | |||||
* 100 | |||||
* 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("settlement_total_fee") | |||||
private Integer settlementTotalFee; | |||||
/** | |||||
* <pre> | |||||
* 货币种类 | |||||
* fee_type | |||||
* 否 | |||||
* String(8) | |||||
* CNY | |||||
* 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("fee_type") | |||||
private String feeType; | |||||
/** | |||||
* <pre> | |||||
* 现金支付金额 | |||||
* cash_fee | |||||
* 是 | |||||
* Int | |||||
* 100 | |||||
* 现金支付金额订单现金支付金额,详见支付金额 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("cash_fee") | |||||
private Integer cashFee; | |||||
/** | |||||
* <pre> | |||||
* 现金支付货币类型 | |||||
* cash_fee_type | |||||
* 否 | |||||
* String(16) | |||||
* CNY | |||||
* 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("cash_fee_type") | |||||
private String cashFeeType; | |||||
/** | |||||
* <pre> | |||||
* 总代金券金额 | |||||
* coupon_fee | |||||
* 否 | |||||
* Int | |||||
* 10 | |||||
* 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("coupon_fee") | |||||
private Integer couponFee; | |||||
/** | |||||
* <pre> | |||||
* 代金券使用数量 | |||||
* coupon_count | |||||
* 否 | |||||
* Int | |||||
* 1 | |||||
* 代金券使用数量 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("coupon_count") | |||||
private Integer couponCount; | |||||
private List<WxPayOrderNotifyCoupon> couponList; | |||||
/** | |||||
* <pre> | |||||
* 微信支付订单号 | |||||
* transaction_id | |||||
* 是 | |||||
* String(32) | |||||
* 1217752501201407033233368018 | |||||
* 微信支付订单号 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("transaction_id") | |||||
private String transactionId; | |||||
/** | |||||
* <pre> | |||||
* 商户订单号 | |||||
* out_trade_no | |||||
* 是 | |||||
* String(32) | |||||
* 1212321211201407033568112322 | |||||
* 商户系统的订单号,与请求一致。 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("out_trade_no") | |||||
private String outTradeNo; | |||||
/** | |||||
* <pre> | |||||
* 商家数据包 | |||||
* attach | |||||
* 否 | |||||
* String(128) | |||||
* 123456 | |||||
* 商家数据包,原样返回 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("attach") | |||||
private String attach; | |||||
/** | |||||
* <pre> | |||||
* 支付完成时间 | |||||
* time_end | |||||
* 是 | |||||
* String(14) | |||||
* 20141030133525 | |||||
* 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("time_end") | |||||
private String timeEnd; | |||||
public Integer getCouponCount() { | |||||
return couponCount; | |||||
} | |||||
public void setCouponCount(Integer couponCount) { | |||||
this.couponCount = couponCount; | |||||
} | |||||
public List<WxPayOrderNotifyCoupon> getCouponList() { | |||||
return couponList; | |||||
} | |||||
public void setCouponList(List<WxPayOrderNotifyCoupon> couponList) { | |||||
this.couponList = couponList; | |||||
} | |||||
public String getDeviceInfo() { | |||||
return deviceInfo; | |||||
} | |||||
public void setDeviceInfo(String deviceInfo) { | |||||
this.deviceInfo = deviceInfo; | |||||
} | |||||
public String getOpenid() { | |||||
return openid; | |||||
} | |||||
public void setOpenid(String openid) { | |||||
this.openid = openid; | |||||
} | |||||
public String getIsSubscribe() { | |||||
return isSubscribe; | |||||
} | |||||
public void setIsSubscribe(String isSubscribe) { | |||||
this.isSubscribe = isSubscribe; | |||||
} | |||||
public String getTradeType() { | |||||
return tradeType; | |||||
} | |||||
public void setTradeType(String tradeType) { | |||||
this.tradeType = tradeType; | |||||
} | |||||
public String getBankType() { | |||||
return bankType; | |||||
} | |||||
public void setBankType(String bankType) { | |||||
this.bankType = bankType; | |||||
} | |||||
public Integer getTotalFee() { | |||||
return totalFee; | |||||
} | |||||
public void setTotalFee(Integer totalFee) { | |||||
this.totalFee = totalFee; | |||||
} | |||||
public Integer getSettlementTotalFee() { | |||||
return settlementTotalFee; | |||||
} | |||||
public void setSettlementTotalFee(Integer settlementTotalFee) { | |||||
this.settlementTotalFee = settlementTotalFee; | |||||
} | |||||
public String getFeeType() { | |||||
return feeType; | |||||
} | |||||
public void setFeeType(String feeType) { | |||||
this.feeType = feeType; | |||||
} | |||||
public Integer getCashFee() { | |||||
return cashFee; | |||||
} | |||||
public void setCashFee(Integer cashFee) { | |||||
this.cashFee = cashFee; | |||||
} | |||||
public String getCashFeeType() { | |||||
return cashFeeType; | |||||
} | |||||
public void setCashFeeType(String cashFeeType) { | |||||
this.cashFeeType = cashFeeType; | |||||
} | |||||
public Integer getCouponFee() { | |||||
return couponFee; | |||||
} | |||||
public void setCouponFee(Integer couponFee) { | |||||
this.couponFee = couponFee; | |||||
} | |||||
public String getTransactionId() { | |||||
return transactionId; | |||||
} | |||||
public void setTransactionId(String transactionId) { | |||||
this.transactionId = transactionId; | |||||
} | |||||
public String getOutTradeNo() { | |||||
return outTradeNo; | |||||
} | |||||
public void setOutTradeNo(String outTradeNo) { | |||||
this.outTradeNo = outTradeNo; | |||||
} | |||||
public String getAttach() { | |||||
return attach; | |||||
} | |||||
public void setAttach(String attach) { | |||||
this.attach = attach; | |||||
} | |||||
public String getTimeEnd() { | |||||
return timeEnd; | |||||
} | |||||
public void setTimeEnd(String timeEnd) { | |||||
this.timeEnd = timeEnd; | |||||
} | |||||
@Override | |||||
public Map<String,String> toMap(){ | |||||
Map<String,String> resultMap = BeanUtils.xmlBean2Map(this); | |||||
if(this.getCouponCount() != null && this.getCouponCount() > 0){ | |||||
for (int i = 0; i < this.getCouponCount(); i++) { | |||||
WxPayOrderNotifyCoupon coupon = couponList.get(i); | |||||
resultMap.putAll(coupon.toMap(i)); | |||||
} | |||||
} | |||||
return resultMap; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return ToStringUtils.toSimpleString(this); | |||||
} | |||||
public static WxPayOrderNotifyResult fromXML(String xmlString) { | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.processAnnotations(WxPayOrderNotifyResult.class); | |||||
xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider())); | |||||
WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString); | |||||
result.setXmlString(xmlString); | |||||
return result; | |||||
} | |||||
} |
@@ -32,7 +32,7 @@ public class WxMpUser implements Serializable { | |||||
private Integer sexId; | private Integer sexId; | ||||
private String remark; | private String remark; | ||||
private Integer groupId; | private Integer groupId; | ||||
private Integer[] tagIds; | |||||
private Long[] tagIds; | |||||
public Boolean getSubscribe() { | public Boolean getSubscribe() { | ||||
return this.subscribe; | return this.subscribe; | ||||
@@ -150,11 +150,11 @@ public class WxMpUser implements Serializable { | |||||
this.groupId = groupId; | this.groupId = groupId; | ||||
} | } | ||||
public Integer[] getTagIds() { | |||||
public Long[] getTagIds() { | |||||
return this.tagIds; | return this.tagIds; | ||||
} | } | ||||
public void setTagIds(Integer[] tagIds) { | |||||
public void setTagIds(Long[] tagIds) { | |||||
this.tagIds = tagIds; | this.tagIds = tagIds; | ||||
} | } | ||||
@@ -12,14 +12,14 @@ import java.util.List; | |||||
*/ | */ | ||||
public class WxMpUserList { | public class WxMpUserList { | ||||
protected int total = -1; | |||||
protected long total = -1; | |||||
protected int count = -1; | protected int count = -1; | ||||
protected List<String> openIds = new ArrayList<>(); | |||||
protected String nextOpenId; | |||||
public int getTotal() { | |||||
protected List<String> openids = new ArrayList<>(); | |||||
protected String nextOpenid; | |||||
public long getTotal() { | |||||
return this.total; | return this.total; | ||||
} | } | ||||
public void setTotal(int total) { | |||||
public void setTotal(long total) { | |||||
this.total = total; | this.total = total; | ||||
} | } | ||||
public int getCount() { | public int getCount() { | ||||
@@ -28,19 +28,19 @@ public class WxMpUserList { | |||||
public void setCount(int count) { | public void setCount(int count) { | ||||
this.count = count; | this.count = count; | ||||
} | } | ||||
public List<String> getOpenIds() { | |||||
return this.openIds; | |||||
public List<String> getOpenids() { | |||||
return this.openids; | |||||
} | } | ||||
public void setOpenIds(List<String> openIds) { | |||||
this.openIds = openIds; | |||||
public void setOpenids(List<String> openids) { | |||||
this.openids = openids; | |||||
} | } | ||||
public String getNextOpenId() { | |||||
return this.nextOpenId; | |||||
public String getNextOpenid() { | |||||
return this.nextOpenid; | |||||
} | } | ||||
public void setNextOpenId(String nextOpenId) { | |||||
this.nextOpenId = nextOpenId; | |||||
public void setNextOpenid(String nextOpenid) { | |||||
this.nextOpenid = nextOpenid; | |||||
} | } | ||||
public static WxMpUserList fromJson(String json) { | public static WxMpUserList fromJson(String json) { | ||||
return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUserList.class); | return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUserList.class); | ||||
} | } | ||||
@@ -12,11 +12,38 @@ import java.util.List; | |||||
public class WxMpTemplateMessage implements Serializable { | public class WxMpTemplateMessage implements Serializable { | ||||
private static final long serialVersionUID = 5063374783759519418L; | private static final long serialVersionUID = 5063374783759519418L; | ||||
/** | |||||
* 接收者openid | |||||
*/ | |||||
private String toUser; | private String toUser; | ||||
/** | |||||
* 模板ID | |||||
*/ | |||||
private String templateId; | private String templateId; | ||||
/** | |||||
* <pre> | |||||
* 跳小程序所需数据,不需跳小程序可不用传该数据 | |||||
* url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。 | |||||
* 开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。 | |||||
* </pre> | |||||
*/ | |||||
private String url; | private String url; | ||||
/** | |||||
* 模板跳转链接 | |||||
* @see #url | |||||
*/ | |||||
private MiniProgram miniProgram; | |||||
/** | |||||
* 模板数据 | |||||
*/ | |||||
private List<WxMpTemplateData> data = new ArrayList<>(); | private List<WxMpTemplateData> data = new ArrayList<>(); | ||||
public WxMpTemplateMessage() { | |||||
} | |||||
public String getToUser() { | public String getToUser() { | ||||
return this.toUser; | return this.toUser; | ||||
} | } | ||||
@@ -53,6 +80,14 @@ public class WxMpTemplateMessage implements Serializable { | |||||
this.data.add(datum); | this.data.add(datum); | ||||
} | } | ||||
public MiniProgram getMiniProgram() { | |||||
return this.miniProgram; | |||||
} | |||||
public void setMiniProgram(MiniProgram miniProgram) { | |||||
this.miniProgram = miniProgram; | |||||
} | |||||
public String toJson() { | public String toJson() { | ||||
return WxMpGsonBuilder.INSTANCE.create().toJson(this); | return WxMpGsonBuilder.INSTANCE.create().toJson(this); | ||||
} | } | ||||
@@ -61,11 +96,41 @@ public class WxMpTemplateMessage implements Serializable { | |||||
return new WxMpTemplateMessageBuilder(); | return new WxMpTemplateMessageBuilder(); | ||||
} | } | ||||
public static class MiniProgram { | |||||
private String appid; | |||||
private String pagePath; | |||||
public MiniProgram() { | |||||
} | |||||
public MiniProgram(String appid, String pagePath) { | |||||
this.appid = appid; | |||||
this.pagePath = pagePath; | |||||
} | |||||
public String getAppid() { | |||||
return this.appid; | |||||
} | |||||
public void setAppid(String appid) { | |||||
this.appid = appid; | |||||
} | |||||
public String getPagePath() { | |||||
return this.pagePath; | |||||
} | |||||
public void setPagePath(String pagePath) { | |||||
this.pagePath = pagePath; | |||||
} | |||||
} | |||||
public static class WxMpTemplateMessageBuilder { | public static class WxMpTemplateMessageBuilder { | ||||
private String toUser; | private String toUser; | ||||
private String templateId; | private String templateId; | ||||
private String url; | private String url; | ||||
private List<WxMpTemplateData> data = new ArrayList<>(); | private List<WxMpTemplateData> data = new ArrayList<>(); | ||||
private MiniProgram miniProgram; | |||||
public WxMpTemplateMessageBuilder toUser(String toUser) { | public WxMpTemplateMessageBuilder toUser(String toUser) { | ||||
this.toUser = toUser; | this.toUser = toUser; | ||||
@@ -95,12 +160,18 @@ public class WxMpTemplateMessage implements Serializable { | |||||
return this; | return this; | ||||
} | } | ||||
public WxMpTemplateMessageBuilder miniProgram(MiniProgram miniProgram) { | |||||
this.miniProgram = miniProgram; | |||||
return this; | |||||
} | |||||
public WxMpTemplateMessage build() { | public WxMpTemplateMessage build() { | ||||
WxMpTemplateMessage m = new WxMpTemplateMessage(); | WxMpTemplateMessage m = new WxMpTemplateMessage(); | ||||
m.toUser = this.toUser; | m.toUser = this.toUser; | ||||
m.templateId = this.templateId; | m.templateId = this.templateId; | ||||
m.url = this.url; | m.url = this.url; | ||||
m.data = this.data; | m.data = this.data; | ||||
m.miniProgram = this.miniProgram; | |||||
return m; | return m; | ||||
} | } | ||||
} | } | ||||
@@ -16,7 +16,8 @@ public class WxMpMassPreviewMessageGsonAdapter implements JsonSerializer<WxMpMas | |||||
@Override | @Override | ||||
public JsonElement serialize(WxMpMassPreviewMessage wxMpMassPreviewMessage, Type type, JsonSerializationContext jsonSerializationContext) { | public JsonElement serialize(WxMpMassPreviewMessage wxMpMassPreviewMessage, Type type, JsonSerializationContext jsonSerializationContext) { | ||||
JsonObject jsonObject = new JsonObject(); | JsonObject jsonObject = new JsonObject(); | ||||
jsonObject.addProperty("towxname", wxMpMassPreviewMessage.getToWxUsername()); | |||||
jsonObject.addProperty("towxname", wxMpMassPreviewMessage.getToWxUserName()); | |||||
jsonObject.addProperty("touser", wxMpMassPreviewMessage.getToWxUserOpenid()); | |||||
if (WxConsts.MASS_MSG_NEWS.equals(wxMpMassPreviewMessage.getMsgType())) { | if (WxConsts.MASS_MSG_NEWS.equals(wxMpMassPreviewMessage.getMsgType())) { | ||||
JsonObject news = new JsonObject(); | JsonObject news = new JsonObject(); | ||||
news.addProperty("media_id", wxMpMassPreviewMessage.getMediaId()); | news.addProperty("media_id", wxMpMassPreviewMessage.getMediaId()); | ||||
@@ -28,6 +28,13 @@ public class WxMpTemplateMessageGsonAdapter implements JsonSerializer<WxMpTempla | |||||
messageJson.addProperty("url", message.getUrl()); | messageJson.addProperty("url", message.getUrl()); | ||||
} | } | ||||
if(message.getMiniProgram() !=null){ | |||||
JsonObject miniProgramJson = new JsonObject(); | |||||
miniProgramJson.addProperty("appid", message.getMiniProgram().getAppid()); | |||||
miniProgramJson.addProperty("pagepath", message.getMiniProgram().getPagePath()); | |||||
messageJson.add("miniprogram", miniProgramJson); | |||||
} | |||||
JsonObject data = new JsonObject(); | JsonObject data = new JsonObject(); | ||||
messageJson.add("data", data); | messageJson.add("data", data); | ||||
@@ -36,7 +36,7 @@ public class WxMpUserGsonAdapter implements JsonDeserializer<WxMpUser> { | |||||
Integer sexId = GsonHelper.getInteger(o, "sex"); | Integer sexId = GsonHelper.getInteger(o, "sex"); | ||||
wxMpUser.setRemark(GsonHelper.getString(o, "remark")); | wxMpUser.setRemark(GsonHelper.getString(o, "remark")); | ||||
wxMpUser.setGroupId(GsonHelper.getInteger(o, "groupid")); | wxMpUser.setGroupId(GsonHelper.getInteger(o, "groupid")); | ||||
wxMpUser.setTagIds(GsonHelper.getIntArray(o, "tagid_list")); | |||||
wxMpUser.setTagIds(GsonHelper.getLongArray(o, "tagid_list")); | |||||
wxMpUser.setSexId(sexId); | wxMpUser.setSexId(sexId); | ||||
if (new Integer(1).equals(sexId)) { | if (new Integer(1).equals(sexId)) { | ||||
wxMpUser.setSex("男"); | wxMpUser.setSex("男"); | ||||
@@ -20,13 +20,13 @@ public class WxUserListGsonAdapter implements JsonDeserializer<WxMpUserList> { | |||||
public WxMpUserList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | public WxMpUserList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { | ||||
JsonObject o = json.getAsJsonObject(); | JsonObject o = json.getAsJsonObject(); | ||||
WxMpUserList wxMpUserList = new WxMpUserList(); | WxMpUserList wxMpUserList = new WxMpUserList(); | ||||
wxMpUserList.setTotal(GsonHelper.getInteger(o, "total")); | |||||
wxMpUserList.setTotal(GsonHelper.getLong(o, "total")); | |||||
wxMpUserList.setCount(GsonHelper.getInteger(o, "count")); | wxMpUserList.setCount(GsonHelper.getInteger(o, "count")); | ||||
wxMpUserList.setNextOpenId(GsonHelper.getString(o, "next_openid")); | |||||
wxMpUserList.setNextOpenid(GsonHelper.getString(o, "next_openid")); | |||||
if (o.get("data") != null && !o.get("data").isJsonNull() && !o.get("data").getAsJsonObject().get("openid").isJsonNull()) { | if (o.get("data") != null && !o.get("data").isJsonNull() && !o.get("data").getAsJsonObject().get("openid").isJsonNull()) { | ||||
JsonArray data = o.get("data").getAsJsonObject().get("openid").getAsJsonArray(); | JsonArray data = o.get("data").getAsJsonObject().get("openid").getAsJsonArray(); | ||||
for (int i = 0; i < data.size(); i++) { | for (int i = 0; i < data.size(); i++) { | ||||
wxMpUserList.getOpenIds().add(GsonHelper.getAsString(data.get(i))); | |||||
wxMpUserList.getOpenids().add(GsonHelper.getAsString(data.get(i))); | |||||
} | } | ||||
} | } | ||||
return wxMpUserList; | return wxMpUserList; | ||||
@@ -49,7 +49,7 @@ public class XStreamTransformer { | |||||
* @param clz 类型 | * @param clz 类型 | ||||
* @param xStream xml解析器 | * @param xStream xml解析器 | ||||
*/ | */ | ||||
public static void register(Class<?> clz, XStream xStream) { | |||||
private static void register(Class<?> clz, XStream xStream) { | |||||
CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | CLASS_2_XSTREAM_INSTANCE.put(clz, xStream); | ||||
} | } | ||||
@@ -57,10 +57,15 @@ public class XStreamTransformer { | |||||
* 会自动注册该类及其子类 | * 会自动注册该类及其子类 | ||||
* @param clz 要注册的类 | * @param clz 要注册的类 | ||||
*/ | */ | ||||
public static void registerClass(Class<?> clz) { | |||||
private static void registerClass(Class<?> clz) { | |||||
XStream xstream = XStreamInitializer.getInstance(); | XStream xstream = XStreamInitializer.getInstance(); | ||||
xstream.processAnnotations(clz); | xstream.processAnnotations(clz); | ||||
xstream.processAnnotations(getInnerClasses(clz)); | xstream.processAnnotations(getInnerClasses(clz)); | ||||
if(clz.equals(WxMpXmlMessage.class)){ | |||||
// 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId | |||||
xstream.aliasField("MsgID", WxMpXmlMessage.class, "msgId"); | |||||
} | |||||
register(clz, xstream); | register(clz, xstream); | ||||
} | } | ||||
@@ -6,6 +6,7 @@ import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import me.chanjar.weixin.mp.api.test.ApiTestModule; | import me.chanjar.weixin.mp.api.test.ApiTestModule; | ||||
import me.chanjar.weixin.mp.api.test.TestConfigStorage; | import me.chanjar.weixin.mp.api.test.TestConfigStorage; | ||||
import me.chanjar.weixin.mp.api.test.TestConstants; | |||||
import me.chanjar.weixin.mp.bean.WxMpMassNews; | import me.chanjar.weixin.mp.bean.WxMpMassNews; | ||||
import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage; | import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage; | ||||
import me.chanjar.weixin.mp.bean.WxMpMassTagMessage; | import me.chanjar.weixin.mp.bean.WxMpMassTagMessage; | ||||
@@ -101,7 +102,7 @@ public class WxMpMassMessageAPITest { | |||||
.getSystemResourceAsStream("mm.mp4")) { | .getSystemResourceAsStream("mm.mp4")) { | ||||
// 上传视频到媒体库 | // 上传视频到媒体库 | ||||
WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() | WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() | ||||
.mediaUpload(WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, inputStream); | |||||
.mediaUpload(WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, inputStream); | |||||
Assert.assertNotNull(uploadMediaRes); | Assert.assertNotNull(uploadMediaRes); | ||||
Assert.assertNotNull(uploadMediaRes.getMediaId()); | Assert.assertNotNull(uploadMediaRes.getMediaId()); | ||||
@@ -122,7 +123,7 @@ public class WxMpMassMessageAPITest { | |||||
try (InputStream inputStream = ClassLoader | try (InputStream inputStream = ClassLoader | ||||
.getSystemResourceAsStream("mm.jpeg")) { | .getSystemResourceAsStream("mm.jpeg")) { | ||||
WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() | WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() | ||||
.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, inputStream); | |||||
.mediaUpload(WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, inputStream); | |||||
Assert.assertNotNull(uploadMediaRes); | Assert.assertNotNull(uploadMediaRes); | ||||
Assert.assertNotNull(uploadMediaRes.getMediaId()); | Assert.assertNotNull(uploadMediaRes.getMediaId()); | ||||
messages[1] = new Object[]{WxConsts.MASS_MSG_IMAGE, uploadMediaRes.getMediaId()}; | messages[1] = new Object[]{WxConsts.MASS_MSG_IMAGE, uploadMediaRes.getMediaId()}; | ||||
@@ -134,7 +135,7 @@ public class WxMpMassMessageAPITest { | |||||
try (InputStream inputStream = ClassLoader | try (InputStream inputStream = ClassLoader | ||||
.getSystemResourceAsStream("mm.mp3")) { | .getSystemResourceAsStream("mm.mp3")) { | ||||
WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() | WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() | ||||
.mediaUpload(WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, inputStream); | |||||
.mediaUpload(WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, inputStream); | |||||
Assert.assertNotNull(uploadMediaRes); | Assert.assertNotNull(uploadMediaRes); | ||||
Assert.assertNotNull(uploadMediaRes.getMediaId()); | Assert.assertNotNull(uploadMediaRes.getMediaId()); | ||||
messages[2] = new Object[]{WxConsts.MASS_MSG_VOICE, uploadMediaRes.getMediaId()}; | messages[2] = new Object[]{WxConsts.MASS_MSG_VOICE, uploadMediaRes.getMediaId()}; | ||||
@@ -147,7 +148,7 @@ public class WxMpMassMessageAPITest { | |||||
.getSystemResourceAsStream("mm.jpeg")) { | .getSystemResourceAsStream("mm.jpeg")) { | ||||
// 上传照片到媒体库 | // 上传照片到媒体库 | ||||
WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() | WxMediaUploadResult uploadMediaRes = this.wxService.getMaterialService() | ||||
.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, inputStream); | |||||
.mediaUpload(WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, inputStream); | |||||
Assert.assertNotNull(uploadMediaRes); | Assert.assertNotNull(uploadMediaRes); | ||||
Assert.assertNotNull(uploadMediaRes.getMediaId()); | Assert.assertNotNull(uploadMediaRes.getMediaId()); | ||||
@@ -7,6 +7,7 @@ import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import me.chanjar.weixin.common.util.fs.FileUtils; | import me.chanjar.weixin.common.util.fs.FileUtils; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.api.test.ApiTestModule; | import me.chanjar.weixin.mp.api.test.ApiTestModule; | ||||
import me.chanjar.weixin.mp.api.test.TestConstants; | |||||
import me.chanjar.weixin.mp.bean.material.*; | import me.chanjar.weixin.mp.bean.material.*; | ||||
import org.testng.annotations.*; | import org.testng.annotations.*; | ||||
@@ -45,10 +46,10 @@ public class WxMpMaterialServiceImplTest { | |||||
@DataProvider | @DataProvider | ||||
public Object[][] mediaFiles() { | public Object[][] mediaFiles() { | ||||
return new Object[][]{ | return new Object[][]{ | ||||
new Object[]{WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, "mm.jpeg"}, | |||||
new Object[]{WxConsts.MEDIA_VOICE, WxConsts.FILE_MP3, "mm.mp3"}, | |||||
new Object[]{WxConsts.MEDIA_VIDEO, WxConsts.FILE_MP4, "mm.mp4"}, | |||||
new Object[]{WxConsts.MEDIA_THUMB, WxConsts.FILE_JPG, "mm.jpeg"} | |||||
new Object[]{WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, "mm.jpeg"}, | |||||
new Object[]{WxConsts.MEDIA_VOICE, TestConstants.FILE_MP3, "mm.mp3"}, | |||||
new Object[]{WxConsts.MEDIA_VIDEO, TestConstants.FILE_MP4, "mm.mp4"}, | |||||
new Object[]{WxConsts.MEDIA_THUMB, TestConstants.FILE_JPG, "mm.jpeg"} | |||||
}; | }; | ||||
} | } | ||||
@@ -93,43 +93,38 @@ public class WxMpMenuServiceImplTest { | |||||
} | } | ||||
@Test | @Test | ||||
public void testCreateMenu2() throws WxErrorException { | |||||
String a = "{\n" | |||||
+ " \"menu\": {\n" | |||||
+ " \"button\": [\n" | |||||
+ " {\n" | |||||
+ " \"type\": \"click\",\n" | |||||
+ " \"name\": \"今日歌曲\",\n" | |||||
+ " \"key\": \"V1001_TODAY_MUSIC\"\n" | |||||
+ " },\n" | |||||
+ " {\n" | |||||
+ " \"type\": \"click\",\n" | |||||
+ " \"name\": \"歌手简介\",\n" | |||||
+ " \"key\": \"V1001_TODAY_SINGER\"\n" | |||||
+ " },\n" | |||||
+ " {\n" | |||||
+ " \"name\": \"菜单\",\n" | |||||
+ " \"sub_button\": [\n" | |||||
+ " {\n" | |||||
+ " \"type\": \"view\",\n" | |||||
+ " \"name\": \"搜索\",\n" | |||||
+ " \"url\": \"http://www.soso.com/\"\n" | |||||
+ " },\n" | |||||
+ " {\n" | |||||
+ " \"type\": \"view\",\n" | |||||
+ " \"name\": \"视频\",\n" | |||||
+ " \"url\": \"http://v.qq.com/\"\n" | |||||
+ " },\n" | |||||
+ " {\n" | |||||
+ " \"type\": \"click\",\n" | |||||
+ " \"name\": \"赞一下我们\",\n" | |||||
+ " \"key\": \"V1001_GOOD\"\n" | |||||
+ " }\n" | |||||
+ " ]\n" | |||||
+ " }\n" | |||||
+ " ]\n" | |||||
+ " }\n" | |||||
+ "}"; | |||||
public void testCreateMenu_by_json() throws WxErrorException { | |||||
String a = "{\n" + | |||||
" \"button\": [\n" + | |||||
" {\n" + | |||||
" \"type\": \"click\",\n" + | |||||
" \"name\": \"今日歌曲\",\n" + | |||||
" \"key\": \"V1001_TODAY_MUSIC\"\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"name\": \"菜单\",\n" + | |||||
" \"sub_button\": [\n" + | |||||
" {\n" + | |||||
" \"type\": \"view\",\n" + | |||||
" \"name\": \"搜索\",\n" + | |||||
" \"url\": \"http://www.soso.com/\"\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"type\": \"miniprogram\",\n" + | |||||
" \"name\": \"wxa\",\n" + | |||||
" \"url\": \"http://mp.weixin.qq.com\",\n" + | |||||
" \"appid\": \"wx286b93c14bbf93aa\",\n" + | |||||
" \"pagepath\": \"pages/lunar/index.html\"\n" + | |||||
" },\n" + | |||||
" {\n" + | |||||
" \"type\": \"click\",\n" + | |||||
" \"name\": \"赞一下我们\",\n" + | |||||
" \"key\": \"V1001_GOOD\"\n" + | |||||
" }\n" + | |||||
" ]\n" + | |||||
" }\n" + | |||||
" ]\n" + | |||||
"}"; | |||||
WxMenu menu = WxMenu.fromJson(a); | WxMenu menu = WxMenu.fromJson(a); | ||||
System.out.println(menu.toJson()); | System.out.println(menu.toJson()); | ||||
@@ -157,9 +152,11 @@ public class WxMpMenuServiceImplTest { | |||||
button1.setKey("V1001_TODAY_MUSIC"); | button1.setKey("V1001_TODAY_MUSIC"); | ||||
WxMenuButton button2 = new WxMenuButton(); | WxMenuButton button2 = new WxMenuButton(); | ||||
button2.setType(WxConsts.BUTTON_CLICK); | |||||
button2.setName("歌手简介"); | |||||
button2.setKey("V1001_TODAY_SINGER"); | |||||
button2.setType(WxConsts.BUTTON_MINIPROGRAM); | |||||
button2.setName("小程序"); | |||||
button2.setAppId("wx286b93c14bbf93aa"); | |||||
button2.setPagePath("pages/lunar/index.html"); | |||||
button2.setUrl("http://mp.weixin.qq.com"); | |||||
WxMenuButton button3 = new WxMenuButton(); | WxMenuButton button3 = new WxMenuButton(); | ||||
button3.setName("菜单"); | button3.setName("菜单"); | ||||
@@ -1,195 +0,0 @@ | |||||
package me.chanjar.weixin.mp.api.impl; | |||||
import com.github.binarywang.utils.qrcode.QrcodeUtils; | |||||
import com.google.inject.Inject; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
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.pay.request.*; | |||||
import me.chanjar.weixin.mp.bean.pay.result.*; | |||||
import org.testng.annotations.*; | |||||
import java.io.File; | |||||
import java.nio.file.Files; | |||||
import java.nio.file.Path; | |||||
import static org.testng.Assert.*; | |||||
/** | |||||
* 测试支付相关接口 | |||||
* Created by Binary Wang on 2016/7/28. | |||||
* | |||||
* @author binarywang (https://github.com/binarywang) | |||||
*/ | |||||
@Test | |||||
@Guice(modules = ApiTestModule.class) | |||||
public class WxMpPayServiceImplTest { | |||||
@Inject | |||||
protected WxMpService wxService; | |||||
@Test | |||||
public void testGetPayInfo() throws Exception { | |||||
} | |||||
@Test | |||||
public void testDownloadBill() throws Exception { | |||||
File file = this.wxService.getPayService().downloadBill("20170101", "ALL", "GZIP", "1111111"); | |||||
assertNotNull(file); | |||||
//必填字段为空时,抛出异常 | |||||
this.wxService.getPayService().downloadBill("", "", "", null); | |||||
} | |||||
@Test | |||||
public void testReport() throws Exception { | |||||
WxPayReportRequest request = new WxPayReportRequest(); | |||||
request.setInterfaceUrl("hahahah"); | |||||
request.setSignType("HMAC-SHA256");//貌似接口未校验此字段 | |||||
request.setExecuteTime(1000); | |||||
request.setReturnCode("aaa"); | |||||
request.setResultCode("aaa"); | |||||
request.setUserIp("8.8.8"); | |||||
this.wxService.getPayService().report(request); | |||||
} | |||||
/** | |||||
* 需要证书的接口需要先执行该方法 | |||||
*/ | |||||
@Test | |||||
public void setSSLKey() { | |||||
TestConfigStorage config = (TestConfigStorage) this.wxService.getWxMpConfigStorage(); | |||||
config.setSslContextFilePath(config.getKeyPath()); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refund(WxPayRefundRequest)} . | |||||
*/ | |||||
@Test(dependsOnMethods = {"setSSLKey"}) | |||||
public void testRefund() throws Exception { | |||||
WxPayRefundRequest request = new WxPayRefundRequest(); | |||||
request.setOutRefundNo("aaa"); | |||||
request.setOutTradeNo("1111"); | |||||
request.setTotalFee(1222); | |||||
request.setRefundFee(111); | |||||
WxPayRefundResult result = this.wxService.getPayService().refund(request); | |||||
System.err.println(result); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#refundQuery(String, String, String, String)} . | |||||
*/ | |||||
@Test | |||||
public void testRefundQuery() throws Exception { | |||||
WxPayRefundQueryResult result; | |||||
result = this.wxService.getPayService().refundQuery("1", "", "", ""); | |||||
System.err.println(result); | |||||
result = this.wxService.getPayService().refundQuery("", "2", "", ""); | |||||
System.err.println(result); | |||||
result = this.wxService.getPayService().refundQuery("", "", "3", ""); | |||||
System.err.println(result); | |||||
result = this.wxService.getPayService().refundQuery("", "", "", "4"); | |||||
System.err.println(result); | |||||
//测试四个参数都填的情况,应该报异常的 | |||||
result = this.wxService.getPayService().refundQuery("1", "2", "3", "4"); | |||||
System.err.println(result); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#sendRedpack(WxPaySendRedpackRequest)} . | |||||
*/ | |||||
@Test(dependsOnMethods = {"setSSLKey"}) | |||||
public void testSendRedpack() throws Exception { | |||||
WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); | |||||
request.setActName("abc"); | |||||
request.setClientIp("aaa"); | |||||
request.setMchBillNo("aaaa"); | |||||
request | |||||
.setReOpenid(((TestConfigStorage) this.wxService.getWxMpConfigStorage()).getOpenid()); | |||||
WxPaySendRedpackResult redpackResult = this.wxService.getPayService().sendRedpack(request); | |||||
System.err.println(redpackResult); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryRedpack(String)}. | |||||
*/ | |||||
@Test(dependsOnMethods = {"setSSLKey"}) | |||||
public void testQueryRedpack() throws Exception { | |||||
WxPayRedpackQueryResult redpackResult = this.wxService.getPayService().queryRedpack("aaaa"); | |||||
System.err.println(redpackResult); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#unifiedOrder(WxPayUnifiedOrderRequest)}. | |||||
*/ | |||||
@Test | |||||
public void testUnifiedOrder() throws WxErrorException { | |||||
WxPayUnifiedOrderResult result = this.wxService.getPayService() | |||||
.unifiedOrder(WxPayUnifiedOrderRequest.builder().body("1111111") | |||||
.totalFee(1).spbillCreateIp("111111").notifyURL("111111") | |||||
.tradeType("JSAPI").openid("122").outTradeNo("111111").build()); | |||||
System.err.println(result); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryOrder(String, String)} . | |||||
*/ | |||||
@Test | |||||
public final void testQueryOrder() throws WxErrorException { | |||||
//System.err.println(this.wxService.getPayService().queryOrder(null, null)); | |||||
System.err.println(this.wxService.getPayService().queryOrder("11212121", null)); | |||||
System.err.println(this.wxService.getPayService().queryOrder(null, "11111")); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#closeOrder(String)} . | |||||
*/ | |||||
@Test | |||||
public final void testCloseOrder() throws WxErrorException { | |||||
System.err.println(this.wxService.getPayService().closeOrder("11212121")); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#entPay(WxEntPayRequest)}. | |||||
*/ | |||||
@Test(dependsOnMethods = {"setSSLKey"}) | |||||
public final void testEntPay() throws WxErrorException { | |||||
WxEntPayRequest request = new WxEntPayRequest(); | |||||
System.err.println(this.wxService.getPayService().entPay(request)); | |||||
} | |||||
/** | |||||
* Test method for {@link me.chanjar.weixin.mp.api.impl.WxMpPayServiceImpl#queryEntPay(String)}. | |||||
*/ | |||||
@Test(dependsOnMethods = {"setSSLKey"}) | |||||
public final void testQueryEntPay() throws WxErrorException { | |||||
System.err.println(this.wxService.getPayService().queryEntPay("11212121")); | |||||
} | |||||
@Test | |||||
public void testCreateScanPayQrcodeMode1() throws Exception { | |||||
String productId = "abc"; | |||||
byte[] bytes = this.wxService.getPayService().createScanPayQrcodeMode1(productId, null, null); | |||||
Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); | |||||
Files.write(qrcodeFilePath, bytes); | |||||
String qrcodeContent = QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()); | |||||
System.out.println(qrcodeContent); | |||||
assertTrue(qrcodeContent.startsWith("weixin://wxpay/bizpayurl?")); | |||||
assertTrue(qrcodeContent.contains("product_id=" + productId)); | |||||
} | |||||
@Test | |||||
public void testCreateScanPayQrcodeMode2() throws Exception { | |||||
String qrcodeContent = "abc"; | |||||
byte[] bytes = this.wxService.getPayService().createScanPayQrcodeMode2(qrcodeContent, null, null); | |||||
Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); | |||||
Files.write(qrcodeFilePath, bytes); | |||||
assertEquals(QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()), qrcodeContent); | |||||
} | |||||
} |
@@ -70,7 +70,7 @@ public class WxMpUserServiceImplTest { | |||||
Assert.assertNotNull(wxMpUserList); | Assert.assertNotNull(wxMpUserList); | ||||
Assert.assertFalse(wxMpUserList.getCount() == -1); | Assert.assertFalse(wxMpUserList.getCount() == -1); | ||||
Assert.assertFalse(wxMpUserList.getTotal() == -1); | Assert.assertFalse(wxMpUserList.getTotal() == -1); | ||||
Assert.assertFalse(wxMpUserList.getOpenIds().size() == -1); | |||||
Assert.assertFalse(wxMpUserList.getOpenids().size() == -1); | |||||
System.out.println(wxMpUserList); | System.out.println(wxMpUserList); | ||||
} | } | ||||
@@ -17,8 +17,7 @@ public class ApiTestModule implements Module { | |||||
@Override | @Override | ||||
public void configure(Binder binder) { | public void configure(Binder binder) { | ||||
try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { | try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { | ||||
TestConfigStorage config = this | |||||
.fromXml(TestConfigStorage.class, is1); | |||||
TestConfigStorage config = this.fromXml(TestConfigStorage.class, is1); | |||||
config.setAccessTokenLock(new ReentrantLock()); | config.setAccessTokenLock(new ReentrantLock()); | ||||
WxMpService wxService = new WxMpServiceImpl(); | WxMpService wxService = new WxMpServiceImpl(); | ||||
wxService.setWxMpConfigStorage(config); | wxService.setWxMpConfigStorage(config); | ||||
@@ -64,10 +64,4 @@ public class TestConfigStorage extends WxMpInMemoryConfigStorage { | |||||
super.accessTokenLock = lock; | super.accessTokenLock = lock; | ||||
} | } | ||||
@Override | |||||
public boolean useSandboxForWxPay() { | |||||
return false; | |||||
//沙箱环境不成熟,有问题无法使用,暂时屏蔽掉 | |||||
// return true; | |||||
} | |||||
} | } |
@@ -0,0 +1,17 @@ | |||||
package me.chanjar.weixin.mp.api.test; | |||||
/** | |||||
* <pre> | |||||
* 仅供测试使用的一些常量 | |||||
* Created by Binary Wang on 2017-3-9. | |||||
* </pre> | |||||
*/ | |||||
public class TestConstants { | |||||
/////////////////////// | |||||
// 文件类型 | |||||
/////////////////////// | |||||
public static final String FILE_JPG = "jpeg"; | |||||
public static final String FILE_MP3 = "mp3"; | |||||
public static final String FILE_AMR = "amr"; | |||||
public static final String FILE_MP4 = "mp4"; | |||||
} |
@@ -0,0 +1,31 @@ | |||||
package me.chanjar.weixin.mp.bean.template; | |||||
import org.testng.annotations.*; | |||||
import static org.testng.AssertJUnit.*; | |||||
/** | |||||
* <pre> | |||||
* Created by Binary Wang on 2017-3-30. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
public class WxMpTemplateMessageTest { | |||||
@Test | |||||
public void testToJson() throws Exception { | |||||
WxMpTemplateMessage tm = WxMpTemplateMessage.builder() | |||||
.toUser("OPENID") | |||||
.templateId("ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY") | |||||
.miniProgram(new WxMpTemplateMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar")) | |||||
.url("http://weixin.qq.com/download") | |||||
.build(); | |||||
tm.addWxMpTemplateData( | |||||
new WxMpTemplateData("first", "haahah", "#FF00FF")); | |||||
tm.addWxMpTemplateData( | |||||
new WxMpTemplateData("remark", "heihei", "#FF00FF")); | |||||
assertEquals(tm.toJson(), "{\"touser\":\"OPENID\",\"template_id\":\"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY\",\"url\":\"http://weixin.qq.com/download\",\"miniprogram\":{\"appid\":\"xiaochengxuappid12345\",\"pagepath\":\"index?foo=bar\"},\"data\":{\"first\":{\"value\":\"haahah\",\"color\":\"#FF00FF\"},\"remark\":{\"value\":\"heihei\",\"color\":\"#FF00FF\"}}}"); | |||||
} | |||||
} |
@@ -6,6 +6,7 @@ import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import me.chanjar.weixin.common.session.WxSessionManager; | import me.chanjar.weixin.common.session.WxSessionManager; | ||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler; | import me.chanjar.weixin.mp.api.WxMpMessageHandler; | ||||
import me.chanjar.weixin.mp.api.WxMpService; | import me.chanjar.weixin.mp.api.WxMpService; | ||||
import me.chanjar.weixin.mp.api.test.TestConstants; | |||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; | ||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutImageMessage; | ||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; | ||||
@@ -18,7 +19,7 @@ public class DemoImageHandler implements WxMpMessageHandler { | |||||
WxMpService wxMpService, WxSessionManager sessionManager) { | WxMpService wxMpService, WxSessionManager sessionManager) { | ||||
try { | try { | ||||
WxMediaUploadResult wxMediaUploadResult = wxMpService.getMaterialService() | WxMediaUploadResult wxMediaUploadResult = wxMpService.getMaterialService() | ||||
.mediaUpload(WxConsts.MEDIA_IMAGE, WxConsts.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg")); | |||||
.mediaUpload(WxConsts.MEDIA_IMAGE, TestConstants.FILE_JPG, ClassLoader.getSystemResourceAsStream("mm.jpeg")); | |||||
WxMpXmlOutImageMessage m | WxMpXmlOutImageMessage m | ||||
= WxMpXmlOutMessage | = WxMpXmlOutMessage | ||||
.IMAGE() | .IMAGE() | ||||
@@ -6,7 +6,7 @@ | |||||
<parent> | <parent> | ||||
<groupId>com.github.binarywang</groupId> | <groupId>com.github.binarywang</groupId> | ||||
<artifactId>weixin-java-parent</artifactId> | <artifactId>weixin-java-parent</artifactId> | ||||
<version>2.5.0</version> | |||||
<version>2.5.2.BETA</version> | |||||
</parent> | </parent> | ||||
<artifactId>weixin-java-osgi</artifactId> | <artifactId>weixin-java-osgi</artifactId> | ||||
@@ -0,0 +1,47 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
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"> | |||||
<parent> | |||||
<artifactId>weixin-java-parent</artifactId> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<version>2.6.0</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>weixin-java-pay</artifactId> | |||||
<name>WeiXin Java Tools - PAY</name> | |||||
<description>微信支付 Java SDK</description> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<artifactId>weixin-java-common</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.jodd</groupId> | |||||
<artifactId>jodd-http</artifactId> | |||||
<version>3.7</version> | |||||
<!-- 由于较新的3.8版本需要jdk8,故而此处采用较低版本 --> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.github.binarywang</groupId> | |||||
<artifactId>qrcode-utils</artifactId> | |||||
<version>1.0</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.testng</groupId> | |||||
<artifactId>testng</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.google.inject</groupId> | |||||
<artifactId>guice</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -1,64 +1,63 @@ | |||||
package me.chanjar.weixin.mp.bean.pay; | |||||
import java.io.Serializable; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
/** | |||||
* 支付异步通知代金券详细 | |||||
*/ | |||||
public class WxPayOrderNotifyCoupon implements Serializable { | |||||
/** | |||||
* @fields serialVersionUID | |||||
*/ | |||||
private static final long serialVersionUID = -4165343733538156097L; | |||||
@XStreamAlias("coupon_id") | |||||
private String couponId; | |||||
@XStreamAlias("coupon_type") | |||||
private String couponType; | |||||
@XStreamAlias("coupon_fee") | |||||
private Integer couponFee; | |||||
public String getCouponId() { | |||||
return couponId; | |||||
} | |||||
public void setCouponId(String couponId) { | |||||
this.couponId = couponId; | |||||
} | |||||
public String getCouponType() { | |||||
return couponType; | |||||
} | |||||
public void setCouponType(String couponType) { | |||||
this.couponType = couponType; | |||||
} | |||||
public Integer getCouponFee() { | |||||
return couponFee; | |||||
} | |||||
public void setCouponFee(Integer couponFee) { | |||||
this.couponFee = couponFee; | |||||
} | |||||
public Map<String, String> toMap(int index) { | |||||
Map<String, String> map = new HashMap<>(); | |||||
map.put("coupon_id_" + index, this.getCouponId()); | |||||
map.put("coupon_type_" + index, this.getCouponType()); | |||||
map.put("coupon_fee_" + index, this.getCouponFee() + ""); | |||||
return map; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); | |||||
} | |||||
} | |||||
package com.github.binarywang.wxpay.bean; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import org.apache.commons.lang3.builder.ToStringBuilder; | |||||
import org.apache.commons.lang3.builder.ToStringStyle; | |||||
import java.io.Serializable; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
/** | |||||
* 支付异步通知代金券详细 | |||||
*/ | |||||
public class WxPayOrderNotifyCoupon implements Serializable { | |||||
/** | |||||
* @fields serialVersionUID | |||||
*/ | |||||
private static final long serialVersionUID = -4165343733538156097L; | |||||
@XStreamAlias("coupon_id") | |||||
private String couponId; | |||||
@XStreamAlias("coupon_type") | |||||
private String couponType; | |||||
@XStreamAlias("coupon_fee") | |||||
private Integer couponFee; | |||||
public String getCouponId() { | |||||
return couponId; | |||||
} | |||||
public void setCouponId(String couponId) { | |||||
this.couponId = couponId; | |||||
} | |||||
public String getCouponType() { | |||||
return couponType; | |||||
} | |||||
public void setCouponType(String couponType) { | |||||
this.couponType = couponType; | |||||
} | |||||
public Integer getCouponFee() { | |||||
return couponFee; | |||||
} | |||||
public void setCouponFee(Integer couponFee) { | |||||
this.couponFee = couponFee; | |||||
} | |||||
public Map<String, String> toMap(int index) { | |||||
Map<String, String> map = new HashMap<>(); | |||||
map.put("coupon_id_" + index, this.getCouponId()); | |||||
map.put("coupon_type_" + index, this.getCouponType()); | |||||
map.put("coupon_fee_" + index, this.getCouponFee() + ""); | |||||
return map; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); | |||||
} | |||||
} |
@@ -1,60 +1,64 @@ | |||||
package me.chanjar.weixin.mp.bean.pay; | |||||
import com.thoughtworks.xstream.XStream; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import com.thoughtworks.xstream.annotations.XStreamOmitField; | |||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||||
@XStreamAlias("xml") | |||||
public class WxPayOrderNotifyResponse { | |||||
@XStreamOmitField | |||||
private transient static final String FAIL = "FAIL"; | |||||
@XStreamOmitField | |||||
private transient static final String SUCCESS = "SUCCESS"; | |||||
@XStreamAlias("return_code") | |||||
private String returnCode; | |||||
@XStreamAlias("return_msg") | |||||
private String returnMsg; | |||||
public String getReturnCode() { | |||||
return returnCode; | |||||
} | |||||
public void setReturnCode(String returnCode) { | |||||
this.returnCode = returnCode; | |||||
} | |||||
public String getReturnMsg() { | |||||
return returnMsg; | |||||
} | |||||
public void setReturnMsg(String returnMsg) { | |||||
this.returnMsg = returnMsg; | |||||
} | |||||
public WxPayOrderNotifyResponse() { | |||||
super(); | |||||
} | |||||
public WxPayOrderNotifyResponse(String returnCode, String returnMsg) { | |||||
super(); | |||||
this.returnCode = returnCode; | |||||
this.returnMsg = returnMsg; | |||||
} | |||||
public static String fail(String msg) { | |||||
WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(FAIL, msg); | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.autodetectAnnotations(true); | |||||
return xstream.toXML(response); | |||||
} | |||||
public static String success(String msg) { | |||||
WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(SUCCESS, msg); | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.autodetectAnnotations(true); | |||||
return xstream.toXML(response); | |||||
} | |||||
} | |||||
package com.github.binarywang.wxpay.bean; | |||||
import com.thoughtworks.xstream.XStream; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import com.thoughtworks.xstream.annotations.XStreamConverter; | |||||
import com.thoughtworks.xstream.annotations.XStreamOmitField; | |||||
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter; | |||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||||
@XStreamAlias("xml") | |||||
public class WxPayOrderNotifyResponse { | |||||
@XStreamOmitField | |||||
private transient static final String FAIL = "FAIL"; | |||||
@XStreamOmitField | |||||
private transient static final String SUCCESS = "SUCCESS"; | |||||
@XStreamAlias("return_code") | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
private String returnCode; | |||||
@XStreamConverter(value = XStreamCDataConverter.class) | |||||
@XStreamAlias("return_msg") | |||||
private String returnMsg; | |||||
public WxPayOrderNotifyResponse() { | |||||
super(); | |||||
} | |||||
public WxPayOrderNotifyResponse(String returnCode, String returnMsg) { | |||||
super(); | |||||
this.returnCode = returnCode; | |||||
this.returnMsg = returnMsg; | |||||
} | |||||
public static String fail(String msg) { | |||||
WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(FAIL, msg); | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.autodetectAnnotations(true); | |||||
return xstream.toXML(response); | |||||
} | |||||
public static String success(String msg) { | |||||
WxPayOrderNotifyResponse response = new WxPayOrderNotifyResponse(SUCCESS, msg); | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.autodetectAnnotations(true); | |||||
return xstream.toXML(response); | |||||
} | |||||
public String getReturnCode() { | |||||
return returnCode; | |||||
} | |||||
public void setReturnCode(String returnCode) { | |||||
this.returnCode = returnCode; | |||||
} | |||||
public String getReturnMsg() { | |||||
return returnMsg; | |||||
} | |||||
public void setReturnMsg(String returnMsg) { | |||||
this.returnMsg = returnMsg; | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.annotation.Required; | import me.chanjar.weixin.common.annotation.Required; | ||||
@@ -16,47 +16,25 @@ import me.chanjar.weixin.common.util.ToStringUtils; | |||||
* <li>描述 | * <li>描述 | ||||
* </pre> | * </pre> | ||||
* Created by Binary Wang on 2016/10/19. | * Created by Binary Wang on 2016/10/19. | ||||
* | |||||
* @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxEntPayQueryRequest extends WxPayBaseRequest { | public class WxEntPayQueryRequest extends WxPayBaseRequest { | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 商户号 | |||||
* mch_id | |||||
* 商户订单号 | |||||
* partner_trade_no | |||||
* 是 | * 是 | ||||
* 10000098 | |||||
* String(32) | |||||
* 微信支付分配的商户号 | |||||
* 10000098201411111234567890 | |||||
* String | |||||
* 商户订单号 | |||||
* </pre> | * </pre> | ||||
*/ | |||||
@XStreamAlias("mchid") | |||||
private String mchId; | |||||
/** | |||||
* <pre> | |||||
* 商户订单号 | |||||
* partner_trade_no | |||||
* 是 | |||||
* 10000098201411111234567890 | |||||
* String | |||||
* 商户订单号 | |||||
* </pre> | |||||
*/ | |||||
*/ | |||||
@Required | @Required | ||||
@XStreamAlias("partner_trade_no") | @XStreamAlias("partner_trade_no") | ||||
private String partnerTradeNo; | private String partnerTradeNo; | ||||
@Override | |||||
public String getMchId() { | |||||
return this.mchId; | |||||
} | |||||
@Override | |||||
public void setMchId(String mchId) { | |||||
this.mchId = mchId; | |||||
} | |||||
public String getPartnerTradeNo() { | public String getPartnerTradeNo() { | ||||
return this.partnerTradeNo; | return this.partnerTradeNo; | ||||
} | } | ||||
@@ -65,6 +43,11 @@ public class WxEntPayQueryRequest extends WxPayBaseRequest { | |||||
this.partnerTradeNo = partnerTradeNo; | this.partnerTradeNo = partnerTradeNo; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
//do nothing | |||||
} | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | return ToStringUtils.toSimpleString(this); |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.util.ToStringUtils; | import me.chanjar.weixin.common.util.ToStringUtils; | ||||
@@ -15,144 +15,150 @@ import me.chanjar.weixin.common.util.ToStringUtils; | |||||
* <li>描述 | * <li>描述 | ||||
* </pre> | * </pre> | ||||
* Created by Binary Wang on 2016/10/02. | * Created by Binary Wang on 2016/10/02. | ||||
* | |||||
* @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxEntPayRequest extends WxPayBaseRequest{ | |||||
public class WxEntPayRequest extends WxPayBaseRequest { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 公众账号appid | |||||
* mch_appid | |||||
* 是 | |||||
* wx8888888888888888 | |||||
* String | |||||
* 微信分配的公众账号ID(企业号corpid即为此appId) | |||||
* 公众账号appid | |||||
* mch_appid | |||||
* 是 | |||||
* wx8888888888888888 | |||||
* String | |||||
* 微信分配的公众账号ID(企业号corpid即为此appId) | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("mch_appid") | @XStreamAlias("mch_appid") | ||||
private String mchAppid; | private String mchAppid; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 商户号 | |||||
* mchid | |||||
* 是 | |||||
* 1900000109 | |||||
* String(32) | |||||
* 微信支付分配的商户号 | |||||
* 商户号 | |||||
* mchid | |||||
* 是 | |||||
* 1900000109 | |||||
* String(32) | |||||
* 微信支付分配的商户号 | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("mchid") | @XStreamAlias("mchid") | ||||
private String mchId; | private String mchId; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 设备号 | |||||
* device_info | |||||
* 否 | |||||
* 13467007045764 | |||||
* String(32) | |||||
*微信支付分配的终端设备号 | |||||
* 设备号 | |||||
* device_info | |||||
* 否 | |||||
* 13467007045764 | |||||
* String(32) | |||||
* 微信支付分配的终端设备号 | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("device_info") | @XStreamAlias("device_info") | ||||
private String deviceInfo; | private String deviceInfo; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 商户订单号 | |||||
* partner_trade_no | |||||
* 是 | |||||
* 10000098201411111234567890 | |||||
* String | |||||
* 商户订单号 | |||||
* 商户订单号 | |||||
* partner_trade_no | |||||
* 是 | |||||
* 10000098201411111234567890 | |||||
* String | |||||
* 商户订单号 | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("partner_trade_no") | @XStreamAlias("partner_trade_no") | ||||
private String partnerTradeNo; | private String partnerTradeNo; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 需保持唯一性 用户openid | |||||
* openid | |||||
* 是 | |||||
* oxTWIuGaIt6gTKsQRLau2M0yL16E | |||||
* String | |||||
* 商户appid下,某用户的openid | |||||
* 需保持唯一性 用户openid | |||||
* openid | |||||
* 是 | |||||
* oxTWIuGaIt6gTKsQRLau2M0yL16E | |||||
* String | |||||
* 商户appid下,某用户的openid | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("openid") | @XStreamAlias("openid") | ||||
private String openid; | private String openid; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 校验用户姓名选项 | |||||
* check_name | |||||
* 是 | |||||
* OPTION_CHECK | |||||
* String | |||||
* NO_CHECK:不校验真实姓名 | |||||
* FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) | |||||
* OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功) | |||||
* 校验用户姓名选项 | |||||
* check_name | |||||
* 是 | |||||
* OPTION_CHECK | |||||
* String | |||||
* NO_CHECK:不校验真实姓名 | |||||
* FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) | |||||
* OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功) | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("check_name") | @XStreamAlias("check_name") | ||||
private String checkName; | private String checkName; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 收款用户姓名 | |||||
* re_user_name | |||||
* 可选 | |||||
* 马花花 | |||||
* String | |||||
* 收款用户真实姓名。 | |||||
* 如果check_name设置为FORCE_CHECK或OPTION_CHECK, 则必填用户真实姓名 | |||||
* 收款用户姓名 | |||||
* re_user_name | |||||
* 可选 | |||||
* 马花花 | |||||
* String | |||||
* 收款用户真实姓名。 | |||||
* 如果check_name设置为FORCE_CHECK或OPTION_CHECK, 则必填用户真实姓名 | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("re_user_name") | @XStreamAlias("re_user_name") | ||||
private String reUserName; | private String reUserName; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 金额 | |||||
* amount | |||||
* 是 | |||||
* 10099 | |||||
* int | |||||
* 企业付款金额, 单位为分 | |||||
* 金额 | |||||
* amount | |||||
* 是 | |||||
* 10099 | |||||
* int | |||||
* 企业付款金额, 单位为分 | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("amount") | @XStreamAlias("amount") | ||||
private Integer amount; | private Integer amount; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 企业付款描述信息 | |||||
* desc | |||||
* 是 | |||||
* 理赔 | |||||
* String | |||||
* 企业付款操作说明信息。必填。 | |||||
* 企业付款描述信息 | |||||
* desc | |||||
* 是 | |||||
* 理赔 | |||||
* String | |||||
* 企业付款操作说明信息。必填。 | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("desc") | @XStreamAlias("desc") | ||||
private String description; | private String description; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* Ip地址 | |||||
* spbill_create_ip | |||||
* 是 | |||||
* 192.168.0.1 | |||||
* String(32) | |||||
* 调用接口的机器Ip地址 | |||||
* Ip地址 | |||||
* spbill_create_ip | |||||
* 是 | |||||
* 192.168.0.1 | |||||
* String(32) | |||||
* 调用接口的机器Ip地址 | |||||
* </pre> | * </pre> | ||||
*/ | |||||
*/ | |||||
@XStreamAlias("spbill_create_ip") | @XStreamAlias("spbill_create_ip") | ||||
private String spbillCreateIp; | private String spbillCreateIp; | ||||
@Override | |||||
protected void checkConstraints() { | |||||
} | |||||
@Override | @Override | ||||
public String getAppid() { | public String getAppid() { | ||||
return this.mchAppid; | return this.mchAppid; |
@@ -0,0 +1,46 @@ | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
/** | |||||
* <pre> | |||||
* 授权码查询openid接口请求对象类 | |||||
* Created by Binary Wang on 2017-3-27. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayAuthcode2OpenidRequest extends WxPayBaseRequest { | |||||
/** | |||||
* <pre> | |||||
* 授权码 | |||||
* auth_code | |||||
* 是 | |||||
* String(128) | |||||
* 扫码支付授权码,设备读取用户微信中的条码或者二维码信息 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("auth_code") | |||||
private String authCode; | |||||
public WxPayAuthcode2OpenidRequest() { | |||||
} | |||||
public WxPayAuthcode2OpenidRequest(String authCode) { | |||||
this.authCode = authCode; | |||||
} | |||||
public String getAuthCode() { | |||||
return this.authCode; | |||||
} | |||||
public void setAuthCode(String authCode) { | |||||
this.authCode = authCode; | |||||
} | |||||
@Override | |||||
protected void checkConstraints() { | |||||
// nothing to do | |||||
} | |||||
} |
@@ -1,9 +1,14 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.github.binarywang.wxpay.config.WxPayConfig; | |||||
import com.github.binarywang.wxpay.util.SignUtils; | |||||
import com.thoughtworks.xstream.XStream; | import com.thoughtworks.xstream.XStream; | ||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import me.chanjar.weixin.common.util.BeanUtils; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | import me.chanjar.weixin.common.util.ToStringUtils; | ||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | import me.chanjar.weixin.common.util.xml.XStreamInitializer; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.math.BigDecimal; | import java.math.BigDecimal; | ||||
@@ -23,6 +28,22 @@ import java.math.BigDecimal; | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | ||||
*/ | */ | ||||
public abstract class WxPayBaseRequest { | public abstract class WxPayBaseRequest { | ||||
/** | |||||
* 检查请求参数内容,包括必填参数以及特殊约束 | |||||
*/ | |||||
protected void checkFields() throws WxErrorException { | |||||
//check required fields | |||||
BeanUtils.checkRequiredFields(this); | |||||
//check other parameters | |||||
this.checkConstraints(); | |||||
} | |||||
/** | |||||
* 检查约束情况 | |||||
*/ | |||||
protected abstract void checkConstraints(); | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 公众账号ID | * 公众账号ID | ||||
@@ -47,6 +68,32 @@ public abstract class WxPayBaseRequest { | |||||
*/ | */ | ||||
@XStreamAlias("mch_id") | @XStreamAlias("mch_id") | ||||
protected String mchId; | protected String mchId; | ||||
/** | |||||
* <pre> | |||||
* 服务商模式下的子商户公众账号ID | |||||
* sub_appid | |||||
* 是 | |||||
* String(32) | |||||
* wxd678efh567hg6787 | |||||
* 微信分配的子商户公众账号ID | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("sub_appid") | |||||
protected String subAppId; | |||||
/** | |||||
* <pre> | |||||
* 服务商模式下的子商户号 | |||||
* sub_mch_id | |||||
* 是 | |||||
* String(32) | |||||
* 1230000109 | |||||
* 微信支付分配的子商户号,开发者模式下必填 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("sub_mch_id") | |||||
protected String subMchId; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 随机字符串 | * 随机字符串 | ||||
@@ -128,6 +175,22 @@ public abstract class WxPayBaseRequest { | |||||
this.sign = sign; | this.sign = sign; | ||||
} | } | ||||
public String getSubAppId() { | |||||
return subAppId; | |||||
} | |||||
public void setSubAppId(String subAppId) { | |||||
this.subAppId = subAppId; | |||||
} | |||||
public String getSubMchId() { | |||||
return subMchId; | |||||
} | |||||
public void setSubMchId(String subMchId) { | |||||
this.subMchId = subMchId; | |||||
} | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return ToStringUtils.toSimpleString(this); | return ToStringUtils.toSimpleString(this); | ||||
@@ -138,4 +201,41 @@ public abstract class WxPayBaseRequest { | |||||
xstream.processAnnotations(this.getClass()); | xstream.processAnnotations(this.getClass()); | ||||
return xstream.toXML(this); | return xstream.toXML(this); | ||||
} | } | ||||
/** | |||||
* <pre> | |||||
* 检查参数,并设置签名 | |||||
* 1、检查参数(注意:子类实现需要检查参数的而外功能时,请在调用父类的方法前进行相应判断) | |||||
* 2、补充系统参数,如果未传入则从配置里读取 | |||||
* 3、生成签名,并设置进去 | |||||
* </pre> | |||||
* @param config 支付配置对象,用于读取相应系统配置信息 | |||||
*/ | |||||
public void checkAndSign(WxPayConfig config) throws WxErrorException { | |||||
this.checkFields(); | |||||
if (StringUtils.isBlank(getAppid())) { | |||||
this.setAppid(config.getAppId()); | |||||
} | |||||
if (StringUtils.isBlank(getMchId())) { | |||||
this.setMchId(config.getMchId()); | |||||
} | |||||
if (StringUtils.isBlank(getSubAppId())) { | |||||
this.setSubAppId(config.getSubAppId()); | |||||
} | |||||
if (StringUtils.isBlank(getSubMchId())) { | |||||
this. setSubMchId(config.getSubMchId()); | |||||
} | |||||
if (StringUtils.isBlank(getNonceStr())) { | |||||
this.setNonceStr(String.valueOf(System.currentTimeMillis())); | |||||
} | |||||
//设置签名字段的值 | |||||
this.setSign(SignUtils.createSign(this, config.getMchKey())); | |||||
} | |||||
} | } |
@@ -1,7 +1,11 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.annotation.Required; | import me.chanjar.weixin.common.annotation.Required; | ||||
import org.apache.commons.lang3.ArrayUtils; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.util.Arrays; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -12,6 +16,8 @@ import me.chanjar.weixin.common.annotation.Required; | |||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxPayDownloadBillRequest extends WxPayBaseRequest { | public class WxPayDownloadBillRequest extends WxPayBaseRequest { | ||||
private static final String[] BILL_TYPE = new String[]{"ALL", "REFUND", "SUCCESS"}; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 设备号 | * 设备号 | ||||
@@ -120,4 +126,16 @@ public class WxPayDownloadBillRequest extends WxPayBaseRequest { | |||||
public void setTarType(String tarType) { | public void setTarType(String tarType) { | ||||
this.tarType = tarType; | this.tarType = tarType; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
if (StringUtils.isNotBlank(this.getTarType()) && !"GZIP".equals(this.getTarType())) { | |||||
throw new IllegalArgumentException("tar_type值如果存在,只能为GZIP"); | |||||
} | |||||
if (!ArrayUtils.contains(BILL_TYPE, this.getBillType())) { | |||||
throw new IllegalArgumentException(String.format("bill_tpye目前必须为%s其中之一,实际值:%s", | |||||
Arrays.toString(BILL_TYPE), this.getBillType())); | |||||
} | |||||
} | |||||
} | } |
@@ -0,0 +1,380 @@ | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import me.chanjar.weixin.common.annotation.Required; | |||||
/** | |||||
* <pre> | |||||
* 提交刷卡支付请求对象类 | |||||
* Created by Binary Wang on 2017-3-23. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayMicropayRequest extends WxPayBaseRequest { | |||||
/** | |||||
* <pre> | |||||
* 签名类型 | |||||
* sign_type | |||||
* 否 | |||||
* String(32) | |||||
* HMAC-SHA256 | |||||
* 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5 | |||||
**/ | |||||
@XStreamAlias("sign_type") | |||||
private String signType; | |||||
/** | |||||
* <pre> | |||||
* 商品描述 | |||||
* body | |||||
* 是 | |||||
* String(128) | |||||
* image形象店-深圳腾大- QQ公仔 | |||||
* 商品简单描述,该字段须严格按照规范传递,具体请见参数规定 | |||||
**/ | |||||
@Required | |||||
@XStreamAlias("body") | |||||
private String body; | |||||
/** | |||||
* <pre> | |||||
* 商品详情 | |||||
* detail | |||||
* 否 | |||||
* String(6000) | |||||
* | |||||
* 单品优惠功能字段,需要接入请见详细说明 | |||||
**/ | |||||
@XStreamAlias("detail") | |||||
private String detail; | |||||
/** | |||||
* <pre> | |||||
* 附加数据 | |||||
* attach | |||||
* 否 | |||||
* String(127) | |||||
* 说明 | |||||
* 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 | |||||
**/ | |||||
@XStreamAlias("attach") | |||||
private String attach; | |||||
/** | |||||
* <pre> | |||||
* 商户订单号 | |||||
* out_trade_no | |||||
* 是 | |||||
* String(32) | |||||
* 1217752501201407033233368018 | |||||
* 商户系统内部的订单号,32个字符内、可包含字母,其他说明见商户订单号 | |||||
**/ | |||||
@Required | |||||
@XStreamAlias("out_trade_no") | |||||
private String outTradeNo; | |||||
/** | |||||
* <pre> | |||||
* 订单金额 | |||||
* total_fee | |||||
* 是 | |||||
* Int | |||||
* 888 | |||||
* 订单总金额,单位为分,只能为整数,详见支付金额 | |||||
**/ | |||||
@Required | |||||
@XStreamAlias("total_fee") | |||||
private Integer totalFee; | |||||
/** | |||||
* <pre> | |||||
* 货币类型 | |||||
* fee_type | |||||
* 否 | |||||
* String(16) | |||||
* CNY | |||||
* 符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||||
**/ | |||||
@XStreamAlias("fee_type") | |||||
private String feeType; | |||||
/** | |||||
* <pre> | |||||
* 终端IP | |||||
* spbill_create_ip | |||||
* 是 | |||||
* String(16) | |||||
* 8.8.8.8 | |||||
* 调用微信支付API的机器IP | |||||
**/ | |||||
@Required | |||||
@XStreamAlias("spbill_create_ip") | |||||
private String spbillCreateIp; | |||||
/** | |||||
* <pre> | |||||
* 商品标记 | |||||
* goods_tag | |||||
* 否 | |||||
* String(32) | |||||
* 1234 | |||||
* 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠 | |||||
**/ | |||||
@XStreamAlias("goods_tag") | |||||
private String goodsTag; | |||||
/** | |||||
* <pre> | |||||
* 指定支付方式 | |||||
* limit_pay | |||||
* 否 | |||||
* String(32) | |||||
* no_credit | |||||
* no_credit--指定不能使用信用卡支付 | |||||
**/ | |||||
@XStreamAlias("limit_pay") | |||||
private String limitPay; | |||||
/** | |||||
* <pre> | |||||
* 授权码 | |||||
* auth_code | |||||
* 是 | |||||
* String(128) | |||||
* 120061098828009406 | |||||
* 扫码支付授权码,设备读取用户微信中的条码或者二维码信息注:用户刷卡条形码规则:18位纯数字,以10、11、12、13、14、15开头) | |||||
**/ | |||||
@Required | |||||
@XStreamAlias("auth_code") | |||||
private String authCode; | |||||
private WxPayMicropayRequest(Builder builder) { | |||||
setSignType(builder.signType); | |||||
setBody(builder.body); | |||||
setAppid(builder.appid); | |||||
setDetail(builder.detail); | |||||
setMchId(builder.mchId); | |||||
setAttach(builder.attach); | |||||
setSubAppId(builder.subAppId); | |||||
setOutTradeNo(builder.outTradeNo); | |||||
setSubMchId(builder.subMchId); | |||||
setTotalFee(builder.totalFee); | |||||
setNonceStr(builder.nonceStr); | |||||
setFeeType(builder.feeType); | |||||
setSign(builder.sign); | |||||
setSpbillCreateIp(builder.spbillCreateIp); | |||||
setGoodsTag(builder.goodsTag); | |||||
setLimitPay(builder.limitPay); | |||||
setAuthCode(builder.authCode); | |||||
} | |||||
public static Builder newBuilder() { | |||||
return new Builder(); | |||||
} | |||||
public String getSignType() { | |||||
return this.signType; | |||||
} | |||||
public void setSignType(String signType) { | |||||
this.signType = signType; | |||||
} | |||||
public String getBody() { | |||||
return this.body; | |||||
} | |||||
public void setBody(String body) { | |||||
this.body = body; | |||||
} | |||||
public String getDetail() { | |||||
return this.detail; | |||||
} | |||||
public void setDetail(String detail) { | |||||
this.detail = detail; | |||||
} | |||||
public String getAttach() { | |||||
return this.attach; | |||||
} | |||||
public void setAttach(String attach) { | |||||
this.attach = attach; | |||||
} | |||||
public String getOutTradeNo() { | |||||
return this.outTradeNo; | |||||
} | |||||
public void setOutTradeNo(String outTradeNo) { | |||||
this.outTradeNo = outTradeNo; | |||||
} | |||||
public Integer getTotalFee() { | |||||
return this.totalFee; | |||||
} | |||||
public void setTotalFee(Integer totalFee) { | |||||
this.totalFee = totalFee; | |||||
} | |||||
public String getFeeType() { | |||||
return this.feeType; | |||||
} | |||||
public void setFeeType(String feeType) { | |||||
this.feeType = feeType; | |||||
} | |||||
public String getSpbillCreateIp() { | |||||
return this.spbillCreateIp; | |||||
} | |||||
public void setSpbillCreateIp(String spbillCreateIp) { | |||||
this.spbillCreateIp = spbillCreateIp; | |||||
} | |||||
public String getGoodsTag() { | |||||
return this.goodsTag; | |||||
} | |||||
public void setGoodsTag(String goodsTag) { | |||||
this.goodsTag = goodsTag; | |||||
} | |||||
public String getLimitPay() { | |||||
return this.limitPay; | |||||
} | |||||
public void setLimitPay(String limitPay) { | |||||
this.limitPay = limitPay; | |||||
} | |||||
public String getAuthCode() { | |||||
return this.authCode; | |||||
} | |||||
public void setAuthCode(String authCode) { | |||||
this.authCode = authCode; | |||||
} | |||||
@Override | |||||
protected void checkConstraints() { | |||||
//do nothing | |||||
} | |||||
public static final class Builder { | |||||
private String signType; | |||||
private String body; | |||||
private String appid; | |||||
private String detail; | |||||
private String mchId; | |||||
private String attach; | |||||
private String subAppId; | |||||
private String outTradeNo; | |||||
private String subMchId; | |||||
private Integer totalFee; | |||||
private String nonceStr; | |||||
private String feeType; | |||||
private String sign; | |||||
private String spbillCreateIp; | |||||
private String goodsTag; | |||||
private String limitPay; | |||||
private String authCode; | |||||
private Builder() { | |||||
} | |||||
public Builder signType(String signType) { | |||||
this.signType = signType; | |||||
return this; | |||||
} | |||||
public Builder body(String body) { | |||||
this.body = body; | |||||
return this; | |||||
} | |||||
public Builder appid(String appid) { | |||||
this.appid = appid; | |||||
return this; | |||||
} | |||||
public Builder detail(String detail) { | |||||
this.detail = detail; | |||||
return this; | |||||
} | |||||
public Builder mchId(String mchId) { | |||||
this.mchId = mchId; | |||||
return this; | |||||
} | |||||
public Builder attach(String attach) { | |||||
this.attach = attach; | |||||
return this; | |||||
} | |||||
public Builder subAppId(String subAppId) { | |||||
this.subAppId = subAppId; | |||||
return this; | |||||
} | |||||
public Builder outTradeNo(String outTradeNo) { | |||||
this.outTradeNo = outTradeNo; | |||||
return this; | |||||
} | |||||
public Builder subMchId(String subMchId) { | |||||
this.subMchId = subMchId; | |||||
return this; | |||||
} | |||||
public Builder totalFee(Integer totalFee) { | |||||
this.totalFee = totalFee; | |||||
return this; | |||||
} | |||||
public Builder nonceStr(String nonceStr) { | |||||
this.nonceStr = nonceStr; | |||||
return this; | |||||
} | |||||
public Builder feeType(String feeType) { | |||||
this.feeType = feeType; | |||||
return this; | |||||
} | |||||
public Builder sign(String sign) { | |||||
this.sign = sign; | |||||
return this; | |||||
} | |||||
public Builder spbillCreateIp(String spbillCreateIp) { | |||||
this.spbillCreateIp = spbillCreateIp; | |||||
return this; | |||||
} | |||||
public Builder goodsTag(String goodsTag) { | |||||
this.goodsTag = goodsTag; | |||||
return this; | |||||
} | |||||
public Builder limitPay(String limitPay) { | |||||
this.limitPay = limitPay; | |||||
return this; | |||||
} | |||||
public Builder authCode(String authCode) { | |||||
this.authCode = authCode; | |||||
return this; | |||||
} | |||||
public WxPayMicropayRequest build() { | |||||
return new WxPayMicropayRequest(this); | |||||
} | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
@@ -10,7 +10,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
* </pre> | * </pre> | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxPayOrderCloseRequest extends WxPayBaseRequest{ | |||||
public class WxPayOrderCloseRequest extends WxPayBaseRequest { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -33,4 +33,8 @@ public class WxPayOrderCloseRequest extends WxPayBaseRequest{ | |||||
this.outTradeNo = outTradeNo; | this.outTradeNo = outTradeNo; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
} | |||||
} | } |
@@ -1,6 +1,7 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -14,6 +15,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
* <li>示例值 | * <li>示例值 | ||||
* <li>描述 | * <li>描述 | ||||
* </pre> | * </pre> | ||||
* | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
@@ -60,4 +62,12 @@ public class WxPayOrderQueryRequest extends WxPayBaseRequest { | |||||
public void setOutTradeNo(String outTradeNo) { | public void setOutTradeNo(String outTradeNo) { | ||||
this.outTradeNo = outTradeNo; | this.outTradeNo = outTradeNo; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) || | |||||
(StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo))) { | |||||
throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一"); | |||||
} | |||||
} | |||||
} | } |
@@ -0,0 +1,163 @@ | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
/** | |||||
* <pre> | |||||
* 撤销订单请求类 | |||||
* Created by Binary Wang on 2017-3-23. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayOrderReverseRequest extends WxPayBaseRequest { | |||||
/** | |||||
* <pre> | |||||
* 微信订单号 | |||||
* transaction_id | |||||
* String(28) | |||||
* 1217752501201400000000000000 | |||||
* 微信的订单号,优先使用 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("transaction_id") | |||||
private String transactionId; | |||||
/** | |||||
* <pre> | |||||
* 商户订单号 | |||||
* out_trade_no | |||||
* String(32) | |||||
* 1217752501201400000000000000 | |||||
* 商户系统内部的订单号 | |||||
* transaction_id、out_trade_no二选一,如果同时存在优先级:transaction_id> out_trade_no | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("out_trade_no") | |||||
private String outTradeNo; | |||||
/** | |||||
* <pre> | |||||
* 签名类型 | |||||
* sign_type | |||||
* 否 | |||||
* String(32) | |||||
* HMAC-SHA256 | |||||
* 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5 | |||||
**/ | |||||
@XStreamAlias("sign_type") | |||||
private String signType; | |||||
private WxPayOrderReverseRequest(Builder builder) { | |||||
setTransactionId(builder.transactionId); | |||||
setAppid(builder.appid); | |||||
setOutTradeNo(builder.outTradeNo); | |||||
setMchId(builder.mchId); | |||||
setSignType(builder.signType); | |||||
setSubAppId(builder.subAppId); | |||||
setSubMchId(builder.subMchId); | |||||
setNonceStr(builder.nonceStr); | |||||
setSign(builder.sign); | |||||
} | |||||
public static Builder newBuilder() { | |||||
return new Builder(); | |||||
} | |||||
public String getTransactionId() { | |||||
return this.transactionId; | |||||
} | |||||
public void setTransactionId(String transactionId) { | |||||
this.transactionId = transactionId; | |||||
} | |||||
public String getOutTradeNo() { | |||||
return this.outTradeNo; | |||||
} | |||||
public void setOutTradeNo(String outTradeNo) { | |||||
this.outTradeNo = outTradeNo; | |||||
} | |||||
public String getSignType() { | |||||
return this.signType; | |||||
} | |||||
public void setSignType(String signType) { | |||||
this.signType = signType; | |||||
} | |||||
@Override | |||||
protected void checkConstraints() { | |||||
if (StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) { | |||||
throw new IllegalArgumentException("transaction_id 和 out_trade_no不能同时为空!"); | |||||
} | |||||
} | |||||
public static final class Builder { | |||||
private String transactionId; | |||||
private String appid; | |||||
private String outTradeNo; | |||||
private String mchId; | |||||
private String signType; | |||||
private String subAppId; | |||||
private String subMchId; | |||||
private String nonceStr; | |||||
private String sign; | |||||
private Builder() { | |||||
} | |||||
public Builder transactionId(String transactionId) { | |||||
this.transactionId = transactionId; | |||||
return this; | |||||
} | |||||
public Builder appid(String appid) { | |||||
this.appid = appid; | |||||
return this; | |||||
} | |||||
public Builder outTradeNo(String outTradeNo) { | |||||
this.outTradeNo = outTradeNo; | |||||
return this; | |||||
} | |||||
public Builder mchId(String mchId) { | |||||
this.mchId = mchId; | |||||
return this; | |||||
} | |||||
public Builder signType(String signType) { | |||||
this.signType = signType; | |||||
return this; | |||||
} | |||||
public Builder subAppId(String subAppId) { | |||||
this.subAppId = subAppId; | |||||
return this; | |||||
} | |||||
public Builder subMchId(String subMchId) { | |||||
this.subMchId = subMchId; | |||||
return this; | |||||
} | |||||
public Builder nonceStr(String nonceStr) { | |||||
this.nonceStr = nonceStr; | |||||
return this; | |||||
} | |||||
public Builder sign(String sign) { | |||||
this.sign = sign; | |||||
return this; | |||||
} | |||||
public WxPayOrderReverseRequest build() { | |||||
return new WxPayOrderReverseRequest(this); | |||||
} | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
@@ -54,4 +54,9 @@ public class WxPayRedpackQueryRequest extends WxPayBaseRequest { | |||||
public void setMchBillNo(String mchBillNo) { | public void setMchBillNo(String mchBillNo) { | ||||
this.mchBillNo = mchBillNo; | this.mchBillNo = mchBillNo; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
} | |||||
} | } |
@@ -1,6 +1,7 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import org.apache.commons.lang3.StringUtils; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -132,4 +133,15 @@ public class WxPayRefundQueryRequest extends WxPayBaseRequest { | |||||
public void setRefundId(String refundId) { | public void setRefundId(String refundId) { | ||||
this.refundId = refundId; | this.refundId = refundId; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) | |||||
&& StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) || | |||||
(StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) | |||||
&& StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) { | |||||
throw new IllegalArgumentException("transaction_id,out_trade_no,out_refund_no,refund_id 必须四选一"); | |||||
} | |||||
} | |||||
} | } |
@@ -1,7 +1,13 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.github.binarywang.wxpay.config.WxPayConfig; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.annotation.Required; | import me.chanjar.weixin.common.annotation.Required; | ||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import org.apache.commons.lang3.ArrayUtils; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.util.Arrays; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -13,13 +19,25 @@ import me.chanjar.weixin.common.annotation.Required; | |||||
* <li>类型 | * <li>类型 | ||||
* <li>示例值 | * <li>示例值 | ||||
* <li>描述 | * <li>描述 | ||||
* Created by Binary Wang on 2016-10-08. | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | * @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | ||||
* Created by Binary Wang on 2016-10-08. | |||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxPayRefundRequest extends WxPayBaseRequest { | public class WxPayRefundRequest extends WxPayBaseRequest { | ||||
private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", | |||||
"REFUND_SOURCE_UNSETTLED_FUNDS"}; | |||||
@Override | |||||
public void checkAndSign(WxPayConfig config) throws WxErrorException { | |||||
if (StringUtils.isBlank(this.getOpUserId())) { | |||||
this.setOpUserId(config.getMchId()); | |||||
} | |||||
super.checkAndSign(config); | |||||
} | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 设备号 | * 设备号 | ||||
@@ -143,6 +161,31 @@ public class WxPayRefundRequest extends WxPayBaseRequest { | |||||
@XStreamAlias("refund_account") | @XStreamAlias("refund_account") | ||||
private String refundAccount; | private String refundAccount; | ||||
public WxPayRefundRequest() { | |||||
} | |||||
private WxPayRefundRequest(Builder builder) { | |||||
setDeviceInfo(builder.deviceInfo); | |||||
setAppid(builder.appid); | |||||
setTransactionId(builder.transactionId); | |||||
setMchId(builder.mchId); | |||||
setOutTradeNo(builder.outTradeNo); | |||||
setSubAppId(builder.subAppId); | |||||
setSubMchId(builder.subMchId); | |||||
setOutRefundNo(builder.outRefundNo); | |||||
setNonceStr(builder.nonceStr); | |||||
setTotalFee(builder.totalFee); | |||||
setSign(builder.sign); | |||||
setRefundFee(builder.refundFee); | |||||
setRefundFeeType(builder.refundFeeType); | |||||
setOpUserId(builder.opUserId); | |||||
setRefundAccount(builder.refundAccount); | |||||
} | |||||
public static Builder newBuilder() { | |||||
return new Builder(); | |||||
} | |||||
public String getDeviceInfo() { | public String getDeviceInfo() { | ||||
return this.deviceInfo; | return this.deviceInfo; | ||||
} | } | ||||
@@ -215,4 +258,117 @@ public class WxPayRefundRequest extends WxPayBaseRequest { | |||||
this.refundAccount = refundAccount; | this.refundAccount = refundAccount; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
if (StringUtils.isNotBlank(this.getRefundAccount())) { | |||||
if (!ArrayUtils.contains(REFUND_ACCOUNT, this.getRefundAccount())) { | |||||
throw new IllegalArgumentException(String.format("refund_account目前必须为%s其中之一,实际值:%s", | |||||
Arrays.toString(REFUND_ACCOUNT), this.getRefundAccount())); | |||||
} | |||||
} | |||||
if (StringUtils.isBlank(this.getOutTradeNo()) && StringUtils.isBlank(this.getTransactionId())) { | |||||
throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个"); | |||||
} | |||||
} | |||||
public static final class Builder { | |||||
private String deviceInfo; | |||||
private String appid; | |||||
private String transactionId; | |||||
private String mchId; | |||||
private String outTradeNo; | |||||
private String subAppId; | |||||
private String subMchId; | |||||
private String outRefundNo; | |||||
private String nonceStr; | |||||
private Integer totalFee; | |||||
private String sign; | |||||
private Integer refundFee; | |||||
private String refundFeeType; | |||||
private String opUserId; | |||||
private String refundAccount; | |||||
private Builder() { | |||||
} | |||||
public Builder deviceInfo(String deviceInfo) { | |||||
this.deviceInfo = deviceInfo; | |||||
return this; | |||||
} | |||||
public Builder appid(String appid) { | |||||
this.appid = appid; | |||||
return this; | |||||
} | |||||
public Builder transactionId(String transactionId) { | |||||
this.transactionId = transactionId; | |||||
return this; | |||||
} | |||||
public Builder mchId(String mchId) { | |||||
this.mchId = mchId; | |||||
return this; | |||||
} | |||||
public Builder outTradeNo(String outTradeNo) { | |||||
this.outTradeNo = outTradeNo; | |||||
return this; | |||||
} | |||||
public Builder subAppId(String subAppId) { | |||||
this.subAppId = subAppId; | |||||
return this; | |||||
} | |||||
public Builder subMchId(String subMchId) { | |||||
this.subMchId = subMchId; | |||||
return this; | |||||
} | |||||
public Builder outRefundNo(String outRefundNo) { | |||||
this.outRefundNo = outRefundNo; | |||||
return this; | |||||
} | |||||
public Builder nonceStr(String nonceStr) { | |||||
this.nonceStr = nonceStr; | |||||
return this; | |||||
} | |||||
public Builder totalFee(Integer totalFee) { | |||||
this.totalFee = totalFee; | |||||
return this; | |||||
} | |||||
public Builder sign(String sign) { | |||||
this.sign = sign; | |||||
return this; | |||||
} | |||||
public Builder refundFee(Integer refundFee) { | |||||
this.refundFee = refundFee; | |||||
return this; | |||||
} | |||||
public Builder refundFeeType(String refundFeeType) { | |||||
this.refundFeeType = refundFeeType; | |||||
return this; | |||||
} | |||||
public Builder opUserId(String opUserId) { | |||||
this.opUserId = opUserId; | |||||
return this; | |||||
} | |||||
public Builder refundAccount(String refundAccount) { | |||||
this.refundAccount = refundAccount; | |||||
return this; | |||||
} | |||||
public WxPayRefundRequest build() { | |||||
return new WxPayRefundRequest(this); | |||||
} | |||||
} | |||||
} | } |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.annotation.Required; | import me.chanjar.weixin.common.annotation.Required; | ||||
@@ -270,4 +270,9 @@ public class WxPayReportRequest extends WxPayBaseRequest { | |||||
public void setTime(String time) { | public void setTime(String time) { | ||||
this.time = time; | this.time = time; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
//do nothing | |||||
} | |||||
} | } |
@@ -1,18 +1,19 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
/** | /** | ||||
* 发送红包请求参数对象 | |||||
* Created by Binary Wang on 2016/9/24. | |||||
* 发送红包请求参数对象 | |||||
* Created by Binary Wang on 2016/9/24. | |||||
* | |||||
* @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxPaySendRedpackRequest extends WxPayBaseRequest{ | |||||
public class WxPaySendRedpackRequest extends WxPayBaseRequest { | |||||
/** | /** | ||||
* mch_billno | |||||
* 商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 | |||||
*/ | |||||
* mch_billno | |||||
* 商户订单号(每个订单号必须唯一) 组成:mch_id+yyyymmdd+10位一天内不能重复的数字。 接口根据商户订单号支持重入,如出现超时可再调用。 | |||||
*/ | |||||
@XStreamAlias("mch_billno") | @XStreamAlias("mch_billno") | ||||
private String mchBillNo; | private String mchBillNo; | ||||
@@ -25,60 +26,60 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest{ | |||||
private String sendName; | private String sendName; | ||||
/** | /** | ||||
* re_openid | |||||
* 接受红包的用户 用户在wxappid下的openid | |||||
* re_openid | |||||
* 接受红包的用户 用户在wxappid下的openid | |||||
*/ | */ | ||||
@XStreamAlias("re_openid") | @XStreamAlias("re_openid") | ||||
private String reOpenid; | private String reOpenid; | ||||
/** | /** | ||||
* total_amount | |||||
* 红包总额 | |||||
* total_amount | |||||
* 红包总额 | |||||
*/ | */ | ||||
@XStreamAlias("total_amount") | @XStreamAlias("total_amount") | ||||
private Integer totalAmount; | private Integer totalAmount; | ||||
/** | /** | ||||
* total_num | |||||
* 红包发放总人数 | |||||
* total_num | |||||
* 红包发放总人数 | |||||
*/ | */ | ||||
@XStreamAlias("total_num") | @XStreamAlias("total_num") | ||||
private Integer totalNum; | private Integer totalNum; | ||||
/** | /** | ||||
* amt_type | |||||
* 红包金额设置方式 | |||||
* ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额 | |||||
* 裂变红包必填 | |||||
* amt_type | |||||
* 红包金额设置方式 | |||||
* ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额 | |||||
* 裂变红包必填 | |||||
*/ | */ | ||||
@XStreamAlias("amt_type") | @XStreamAlias("amt_type") | ||||
private String amtType; | private String amtType; | ||||
/** | /** | ||||
* wishing | |||||
* 红包祝福语 | |||||
* wishing | |||||
* 红包祝福语 | |||||
*/ | */ | ||||
@XStreamAlias("wishing") | @XStreamAlias("wishing") | ||||
private String wishing; | private String wishing; | ||||
/** | /** | ||||
* client_ip | |||||
* 服务器Ip地址 | |||||
* 调用接口的机器Ip地址 | |||||
* client_ip | |||||
* 服务器Ip地址 | |||||
* 调用接口的机器Ip地址 | |||||
*/ | */ | ||||
@XStreamAlias("client_ip") | @XStreamAlias("client_ip") | ||||
private String clientIp; | private String clientIp; | ||||
/** | /** | ||||
* act_name | |||||
* 活动名称 | |||||
* act_name | |||||
* 活动名称 | |||||
*/ | */ | ||||
@XStreamAlias("act_name") | @XStreamAlias("act_name") | ||||
private String actName; | private String actName; | ||||
/** | /** | ||||
* remark | |||||
* 备注 | |||||
* remark | |||||
* 备注 | |||||
*/ | */ | ||||
@XStreamAlias("remark") | @XStreamAlias("remark") | ||||
private String remark; | private String remark; | ||||
@@ -216,6 +217,11 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest{ | |||||
this.remark = remark; | this.remark = remark; | ||||
} | } | ||||
@Override | |||||
protected void checkConstraints() { | |||||
} | |||||
@Override | @Override | ||||
public String getAppid() { | public String getAppid() { | ||||
return this.wxAppid; | return this.wxAppid; |
@@ -0,0 +1,46 @@ | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
/** | |||||
* <pre> | |||||
* 转换短链接请求对象类 | |||||
* Created by Binary Wang on 2017-3-27. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayShorturlRequest extends WxPayBaseRequest { | |||||
/** | |||||
* <pre> | |||||
* URL链接 | |||||
* long_url | |||||
* 是 | |||||
* String(512) | |||||
* weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX | |||||
* 需要转换的URL,签名用原串,传输需URLencode | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("long_url") | |||||
private String longUrl; | |||||
public String getLongUrl() { | |||||
return this.longUrl; | |||||
} | |||||
public void setLongUrl(String longUrl) { | |||||
this.longUrl = longUrl; | |||||
} | |||||
public WxPayShorturlRequest() { | |||||
} | |||||
public WxPayShorturlRequest(String longUrl) { | |||||
this.longUrl = longUrl; | |||||
} | |||||
@Override | |||||
protected void checkConstraints() { | |||||
//do nothing | |||||
} | |||||
} |
@@ -1,8 +1,13 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.request; | |||||
package com.github.binarywang.wxpay.bean.request; | |||||
import com.github.binarywang.wxpay.config.WxPayConfig; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import me.chanjar.weixin.common.annotation.Required; | import me.chanjar.weixin.common.annotation.Required; | ||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import org.apache.commons.lang3.ArrayUtils; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.util.Arrays; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -17,10 +22,12 @@ import me.chanjar.weixin.common.annotation.Required; | |||||
* <li>描述 | * <li>描述 | ||||
* </pre> | * </pre> | ||||
* Created by Binary Wang on 2016/9/25. | * Created by Binary Wang on 2016/9/25. | ||||
* | |||||
* @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | ||||
private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
@@ -56,35 +63,35 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||||
* 否 | * 否 | ||||
* String(6000) | * String(6000) | ||||
* { "goods_detail":[ | * { "goods_detail":[ | ||||
{ | |||||
"goods_id":"iphone6s_16G", | |||||
"wxpay_goods_id":"1001", | |||||
"goods_name":"iPhone6s 16G", | |||||
"goods_num":1, | |||||
"price":528800, | |||||
"goods_category":"123456", | |||||
"body":"苹果手机" | |||||
}, | |||||
{ | |||||
"goods_id":"iphone6s_32G", | |||||
"wxpay_goods_id":"1002", | |||||
"goods_name":"iPhone6s 32G", | |||||
"quantity":1, | |||||
"price":608800, | |||||
"goods_category":"123789", | |||||
"body":"苹果手机" | |||||
} | |||||
] | |||||
} | |||||
商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。 | |||||
goods_detail []: | |||||
└ goods_id String 必填 32 商品的编号 | |||||
└ wxpay_goods_id String 可选 32 微信支付定义的统一商品编号 | |||||
└ goods_name String 必填 256 商品名称 | |||||
└ goods_num Int 必填 商品数量 | |||||
└ price Int 必填 商品单价,单位为分 | |||||
└ goods_category String 可选 32 商品类目Id | |||||
└ body String 可选 1000 商品描述信息 | |||||
* { | |||||
* "goods_id":"iphone6s_16G", | |||||
* "wxpay_goods_id":"1001", | |||||
* "goods_name":"iPhone6s 16G", | |||||
* "goods_num":1, | |||||
* "price":528800, | |||||
* "goods_category":"123456", | |||||
* "body":"苹果手机" | |||||
* }, | |||||
* { | |||||
* "goods_id":"iphone6s_32G", | |||||
* "wxpay_goods_id":"1002", | |||||
* "goods_name":"iPhone6s 32G", | |||||
* "quantity":1, | |||||
* "price":608800, | |||||
* "goods_category":"123789", | |||||
* "body":"苹果手机" | |||||
* } | |||||
* ] | |||||
* } | |||||
* 商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保护起来。 | |||||
* goods_detail []: | |||||
* └ goods_id String 必填 32 商品的编号 | |||||
* └ wxpay_goods_id String 可选 32 微信支付定义的统一商品编号 | |||||
* └ goods_name String 必填 256 商品名称 | |||||
* └ goods_num Int 必填 商品数量 | |||||
* └ price Int 必填 商品单价,单位为分 | |||||
* └ goods_category String 可选 32 商品类目Id | |||||
* └ body String 可选 1000 商品描述信息 | |||||
* </pre> | * </pre> | ||||
*/ | */ | ||||
@XStreamAlias("detail") | @XStreamAlias("detail") | ||||
@@ -267,6 +274,9 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||||
@XStreamAlias("openid") | @XStreamAlias("openid") | ||||
private String openid; | private String openid; | ||||
public static WxUnifiedOrderRequestBuilder builder() { | |||||
return new WxUnifiedOrderRequestBuilder(); | |||||
} | |||||
public String getDeviceInfo() { | public String getDeviceInfo() { | ||||
return this.deviceInfo; | return this.deviceInfo; | ||||
@@ -362,6 +372,7 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||||
/** | /** | ||||
* 如果配置中已经设置,可以不设置值 | * 如果配置中已经设置,可以不设置值 | ||||
* | |||||
* @param notifyURL | * @param notifyURL | ||||
*/ | */ | ||||
public void setNotifyURL(String notifyURL) { | public void setNotifyURL(String notifyURL) { | ||||
@@ -374,6 +385,7 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||||
/** | /** | ||||
* 如果配置中已经设置,可以不设置值 | * 如果配置中已经设置,可以不设置值 | ||||
* | |||||
* @param tradeType 交易类型 | * @param tradeType 交易类型 | ||||
*/ | */ | ||||
public void setTradeType(String tradeType) { | public void setTradeType(String tradeType) { | ||||
@@ -404,8 +416,33 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { | |||||
this.openid = openid; | this.openid = openid; | ||||
} | } | ||||
public static WxUnifiedOrderRequestBuilder builder() { | |||||
return new WxUnifiedOrderRequestBuilder(); | |||||
@Override | |||||
protected void checkConstraints() { | |||||
if (!ArrayUtils.contains(TRADE_TYPES, this.getTradeType())) { | |||||
throw new IllegalArgumentException(String.format("trade_type目前必须为%s其中之一,实际值:%s", | |||||
Arrays.toString(TRADE_TYPES), this.getTradeType())); | |||||
} | |||||
if ("JSAPI".equals(this.getTradeType()) && this.getOpenid() == null) { | |||||
throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid"); | |||||
} | |||||
if ("NATIVE".equals(this.getTradeType()) && this.getProductId() == null) { | |||||
throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id"); | |||||
} | |||||
} | |||||
@Override | |||||
public void checkAndSign(WxPayConfig config) throws WxErrorException { | |||||
if (StringUtils.isBlank(this.getNotifyURL())) { | |||||
this.setNotifyURL(config.getNotifyUrl()); | |||||
} | |||||
if (StringUtils.isBlank(this.getTradeType())) { | |||||
this.setTradeType(config.getTradeType()); | |||||
} | |||||
super.checkAndSign(config); | |||||
} | } | ||||
public static class WxUnifiedOrderRequestBuilder { | public static class WxUnifiedOrderRequestBuilder { |
@@ -1,10 +1,11 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
/** | /** | ||||
* 企业付款查询返回结果 | * 企业付款查询返回结果 | ||||
* Created by Binary Wang on 2016/10/19. | * Created by Binary Wang on 2016/10/19. | ||||
* | |||||
* @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") |
@@ -1,10 +1,11 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
/** | /** | ||||
* 企业付款返回结果 | * 企业付款返回结果 | ||||
* Created by Binary Wang on 2016/10/02. | * Created by Binary Wang on 2016/10/02. | ||||
* | |||||
* @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") |
@@ -0,0 +1,40 @@ | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
/** | |||||
* <pre> | |||||
* 授权码查询openid接口请求结果类 | |||||
* Created by Binary Wang on 2017-3-27. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayAuthcode2OpenidResult extends WxPayBaseResult { | |||||
/** | |||||
* <pre> | |||||
* 用户标识 | |||||
* openid | |||||
* 是 | |||||
* String(128) | |||||
* 用户在商户appid下的唯一标识 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("openid") | |||||
private String openid; | |||||
public WxPayAuthcode2OpenidResult() { | |||||
} | |||||
public WxPayAuthcode2OpenidResult(String openid) { | |||||
this.openid = openid; | |||||
} | |||||
public String getOpenid() { | |||||
return this.openid; | |||||
} | |||||
public void setOpenid(String openid) { | |||||
this.openid = openid; | |||||
} | |||||
} |
@@ -0,0 +1,347 @@ | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; | |||||
import com.github.binarywang.wxpay.util.SignUtils; | |||||
import com.google.common.base.Joiner; | |||||
import com.google.common.collect.Maps; | |||||
import com.thoughtworks.xstream.XStream; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import me.chanjar.weixin.common.bean.result.WxError; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.w3c.dom.Document; | |||||
import org.w3c.dom.NodeList; | |||||
import org.xml.sax.SAXException; | |||||
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; | |||||
import java.io.ByteArrayInputStream; | |||||
import java.io.IOException; | |||||
import java.math.BigDecimal; | |||||
import java.util.Map; | |||||
/** | |||||
* <pre> | |||||
* 微信支付结果共用属性类 | |||||
* Created by Binary Wang on 2016-10-24. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
public abstract class WxPayBaseResult { | |||||
/** | |||||
* 返回状态码 | |||||
*/ | |||||
@XStreamAlias("return_code") | |||||
protected String returnCode; | |||||
/** | |||||
* 返回信息 | |||||
*/ | |||||
@XStreamAlias("return_msg") | |||||
protected String returnMsg; | |||||
//当return_code为SUCCESS的时候,还会包括以下字段: | |||||
/** | |||||
* 业务结果 | |||||
*/ | |||||
@XStreamAlias("result_code") | |||||
private String resultCode; | |||||
/** | |||||
* 错误代码 | |||||
*/ | |||||
@XStreamAlias("err_code") | |||||
private String errCode; | |||||
/** | |||||
* 错误代码描述 | |||||
*/ | |||||
@XStreamAlias("err_code_des") | |||||
private String errCodeDes; | |||||
/** | |||||
* 公众账号ID | |||||
*/ | |||||
@XStreamAlias("appid") | |||||
private String appid; | |||||
/** | |||||
* 商户号 | |||||
*/ | |||||
@XStreamAlias("mch_id") | |||||
private String mchId; | |||||
/** | |||||
* 服务商模式下的子公众账号ID | |||||
*/ | |||||
@XStreamAlias("sub_appid") | |||||
private String subAppId; | |||||
/** | |||||
* 服务商模式下的子商户号 | |||||
*/ | |||||
@XStreamAlias("sub_mch_id") | |||||
private String subMchId; | |||||
/** | |||||
* 随机字符串 | |||||
*/ | |||||
@XStreamAlias("nonce_str") | |||||
private String nonceStr; | |||||
/** | |||||
* 签名 | |||||
*/ | |||||
@XStreamAlias("sign") | |||||
private String sign; | |||||
//以下为辅助属性 | |||||
/** | |||||
* xml字符串 | |||||
*/ | |||||
private String xmlString; | |||||
/** | |||||
* xml的Document对象,用于解析xml文本 | |||||
*/ | |||||
private Document xmlDoc; | |||||
/** | |||||
* 将单位分转换成单位圆 | |||||
* | |||||
* @param fee 将要被转换为元的分的数值 | |||||
*/ | |||||
public static String feeToYuan(Integer fee) { | |||||
return new BigDecimal(Double.valueOf(fee) / 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString(); | |||||
} | |||||
/** | |||||
* 从xml字符串创建bean对象 | |||||
*/ | |||||
public static <T extends WxPayBaseResult> T fromXML(String xmlString, Class<T> clz) { | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.processAnnotations(clz); | |||||
T result = (T) xstream.fromXML(xmlString); | |||||
result.setXmlString(xmlString); | |||||
return result; | |||||
} | |||||
public String getXmlString() { | |||||
return this.xmlString; | |||||
} | |||||
public void setXmlString(String xmlString) { | |||||
this.xmlString = xmlString; | |||||
} | |||||
protected Logger getLogger() { | |||||
return LoggerFactory.getLogger(this.getClass()); | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return ToStringUtils.toSimpleString(this); | |||||
} | |||||
public String getReturnCode() { | |||||
return this.returnCode; | |||||
} | |||||
public void setReturnCode(String returnCode) { | |||||
this.returnCode = returnCode; | |||||
} | |||||
public String getReturnMsg() { | |||||
return this.returnMsg; | |||||
} | |||||
public void setReturnMsg(String returnMsg) { | |||||
this.returnMsg = returnMsg; | |||||
} | |||||
public String getResultCode() { | |||||
return this.resultCode; | |||||
} | |||||
public void setResultCode(String resultCode) { | |||||
this.resultCode = resultCode; | |||||
} | |||||
public String getErrCode() { | |||||
return this.errCode; | |||||
} | |||||
public void setErrCode(String errCode) { | |||||
this.errCode = errCode; | |||||
} | |||||
public String getErrCodeDes() { | |||||
return this.errCodeDes; | |||||
} | |||||
public void setErrCodeDes(String errCodeDes) { | |||||
this.errCodeDes = errCodeDes; | |||||
} | |||||
public String getAppid() { | |||||
return this.appid; | |||||
} | |||||
public void setAppid(String appid) { | |||||
this.appid = appid; | |||||
} | |||||
public String getMchId() { | |||||
return this.mchId; | |||||
} | |||||
public void setMchId(String mchId) { | |||||
this.mchId = mchId; | |||||
} | |||||
public String getNonceStr() { | |||||
return this.nonceStr; | |||||
} | |||||
public void setNonceStr(String nonceStr) { | |||||
this.nonceStr = nonceStr; | |||||
} | |||||
public String getSign() { | |||||
return this.sign; | |||||
} | |||||
public void setSign(String sign) { | |||||
this.sign = sign; | |||||
} | |||||
public String getSubAppId() { | |||||
return subAppId; | |||||
} | |||||
public void setSubAppId(String subAppId) { | |||||
this.subAppId = subAppId; | |||||
} | |||||
public String getSubMchId() { | |||||
return subMchId; | |||||
} | |||||
public void setSubMchId(String subMchId) { | |||||
this.subMchId = subMchId; | |||||
} | |||||
/** | |||||
* 将bean通过保存的xml字符串转换成map | |||||
*/ | |||||
public Map<String, String> toMap() { | |||||
if (StringUtils.isBlank(this.xmlString)) { | |||||
throw new RuntimeException("xml数据有问题,请核实!"); | |||||
} | |||||
Map<String, String> result = Maps.newHashMap(); | |||||
Document doc = this.getXmlDoc(); | |||||
try { | |||||
NodeList list = (NodeList) XPathFactory.newInstance().newXPath() | |||||
.compile("/xml/*") | |||||
.evaluate(doc, XPathConstants.NODESET); | |||||
int len = list.getLength(); | |||||
for (int i = 0; i < len; i++) { | |||||
result.put(list.item(i).getNodeName(), list.item(i).getTextContent()); | |||||
} | |||||
} catch (XPathExpressionException e) { | |||||
throw new RuntimeException("非法的xml文本内容:" + xmlString); | |||||
} | |||||
return result; | |||||
} | |||||
/** | |||||
* 将xml字符串转换成Document对象,以便读取其元素值 | |||||
*/ | |||||
protected Document getXmlDoc() { | |||||
if (this.xmlDoc != null) { | |||||
return this.xmlDoc; | |||||
} | |||||
try { | |||||
this.xmlDoc = DocumentBuilderFactory | |||||
.newInstance() | |||||
.newDocumentBuilder() | |||||
.parse(new ByteArrayInputStream(this.xmlString.getBytes("UTF-8"))); | |||||
return xmlDoc; | |||||
} catch (SAXException | IOException | ParserConfigurationException e) { | |||||
throw new RuntimeException("非法的xml文本内容:" + this.xmlString); | |||||
} | |||||
} | |||||
/** | |||||
* 获取xml中元素的值 | |||||
*/ | |||||
protected String getXmlValue(String... path) { | |||||
Document doc = this.getXmlDoc(); | |||||
String expression = String.format("/%s//text()", Joiner.on("/").join(path)); | |||||
try { | |||||
return (String) XPathFactory | |||||
.newInstance() | |||||
.newXPath() | |||||
.compile(expression) | |||||
.evaluate(doc, XPathConstants.STRING); | |||||
} catch (XPathExpressionException e) { | |||||
throw new RuntimeException("未找到相应路径的文本:" + expression); | |||||
} | |||||
} | |||||
/** | |||||
* 获取xml中元素的值,作为int值返回 | |||||
*/ | |||||
protected Integer getXmlValueAsInt(String... path) { | |||||
String result = this.getXmlValue(path); | |||||
if (StringUtils.isBlank(result)) { | |||||
return null; | |||||
} | |||||
return Integer.valueOf(result); | |||||
} | |||||
/** | |||||
* 校验返回结果签名 | |||||
*/ | |||||
public void checkResult(WxPayServiceImpl wxPayService) throws WxErrorException { | |||||
//校验返回结果签名 | |||||
Map<String, String> map = toMap(); | |||||
if (getSign() != null && !SignUtils.checkSign(map, wxPayService.getConfig().getMchKey())) { | |||||
this.getLogger().debug("校验结果签名失败,参数:{}", map); | |||||
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build()); | |||||
} | |||||
//校验结果是否成功 | |||||
if (!"SUCCESS".equalsIgnoreCase(getReturnCode()) | |||||
|| !"SUCCESS".equalsIgnoreCase(getResultCode())) { | |||||
StringBuilder errorMsg = new StringBuilder(); | |||||
if (getReturnCode() != null) { | |||||
errorMsg.append("返回代码:").append(getReturnCode()); | |||||
} | |||||
if (getReturnMsg() != null) { | |||||
errorMsg.append(",返回信息:").append(getReturnMsg()); | |||||
} | |||||
if (getResultCode() != null) { | |||||
errorMsg.append(",结果代码:").append(getResultCode()); | |||||
} | |||||
if (getErrCode() != null) { | |||||
errorMsg.append(",错误代码:").append(getErrCode()); | |||||
} | |||||
if (getErrCodeDes() != null) { | |||||
errorMsg.append(",错误详情:").append(getErrCodeDes()); | |||||
} | |||||
WxError error = WxError.newBuilder() | |||||
.setErrorCode(-1) | |||||
.setErrorMsg(errorMsg.toString()) | |||||
.build(); | |||||
this.getLogger().error("\n结果业务代码异常,返回結果:{},\n{}", map, error); | |||||
throw new WxErrorException(error); | |||||
} | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
@@ -0,0 +1,328 @@ | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
/** | |||||
* <pre> | |||||
* 提交刷卡支付接口响应结果对象类 | |||||
* Created by Binary Wang on 2017-3-23. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayMicropayResult extends WxPayBaseResult { | |||||
/** | |||||
* <pre> | |||||
* 用户标识 | |||||
* openid | |||||
* 是 | |||||
* String(128) | |||||
* Y | |||||
* 用户在商户appid 下的唯一标识 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("openid") | |||||
private String openid; | |||||
/** | |||||
* <pre> | |||||
* 是否关注公众账号 | |||||
* is_subscribe | |||||
* 是 | |||||
* String(1) | |||||
* Y | |||||
* 用户是否关注公众账号,仅在公众账号类型支付有效,取值范围:Y或N;Y-关注;N-未关注 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("is_subscribe") | |||||
private String isSubscribe; | |||||
/** | |||||
* <pre> | |||||
* 交易类型 | |||||
* trade_type | |||||
* 是 | |||||
* String(16) | |||||
* MICROPAY | |||||
* 支付类型为MICROPAY(即扫码支付) | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("trade_type") | |||||
private String tradeType; | |||||
/** | |||||
* <pre> | |||||
* 付款银行 | |||||
* bank_type | |||||
* 是 | |||||
* String(32) | |||||
* CMC | |||||
* 银行类型,采用字符串类型的银行标识,值列表详见银行类型 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("bank_type") | |||||
private String bankType; | |||||
/** | |||||
* <pre> | |||||
* 货币类型 | |||||
* fee_type | |||||
* 否 | |||||
* String(16) | |||||
* CNY | |||||
* 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("fee_type") | |||||
private String feeType; | |||||
/** | |||||
* <pre> | |||||
* 订单金额 | |||||
* total_fee | |||||
* 是 | |||||
* Int | |||||
* 888 | |||||
* 订单总金额,单位为分,只能为整数,详见支付金额 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("total_fee") | |||||
private String totalFee; | |||||
/** | |||||
* <pre> | |||||
* 应结订单金额 | |||||
* settlement_total_fee | |||||
* 否 | |||||
* Int | |||||
* 100 | |||||
* 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("settlement_total_fee") | |||||
private Integer settlementTotalFee; | |||||
/** | |||||
* <pre> | |||||
* 代金券金额 | |||||
* coupon_fee | |||||
* 否 | |||||
* Int | |||||
* 100 | |||||
* “代金券”金额<=订单金额,订单金额-“代金券”金额=现金支付金额,详见支付金额 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("coupon_fee") | |||||
private Integer couponFee; | |||||
/** | |||||
* <pre> | |||||
* 现金支付货币类型 | |||||
* cash_fee_type | |||||
* 否 | |||||
* String(16) | |||||
* CNY | |||||
* 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("cash_fee_type") | |||||
private String cashFeeType; | |||||
/** | |||||
* <pre> | |||||
* 现金支付金额 | |||||
* cash_fee | |||||
* 是 | |||||
* Int | |||||
* 100 | |||||
* 订单现金支付金额,详见支付金额 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("cash_fee") | |||||
private Integer cashFee; | |||||
/** | |||||
* <pre> | |||||
* 微信支付订单号 | |||||
* transaction_id | |||||
* 是 | |||||
* String(32) | |||||
* 1217752501201407033233368018 | |||||
* 微信支付订单号 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("transaction_id") | |||||
private String transactionId; | |||||
/** | |||||
* <pre> | |||||
* 商户订单号 | |||||
* out_trade_no | |||||
* 是 | |||||
* String(32) | |||||
* 1217752501201407033233368018 | |||||
* 商户系统的订单号,与请求一致。 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("out_trade_no") | |||||
private String outTradeNo; | |||||
/** | |||||
* <pre> | |||||
* 商家数据包 | |||||
* attach | |||||
* 否 | |||||
* String(128) | |||||
* 123456 | |||||
* 商家数据包,原样返回 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("attach") | |||||
private String attach; | |||||
/** | |||||
* <pre> | |||||
* 支付完成时间 | |||||
* time_end | |||||
* 是 | |||||
* String(14) | |||||
* 20141030133525 | |||||
* 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。详见时间规则 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("time_end") | |||||
private String timeEnd; | |||||
/** | |||||
* <pre> | |||||
* 营销详情 | |||||
* promotion_detail | |||||
* 否 | |||||
* String(6000) | |||||
* 示例见下文 | |||||
* 新增返回,单品优惠功能字段,需要接入请见详细说明 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("promotion_detail") | |||||
private String promotionDetail; | |||||
public String getOpenid() { | |||||
return this.openid; | |||||
} | |||||
public void setOpenid(String openid) { | |||||
this.openid = openid; | |||||
} | |||||
public String getIsSubscribe() { | |||||
return this.isSubscribe; | |||||
} | |||||
public void setIsSubscribe(String isSubscribe) { | |||||
this.isSubscribe = isSubscribe; | |||||
} | |||||
public String getTradeType() { | |||||
return this.tradeType; | |||||
} | |||||
public void setTradeType(String tradeType) { | |||||
this.tradeType = tradeType; | |||||
} | |||||
public String getBankType() { | |||||
return this.bankType; | |||||
} | |||||
public void setBankType(String bankType) { | |||||
this.bankType = bankType; | |||||
} | |||||
public String getFeeType() { | |||||
return this.feeType; | |||||
} | |||||
public void setFeeType(String feeType) { | |||||
this.feeType = feeType; | |||||
} | |||||
public String getTotalFee() { | |||||
return this.totalFee; | |||||
} | |||||
public void setTotalFee(String totalFee) { | |||||
this.totalFee = totalFee; | |||||
} | |||||
public Integer getSettlementTotalFee() { | |||||
return this.settlementTotalFee; | |||||
} | |||||
public void setSettlementTotalFee(Integer settlementTotalFee) { | |||||
this.settlementTotalFee = settlementTotalFee; | |||||
} | |||||
public Integer getCouponFee() { | |||||
return this.couponFee; | |||||
} | |||||
public void setCouponFee(Integer couponFee) { | |||||
this.couponFee = couponFee; | |||||
} | |||||
public String getCashFeeType() { | |||||
return this.cashFeeType; | |||||
} | |||||
public void setCashFeeType(String cashFeeType) { | |||||
this.cashFeeType = cashFeeType; | |||||
} | |||||
public Integer getCashFee() { | |||||
return this.cashFee; | |||||
} | |||||
public void setCashFee(Integer cashFee) { | |||||
this.cashFee = cashFee; | |||||
} | |||||
public String getTransactionId() { | |||||
return this.transactionId; | |||||
} | |||||
public void setTransactionId(String transactionId) { | |||||
this.transactionId = transactionId; | |||||
} | |||||
public String getOutTradeNo() { | |||||
return this.outTradeNo; | |||||
} | |||||
public void setOutTradeNo(String outTradeNo) { | |||||
this.outTradeNo = outTradeNo; | |||||
} | |||||
public String getAttach() { | |||||
return this.attach; | |||||
} | |||||
public void setAttach(String attach) { | |||||
this.attach = attach; | |||||
} | |||||
public String getTimeEnd() { | |||||
return this.timeEnd; | |||||
} | |||||
public void setTimeEnd(String timeEnd) { | |||||
this.timeEnd = timeEnd; | |||||
} | |||||
public String getPromotionDetail() { | |||||
return this.promotionDetail; | |||||
} | |||||
public void setPromotionDetail(String promotionDetail) { | |||||
this.promotionDetail = promotionDetail; | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
@@ -0,0 +1,392 @@ | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon; | |||||
import com.github.binarywang.wxpay.converter.WxPayOrderNotifyResultConverter; | |||||
import com.thoughtworks.xstream.XStream; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import me.chanjar.weixin.common.util.BeanUtils; | |||||
import me.chanjar.weixin.common.util.ToStringUtils; | |||||
import me.chanjar.weixin.common.util.xml.XStreamInitializer; | |||||
import java.io.Serializable; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
/** | |||||
* 支付结果通用通知 ,文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | |||||
* | |||||
* @author aimilin6688 | |||||
* @since 2.5.0 | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializable { | |||||
private static final long serialVersionUID = 5389718115223345496L; | |||||
/** | |||||
* <pre> | |||||
* 设备号 | |||||
* device_info | |||||
* 否 | |||||
* String(32) | |||||
* 013467007045764 | |||||
* 微信支付分配的终端设备号, | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("device_info") | |||||
private String deviceInfo; | |||||
/** | |||||
* <pre> | |||||
* 用户标识 | |||||
* openid | |||||
* 是 | |||||
* String(128) | |||||
* wxd930ea5d5a258f4f | |||||
* 用户在商户appid下的唯一标识 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("openid") | |||||
private String openid; | |||||
/** | |||||
* <pre> | |||||
* 是否关注公众账号 | |||||
* is_subscribe | |||||
* 否 | |||||
* String(1) | |||||
* Y | |||||
* 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("is_subscribe") | |||||
private String isSubscribe; | |||||
/** | |||||
* <pre> | |||||
* 交易类型 | |||||
* trade_type | |||||
* 是 | |||||
* String(16) | |||||
* JSAPI JSAPI、NATIVE、APP | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("trade_type") | |||||
private String tradeType; | |||||
/** | |||||
* <pre> | |||||
* 付款银行 | |||||
* bank_type | |||||
* 是 | |||||
* String(16) | |||||
* CMC | |||||
* 银行类型,采用字符串类型的银行标识,银行类型见银行列表 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("bank_type") | |||||
private String bankType; | |||||
/** | |||||
* <pre> | |||||
* 订单金额 | |||||
* total_fee | |||||
* 是 | |||||
* Int | |||||
* 100 | |||||
* 订单总金额,单位为分 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("total_fee") | |||||
private Integer totalFee; | |||||
/** | |||||
* <pre> | |||||
* 应结订单金额 | |||||
* settlement_total_fee | |||||
* 否 | |||||
* Int | |||||
* 100 | |||||
* 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("settlement_total_fee") | |||||
private Integer settlementTotalFee; | |||||
/** | |||||
* <pre> | |||||
* 货币种类 | |||||
* fee_type | |||||
* 否 | |||||
* String(8) | |||||
* CNY | |||||
* 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("fee_type") | |||||
private String feeType; | |||||
/** | |||||
* <pre> | |||||
* 现金支付金额 | |||||
* cash_fee | |||||
* 是 | |||||
* Int | |||||
* 100 | |||||
* 现金支付金额订单现金支付金额,详见支付金额 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("cash_fee") | |||||
private Integer cashFee; | |||||
/** | |||||
* <pre> | |||||
* 现金支付货币类型 | |||||
* cash_fee_type | |||||
* 否 | |||||
* String(16) | |||||
* CNY | |||||
* 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("cash_fee_type") | |||||
private String cashFeeType; | |||||
/** | |||||
* <pre> | |||||
* 总代金券金额 | |||||
* coupon_fee | |||||
* 否 | |||||
* Int | |||||
* 10 | |||||
* 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("coupon_fee") | |||||
private Integer couponFee; | |||||
/** | |||||
* <pre> | |||||
* 代金券使用数量 | |||||
* coupon_count | |||||
* 否 | |||||
* Int | |||||
* 1 | |||||
* 代金券使用数量 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("coupon_count") | |||||
private Integer couponCount; | |||||
private List<WxPayOrderNotifyCoupon> couponList; | |||||
/** | |||||
* <pre> | |||||
* 微信支付订单号 | |||||
* transaction_id | |||||
* 是 | |||||
* String(32) | |||||
* 1217752501201407033233368018 | |||||
* 微信支付订单号 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("transaction_id") | |||||
private String transactionId; | |||||
/** | |||||
* <pre> | |||||
* 商户订单号 | |||||
* out_trade_no | |||||
* 是 | |||||
* String(32) | |||||
* 1212321211201407033568112322 | |||||
* 商户系统的订单号,与请求一致。 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("out_trade_no") | |||||
private String outTradeNo; | |||||
/** | |||||
* <pre> | |||||
* 商家数据包 | |||||
* attach | |||||
* 否 | |||||
* String(128) | |||||
* 123456 | |||||
* 商家数据包,原样返回 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("attach") | |||||
private String attach; | |||||
/** | |||||
* <pre> | |||||
* 支付完成时间 | |||||
* time_end | |||||
* 是 | |||||
* String(14) | |||||
* 20141030133525 | |||||
* 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("time_end") | |||||
private String timeEnd; | |||||
public static WxPayOrderNotifyResult fromXML(String xmlString) { | |||||
XStream xstream = XStreamInitializer.getInstance(); | |||||
xstream.processAnnotations(WxPayOrderNotifyResult.class); | |||||
xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider())); | |||||
WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString); | |||||
result.setXmlString(xmlString); | |||||
return result; | |||||
} | |||||
public Integer getCouponCount() { | |||||
return couponCount; | |||||
} | |||||
public void setCouponCount(Integer couponCount) { | |||||
this.couponCount = couponCount; | |||||
} | |||||
public List<WxPayOrderNotifyCoupon> getCouponList() { | |||||
return couponList; | |||||
} | |||||
public void setCouponList(List<WxPayOrderNotifyCoupon> couponList) { | |||||
this.couponList = couponList; | |||||
} | |||||
public String getDeviceInfo() { | |||||
return deviceInfo; | |||||
} | |||||
public void setDeviceInfo(String deviceInfo) { | |||||
this.deviceInfo = deviceInfo; | |||||
} | |||||
public String getOpenid() { | |||||
return openid; | |||||
} | |||||
public void setOpenid(String openid) { | |||||
this.openid = openid; | |||||
} | |||||
public String getIsSubscribe() { | |||||
return isSubscribe; | |||||
} | |||||
public void setIsSubscribe(String isSubscribe) { | |||||
this.isSubscribe = isSubscribe; | |||||
} | |||||
public String getTradeType() { | |||||
return tradeType; | |||||
} | |||||
public void setTradeType(String tradeType) { | |||||
this.tradeType = tradeType; | |||||
} | |||||
public String getBankType() { | |||||
return bankType; | |||||
} | |||||
public void setBankType(String bankType) { | |||||
this.bankType = bankType; | |||||
} | |||||
public Integer getTotalFee() { | |||||
return totalFee; | |||||
} | |||||
public void setTotalFee(Integer totalFee) { | |||||
this.totalFee = totalFee; | |||||
} | |||||
public Integer getSettlementTotalFee() { | |||||
return settlementTotalFee; | |||||
} | |||||
public void setSettlementTotalFee(Integer settlementTotalFee) { | |||||
this.settlementTotalFee = settlementTotalFee; | |||||
} | |||||
public String getFeeType() { | |||||
return feeType; | |||||
} | |||||
public void setFeeType(String feeType) { | |||||
this.feeType = feeType; | |||||
} | |||||
public Integer getCashFee() { | |||||
return cashFee; | |||||
} | |||||
public void setCashFee(Integer cashFee) { | |||||
this.cashFee = cashFee; | |||||
} | |||||
public String getCashFeeType() { | |||||
return cashFeeType; | |||||
} | |||||
public void setCashFeeType(String cashFeeType) { | |||||
this.cashFeeType = cashFeeType; | |||||
} | |||||
public Integer getCouponFee() { | |||||
return couponFee; | |||||
} | |||||
public void setCouponFee(Integer couponFee) { | |||||
this.couponFee = couponFee; | |||||
} | |||||
public String getTransactionId() { | |||||
return transactionId; | |||||
} | |||||
public void setTransactionId(String transactionId) { | |||||
this.transactionId = transactionId; | |||||
} | |||||
public String getOutTradeNo() { | |||||
return outTradeNo; | |||||
} | |||||
public void setOutTradeNo(String outTradeNo) { | |||||
this.outTradeNo = outTradeNo; | |||||
} | |||||
public String getAttach() { | |||||
return attach; | |||||
} | |||||
public void setAttach(String attach) { | |||||
this.attach = attach; | |||||
} | |||||
public String getTimeEnd() { | |||||
return timeEnd; | |||||
} | |||||
public void setTimeEnd(String timeEnd) { | |||||
this.timeEnd = timeEnd; | |||||
} | |||||
@Override | |||||
public Map<String, String> toMap() { | |||||
Map<String, String> resultMap = BeanUtils.xmlBean2Map(this); | |||||
if (this.getCouponCount() != null && this.getCouponCount() > 0) { | |||||
for (int i = 0; i < this.getCouponCount(); i++) { | |||||
WxPayOrderNotifyCoupon coupon = couponList.get(i); | |||||
resultMap.putAll(coupon.toMap(i)); | |||||
} | |||||
} | |||||
return resultMap; | |||||
} | |||||
@Override | |||||
public String toString() { | |||||
return ToStringUtils.toSimpleString(this); | |||||
} | |||||
} |
@@ -1,8 +1,7 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import io.restassured.path.xml.XmlPath; | |||||
import java.util.List; | import java.util.List; | ||||
@@ -395,11 +394,10 @@ public class WxPayOrderQueryResult extends WxPayBaseResult { | |||||
public void composeCoupons() { | public void composeCoupons() { | ||||
if (this.couponCount != null && this.couponCount > 0) { | if (this.couponCount != null && this.couponCount > 0) { | ||||
this.coupons = Lists.newArrayList(); | this.coupons = Lists.newArrayList(); | ||||
XmlPath xmlPath = new XmlPath(this.getXmlString()); | |||||
for (int i = 0; i < this.couponCount; i++){ | |||||
this.coupons.add(new Coupon(this.getXmlValue(xmlPath, "xml.coupon_type_" + i, String.class), | |||||
this.getXmlValue(xmlPath, "xml.coupon_id_" + i, String.class), | |||||
this.getXmlValue(xmlPath, "xml.coupon_fee_" + i, Integer.class))); | |||||
for (int i = 0; i < this.couponCount; i++) { | |||||
this.coupons.add(new Coupon(this.getXmlValue("xml/coupon_type_" + i), | |||||
this.getXmlValue("xml/coupon_id_" + i), | |||||
this.getXmlValueAsInt("xml/coupon_fee_" + i))); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,35 @@ | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
/** | |||||
* <pre> | |||||
* 撤销订单响应结果类 | |||||
* Created by Binary Wang on 2017-3-23. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayOrderReverseResult extends WxPayBaseResult { | |||||
/** | |||||
* <pre> | |||||
* 是否重调 | |||||
* recall | |||||
* 是 | |||||
* String(1) | |||||
* Y | |||||
* 是否需要继续调用撤销,Y-需要,N-不需要 | |||||
* </pre> | |||||
**/ | |||||
@XStreamAlias("recall") | |||||
private String isRecall; | |||||
public String getIsRecall() { | |||||
return this.isRecall; | |||||
} | |||||
public void setIsRecall(String isRecall) { | |||||
this.isRecall = isRecall; | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
@@ -1,8 +1,7 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import io.restassured.path.xml.XmlPath; | |||||
import java.util.List; | import java.util.List; | ||||
@@ -186,26 +185,25 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||||
/** | /** | ||||
* 组装生成退款记录属性的内容 | * 组装生成退款记录属性的内容 | ||||
*/ | |||||
*/ | |||||
public void composeRefundRecords() { | public void composeRefundRecords() { | ||||
if (this.refundCount != null && this.refundCount > 0) { | if (this.refundCount != null && this.refundCount > 0) { | ||||
this.refundRecords = Lists.newArrayList(); | this.refundRecords = Lists.newArrayList(); | ||||
XmlPath xmlPath = new XmlPath(this.getXmlString()); | |||||
for (int i = 0; i < this.refundCount; i++) { | for (int i = 0; i < this.refundCount; i++) { | ||||
RefundRecord refundRecord = new RefundRecord(); | RefundRecord refundRecord = new RefundRecord(); | ||||
this.refundRecords.add(refundRecord); | this.refundRecords.add(refundRecord); | ||||
refundRecord.setOutRefundNo(this.getXmlValue(xmlPath, "xml.out_refund_no_" + i, String.class)); | |||||
refundRecord.setRefundId(this.getXmlValue(xmlPath, "xml.refund_id_" + i, String.class)); | |||||
refundRecord.setRefundChannel(this.getXmlValue(xmlPath, "xml.refund_channel_" + i, String.class)); | |||||
refundRecord.setRefundFee(this.getXmlValue(xmlPath, "xml.refund_fee_" + i, Integer.class)); | |||||
refundRecord.setSettlementRefundFee(this.getXmlValue(xmlPath, "xml.settlement_refund_fee_" + i, Integer.class)); | |||||
refundRecord.setCouponType(this.getXmlValue(xmlPath, "xml.coupon_type_" + i, String.class)); | |||||
refundRecord.setCouponRefundFee(this.getXmlValue(xmlPath, "xml.coupon_refund_fee_" + i, Integer.class)); | |||||
refundRecord.setCouponRefundCount(this.getXmlValue(xmlPath, "xml.coupon_refund_count_" + i, Integer.class)); | |||||
refundRecord.setRefundStatus(this.getXmlValue(xmlPath, "xml.refund_status_" + i, String.class)); | |||||
refundRecord.setRefundRecvAccout(this.getXmlValue(xmlPath, "xml.refund_recv_accout_" + i, String.class)); | |||||
refundRecord.setOutRefundNo(this.getXmlValue("xml/out_refund_no_" + i)); | |||||
refundRecord.setRefundId(this.getXmlValue("xml/refund_id_" + i)); | |||||
refundRecord.setRefundChannel(this.getXmlValue("xml/refund_channel_" + i)); | |||||
refundRecord.setRefundFee(this.getXmlValueAsInt("xml/refund_fee_" + i)); | |||||
refundRecord.setSettlementRefundFee(this.getXmlValueAsInt("xml/settlement_refund_fee_" + i)); | |||||
refundRecord.setCouponType(this.getXmlValue("xml/coupon_type_" + i)); | |||||
refundRecord.setCouponRefundFee(this.getXmlValueAsInt("xml/coupon_refund_fee_" + i)); | |||||
refundRecord.setCouponRefundCount(this.getXmlValueAsInt("xml/coupon_refund_count_" + i)); | |||||
refundRecord.setRefundStatus(this.getXmlValue("xml/refund_status_" + i)); | |||||
refundRecord.setRefundRecvAccout(this.getXmlValue("xml/refund_recv_accout_" + i)); | |||||
if (refundRecord.getCouponRefundCount() == null || refundRecord.getCouponRefundCount() == 0) { | if (refundRecord.getCouponRefundCount() == null || refundRecord.getCouponRefundCount() == 0) { | ||||
continue; | continue; | ||||
@@ -215,8 +213,8 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { | |||||
for (int j = 0; j < refundRecord.getCouponRefundCount(); j++) { | for (int j = 0; j < refundRecord.getCouponRefundCount(); j++) { | ||||
coupons.add( | coupons.add( | ||||
new RefundRecord.RefundCoupon( | new RefundRecord.RefundCoupon( | ||||
this.getXmlValue(xmlPath, "xml.coupon_refund_id_" + i + "_" + j, String.class), | |||||
this.getXmlValue(xmlPath, "xml.coupon_refund_fee_" + i + "_" + j, Integer.class) | |||||
this.getXmlValue("xml/coupon_refund_id_" + i + "_" + j), | |||||
this.getXmlValueAsInt("xml/coupon_refund_fee_" + i + "_" + j) | |||||
) | ) | ||||
); | ); | ||||
} | } |
@@ -1,19 +1,19 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
import java.io.Serializable; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import java.io.Serializable; | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 微信支付-申请退款返回结果 | * 微信支付-申请退款返回结果 | ||||
* https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 | * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 | ||||
* </pre> | * </pre> | ||||
* @author liukaitj | |||||
* | * | ||||
* @author liukaitj | |||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxPayRefundResult extends WxPayBaseResult implements Serializable{ | |||||
public class WxPayRefundResult extends WxPayBaseResult implements Serializable { | |||||
private static final long serialVersionUID = 1L; | private static final long serialVersionUID = 1L; | ||||
@XStreamAlias("device_info") | @XStreamAlias("device_info") |
@@ -1,14 +1,14 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
import java.io.Serializable; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
import java.io.Serializable; | |||||
/** | /** | ||||
* 向微信用户个人发现金红包返回结果 | * 向微信用户个人发现金红包返回结果 | ||||
* https://pay.weixin.qq.com/wiki/doc/api/cash_coupon.php?chapter=13_5 | * https://pay.weixin.qq.com/wiki/doc/api/cash_coupon.php?chapter=13_5 | ||||
* @author kane | |||||
* | * | ||||
* @author kane | |||||
*/ | */ | ||||
@XStreamAlias("xml") | @XStreamAlias("xml") | ||||
public class WxPaySendRedpackResult extends WxPayBaseResult implements Serializable { | public class WxPaySendRedpackResult extends WxPayBaseResult implements Serializable { |
@@ -0,0 +1,34 @@ | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
/** | |||||
* <pre> | |||||
* 转换短链接结果对象类 | |||||
* Created by Binary Wang on 2017-3-27. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("xml") | |||||
public class WxPayShorturlResult extends WxPayBaseResult { | |||||
/** | |||||
* <pre> | |||||
* URL链接 | |||||
* short_url | |||||
* 是 | |||||
* String(64) | |||||
* weixin://wxpay/s/XXXXXX | |||||
* 转换后的URL | |||||
* </pre> | |||||
*/ | |||||
@XStreamAlias("short_url") | |||||
private String shortUrl; | |||||
public String getShortUrl() { | |||||
return this.shortUrl; | |||||
} | |||||
public void setShortUrl(String shortUrl) { | |||||
this.shortUrl = shortUrl; | |||||
} | |||||
} |
@@ -1,11 +1,11 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | import com.thoughtworks.xstream.annotations.XStreamAlias; | ||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果 | * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果 | ||||
* 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | |||||
* 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @author chanjarster | * @author chanjarster |
@@ -0,0 +1,24 @@ | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import java.io.Serializable; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
public class WxScanPayNotifyResult extends WxPayBaseResult implements Serializable{ | |||||
private static final long serialVersionUID = 3381324564266118986L; | |||||
/** | |||||
* 预支付ID | |||||
*/ | |||||
@XStreamAlias("prepay_id") | |||||
private String prepayId; | |||||
public String getPrepayId() { | |||||
return prepayId; | |||||
} | |||||
public void setPrepayId(String prepayId) { | |||||
this.prepayId = prepayId; | |||||
} | |||||
} |
@@ -0,0 +1,157 @@ | |||||
package com.github.binarywang.wxpay.config; | |||||
import org.apache.http.ssl.SSLContexts; | |||||
import javax.net.ssl.SSLContext; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.security.KeyStore; | |||||
/** | |||||
* 微信支付配置 | |||||
* | |||||
* @author Binary Wang (https://github.com/binarywang) | |||||
*/ | |||||
public class WxPayConfig { | |||||
private String appId; | |||||
private String subAppId; | |||||
private String mchId; | |||||
private String mchKey; | |||||
private String subMchId; | |||||
private String notifyUrl; | |||||
private String tradeType; | |||||
private SSLContext sslContext; | |||||
private String keyPath; | |||||
public void setNotifyUrl(String notifyUrl) { | |||||
this.notifyUrl = notifyUrl; | |||||
} | |||||
public void setTradeType(String tradeType) { | |||||
this.tradeType = tradeType; | |||||
} | |||||
/** | |||||
* 设置证书 | |||||
* @param keyPath apiclient_cert.p12的文件的绝对路径 | |||||
*/ | |||||
public void setKeyPath(String keyPath) { | |||||
this.keyPath = keyPath; | |||||
} | |||||
public String getKeyPath() { | |||||
return keyPath; | |||||
} | |||||
public void setAppId(String appId) { | |||||
this.appId = appId; | |||||
} | |||||
public void setSubAppId(String subAppId) { | |||||
this.subAppId = subAppId; | |||||
} | |||||
public void setMchId(String mchId) { | |||||
this.mchId = mchId; | |||||
} | |||||
public void setMchKey(String mchKey) { | |||||
this.mchKey = mchKey; | |||||
} | |||||
public void setSubMchId(String subMchId) { | |||||
this.subMchId = subMchId; | |||||
} | |||||
public void setSslContext(SSLContext sslContext) { | |||||
this.sslContext = sslContext; | |||||
} | |||||
/** | |||||
* 商户号 | |||||
*/ | |||||
public String getMchId() { | |||||
return this.mchId; | |||||
} | |||||
/** | |||||
* 商户密钥 | |||||
*/ | |||||
public String getMchKey() { | |||||
return this.mchKey; | |||||
} | |||||
/** | |||||
* 公众号appid | |||||
*/ | |||||
public String getAppId() { | |||||
return this.appId; | |||||
} | |||||
/** | |||||
* 服务商模式下的子商户公众账号ID | |||||
*/ | |||||
public String getSubAppId() { | |||||
return this.subAppId; | |||||
} | |||||
/** | |||||
* 服务商模式下的子商户号 | |||||
*/ | |||||
public String getSubMchId() { | |||||
return this.subMchId; | |||||
} | |||||
/** | |||||
* 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。 | |||||
*/ | |||||
public String getNotifyUrl() { | |||||
return this.notifyUrl; | |||||
} | |||||
/** | |||||
* 交易类型 | |||||
* <pre> | |||||
* JSAPI--公众号支付 | |||||
* NATIVE--原生扫码支付 | |||||
* APP--app支付 | |||||
* </pre> | |||||
*/ | |||||
public String getTradeType() { | |||||
return this.tradeType; | |||||
} | |||||
public SSLContext getSslContext() { | |||||
return this.sslContext; | |||||
} | |||||
/** | |||||
* 微信支付是否使用仿真测试环境 | |||||
* 默认不使用 | |||||
*/ | |||||
public boolean useSandboxForWxPay() { | |||||
return false; | |||||
} | |||||
public SSLContext initSSLContext() { | |||||
if (null == mchId) { | |||||
throw new IllegalArgumentException("请确保商户号mch_id已设置"); | |||||
} | |||||
File file = new File(this.keyPath); | |||||
if (!file.exists()) { | |||||
throw new RuntimeException("证书文件:【" + file.getPath() + "】不存在!"); | |||||
} | |||||
try { | |||||
FileInputStream inputStream = new FileInputStream(file); | |||||
KeyStore keystore = KeyStore.getInstance("PKCS12"); | |||||
char[] partnerId2charArray = mchId.toCharArray(); | |||||
keystore.load(inputStream, partnerId2charArray); | |||||
this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build(); | |||||
return this.sslContext; | |||||
} catch (Exception e) { | |||||
throw new RuntimeException("证书文件有问题,请核实!", e); | |||||
} | |||||
} | |||||
} |
@@ -1,130 +1,129 @@ | |||||
package me.chanjar.weixin.mp.bean.pay; | |||||
import java.beans.PropertyDescriptor; | |||||
import java.lang.reflect.Field; | |||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import com.google.common.base.Function; | |||||
import com.google.common.collect.Maps; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import com.thoughtworks.xstream.converters.MarshallingContext; | |||||
import com.thoughtworks.xstream.converters.UnmarshallingContext; | |||||
import com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter; | |||||
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; | |||||
import com.thoughtworks.xstream.io.HierarchicalStreamReader; | |||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | |||||
import com.thoughtworks.xstream.mapper.Mapper; | |||||
import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderNotifyResult; | |||||
public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter { | |||||
public WxPayOrderNotifyResultConverter(Mapper mapper, ReflectionProvider reflectionProvider) { | |||||
super(mapper, reflectionProvider); | |||||
} | |||||
@Override | |||||
@SuppressWarnings("rawtypes") | |||||
public boolean canConvert(Class type) { | |||||
return type.equals(WxPayOrderNotifyResult.class); | |||||
} | |||||
@Override | |||||
public void marshal(Object original, HierarchicalStreamWriter writer, MarshallingContext context) { | |||||
super.marshal(original, writer, context); | |||||
WxPayOrderNotifyResult obj = (WxPayOrderNotifyResult) original; | |||||
List<WxPayOrderNotifyCoupon> list = obj.getCouponList(); | |||||
if (list == null || list.size() == 0) { | |||||
return; | |||||
} | |||||
for (int i = 0; i < list.size(); i++) { | |||||
WxPayOrderNotifyCoupon coupon = list.get(i); | |||||
writer.startNode("coupon_id_" + i); | |||||
writer.setValue(coupon.getCouponId()); | |||||
writer.endNode(); | |||||
writer.startNode("coupon_type_" + i); | |||||
writer.setValue(coupon.getCouponType()); | |||||
writer.endNode(); | |||||
writer.startNode("coupon_fee_" + i); | |||||
writer.setValue(coupon.getCouponFee() + ""); | |||||
writer.endNode(); | |||||
} | |||||
} | |||||
@Override | |||||
protected void marshallField(MarshallingContext context, Object newObj, Field field) { | |||||
if (field.getName().equals("couponList")) { | |||||
return; | |||||
} else { | |||||
super.marshallField(context, newObj, field); | |||||
} | |||||
} | |||||
@Override | |||||
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { | |||||
WxPayOrderNotifyResult obj = new WxPayOrderNotifyResult(); | |||||
List<Field> fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields())); | |||||
fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields())); | |||||
Map<String, Field> fieldMap = getFieldMap(fields); | |||||
List<WxPayOrderNotifyCoupon> coupons = new ArrayList<>(10); | |||||
while (reader.hasMoreChildren()) { | |||||
reader.moveDown(); | |||||
if (fieldMap.containsKey(reader.getNodeName())) { | |||||
Field field = fieldMap.get(reader.getNodeName()); | |||||
setFieldValue(context, obj, field); | |||||
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_id_")) { | |||||
String id = (String) context.convertAnother(obj, String.class); | |||||
getIndex(coupons, reader.getNodeName()).setCouponId(id); | |||||
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_type_")) { | |||||
String type = (String) context.convertAnother(obj, String.class); | |||||
getIndex(coupons, reader.getNodeName()).setCouponType(type); | |||||
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_fee_")) { | |||||
Integer fee = (Integer) context.convertAnother(obj, Integer.class); | |||||
getIndex(coupons, reader.getNodeName()).setCouponFee(fee); | |||||
} | |||||
reader.moveUp(); | |||||
} | |||||
obj.setCouponList(coupons); | |||||
return obj; | |||||
} | |||||
private void setFieldValue(UnmarshallingContext context, WxPayOrderNotifyResult obj, Field field) { | |||||
Object val = context.convertAnother(obj, field.getType()); | |||||
try { | |||||
if (val != null) { | |||||
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), obj.getClass()); | |||||
pd.getWriteMethod().invoke(obj, val); | |||||
} | |||||
} catch (Exception e) { | |||||
} | |||||
} | |||||
private Map<String, Field> getFieldMap(List<Field> fields) { | |||||
Map<String, Field> fieldMap = Maps.uniqueIndex(fields, new Function<Field, String>() { | |||||
@Override | |||||
public String apply(Field field) { | |||||
if (field.isAnnotationPresent(XStreamAlias.class)) { | |||||
return field.getAnnotation(XStreamAlias.class).value(); | |||||
} | |||||
return field.getName(); | |||||
} | |||||
}); | |||||
return fieldMap; | |||||
} | |||||
private WxPayOrderNotifyCoupon getIndex(List<WxPayOrderNotifyCoupon> coupons, String nodeName) { | |||||
Integer index = Integer.valueOf(StringUtils.substring(nodeName, nodeName.lastIndexOf("_") + 1)); | |||||
if (index >= coupons.size() || coupons.get(index) == null) { | |||||
coupons.add(index, new WxPayOrderNotifyCoupon()); | |||||
} | |||||
return coupons.get(index); | |||||
} | |||||
} | |||||
package com.github.binarywang.wxpay.converter; | |||||
import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon; | |||||
import com.github.binarywang.wxpay.bean.result.WxPayOrderNotifyResult; | |||||
import com.google.common.base.Function; | |||||
import com.google.common.collect.Maps; | |||||
import com.thoughtworks.xstream.annotations.XStreamAlias; | |||||
import com.thoughtworks.xstream.converters.MarshallingContext; | |||||
import com.thoughtworks.xstream.converters.UnmarshallingContext; | |||||
import com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter; | |||||
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; | |||||
import com.thoughtworks.xstream.io.HierarchicalStreamReader; | |||||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | |||||
import com.thoughtworks.xstream.mapper.Mapper; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.beans.PropertyDescriptor; | |||||
import java.lang.reflect.Field; | |||||
import java.util.ArrayList; | |||||
import java.util.Arrays; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter { | |||||
public WxPayOrderNotifyResultConverter(Mapper mapper, ReflectionProvider reflectionProvider) { | |||||
super(mapper, reflectionProvider); | |||||
} | |||||
@Override | |||||
@SuppressWarnings("rawtypes") | |||||
public boolean canConvert(Class type) { | |||||
return type.equals(WxPayOrderNotifyResult.class); | |||||
} | |||||
@Override | |||||
public void marshal(Object original, HierarchicalStreamWriter writer, MarshallingContext context) { | |||||
super.marshal(original, writer, context); | |||||
WxPayOrderNotifyResult obj = (WxPayOrderNotifyResult) original; | |||||
List<WxPayOrderNotifyCoupon> list = obj.getCouponList(); | |||||
if (list == null || list.size() == 0) { | |||||
return; | |||||
} | |||||
for (int i = 0; i < list.size(); i++) { | |||||
WxPayOrderNotifyCoupon coupon = list.get(i); | |||||
writer.startNode("coupon_id_" + i); | |||||
writer.setValue(coupon.getCouponId()); | |||||
writer.endNode(); | |||||
writer.startNode("coupon_type_" + i); | |||||
writer.setValue(coupon.getCouponType()); | |||||
writer.endNode(); | |||||
writer.startNode("coupon_fee_" + i); | |||||
writer.setValue(coupon.getCouponFee() + ""); | |||||
writer.endNode(); | |||||
} | |||||
} | |||||
@Override | |||||
protected void marshallField(MarshallingContext context, Object newObj, Field field) { | |||||
if (field.getName().equals("couponList")) { | |||||
return; | |||||
} else { | |||||
super.marshallField(context, newObj, field); | |||||
} | |||||
} | |||||
@Override | |||||
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { | |||||
WxPayOrderNotifyResult obj = new WxPayOrderNotifyResult(); | |||||
List<Field> fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields())); | |||||
fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields())); | |||||
Map<String, Field> fieldMap = getFieldMap(fields); | |||||
List<WxPayOrderNotifyCoupon> coupons = new ArrayList<>(10); | |||||
while (reader.hasMoreChildren()) { | |||||
reader.moveDown(); | |||||
if (fieldMap.containsKey(reader.getNodeName())) { | |||||
Field field = fieldMap.get(reader.getNodeName()); | |||||
setFieldValue(context, obj, field); | |||||
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_id_")) { | |||||
String id = (String) context.convertAnother(obj, String.class); | |||||
getIndex(coupons, reader.getNodeName()).setCouponId(id); | |||||
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_type_")) { | |||||
String type = (String) context.convertAnother(obj, String.class); | |||||
getIndex(coupons, reader.getNodeName()).setCouponType(type); | |||||
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_fee_")) { | |||||
Integer fee = (Integer) context.convertAnother(obj, Integer.class); | |||||
getIndex(coupons, reader.getNodeName()).setCouponFee(fee); | |||||
} | |||||
reader.moveUp(); | |||||
} | |||||
obj.setCouponList(coupons); | |||||
return obj; | |||||
} | |||||
private void setFieldValue(UnmarshallingContext context, WxPayOrderNotifyResult obj, Field field) { | |||||
Object val = context.convertAnother(obj, field.getType()); | |||||
try { | |||||
if (val != null) { | |||||
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), obj.getClass()); | |||||
pd.getWriteMethod().invoke(obj, val); | |||||
} | |||||
} catch (Exception e) { | |||||
} | |||||
} | |||||
private Map<String, Field> getFieldMap(List<Field> fields) { | |||||
Map<String, Field> fieldMap = Maps.uniqueIndex(fields, new Function<Field, String>() { | |||||
@Override | |||||
public String apply(Field field) { | |||||
if (field.isAnnotationPresent(XStreamAlias.class)) { | |||||
return field.getAnnotation(XStreamAlias.class).value(); | |||||
} | |||||
return field.getName(); | |||||
} | |||||
}); | |||||
return fieldMap; | |||||
} | |||||
private WxPayOrderNotifyCoupon getIndex(List<WxPayOrderNotifyCoupon> coupons, String nodeName) { | |||||
Integer index = Integer.valueOf(StringUtils.substring(nodeName, nodeName.lastIndexOf("_") + 1)); | |||||
if (index >= coupons.size() || coupons.get(index) == null) { | |||||
coupons.add(index, new WxPayOrderNotifyCoupon()); | |||||
} | |||||
return coupons.get(index); | |||||
} | |||||
} |
@@ -1,23 +1,26 @@ | |||||
package me.chanjar.weixin.mp.api; | |||||
package com.github.binarywang.wxpay.service; | |||||
import com.github.binarywang.wxpay.bean.request.*; | |||||
import com.github.binarywang.wxpay.bean.result.*; | |||||
import com.github.binarywang.wxpay.config.WxPayConfig; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | import me.chanjar.weixin.common.exception.WxErrorException; | ||||
import me.chanjar.weixin.mp.bean.pay.request.*; | |||||
import me.chanjar.weixin.mp.bean.pay.result.*; | |||||
import java.io.File; | import java.io.File; | ||||
import java.util.Map; | import java.util.Map; | ||||
/** | /** | ||||
* <pre> | |||||
* 微信支付相关接口 | * 微信支付相关接口 | ||||
* Created by Binary Wang on 2016/7/28. | * Created by Binary Wang on 2016/7/28. | ||||
* </pre> | |||||
* | * | ||||
* @author binarywang (https://github.com/binarywang) | * @author binarywang (https://github.com/binarywang) | ||||
*/ | */ | ||||
public interface WxMpPayService { | |||||
public interface WxPayService { | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) | |||||
* 查询订单(详见https://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) | |||||
* 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。 | * 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。 | ||||
* 需要调用查询接口的情况: | * 需要调用查询接口的情况: | ||||
* ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; | * ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; | ||||
@@ -28,7 +31,7 @@ public interface WxMpPayService { | |||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param transactionId 微信订单号 | * @param transactionId 微信订单号 | ||||
* @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 | |||||
* @param outTradeNo 商户系统内部的订单号,当没提供transactionId时需要传这个。 | |||||
*/ | */ | ||||
WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; | WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; | ||||
@@ -44,12 +47,12 @@ public interface WxMpPayService { | |||||
* 是否需要证书: 不需要。 | * 是否需要证书: 不需要。 | ||||
* </pre> | * </pre> | ||||
* | * | ||||
* @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 | |||||
* @param outTradeNo 商户系统内部的订单号 | |||||
*/ | */ | ||||
WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException; | WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException; | ||||
/** | /** | ||||
* 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | |||||
* 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) | |||||
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" | * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识" | ||||
* 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder | * 接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder | ||||
* | * | ||||
@@ -65,6 +68,16 @@ public interface WxMpPayService { | |||||
*/ | */ | ||||
Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; | Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; | ||||
/** | |||||
* 获取配置 | |||||
*/ | |||||
WxPayConfig getConfig(); | |||||
/** | |||||
* 设置配置对象 | |||||
*/ | |||||
void setConfig(WxPayConfig config); | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 微信支付-申请退款 | * 微信支付-申请退款 | ||||
@@ -81,7 +94,8 @@ public interface WxMpPayService { | |||||
* <pre> | * <pre> | ||||
* 微信支付-查询退款 | * 微信支付-查询退款 | ||||
* 应用场景: | * 应用场景: | ||||
* 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。 | |||||
* 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账, | |||||
* 银行卡支付的退款3个工作日后重新查询退款状态。 | |||||
* 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5 | * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5 | ||||
* 接口链接:https://api.mch.weixin.qq.com/pay/refundquery | * 接口链接:https://api.mch.weixin.qq.com/pay/refundquery | ||||
* </pre> | * </pre> | ||||
@@ -93,92 +107,15 @@ public interface WxMpPayService { | |||||
* @param refundId 微信退款单号 | * @param refundId 微信退款单号 | ||||
* @return 退款信息 | * @return 退款信息 | ||||
*/ | */ | ||||
WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException; | |||||
WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) | |||||
throws WxErrorException; | |||||
/** | /** | ||||
* 读取支付结果通知 | * 读取支付结果通知 | ||||
* 详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | |||||
* 详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7 | |||||
*/ | */ | ||||
WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException; | WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException; | ||||
/** | |||||
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||||
* | |||||
* @param xmlbean Bean需要标记有XML注解,默认使用配置中的PartnerKey进行签名 | |||||
* @return 签名字符串 | |||||
* @see #createSign(Map, String) | |||||
* @since 2.5.0 | |||||
*/ | |||||
String createSign(Object xmlbean); | |||||
/** | |||||
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||||
* | |||||
* @param xmlbean Bean需要标记有XML注解 | |||||
* @param signKey 签名Key | |||||
* @return 签名字符串 | |||||
* @see #createSign(Map, String) | |||||
*/ | |||||
String createSign(Object xmlbean, String signKey); | |||||
/** | |||||
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||||
* | |||||
* @param prams 参数信息,默认使用配置中的PartnerKey进行签名 | |||||
* @return 签名字符串 | |||||
* @see #createSign(Map, String) | |||||
*/ | |||||
String createSign(Map<String, String> prams); | |||||
/** | |||||
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||||
* | |||||
* @param prams 参数信息 | |||||
* @param signKey 签名Key | |||||
* @return 签名字符串 | |||||
*/ | |||||
String createSign(Map<String, String> prams, String signKey); | |||||
/** | |||||
* 校验签名是否正确,默认使用配置中的PartnerKey进行签名 | |||||
* | |||||
* @param xmlbean Bean需要标记有XML注解 | |||||
* @return true - 签名校验成功,false - 签名校验失败 | |||||
* @see #checkSign(Map, String) | |||||
*/ | |||||
boolean checkSign(Object xmlbean); | |||||
/** | |||||
* 校验签名是否正确 | |||||
* | |||||
* @param xmlbean Bean需要标记有XML注解 | |||||
* @param signKey 校验的签名Key | |||||
* @return true - 签名校验成功,false - 签名校验失败 | |||||
* @see #checkSign(Map, String) | |||||
*/ | |||||
boolean checkSign(Object xmlbean, String signKey); | |||||
/** | |||||
* 校验签名是否正确,默认使用配置中的PartnerKey进行签名 | |||||
* | |||||
* @param prams 需要校验的参数Map | |||||
* @return true - 签名校验成功,false - 签名校验失败 | |||||
* @see #checkSign(Map, String) | |||||
*/ | |||||
boolean checkSign(Map<String, String> prams); | |||||
/** | |||||
* 校验签名是否正确 | |||||
* | |||||
* @param params 需要校验的参数Map | |||||
* @param signKey 校验的签名Key | |||||
* @return true - 签名校验成功,false - 签名校验失败 | |||||
* @see #checkSign(Map, String) | |||||
*/ | |||||
boolean checkSign(Map<String, String> params, String signKey); | |||||
/** | /** | ||||
* 发送微信红包给个人用户 | * 发送微信红包给个人用户 | ||||
* <pre> | * <pre> | ||||
@@ -248,6 +185,20 @@ public interface WxMpPayService { | |||||
*/ | */ | ||||
byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength); | byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength); | ||||
/** | |||||
* <pre> | |||||
* 扫码支付模式一生成二维码的方法 | |||||
* 二维码中的内容为链接,形式为: | |||||
* weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX | |||||
* 其中XXXXX为商户需要填写的内容,商户将该链接生成二维码,如需要打印发布二维码,需要采用此格式。商户可调用第三方库生成二维码图片。 | |||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4 | |||||
* </pre> | |||||
* | |||||
* @param productId 产品Id | |||||
* @return 生成的二维码URL连接 | |||||
*/ | |||||
String createScanPayQrcodeMode1(String productId); | |||||
/** | /** | ||||
* <pre> | * <pre> | ||||
* 扫码支付模式二生成二维码的方法 | * 扫码支付模式二生成二维码的方法 | ||||
@@ -296,4 +247,81 @@ public interface WxMpPayService { | |||||
* @return 保存到本地的临时文件 | * @return 保存到本地的临时文件 | ||||
*/ | */ | ||||
File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException; | File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException; | ||||
/** | |||||
* <pre> | |||||
* 提交刷卡支付 | |||||
* 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1 | |||||
* 应用场景: | |||||
* 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。 | |||||
* 提醒1:提交支付请求后微信会同步返回支付结果。当返回结果为“系统错误”时,商户系统等待5秒后调用【查询订单API】,查询支付实际交易结果;当返回结果为“USERPAYING”时,商户系统可设置间隔时间(建议10秒)重新查询支付结果,直到支付成功或超时(建议30秒); | |||||
* 提醒2:在调用查询接口返回后,如果交易状况不明晰,请调用【撤销订单API】,此时如果交易失败则关闭订单,该单不能再支付成功;如果交易成功,则将扣款退回到用户账户。当撤销无返回或错误时,请再次调用。注意:请勿扣款后立即调用【撤销订单API】,建议至少15秒后再调用。撤销订单API需要双向证书。 | |||||
* 接口地址: https://api.mch.weixin.qq.com/pay/micropay | |||||
* 是否需要证书:不需要。 | |||||
* </pre> | |||||
*/ | |||||
WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 撤销订单API | |||||
* 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3 | |||||
* 应用场景: | |||||
* 支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭; | |||||
* 如果用户支付成功,微信支付系统会将此订单资金退还给用户。 | |||||
* 注意:7天以内的交易单可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。 | |||||
* 提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。 | |||||
* 调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。 | |||||
* 接口链接 :https://api.mch.weixin.qq.com/secapi/pay/reverse | |||||
* 是否需要证书:请求需要双向证书。 | |||||
* </pre> | |||||
*/ | |||||
WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 转换短链接 | |||||
* 文档地址: | |||||
* https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_9&index=8 | |||||
* 应用场景: | |||||
* 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),减小二维码数据量,提升扫描速度和精确度。 | |||||
* 接口地址:https://api.mch.weixin.qq.com/tools/shorturl | |||||
* 是否需要证书:否 | |||||
* </pre> | |||||
* @param request 请求对象 | |||||
*/ | |||||
String shorturl(WxPayShorturlRequest request) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 转换短链接 | |||||
* </pre> | |||||
* @see WxPayService#shorturl(WxPayShorturlRequest) | |||||
* @param longUrl 需要被压缩的网址 | |||||
*/ | |||||
String shorturl(String longUrl) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 授权码查询OPENID接口 | |||||
* 通过授权码查询公众号Openid,调用查询后,该授权码只能由此商户号发起扣款,直至授权码更新。 | |||||
* 文档地址: | |||||
* https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_13&index=9 | |||||
* 接口链接: | |||||
* https://api.mch.weixin.qq.com/tools/authcodetoopenid | |||||
* </pre> | |||||
* @param request 请求对象 | |||||
* @return openid | |||||
*/ | |||||
String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException; | |||||
/** | |||||
* <pre> | |||||
* 授权码查询OPENID接口 | |||||
* </pre> | |||||
* @see WxPayService#authcode2Openid(WxPayAuthcode2OpenidRequest) | |||||
* @param authCode 授权码 | |||||
* @return openid | |||||
*/ | |||||
String authcode2Openid(String authCode) throws WxErrorException; | |||||
} | } |
@@ -0,0 +1,416 @@ | |||||
package com.github.binarywang.wxpay.service.impl; | |||||
import com.github.binarywang.utils.qrcode.QrcodeUtils; | |||||
import com.github.binarywang.wxpay.bean.request.*; | |||||
import com.github.binarywang.wxpay.bean.result.*; | |||||
import com.github.binarywang.wxpay.config.WxPayConfig; | |||||
import com.github.binarywang.wxpay.service.WxPayService; | |||||
import com.github.binarywang.wxpay.util.SignUtils; | |||||
import com.google.common.collect.Maps; | |||||
import jodd.http.HttpRequest; | |||||
import jodd.http.HttpResponse; | |||||
import me.chanjar.weixin.common.bean.result.WxError; | |||||
import me.chanjar.weixin.common.exception.WxErrorException; | |||||
import org.apache.commons.lang3.CharEncoding; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.apache.http.Consts; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpPost; | |||||
import org.apache.http.conn.ssl.DefaultHostnameVerifier; | |||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||||
import org.apache.http.entity.StringEntity; | |||||
import org.apache.http.impl.client.CloseableHttpClient; | |||||
import org.apache.http.impl.client.HttpClients; | |||||
import org.apache.http.util.EntityUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import javax.net.ssl.SSLContext; | |||||
import java.io.File; | |||||
import java.io.UnsupportedEncodingException; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
/** | |||||
* Created by Binary Wang on 2016/7/28. | |||||
* | |||||
* @author binarywang (https://github.com/binarywang) | |||||
*/ | |||||
public class WxPayServiceImpl implements WxPayService { | |||||
private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com"; | |||||
private final Logger log = LoggerFactory.getLogger(this.getClass()); | |||||
private WxPayConfig config; | |||||
@Override | |||||
public WxPayConfig getConfig() { | |||||
return this.config; | |||||
} | |||||
@Override | |||||
public void setConfig(WxPayConfig config) { | |||||
this.config = config; | |||||
} | |||||
private String getPayBaseUrl() { | |||||
if (this.getConfig().useSandboxForWxPay()) { | |||||
return PAY_BASE_URL + "/sandboxnew"; | |||||
} | |||||
return PAY_BASE_URL; | |||||
} | |||||
@Override | |||||
public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/secapi/pay/refund"; | |||||
String responseContent = this.postWithKey(url, request.toXML()); | |||||
WxPayRefundResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundResult.class); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) | |||||
throws WxErrorException { | |||||
WxPayRefundQueryRequest request = new WxPayRefundQueryRequest(); | |||||
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||||
request.setTransactionId(StringUtils.trimToNull(transactionId)); | |||||
request.setOutRefundNo(StringUtils.trimToNull(outRefundNo)); | |||||
request.setRefundId(StringUtils.trimToNull(refundId)); | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/pay/refundquery"; | |||||
String responseContent = this.post(url, request.toXML()); | |||||
WxPayRefundQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundQueryResult.class); | |||||
result.composeRefundRecords(); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException { | |||||
try { | |||||
log.debug("微信支付回调参数详细:{}", xmlData); | |||||
WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData); | |||||
log.debug("微信支付回调结果对象:{}", result); | |||||
result.checkResult(this); | |||||
return result; | |||||
} catch (WxErrorException e) { | |||||
log.error(e.getMessage(), e); | |||||
throw e; | |||||
} catch (Exception e) { | |||||
log.error(e.getMessage(), e); | |||||
throw new WxErrorException(WxError.newBuilder().setErrorMsg("发生异常" + e.getMessage()).build()); | |||||
} | |||||
} | |||||
@Override | |||||
public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack"; | |||||
if (request.getAmtType() != null) { | |||||
//裂变红包 | |||||
url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack"; | |||||
} | |||||
String responseContent = this.postWithKey(url, request.toXML()); | |||||
WxPaySendRedpackResult result = WxPayBaseResult.fromXML(responseContent, WxPaySendRedpackResult.class); | |||||
//毋须校验,因为没有返回签名信息 | |||||
// this.checkResult(result); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException { | |||||
WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest(); | |||||
request.setMchBillNo(mchBillNo); | |||||
request.setBillType("MCHT"); | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo"; | |||||
String responseContent = this.postWithKey(url, request.toXML()); | |||||
WxPayRedpackQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRedpackQueryResult.class); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException { | |||||
WxPayOrderQueryRequest request = new WxPayOrderQueryRequest(); | |||||
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||||
request.setTransactionId(StringUtils.trimToNull(transactionId)); | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/pay/orderquery"; | |||||
String responseContent = this.post(url, request.toXML()); | |||||
if (StringUtils.isBlank(responseContent)) { | |||||
throw new WxErrorException(WxError.newBuilder().setErrorMsg("无响应结果").build()); | |||||
} | |||||
WxPayOrderQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderQueryResult.class); | |||||
result.composeCoupons(); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException { | |||||
if (StringUtils.isBlank(outTradeNo)) { | |||||
throw new IllegalArgumentException("out_trade_no不能为空"); | |||||
} | |||||
WxPayOrderCloseRequest request = new WxPayOrderCloseRequest(); | |||||
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo)); | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/pay/closeorder"; | |||||
String responseContent = this.post(url, request.toXML()); | |||||
WxPayOrderCloseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderCloseResult.class); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/pay/unifiedorder"; | |||||
String responseContent = this.post(url, request.toXML()); | |||||
WxPayUnifiedOrderResult result = WxPayBaseResult.fromXML(responseContent, WxPayUnifiedOrderResult.class); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException { | |||||
WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request); | |||||
String prepayId = unifiedOrderResult.getPrepayId(); | |||||
if (StringUtils.isBlank(prepayId)) { | |||||
throw new RuntimeException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。", | |||||
unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes())); | |||||
} | |||||
Map<String, String> payInfo = new HashMap<>(); | |||||
payInfo.put("appId", getConfig().getAppId()); | |||||
// 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。 | |||||
// 但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 | |||||
payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); | |||||
payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis())); | |||||
payInfo.put("package", "prepay_id=" + prepayId); | |||||
payInfo.put("signType", "MD5"); | |||||
if ("NATIVE".equals(request.getTradeType())) { | |||||
payInfo.put("codeUrl", unifiedOrderResult.getCodeURL()); | |||||
} | |||||
payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey())); | |||||
return payInfo; | |||||
} | |||||
@Override | |||||
public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers"; | |||||
String responseContent = this.postWithKey(url, request.toXML()); | |||||
WxEntPayResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayResult.class); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException { | |||||
WxEntPayQueryRequest request = new WxEntPayQueryRequest(); | |||||
request.setPartnerTradeNo(partnerTradeNo); | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo"; | |||||
String responseContent = this.postWithKey(url, request.toXML()); | |||||
WxEntPayQueryResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayQueryResult.class); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) { | |||||
String content = this.createScanPayQrcodeMode1(productId); | |||||
return this.createQrcode(content, logoFile, sideLength); | |||||
} | |||||
@Override | |||||
public String createScanPayQrcodeMode1(String productId) { | |||||
//weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX | |||||
StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?"); | |||||
Map<String, String> params = Maps.newHashMap(); | |||||
params.put("appid", this.getConfig().getAppId()); | |||||
params.put("mch_id", this.getConfig().getMchId()); | |||||
params.put("product_id", productId); | |||||
params.put("time_stamp", String.valueOf(System.currentTimeMillis() / 1000));//这里需要秒,10位数字 | |||||
params.put("nonce_str", String.valueOf(System.currentTimeMillis())); | |||||
String sign = SignUtils.createSign(params, this.getConfig().getMchKey()); | |||||
params.put("sign", sign); | |||||
for (String key : params.keySet()) { | |||||
codeUrl.append(key + "=" + params.get(key) + "&"); | |||||
} | |||||
String content = codeUrl.toString().substring(0, codeUrl.length() - 1); | |||||
log.debug("扫码支付模式一生成二维码的URL:{}", content); | |||||
return content; | |||||
} | |||||
@Override | |||||
public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) { | |||||
return this.createQrcode(codeUrl, logoFile, sideLength); | |||||
} | |||||
private byte[] createQrcode(String content, File logoFile, Integer sideLength) { | |||||
if (sideLength == null || sideLength < 1) { | |||||
return QrcodeUtils.createQrcode(content, logoFile); | |||||
} | |||||
return QrcodeUtils.createQrcode(content, sideLength, logoFile); | |||||
} | |||||
public void report(WxPayReportRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/payitil/report"; | |||||
String responseContent = this.post(url, request.toXML()); | |||||
WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); | |||||
result.checkResult(this); | |||||
} | |||||
@Override | |||||
public File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException { | |||||
WxPayDownloadBillRequest request = new WxPayDownloadBillRequest(); | |||||
request.setBillType(billType); | |||||
request.setBillDate(billDate); | |||||
request.setTarType(tarType); | |||||
request.setDeviceInfo(deviceInfo); | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/pay/downloadbill"; | |||||
//TODO 返回的内容可能是文件流,也有可能是xml,需要区分对待 | |||||
String responseContent = this.post(url, request.toXML()); | |||||
WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class); | |||||
result.checkResult(this); | |||||
//TODO 待实现,暂时无测试帐号,无法调试 | |||||
return null; | |||||
} | |||||
@Override | |||||
public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/pay/micropay"; | |||||
String responseContent = this.post(url, request.toXML()); | |||||
WxPayMicropayResult result = WxPayBaseResult.fromXML(responseContent, WxPayMicropayResult.class); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/secapi/pay/reverse"; | |||||
String responseContent = this.postWithKey(url, request.toXML()); | |||||
WxPayOrderReverseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderReverseResult.class); | |||||
result.checkResult(this); | |||||
return result; | |||||
} | |||||
@Override | |||||
public String shorturl(WxPayShorturlRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/tools/shorturl"; | |||||
String responseContent = this.post(url, request.toXML()); | |||||
WxPayShorturlResult result = WxPayBaseResult.fromXML(responseContent, WxPayShorturlResult.class); | |||||
result.checkResult(this); | |||||
return result.getShortUrl(); | |||||
} | |||||
@Override | |||||
public String shorturl(String longUrl) throws WxErrorException { | |||||
return this.shorturl(new WxPayShorturlRequest(longUrl)); | |||||
} | |||||
@Override | |||||
public String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException { | |||||
request.checkAndSign(this.getConfig()); | |||||
String url = this.getPayBaseUrl() + "/tools/authcodetoopenid"; | |||||
String responseContent = this.post(url, request.toXML()); | |||||
WxPayAuthcode2OpenidResult result = WxPayBaseResult.fromXML(responseContent, WxPayAuthcode2OpenidResult.class); | |||||
result.checkResult(this); | |||||
return result.getOpenid(); | |||||
} | |||||
@Override | |||||
public String authcode2Openid(String authCode) throws WxErrorException { | |||||
return this.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode)); | |||||
} | |||||
private String post(String url, String xmlParam) { | |||||
String requestString = xmlParam; | |||||
try { | |||||
requestString = new String(xmlParam.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1); | |||||
} catch (UnsupportedEncodingException e) { | |||||
//实际上不会发生该异常 | |||||
e.printStackTrace(); | |||||
} | |||||
HttpRequest request = HttpRequest.post(url).body(requestString); | |||||
HttpResponse response = request.send(); | |||||
String responseString = null; | |||||
try { | |||||
responseString = new String(response.bodyText().getBytes(CharEncoding.ISO_8859_1), CharEncoding.UTF_8); | |||||
} catch (UnsupportedEncodingException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, xmlParam, responseString); | |||||
return responseString; | |||||
} | |||||
/** | |||||
* 由于暂时未找到使用jodd-http实现证书配置的办法,故而暂时使用httpclient | |||||
*/ | |||||
private String postWithKey(String url, String requestStr) throws WxErrorException { | |||||
try { | |||||
SSLContext sslContext = this.getConfig().getSslContext(); | |||||
if (null == sslContext) { | |||||
sslContext = this.getConfig().initSSLContext(); | |||||
} | |||||
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, | |||||
new String[]{"TLSv1"}, null, new DefaultHostnameVerifier()); | |||||
HttpPost httpPost = new HttpPost(url); | |||||
try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) { | |||||
httpPost.setEntity(new StringEntity(new String(requestStr.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1))); | |||||
try (CloseableHttpResponse response = httpclient.execute(httpPost)) { | |||||
String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8); | |||||
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result); | |||||
return result; | |||||
} | |||||
} finally { | |||||
httpPost.releaseConnection(); | |||||
} | |||||
} catch (Exception e) { | |||||
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage()); | |||||
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,84 @@ | |||||
package com.github.binarywang.wxpay.util; | |||||
import me.chanjar.weixin.common.util.BeanUtils; | |||||
import org.apache.commons.codec.digest.DigestUtils; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import java.util.Map; | |||||
import java.util.SortedMap; | |||||
import java.util.TreeMap; | |||||
/** | |||||
* <pre> | |||||
* 签名相关工具类 | |||||
* Created by Binary Wang on 2017-3-23. | |||||
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a> | |||||
* </pre> | |||||
*/ | |||||
public class SignUtils { | |||||
/** | |||||
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||||
* | |||||
* @param xmlBean Bean需要标记有XML注解 | |||||
* @param signKey 签名Key | |||||
* @return 签名字符串 | |||||
*/ | |||||
public static String createSign(Object xmlBean, String signKey) { | |||||
return createSign(BeanUtils.xmlBean2Map(xmlBean), signKey); | |||||
} | |||||
/** | |||||
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3) | |||||
* | |||||
* @param params 参数信息 | |||||
* @param signKey 签名Key | |||||
* @return 签名字符串 | |||||
*/ | |||||
public static String createSign(Map<String, String> params, String signKey) { | |||||
// if (this.getConfig().useSandboxForWxPay()) { | |||||
// //使用仿真测试环境 | |||||
// //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用仿真测试环境 | |||||
// signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; | |||||
// //return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; | |||||
// } | |||||
SortedMap<String, String> sortedMap = new TreeMap<>(params); | |||||
StringBuilder toSign = new StringBuilder(); | |||||
for (String key : sortedMap.keySet()) { | |||||
String value = params.get(key); | |||||
if (StringUtils.isNotEmpty(value) && !"sign".equals(key) && !"key".equals(key)) { | |||||
toSign.append(key + "=" + value + "&"); | |||||
} | |||||
} | |||||
toSign.append("key=" + signKey); | |||||
return DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | |||||
} | |||||
/** | |||||
* 校验签名是否正确 | |||||
* | |||||
* @param xmlBean Bean需要标记有XML注解 | |||||
* @param signKey 校验的签名Key | |||||
* @return true - 签名校验成功,false - 签名校验失败 | |||||
* @see #checkSign(Map, String) | |||||
*/ | |||||
public static boolean checkSign(Object xmlBean, String signKey) { | |||||
return checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey); | |||||
} | |||||
/** | |||||
* 校验签名是否正确 | |||||
* | |||||
* @param params 需要校验的参数Map | |||||
* @param signKey 校验的签名Key | |||||
* @return true - 签名校验成功,false - 签名校验失败 | |||||
* @see #checkSign(Map, String) | |||||
*/ | |||||
public static boolean checkSign(Map<String, String> params, String signKey) { | |||||
String sign = createSign(params, signKey); | |||||
return sign.equals(params.get("sign")); | |||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
package me.chanjar.weixin.mp.bean.pay.result; | |||||
package com.github.binarywang.wxpay.bean.result; | |||||
import org.testng.*; | import org.testng.*; | ||||
import org.testng.annotations.*; | import org.testng.annotations.*; | ||||
@@ -12,6 +12,15 @@ import java.util.Map; | |||||
* </pre> | * </pre> | ||||
*/ | */ | ||||
public class WxPayBaseResultTest { | public class WxPayBaseResultTest { | ||||
@Test | |||||
public void testGetXmlValue() throws Exception { | |||||
} | |||||
@Test | |||||
public void testXml2Doc() throws Exception { | |||||
} | |||||
@Test | @Test | ||||
public void testToMap() throws Exception { | public void testToMap() throws Exception { | ||||
WxPayOrderQueryResult result = new WxPayOrderQueryResult(); | WxPayOrderQueryResult result = new WxPayOrderQueryResult(); | ||||
@@ -39,11 +48,17 @@ public class WxPayBaseResultTest { | |||||
Map<String, String> map = result.toMap(); | Map<String, String> map = result.toMap(); | ||||
System.out.println(map); | System.out.println(map); | ||||
Assert.assertEquals("SUCCESS", map.get("return_code")); | |||||
Assert.assertEquals("订单额外描述", map.get("attach")); | |||||
Assert.assertEquals(map.get("return_code"), "SUCCESS"); | |||||
Assert.assertEquals(map.get("attach"), "订单额外描述"); | |||||
} | |||||
result.setXmlString(""); | |||||
System.out.println(result.toMap()); | |||||
@Test(expectedExceptions = {RuntimeException.class}) | |||||
public void testToMap_with_empty_xmlString() { | |||||
WxPayOrderQueryResult result = new WxPayOrderQueryResult(); | |||||
result.setXmlString(" "); | |||||
Map<String, String> map = result.toMap(); | |||||
System.out.println(map); | |||||
} | } | ||||
} | } |