diff --git a/build.gradle b/build.gradle index e5231e24..23ec2c52 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ allprojects { apply plugin: 'maven' group = 'com.github.binarywang' - version = '2.5.0' + version = '2.6.0' } subprojects { @@ -27,7 +27,6 @@ subprojects { compile group: 'commons-codec', name: 'commons-codec', version:'1.10' compile group: 'commons-io', name: 'commons-io', version:'2.5' 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' testCompile group: 'ch.qos.logback', name: 'logback-classic', version:'1.1.2' } diff --git a/CONTRIBUTION.md b/contribution.md similarity index 100% rename from CONTRIBUTION.md rename to contribution.md diff --git a/pom.xml b/pom.xml index 57ae6b92..2447fd71 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.github.binarywang weixin-java-parent - 2.5.0 + 2.6.0 pom WeiXin Java Tools - Parent 微信公众号、企业号上级POM @@ -86,7 +86,8 @@ weixin-java-common weixin-java-cp weixin-java-mp - weixin-java-osgi + weixin-java-pay + @@ -105,6 +106,7 @@ 2.5 1.10 9.3.0.RC0 + 2.9.0 @@ -113,6 +115,11 @@ slf4j-api ${slf4j.version} + + com.thoughtworks.xstream + xstream + 1.4.9 + ch.qos.logback logback-classic @@ -152,17 +159,12 @@ com.google.guava guava + ${guava.version} - - junit - junit - 4.11 - test - com.google.inject guice @@ -194,9 +196,9 @@ test - com.google.guava - guava - ${guava.version} + redis.clients + jedis + ${jedis.version} diff --git a/readme.md b/readme.md index e533f7da..5f10da8a 100644 --- a/readme.md +++ b/readme.md @@ -6,14 +6,15 @@ ### 注意事项: 1. 声明: ***本项目Fork自chanjarster/weixin-java-tools,但由于原项目已停止维护,故单独维护和发布,且发布到maven上的groupId也会不同,详细信息见下文。*** 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. 自2.0.0版本以来,公众号的接口调整比较大,主要是为了解决主接口类过于庞大不方便管理的问题,将接口实现代码按模块进行拆分。 +1. 自2.6.0版本开始,微信支付相关功能抽出独立为一个模块,详细使用方式请参考相关demo; 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. 本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. 如果想贡献代码,请阅读[【代码贡献指南】](CONTRIBUTION.md); +1. 如果想贡献代码,请阅读[【代码贡献指南】](contribution.md); 1. 捐助渠道已开通,如有意向请前往托管于码云的项目首页(具体地址见下文)的页面评论区上方,可以找到“捐助”按钮,非常感谢各位捐助的同学! --------------------------------- @@ -58,12 +59,12 @@ maven: com.github.binarywang weixin-java-pay - 2.5.7.BETA + 2.6.0 ``` gradle: ```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: com.github.binarywang weixin-java-mp - 2.5.0 + 2.6.0 ``` gradle: ```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: com.github.binarywang weixin-java-cp - 2.5.0 + 2.6.0 ``` gradle: ```groovy -compile 'com.github.binarywang:weixin-java-cp:2.5.0' +compile 'com.github.binarywang:weixin-java-cp:2.6.0' ``` diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml index 12611c12..946c65c2 100644 --- a/weixin-java-common/pom.xml +++ b/weixin-java-common/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.5.0 + 2.6.0 weixin-java-common @@ -14,16 +14,6 @@ 微信公众号、企业号Java SDK Common - - com.thoughtworks.xstream - xstream - 1.4.7 - - - junit - junit - test - org.testng testng diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java index 3553c5f6..435ba8e0 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java @@ -55,15 +55,15 @@ public class WxConsts { /////////////////////// public static final String MASS_ST_SUCCESS = "send success"; 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_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话 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_THUMB = "thumb"; 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"; - - - /////////////////////// - // 自定义菜单的按钮类型 - /////////////////////// /** * 跳转URL */ public static final String BUTTON_VIEW = "view"; + /** + * 跳转到小程序 + */ + public static final String BUTTON_MINIPROGRAM = "miniprogram"; /** * 扫码推事件 */ @@ -187,6 +185,7 @@ public class WxConsts { * 跳转图文消息URL */ public static final String BUTTON_VIEW_LIMITED = "view_limited"; + /** * 不弹出授权页面,直接跳转,只能获取用户openid */ @@ -216,14 +215,14 @@ public class WxConsts { static { MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); 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, "涉嫌其他"); } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java index 48979f7c..aa6ea4cf 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java @@ -1,5 +1,6 @@ package me.chanjar.weixin.common.bean.menu; +import com.google.gson.annotations.SerializedName; import me.chanjar.weixin.common.util.ToStringUtils; import java.io.Serializable; @@ -9,12 +10,65 @@ import java.util.List; public class WxMenuButton implements Serializable { private static final long serialVersionUID = -1070939403109776555L; + /** + *
+   * 菜单的响应动作类型:
+   * view表示网页类型,
+   * click表示点击类型,
+   * miniprogram表示小程序类型
+   * 
+ */ private String type; + + /** + * 菜单标题,不超过16个字节,子菜单不超过60个字节 + */ private String name; + + /** + *
+   * 菜单KEY值,用于消息接口推送,不超过128字节
+   * click等点击类型必须
+   * 
+ */ private String key; + + /** + *
+   * 网页链接,用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。
+   * view、miniprogram类型必须
+   * 
+ */ private String url; + + /** + *
+   * 调用新增永久素材接口返回的合法media_id
+   * media_id类型和view_limited类型必须
+   * 
+ */ + @SerializedName("media_id") private String mediaId; + /** + *
+   * 小程序的appid
+   * miniprogram类型必须
+   * 
+ */ + @SerializedName("appid") + private String appId; + + /** + *
+   * 小程序的页面路径
+   * miniprogram类型必须
+   * 
+ */ + @SerializedName("pagepath") + private String pagePath; + + @SerializedName("sub_button") private List subButtons = new ArrayList<>(); @Override @@ -69,4 +123,20 @@ public class WxMenuButton implements Serializable { public void setMediaId(String 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; + } } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java index ec30c6b0..6166da9e 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java @@ -9,13 +9,13 @@ package me.chanjar.weixin.common.util.json; -import java.util.List; - import com.google.common.collect.Lists; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import java.util.List; + public class GsonHelper { @@ -130,6 +130,20 @@ public class GsonHelper { 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 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) { return element == null ? null : element.getAsJsonArray(); } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java index 578aa41c..526b5f29 100644 --- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java @@ -46,6 +46,8 @@ public class WxMenuGsonAdapter implements JsonSerializer, JsonDeserializ buttonJson.addProperty("key", button.getKey()); buttonJson.addProperty("url", button.getUrl()); buttonJson.addProperty("media_id", button.getMediaId()); + buttonJson.addProperty("appid", button.getAppId()); + buttonJson.addProperty("pagepath", button.getPagePath()); if (button.getSubButtons() != null && button.getSubButtons().size() > 0) { JsonArray buttonArray = new JsonArray(); for (WxMenuButton sub_button : button.getSubButtons()) { @@ -114,6 +116,8 @@ public class WxMenuGsonAdapter implements JsonSerializer, JsonDeserializ button.setUrl(GsonHelper.getString(json, "url")); button.setType(GsonHelper.getString(json, "type")); button.setMediaId(GsonHelper.getString(json, "media_id")); + button.setAppId(GsonHelper.getString(json, "appid")); + button.setPagePath(GsonHelper.getString(json, "pagepath")); return button; } diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java b/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java new file mode 100644 index 00000000..7904da20 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java @@ -0,0 +1,139 @@ +package me.chanjar.weixin.cp.bean.article; + +/** + *
+ *  Created by BinaryWang on 2017/3/27.
+ * 
+ * @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); + } + } +} diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java b/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java new file mode 100644 index 00000000..d4c056d5 --- /dev/null +++ b/weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java @@ -0,0 +1,49 @@ +package me.chanjar.weixin.cp.bean.article; + +/** + *
+ *  Created by BinaryWang on 2017/3/27.
+ * 
+ * + * @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; + } + +} diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml index 6478ada2..88f83f0f 100644 --- a/weixin-java-cp/pom.xml +++ b/weixin-java-cp/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.5.0 + 2.6.0 weixin-java-cp @@ -20,9 +20,8 @@ ${project.version}
- junit - junit - test + redis.clients + jedis org.testng @@ -49,12 +48,6 @@ jetty-servlet test - - redis.clients - jedis - 2.9.0 - true -
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java index b5bf50c9..bd0b322f 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java @@ -28,9 +28,9 @@ public interface WxCpService { * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性 * * - * @param msgSignature - * @param timestamp - * @param nonce + * @param msgSignature 消息签名 + * @param timestamp 时间戳 + * @param nonce 随机数 * @param data 微信传输过来的数据,有可能是echoStr,有可能是xml消息 */ boolean checkSignature(String msgSignature, String timestamp, String nonce, String data); @@ -41,14 +41,13 @@ public interface WxCpService { * 企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。 * * - * @param userId + * @param userId 用户id */ void userAuthenticated(String userId) throws WxErrorException; /** * 获取access_token, 不强制刷新access_token * - * @throws WxErrorException * @see #getAccessToken(boolean) */ String getAccessToken() throws WxErrorException; @@ -63,14 +62,12 @@ public interface WxCpService { * * * @param forceRefresh 强制刷新 - * @throws me.chanjar.weixin.common.exception.WxErrorException */ String getAccessToken(boolean forceRefresh) throws WxErrorException; /** * 获得jsapi_ticket,不强制刷新jsapi_ticket * - * @throws WxErrorException * @see #getJsapiTicket(boolean) */ String getJsapiTicket() throws WxErrorException; @@ -84,7 +81,6 @@ public interface WxCpService { * * * @param 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 fileType 文件类型,请看{@link me.chanjar.weixin.common.api.WxConsts} * @param inputStream 输入流 - * @throws WxErrorException */ 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) */ WxMediaUploadResult mediaUpload(String mediaType, File file) throws WxErrorException; @@ -133,20 +127,18 @@ public interface WxCpService { * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=上传下载多媒体文件 * * - * @param media_id + * @param mediaId 媒体id * @return 保存到本地的临时文件 - * @throws WxErrorException */ - File mediaDownload(String media_id) throws WxErrorException; + File mediaDownload(String mediaId) throws WxErrorException; /** *
    * 发送消息
-   * 详情请见: 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
    * 
* - * @param message - * @throws WxErrorException + * @param message 要发送的消息对象 */ void messageSend(WxCpMessage message) throws WxErrorException; @@ -158,8 +150,7 @@ public interface WxCpService { * 注意: 这个方法使用WxCpConfigStorage里的agentId * * - * @param menu - * @throws WxErrorException + * @param menu 菜单对象 * @see #menuCreate(Integer, WxMenu) */ void menuCreate(WxMenu menu) throws WxErrorException; @@ -173,8 +164,7 @@ public interface WxCpService { * * * @param agentId 企业号应用的id - * @param menu - * @throws WxErrorException + * @param menu 菜单对象 * @see #menuCreate(me.chanjar.weixin.common.bean.menu.WxMenu) */ void menuCreate(Integer agentId, WxMenu menu) throws WxErrorException; @@ -187,7 +177,6 @@ public interface WxCpService { * 注意: 这个方法使用WxCpConfigStorage里的agentId * * - * @throws WxErrorException * @see #menuDelete(Integer) */ void menuDelete() throws WxErrorException; @@ -201,7 +190,6 @@ public interface WxCpService { * * * @param agentId 企业号应用的id - * @throws WxErrorException * @see #menuDelete() */ void menuDelete(Integer agentId) throws WxErrorException; @@ -214,7 +202,6 @@ public interface WxCpService { * 注意: 这个方法使用WxCpConfigStorage里的agentId * * - * @throws WxErrorException * @see #menuGet(Integer) */ WxMenu menuGet() throws WxErrorException; @@ -228,7 +215,6 @@ public interface WxCpService { * * * @param agentId 企业号应用的id - * @throws WxErrorException * @see #menuGet() */ WxMenu menuGet(Integer agentId) throws WxErrorException; @@ -242,7 +228,6 @@ public interface WxCpService { * * @param depart 部门 * @return 部门id - * @throws WxErrorException */ Integer departCreate(WxCpDepart depart) throws WxErrorException; @@ -251,8 +236,6 @@ public interface WxCpService { * 部门管理接口 - 查询所有部门 * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=部门管理接口 * - * - * @throws WxErrorException */ List departGet() throws WxErrorException; @@ -264,7 +247,6 @@ public interface WxCpService { * * * @param group 要更新的group,group的id,name必须设置 - * @throws WxErrorException */ void departUpdate(WxCpDepart group) throws WxErrorException; @@ -273,8 +255,7 @@ public interface WxCpService { * 部门管理接口 - 删除部门 * * - * @param departId - * @throws WxErrorException + * @param departId 部门id */ void departDelete(Integer departId) throws WxErrorException; @@ -288,7 +269,6 @@ public interface WxCpService { * @param departId 必填。部门id * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 - * @throws WxErrorException */ List userList(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; @@ -302,73 +282,66 @@ public interface WxCpService { * @param departId 必填。部门id * @param fetchChild 非必填。1/0:是否递归获取子部门下面的成员 * @param status 非必填。0获取全部员工,1获取已关注成员列表,2获取禁用成员列表,4获取未关注成员列表。status可叠加 - * @throws WxErrorException */ List departGetUsers(Integer departId, Boolean fetchChild, Integer status) throws WxErrorException; /** * 新建用户 * - * @param user - * @throws WxErrorException + * @param user 用户对象 */ void userCreate(WxCpUser user) throws WxErrorException; /** * 更新用户 * - * @param user - * @throws WxErrorException + * @param user 用户对象 */ void userUpdate(WxCpUser user) throws WxErrorException; /** * 删除用户 * - * @param userid - * @throws WxErrorException + * @param userid 用户id */ void userDelete(String userid) throws WxErrorException; /** *
    * 批量删除成员
-   *
    * 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
    * 
* * @param userids 员工UserID列表。对应管理端的帐号 - * @throws WxErrorException */ void userDelete(String[] userids) throws WxErrorException; /** * 获取用户 * - * @param userid - * @throws WxErrorException + * @param userid 用户id */ WxCpUser userGet(String userid) throws WxErrorException; /** * 创建标签 * - * @param tagName + * @param tagName 标签名 */ String tagCreate(String tagName) throws WxErrorException; /** * 更新标签 * - * @param tagId - * @param tagName + * @param tagId 标签id + * @param tagName 标签名 */ void tagUpdate(String tagId, String tagName) throws WxErrorException; /** * 删除标签 * - * @param tagId + * @param tagId 标签id */ void tagDelete(String tagId) throws WxErrorException; @@ -380,24 +353,24 @@ public interface WxCpService { /** * 获取标签成员 * - * @param tagId + * @param tagId 标签ID */ List tagGetUsers(String tagId) throws WxErrorException; /** * 增加标签成员 * - * @param tagId - * @param userIds + * @param tagId 标签id + * @param userIds 用户ID 列表 */ void tagAddUsers(String tagId, List userIds, List partyIds) throws WxErrorException; /** - *
+   * 
    * 构造oauth2授权的url连接
    * 
* - * @param state + * @param state 状态码 * @return url */ String oauth2buildAuthorizationUrl(String state); @@ -408,8 +381,8 @@ public interface WxCpService { * 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=企业获取code *
* - * @param redirectUri - * @param state + * @param redirectUri 跳转链接地址 + * @param state 状态码 * @return url */ String oauth2buildAuthorizationUrl(String redirectUri, String state); @@ -423,7 +396,7 @@ public interface WxCpService { * 注意: 这个方法使用WxCpConfigStorage里的agentId * * - * @param code + * @param code 微信oauth授权返回的代码 * @return [userid, deviceid] * @see #oauth2getUserInfo(Integer, String) */ @@ -439,7 +412,7 @@ public interface WxCpService { * * * @param agentId 企业号应用的id - * @param code + * @param code 微信oauth授权返回的代码 * @return [userid, deviceid] * @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 userIds) throws WxErrorException; @@ -463,7 +436,6 @@ public interface WxCpService { * @param userId 用户的userid * @param inviteTips 推送到微信上的提示语(只有认证号可以使用)。当使用微信推送时,该字段默认为“请关注XXX企业号”,邮件邀请时,该字段无效。 * @return 1:微信邀请 2.邮件邀请 - * @throws WxErrorException */ int invite(String userId, String inviteTips) throws WxErrorException; @@ -474,25 +446,22 @@ public interface WxCpService { * * * @return { "ip_list": ["101.226.103.*", "101.226.62.*"] } - * @throws WxErrorException */ String[] getCallbackIp() throws WxErrorException; /** * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的GET请求 * - * @param url - * @param queryParam - * @throws WxErrorException + * @param url 接口地址 + * @param queryParam 请求参数 */ String get(String url, String queryParam) throws WxErrorException; /** * 当本Service没有实现某个API的时候,可以用这个,针对所有微信API中的POST请求 * - * @param url - * @param postData - * @throws WxErrorException + * @param url 接口地址 + * @param postData 请求body字符串 */ String post(String url, String postData) throws WxErrorException; @@ -503,19 +472,18 @@ public interface WxCpService { * 可以参考,{@link me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor}的实现方法 * * - * @param executor - * @param uri - * @param data - * @param - * @param - * @throws WxErrorException + * @param executor 执行器 + * @param uri 请求地址 + * @param data 参数 + * @param 请求值类型 + * @param 返回值类型 */ T execute(RequestExecutor executor, String uri, E data) throws WxErrorException; /** * 注入 {@link WxCpConfigStorage} 的实现 * - * @param wxConfigProvider + * @param wxConfigProvider 配置对象 */ void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider); @@ -525,7 +493,7 @@ public interface WxCpService { * 默认:1000ms * * - * @param retrySleepMillis + * @param retrySleepMillis 重试休息时间 */ void setRetrySleepMillis(int retrySleepMillis); @@ -535,7 +503,7 @@ public interface WxCpService { * 默认:5次 * * - * @param maxRetryTimes + * @param maxRetryTimes 最大重试次数 */ void setMaxRetryTimes(int maxRetryTimes); @@ -550,7 +518,7 @@ public interface WxCpService { * 获取某个sessionId对应的session,如果sessionId没有对应的session,若create为true则新建一个,否则返回null。 * * @param id id可以为任意字符串,建议使用FromUserName作为id - * @param create + * @param create 是否新建 */ WxSession getSession(String id, boolean create); @@ -560,31 +528,26 @@ public interface WxCpService { * WxCpService默认使用的是{@link me.chanjar.weixin.common.session.StandardSessionManager} * * - * @param sessionManager + * @param sessionManager 会话管理器 */ void setSessionManager(WxSessionManager sessionManager); /** * 上传部门列表覆盖企业号上的部门信息 * - * @param mediaId - * @throws WxErrorException + * @param mediaId 媒体id */ String replaceParty(String mediaId) throws WxErrorException; /** * 上传用户列表覆盖企业号上的用户信息 * - * @param mediaId - * @throws WxErrorException + * @param mediaId 媒体id */ String replaceUser(String mediaId) throws WxErrorException; /** * 获取异步任务结果 - * - * @param joinId - * @throws WxErrorException */ String getTaskResult(String joinId) throws WxErrorException; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java index a10eb42e..0105e78d 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java @@ -238,12 +238,12 @@ public class WxCpServiceImpl implements WxCpService { } @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"; return execute( new MediaDownloadRequestExecutor( this.configStorage.getTmpDirFile()), - url, "media_id=" + media_id); + url, "media_id=" + mediaId); } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java index c25c170b..324df209 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java @@ -1,5 +1,7 @@ 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.util.json.WxCpGsonBuilder; @@ -28,7 +30,16 @@ public class WxCpMessage implements Serializable { private String musicUrl; private String hqMusicUrl; private String safe; - private List articles = new ArrayList<>(); + private List articles = new ArrayList<>(); + private List mpnewsArticles = new ArrayList<>(); + + public List getMpnewsArticles() { + return mpnewsArticles; + } + + public void setMpnewsArticles(List mpnewsArticles) { + this.mpnewsArticles = mpnewsArticles; + } /** * 获得文本消息builder @@ -65,6 +76,13 @@ public class WxCpMessage implements Serializable { return new NewsBuilder(); } + /** + * 获得mpnews图文消息builder + */ + public static MpnewsBuilder MPNEWS() { + return new MpnewsBuilder(); + } + /** * 获得文件消息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_VIDEO} * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_NEWS} + * {@link me.chanjar.weixin.common.api.WxConsts#CUSTOM_MSG_MPNEWS} * * - * @param msgType + * @param msgType 消息类型 */ public void setMsgType(String msgType) { this.msgType = msgType; @@ -189,11 +208,11 @@ public class WxCpMessage implements Serializable { this.hqMusicUrl = hqMusicUrl; } - public List getArticles() { + public List getArticles() { return this.articles; } - public void setArticles(List articles) { + public void setArticles(List articles) { this.articles = articles; } @@ -201,45 +220,4 @@ public class WxCpMessage implements Serializable { 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; - } - - } - } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java new file mode 100644 index 00000000..21ab2549 --- /dev/null +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java @@ -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 + *
+ * 用法:
+ * WxCustomMessage m = WxCustomMessage.MPNEWS().addArticle(article).toUser(...).build();
+ * 
+ * + * @author Binary Wang + */ +public final class MpnewsBuilder extends BaseBuilder { + private List 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; + } +} diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java index 8ea90872..e0df095e 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java @@ -2,6 +2,7 @@ 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.NewArticle; import java.util.ArrayList; import java.util.List; @@ -17,13 +18,13 @@ import java.util.List; */ public final class NewsBuilder extends BaseBuilder { - private List articles = new ArrayList<>(); + private List articles = new ArrayList<>(); public NewsBuilder() { this.msgType = WxConsts.CUSTOM_MSG_NEWS; } - public NewsBuilder addArticle(WxCpMessage.WxArticle article) { + public NewsBuilder addArticle(NewArticle article) { this.articles.add(article); return this; } diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java index 7bf09c71..949709ab 100644 --- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java +++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java @@ -11,6 +11,8 @@ package me.chanjar.weixin.cp.util.json; import com.google.gson.*; import me.chanjar.weixin.common.api.WxConsts; 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 java.lang.reflect.Type; @@ -71,7 +73,7 @@ public class WxCpMessageGsonAdapter implements JsonSerializer { if (WxConsts.CUSTOM_MSG_NEWS.equals(message.getMsgType())) { JsonObject newsJsonObject = new JsonObject(); JsonArray articleJsonArray = new JsonArray(); - for (WxCpMessage.WxArticle article : message.getArticles()) { + for (NewArticle article : message.getArticles()) { JsonObject articleJson = new JsonObject(); articleJson.addProperty("title", article.getTitle()); articleJson.addProperty("description", article.getDescription()); @@ -83,6 +85,29 @@ public class WxCpMessageGsonAdapter implements JsonSerializer { 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; } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/TestConstants.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/TestConstants.java new file mode 100644 index 00000000..03c84e92 --- /dev/null +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/TestConstants.java @@ -0,0 +1,17 @@ +package me.chanjar.weixin.cp.api; + +/** + *
+ * 仅供测试使用的一些常量
+ * Created by Binary Wang on 2017-3-9.
+ * 
+ */ +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"; +} diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java index fe379226..d9fb2605 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java @@ -54,10 +54,10 @@ public class WxCpMediaAPITest { @DataProvider public Object[][] uploadMedia() { 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"} }; } diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java index 12b62e34..ce75b066 100644 --- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java +++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java @@ -1,10 +1,13 @@ package me.chanjar.weixin.cp.bean; 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.annotations.Test; +import static org.testng.Assert.assertEquals; + @Test public class WxCpMessageTest { @@ -13,12 +16,12 @@ public class WxCpMessageTest { reply.setToUser("OPENID"); reply.setMsgType(WxConsts.CUSTOM_MSG_TEXT); 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() { 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() { @@ -26,12 +29,12 @@ public class WxCpMessageTest { reply.setToUser("OPENID"); reply.setMsgType(WxConsts.CUSTOM_MSG_IMAGE); 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() { 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() { @@ -39,12 +42,12 @@ public class WxCpMessageTest { reply.setToUser("OPENID"); reply.setMsgType(WxConsts.CUSTOM_MSG_VOICE); 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() { 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() { @@ -55,12 +58,12 @@ public class WxCpMessageTest { reply.setThumbMediaId("MEDIA_ID"); reply.setTitle("TITLE"); 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() { 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() { @@ -68,14 +71,14 @@ public class WxCpMessageTest { reply.setToUser("OPENID"); reply.setMsgType(WxConsts.CUSTOM_MSG_NEWS); - WxArticle article1 = new WxArticle(); + NewArticle article1 = new NewArticle(); article1.setUrl("URL"); article1.setPicUrl("PIC_URL"); article1.setDescription("Is Really A Happy Day"); article1.setTitle("Happy Day"); reply.getArticles().add(article1); - WxArticle article2 = new WxArticle(); + NewArticle article2 = new NewArticle(); article2.setUrl("URL"); article2.setPicUrl("PIC_URL"); article2.setDescription("Is Really A Happy Day"); @@ -83,17 +86,17 @@ public class WxCpMessageTest { 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() { - WxArticle article1 = new WxArticle(); + NewArticle article1 = new NewArticle(); article1.setUrl("URL"); article1.setPicUrl("PIC_URL"); article1.setDescription("Is Really A Happy Day"); article1.setTitle("Happy Day"); - WxArticle article2 = new WxArticle(); + NewArticle article2 = new NewArticle(); article2.setUrl("URL"); article2.setPicUrl("PIC_URL"); 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(); - 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\"}}"); } } diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml index 7070d018..3f4bf649 100644 --- a/weixin-java-mp/pom.xml +++ b/weixin-java-mp/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.5.0 + 2.6.0 weixin-java-mp WeiXin Java Tools - MP @@ -18,21 +18,11 @@ weixin-java-common ${project.version} - - junit - junit - test - org.testng testng test - - org.mockito - mockito-all - test - com.google.inject guice @@ -55,14 +45,8 @@ test - io.rest-assured - xml-path - 3.0.1 - - - com.github.binarywang - qrcode-utils - 1.0 + redis.clients + jedis diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java index 79ae9318..f4053899 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java @@ -3,7 +3,6 @@ package me.chanjar.weixin.mp.api; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder; -import javax.net.ssl.SSLContext; import java.io.File; import java.util.concurrent.locks.Lock; @@ -82,27 +81,6 @@ public interface WxMpConfigStorage { String getSecret(); - String getPartnerId(); - - String getPartnerKey(); - - /** - * 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。 - * - * @since 2.5.0 - */ - String getNotifyURL(); - - /** - * 交易类型 - *
-   * JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
-   * 
- * - * @since 2.5.0 - */ - String getTradeType(); - String getToken(); String getAesKey(); @@ -121,17 +99,6 @@ public interface WxMpConfigStorage { File getTmpDirFile(); - SSLContext getSslContext(); - - void setSslContext(SSLContext sslContext); - - /** - * 在此之前,必须将partnerId进行赋值 - * - * @param filePath apiclient_cert.p12的文件的绝对路径 - */ - void setSslContextFilePath(String filePath) throws Exception; - /** * http client builder * @@ -144,8 +111,4 @@ public interface WxMpConfigStorage { */ boolean autoRefreshToken(); - /** - * 微信支付是否使用仿真测试环境 - */ - boolean useSandboxForWxPay(); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java index a565011c..2580d23a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java @@ -76,5 +76,24 @@ public interface WxMpDeviceService { */ WxDeviceBindResult compelUnbind(WxDeviceBind wxDeviceBind) throws WxErrorException; + /** + *
+   *   通过device type和device id 获取设备主人的openid
+   *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-11
+   * 
+ * @param deviceType 设备类型,目前为"公众账号原始ID" + * @param deviceId 设备ID + * @return WxDeviceOpenIdResult + */ + WxDeviceOpenIdResult getOpenId(String deviceType,String deviceId) throws WxErrorException; + /** + *
+   *   通过openid获取用户在当前devicetype下绑定的deviceid列表`
+   *   详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-12
+   * 
+ * @param openId 要查询的用户的openid + * @return WxDeviceBindDeviceResult + */ + WxDeviceBindDeviceResult getBindDevice(String openId) throws WxErrorException; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java index f2292684..4bd1daec 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java @@ -3,12 +3,8 @@ package me.chanjar.weixin.mp.api; import me.chanjar.weixin.common.bean.WxAccessToken; import me.chanjar.weixin.common.util.ToStringUtils; 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.FileInputStream; -import java.security.KeyStore; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -21,10 +17,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { protected volatile String appId; 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 accessToken; protected volatile String aesKey; @@ -52,8 +44,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { */ protected volatile File tmpDirFile; - protected volatile SSLContext sslContext; - protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder; @Override @@ -61,10 +51,6 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { return this.accessToken; } - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - @Override public Lock getAccessTokenLock() { return this.accessTokenLock; @@ -96,23 +82,11 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { return this.jsapiTicket; } - public void setJsapiTicket(String jsapiTicket) { - this.jsapiTicket = jsapiTicket; - } - @Override public Lock getJsapiTicketLock() { return this.jsapiTicketLock; } - public long getJsapiTicketExpiresTime() { - return this.jsapiTicketExpiresTime; - } - - public void setJsapiTicketExpiresTime(long jsapiTicketExpiresTime) { - this.jsapiTicketExpiresTime = jsapiTicketExpiresTime; - } - @Override public boolean isJsapiTicketExpired() { return System.currentTimeMillis() > this.jsapiTicketExpiresTime; @@ -256,88 +230,49 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { } @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 - 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 @@ -345,8 +280,4 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage { return true; } - @Override - public boolean useSandboxForWxPay() { - return false; - } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java new file mode 100644 index 00000000..b2bb9b4e --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java @@ -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; + } +} \ No newline at end of file diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java index ddfd02a5..ba8665e9 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java @@ -6,8 +6,10 @@ import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket; import java.io.File; /** + *
  * 二维码相关操作接口
  * 文档地址:https://mp.weixin.qq.com/wiki?action=doc&id=mp1443433542&t=0.9274944716856435
+ * 
* @author Binary Wang */ public interface WxMpQrcodeService { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java index dc05a2ee..cf1401a5 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java @@ -311,13 +311,6 @@ public interface WxMpService { */ WxMpCardService getCardService(); - /** - * 返回微信支付相关接口方法的实现类对象,以方便调用其各个接口 - * - * @return WxMpPayService - */ - WxMpPayService getPayService(); - /** * 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口 * diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java index e7dbea1b..5c942365 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java @@ -23,7 +23,7 @@ public class WxMpDeviceServiceImpl implements WxMpDeviceService { @Override public TransMsgResp transMsg(WxDeviceMsg msg) throws WxErrorException { 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); } @@ -37,21 +37,21 @@ public class WxMpDeviceServiceImpl implements WxMpDeviceService { @Override public WxDeviceAuthorizeResult authorize(WxDeviceAuthorize wxDeviceAuthorize) throws WxErrorException { 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); } @Override public WxDeviceBindResult bind(WxDeviceBind wxDeviceBind) throws WxErrorException { 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); } @Override public WxDeviceBindResult compelBind(WxDeviceBind wxDeviceBind) throws WxErrorException { 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); } @@ -68,5 +68,19 @@ public class WxMpDeviceServiceImpl implements WxMpDeviceService { String response = this.wxMpService.post(url, wxDeviceBind.toJson()); 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); + } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java deleted file mode 100644 index b0ccacd9..00000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java +++ /dev/null @@ -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 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 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 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 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 params) { - return this.createSign(params, this.getConfig().getPartnerKey()); - } - - @Override - public String createSign(Map params, String signKey) { - if(this.getConfig().useSandboxForWxPay()){ - //使用仿真测试环境 - //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用这个仿真测试环境 - signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; - //return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; - } - - SortedMap 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 params) { - return this.checkSign(params, getConfig().getPartnerKey()); - } - - @Override - public boolean checkSign(Map params, String signKey) { - String sign = this.createSign(params, signKey); - return sign.equals(params.get("sign")); - } -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java index 26d86baa..8659f2af 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java @@ -43,9 +43,8 @@ public class WxMpQrcodeServiceImpl implements WxMpQrcodeService { String url = API_URL_PREFIX + "/create"; JsonObject json = new JsonObject(); json.addProperty("action_name", "QR_SCENE"); - if (expireSeconds != null) { - json.addProperty("expire_seconds", expireSeconds); - } + json.addProperty("expire_seconds", expireSeconds); + JsonObject actionInfo = new JsonObject(); JsonObject scene = new JsonObject(); scene.addProperty("scene_id", sceneId); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java index eb1e58aa..406e8712 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java @@ -28,9 +28,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; 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 { private static final JsonParser JSON_PARSER = new JsonParser(); @@ -45,7 +42,6 @@ public class WxMpServiceImpl implements WxMpService { private WxMpUserTagService tagService = new WxMpUserTagServiceImpl(this); private WxMpQrcodeService qrCodeService = new WxMpQrcodeServiceImpl(this); private WxMpCardService cardService = new WxMpCardServiceImpl(this); - private WxMpPayService payService = new WxMpPayServiceImpl(this); private WxMpStoreService storeService = new WxMpStoreServiceImpl(this); private WxMpDataCubeService dataCubeService = new WxMpDataCubeServiceImpl(this); private WxMpUserBlacklistService blackListService = new WxMpUserBlacklistServiceImpl(this); @@ -59,7 +55,7 @@ public class WxMpServiceImpl implements WxMpService { @Override public boolean checkSignature(String timestamp, String nonce, String signature) { try { - return SHA1.gen(this.wxMpConfigStorage.getToken(), timestamp, nonce) + return SHA1.gen(this.getWxMpConfigStorage().getToken(), timestamp, nonce) .equals(signature); } catch (Exception e) { return false; @@ -73,18 +69,18 @@ public class WxMpServiceImpl implements WxMpService { @Override public String getAccessToken(boolean forceRefresh) throws WxErrorException { - Lock lock = this.wxMpConfigStorage.getAccessTokenLock(); + Lock lock = this.getWxMpConfigStorage().getAccessTokenLock(); try { lock.lock(); 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" + - "&appid=" + this.wxMpConfigStorage.getAppId() + "&secret=" - + this.wxMpConfigStorage.getSecret(); + "&appid=" + this.getWxMpConfigStorage().getAppId() + "&secret=" + + this.getWxMpConfigStorage().getSecret(); try { HttpGet httpGet = new HttpGet(url); if (this.httpProxy != null) { @@ -98,7 +94,7 @@ public class WxMpServiceImpl implements WxMpService { throw new WxErrorException(error); } WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); - this.wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), + this.getWxMpConfigStorage().updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); } finally { httpGet.releaseConnection(); @@ -110,7 +106,7 @@ public class WxMpServiceImpl implements WxMpService { } finally { lock.unlock(); } - return this.wxMpConfigStorage.getAccessToken(); + return this.getWxMpConfigStorage().getAccessToken(); } @Override @@ -120,27 +116,27 @@ public class WxMpServiceImpl implements WxMpService { @Override public String getJsapiTicket(boolean forceRefresh) throws WxErrorException { - Lock lock = this.wxMpConfigStorage.getJsapiTicketLock(); + Lock lock = this.getWxMpConfigStorage().getJsapiTicketLock(); try { lock.lock(); 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 responseContent = execute(new SimpleGetRequestExecutor(), url, null); JsonElement tmpJsonElement = JSON_PARSER.parse(responseContent); JsonObject tmpJsonObject = tmpJsonElement.getAsJsonObject(); String jsapiTicket = tmpJsonObject.get("ticket").getAsString(); int expiresInSeconds = tmpJsonObject.get("expires_in").getAsInt(); - this.wxMpConfigStorage.updateJsapiTicket(jsapiTicket, expiresInSeconds); + this.getWxMpConfigStorage().updateJsapiTicket(jsapiTicket, expiresInSeconds); } } finally { lock.unlock(); } - return this.wxMpConfigStorage.getJsapiTicket(); + return this.getWxMpConfigStorage().getJsapiTicket(); } @Override @@ -151,7 +147,7 @@ public class WxMpServiceImpl implements WxMpService { String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket, "noncestr=" + noncestr, "timestamp=" + timestamp, "url=" + url); WxJsapiSignature jsapiSignature = new WxJsapiSignature(); - jsapiSignature.setAppId(this.wxMpConfigStorage.getAppId()); + jsapiSignature.setAppId(this.getWxMpConfigStorage().getAppId()); jsapiSignature.setTimestamp(timestamp); jsapiSignature.setNonceStr(noncestr); jsapiSignature.setUrl(url); @@ -216,7 +212,7 @@ public class WxMpServiceImpl implements WxMpService { public String oauth2buildAuthorizationUrl(String redirectURI, String scope, String state) { StringBuilder url = new StringBuilder(); 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("&response_type=code"); url.append("&scope=").append(scope); @@ -232,7 +228,7 @@ public class WxMpServiceImpl implements WxMpService { String state) { StringBuilder url = new StringBuilder(); 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("&response_type=code"); url.append("&scope=").append(scope); @@ -258,8 +254,8 @@ public class WxMpServiceImpl implements WxMpService { public WxMpOAuth2AccessToken oauth2getAccessToken(String code) throws WxErrorException { StringBuilder url = new StringBuilder(); 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("&grant_type=authorization_code"); @@ -270,7 +266,7 @@ public class WxMpServiceImpl implements WxMpService { public WxMpOAuth2AccessToken oauth2refreshAccessToken(String refreshToken) throws WxErrorException { StringBuilder url = new StringBuilder(); 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("&refresh_token=").append(refreshToken); @@ -397,8 +393,8 @@ public class WxMpServiceImpl implements WxMpService { */ if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { // 强制设置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); } } @@ -424,26 +420,19 @@ public class WxMpServiceImpl implements WxMpService { } private void initHttpClient() { - ApacheHttpClientBuilder apacheHttpClientBuilder = this.wxMpConfigStorage - .getApacheHttpClientBuilder(); + WxMpConfigStorage configStorage = this.getWxMpConfigStorage(); + ApacheHttpClientBuilder apacheHttpClientBuilder = configStorage.getApacheHttpClientBuilder(); if (null == apacheHttpClientBuilder) { 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(); @@ -505,11 +494,6 @@ public class WxMpServiceImpl implements WxMpService { return this.cardService; } - @Override - public WxMpPayService getPayService() { - return this.payService; - } - @Override public WxMpDataCubeService getDataCubeService() { return this.dataCubeService; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java index b49ffc38..d70ac570 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java @@ -9,7 +9,8 @@ import java.io.Serializable; */ public class WxMpMassPreviewMessage implements Serializable { private static final long serialVersionUID = 9095211638358424020L; - private String toWxUsername; + private String toWxUserName; + private String toWxUserOpenid; private String msgType; private String content; private String mediaId; @@ -18,12 +19,12 @@ public class WxMpMassPreviewMessage implements Serializable { 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() { @@ -41,7 +42,7 @@ public class WxMpMassPreviewMessage implements Serializable { * 如果msgtype和media_id不匹配的话,会返回系统繁忙的错误 * * - * @param msgType + * @param msgType 消息类型 */ public void setMsgType(String msgType) { this.msgType = msgType; @@ -63,6 +64,14 @@ public class WxMpMassPreviewMessage implements Serializable { this.mediaId = mediaId; } + public String getToWxUserOpenid() { + return this.toWxUserOpenid; + } + + public void setToWxUserOpenid(String toWxUserOpenid) { + this.toWxUserOpenid = toWxUserOpenid; + } + public String toJson() { return WxMpGsonBuilder.INSTANCE.create().toJson(this); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java index 41629c55..b0dd6ec3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java @@ -38,23 +38,25 @@ public class BaseResp extends AbstractDeviceBean{ } 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; } } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java new file mode 100644 index 00000000..d0ed2dcd --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java @@ -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 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; + } + } + +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java new file mode 100644 index 00000000..0a23eddd --- /dev/null +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java @@ -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 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 getOpenIds() { + return openIds; + } + + public void setOpenIds(List openIds) { + this.openIds = openIds; + } + + public RespMsg getRespMsg() { + return respMsg; + } + + public void setRespMsg(RespMsg respMsg) { + this.respMsg = respMsg; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java index 9c89ff67..ff3028be 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java @@ -6,15 +6,15 @@ import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder; /** * Created by keungtung on 10/12/2016. */ -public class WxDeviceQrCodeResult extends AbstractDeviceBean{ +public class WxDeviceQrCodeResult extends AbstractDeviceBean { @SerializedName("deviceid") private String deviceId; @SerializedName("qrticket") private String qrTicket; @SerializedName("devicelicence") private String deviceLicence; - @SerializedName("resp_msg") - private RespMsg respMsg; + @SerializedName("base_resp") + private BaseResp baseResp; public static WxDeviceQrCodeResult fromJson(String json) { return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxDeviceQrCodeResult.class); @@ -28,12 +28,12 @@ public class WxDeviceQrCodeResult extends AbstractDeviceBean{ 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() { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResult.java deleted file mode 100644 index 4dd133ab..00000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResult.java +++ /dev/null @@ -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; - -/** - *
- * 微信支付结果共用属性类
- * Created by Binary Wang on 2016-10-24.
- * @author binarywang(Binary Wang)
- * 
- */ -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 fromXML(String xmlString, Class 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 toMap() { - Map 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 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 getXmlValue(XmlPath xmlPath, String path, Class 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("暂时不支持此种类型的数据"); - } -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderNotifyResult.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderNotifyResult.java deleted file mode 100644 index 1cf783a8..00000000 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderNotifyResult.java +++ /dev/null @@ -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; - - - /** - *
-	 * 设备号
-	 * device_info
-	 * 否
-	 * String(32)
-	 * 013467007045764
-	 * 微信支付分配的终端设备号,
-	 * 
- */ - @XStreamAlias("device_info") - private String deviceInfo; - - /** - *
-	 * 用户标识
-	 * openid
-	 * 是
-	 * String(128)
-	 * wxd930ea5d5a258f4f
-	 * 用户在商户appid下的唯一标识
-	 * 
- */ - @XStreamAlias("openid") - private String openid; - - /** - *
-	 * 是否关注公众账号
-	 * is_subscribe
-	 * 否
-	 * String(1)
-	 * Y
-	 * 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
-	 * 
- */ - @XStreamAlias("is_subscribe") - private String isSubscribe; - - - /** - *
-	 * 交易类型
-	 * trade_type
-	 * 是
-	 * String(16)
-	 * JSAPI	JSAPI、NATIVE、APP
-	 * 
- */ - @XStreamAlias("trade_type") - private String tradeType; - - - /** - *
-	 * 付款银行
-	 * bank_type
-	 * 是
-	 * String(16)
-	 * CMC
-	 * 银行类型,采用字符串类型的银行标识,银行类型见银行列表
-	 * 
- */ - @XStreamAlias("bank_type") - private String bankType; - - - /** - *
-	 * 订单金额
-	 * total_fee
-	 * 是
-	 * Int
-	 * 100
-	 * 订单总金额,单位为分
-	 * 
- */ - @XStreamAlias("total_fee") - private Integer totalFee; - /** - *
-	 * 应结订单金额
-	 * settlement_total_fee
-	 * 否
-	 * Int
-	 * 100
-	 * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
-	 * 
- */ - @XStreamAlias("settlement_total_fee") - private Integer settlementTotalFee; - /** - *
-	 * 货币种类
-	 * fee_type
-	 * 否
-	 * String(8)
-	 * CNY
-	 * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
-	 * 
- */ - @XStreamAlias("fee_type") - private String feeType; - /** - *
-	 * 现金支付金额
-	 * cash_fee
-	 * 是
-	 * Int
-	 * 100
-	 * 现金支付金额订单现金支付金额,详见支付金额
-	 * 
- */ - @XStreamAlias("cash_fee") - private Integer cashFee; - /** - *
-	 * 现金支付货币类型
-	 * cash_fee_type
-	 * 否
-	 * String(16)
-	 * CNY
-	 * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
-	 * 
- */ - @XStreamAlias("cash_fee_type") - private String cashFeeType; - /** - *
-	 * 总代金券金额
-	 * coupon_fee
-	 * 否
-	 * Int
-	 * 10
-	 * 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
-	 * 
- */ - @XStreamAlias("coupon_fee") - private Integer couponFee; - - /** - *
-   * 代金券使用数量
-   * coupon_count
-   * 否
-   * Int
-   * 1
-   * 代金券使用数量
-   * 
- */ - @XStreamAlias("coupon_count") - private Integer couponCount; - - private List couponList; - - /** - *
-	 * 微信支付订单号
-	 * transaction_id
-	 * 是
-	 * String(32)
-	 * 1217752501201407033233368018
-	 * 微信支付订单号
-	 * 
- */ - @XStreamAlias("transaction_id") - private String transactionId; - - /** - *
-	 * 商户订单号
-	 * out_trade_no
-	 * 是
-	 * String(32)
-	 * 1212321211201407033568112322
-	 * 商户系统的订单号,与请求一致。
-	 * 
- */ - @XStreamAlias("out_trade_no") - private String outTradeNo; - /** - *
-	 * 商家数据包
-	 * attach
-	 * 否
-	 * String(128)
-	 * 123456
-	 * 商家数据包,原样返回
-	 * 
- */ - @XStreamAlias("attach") - private String attach; - /** - *
-	 * 支付完成时间
-	 * time_end
-	 * 是
-	 * String(14)
-	 * 20141030133525
-	 * 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
-	 * 
- */ - @XStreamAlias("time_end") - private String timeEnd; - - public Integer getCouponCount() { - return couponCount; - } - - public void setCouponCount(Integer couponCount) { - this.couponCount = couponCount; - } - - public List getCouponList() { - return couponList; - } - - public void setCouponList(List 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 toMap(){ - Map 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; - } -} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java index 8d7090fd..7131de5c 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java @@ -32,7 +32,7 @@ public class WxMpUser implements Serializable { private Integer sexId; private String remark; private Integer groupId; - private Integer[] tagIds; + private Long[] tagIds; public Boolean getSubscribe() { return this.subscribe; @@ -150,11 +150,11 @@ public class WxMpUser implements Serializable { this.groupId = groupId; } - public Integer[] getTagIds() { + public Long[] getTagIds() { return this.tagIds; } - public void setTagIds(Integer[] tagIds) { + public void setTagIds(Long[] tagIds) { this.tagIds = tagIds; } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java index 94b51f6c..87ce18cd 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java @@ -12,14 +12,14 @@ import java.util.List; */ public class WxMpUserList { - protected int total = -1; + protected long total = -1; protected int count = -1; - protected List openIds = new ArrayList<>(); - protected String nextOpenId; - public int getTotal() { + protected List openids = new ArrayList<>(); + protected String nextOpenid; + public long getTotal() { return this.total; } - public void setTotal(int total) { + public void setTotal(long total) { this.total = total; } public int getCount() { @@ -28,19 +28,19 @@ public class WxMpUserList { public void setCount(int count) { this.count = count; } - public List getOpenIds() { - return this.openIds; + public List getOpenids() { + return this.openids; } - public void setOpenIds(List openIds) { - this.openIds = openIds; + public void setOpenids(List 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) { return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUserList.class); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java index df8e708d..f70090ef 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java @@ -12,11 +12,38 @@ import java.util.List; public class WxMpTemplateMessage implements Serializable { private static final long serialVersionUID = 5063374783759519418L; + /** + * 接收者openid + */ private String toUser; + + /** + * 模板ID + */ private String templateId; + + /** + *
+   * 跳小程序所需数据,不需跳小程序可不用传该数据
+   * url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。
+   * 开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。
+   * 
+ */ private String url; + /** + * 模板跳转链接 + * @see #url + */ + private MiniProgram miniProgram; + + /** + * 模板数据 + */ private List data = new ArrayList<>(); + public WxMpTemplateMessage() { + } + public String getToUser() { return this.toUser; } @@ -53,6 +80,14 @@ public class WxMpTemplateMessage implements Serializable { this.data.add(datum); } + public MiniProgram getMiniProgram() { + return this.miniProgram; + } + + public void setMiniProgram(MiniProgram miniProgram) { + this.miniProgram = miniProgram; + } + public String toJson() { return WxMpGsonBuilder.INSTANCE.create().toJson(this); } @@ -61,11 +96,41 @@ public class WxMpTemplateMessage implements Serializable { 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 { private String toUser; private String templateId; private String url; private List data = new ArrayList<>(); + private MiniProgram miniProgram; public WxMpTemplateMessageBuilder toUser(String toUser) { this.toUser = toUser; @@ -95,12 +160,18 @@ public class WxMpTemplateMessage implements Serializable { return this; } + public WxMpTemplateMessageBuilder miniProgram(MiniProgram miniProgram) { + this.miniProgram = miniProgram; + return this; + } + public WxMpTemplateMessage build() { WxMpTemplateMessage m = new WxMpTemplateMessage(); m.toUser = this.toUser; m.templateId = this.templateId; m.url = this.url; m.data = this.data; + m.miniProgram = this.miniProgram; return m; } } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java index fd3e52df..fd210fb3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java @@ -16,7 +16,8 @@ public class WxMpMassPreviewMessageGsonAdapter implements JsonSerializer { Integer sexId = GsonHelper.getInteger(o, "sex"); wxMpUser.setRemark(GsonHelper.getString(o, "remark")); wxMpUser.setGroupId(GsonHelper.getInteger(o, "groupid")); - wxMpUser.setTagIds(GsonHelper.getIntArray(o, "tagid_list")); + wxMpUser.setTagIds(GsonHelper.getLongArray(o, "tagid_list")); wxMpUser.setSexId(sexId); if (new Integer(1).equals(sexId)) { wxMpUser.setSex("男"); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java index 1c06d074..e150a63f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java @@ -20,13 +20,13 @@ public class WxUserListGsonAdapter implements JsonDeserializer { public WxMpUserList deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject o = json.getAsJsonObject(); WxMpUserList wxMpUserList = new WxMpUserList(); - wxMpUserList.setTotal(GsonHelper.getInteger(o, "total")); + wxMpUserList.setTotal(GsonHelper.getLong(o, "total")); 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()) { JsonArray data = o.get("data").getAsJsonObject().get("openid").getAsJsonArray(); 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; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java index 6edd6536..e2c0e0ea 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java +++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java @@ -49,7 +49,7 @@ public class XStreamTransformer { * @param clz 类型 * @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); } @@ -57,10 +57,15 @@ public class XStreamTransformer { * 会自动注册该类及其子类 * @param clz 要注册的类 */ - public static void registerClass(Class clz) { + private static void registerClass(Class clz) { XStream xstream = XStreamInitializer.getInstance(); xstream.processAnnotations(clz); xstream.processAnnotations(getInnerClasses(clz)); + if(clz.equals(WxMpXmlMessage.class)){ + // 操蛋的微信,模板消息推送成功的消息是MsgID,其他消息推送过来是MsgId + xstream.aliasField("MsgID", WxMpXmlMessage.class, "msgId"); + } + register(clz, xstream); } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java index 3c42f66c..dd7541ff 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java @@ -6,6 +6,7 @@ import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.mp.api.test.ApiTestModule; 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.WxMpMassOpenIdsMessage; import me.chanjar.weixin.mp.bean.WxMpMassTagMessage; @@ -101,7 +102,7 @@ public class WxMpMassMessageAPITest { .getSystemResourceAsStream("mm.mp4")) { // 上传视频到媒体库 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.getMediaId()); @@ -122,7 +123,7 @@ public class WxMpMassMessageAPITest { try (InputStream inputStream = ClassLoader .getSystemResourceAsStream("mm.jpeg")) { 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.getMediaId()); messages[1] = new Object[]{WxConsts.MASS_MSG_IMAGE, uploadMediaRes.getMediaId()}; @@ -134,7 +135,7 @@ public class WxMpMassMessageAPITest { try (InputStream inputStream = ClassLoader .getSystemResourceAsStream("mm.mp3")) { 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.getMediaId()); messages[2] = new Object[]{WxConsts.MASS_MSG_VOICE, uploadMediaRes.getMediaId()}; @@ -147,7 +148,7 @@ public class WxMpMassMessageAPITest { .getSystemResourceAsStream("mm.jpeg")) { // 上传照片到媒体库 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.getMediaId()); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java index ca90a7b5..6bb1e58b 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java @@ -7,6 +7,7 @@ import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.util.fs.FileUtils; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.test.ApiTestModule; +import me.chanjar.weixin.mp.api.test.TestConstants; import me.chanjar.weixin.mp.bean.material.*; import org.testng.annotations.*; @@ -45,10 +46,10 @@ public class WxMpMaterialServiceImplTest { @DataProvider public Object[][] mediaFiles() { 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"} }; } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java index 42819b2f..dac37f26 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java @@ -93,43 +93,38 @@ public class WxMpMenuServiceImplTest { } @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); System.out.println(menu.toJson()); @@ -157,9 +152,11 @@ public class WxMpMenuServiceImplTest { button1.setKey("V1001_TODAY_MUSIC"); 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(); button3.setName("菜单"); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java deleted file mode 100644 index 2783ee7f..00000000 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java +++ /dev/null @@ -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); - } - - -} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java index 09ba9e99..27f11791 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java @@ -70,7 +70,7 @@ public class WxMpUserServiceImplTest { Assert.assertNotNull(wxMpUserList); Assert.assertFalse(wxMpUserList.getCount() == -1); Assert.assertFalse(wxMpUserList.getTotal() == -1); - Assert.assertFalse(wxMpUserList.getOpenIds().size() == -1); + Assert.assertFalse(wxMpUserList.getOpenids().size() == -1); System.out.println(wxMpUserList); } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java index c957bde8..31ad47b6 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java @@ -17,8 +17,7 @@ public class ApiTestModule implements Module { @Override public void configure(Binder binder) { 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()); WxMpService wxService = new WxMpServiceImpl(); wxService.setWxMpConfigStorage(config); diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java index 0dc91151..6400f81d 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java @@ -64,10 +64,4 @@ public class TestConfigStorage extends WxMpInMemoryConfigStorage { super.accessTokenLock = lock; } - @Override - public boolean useSandboxForWxPay() { - return false; - //沙箱环境不成熟,有问题无法使用,暂时屏蔽掉 - // return true; - } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConstants.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConstants.java new file mode 100644 index 00000000..557e0dc0 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConstants.java @@ -0,0 +1,17 @@ +package me.chanjar.weixin.mp.api.test; + +/** + *
+ * 仅供测试使用的一些常量
+ * Created by Binary Wang on 2017-3-9.
+ * 
+ */ +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"; +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java new file mode 100644 index 00000000..ac415ff4 --- /dev/null +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java @@ -0,0 +1,31 @@ +package me.chanjar.weixin.mp.bean.template; + +import org.testng.annotations.*; + +import static org.testng.AssertJUnit.*; + +/** + *
+ * Created by Binary Wang on 2017-3-30.
+ * @author binarywang(Binary Wang)
+ * 
+ */ +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\"}}}"); + } + +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java index 2cfb8ce3..2390071b 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java +++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java @@ -6,6 +6,7 @@ import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; 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.WxMpXmlOutImageMessage; import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; @@ -18,7 +19,7 @@ public class DemoImageHandler implements WxMpMessageHandler { WxMpService wxMpService, WxSessionManager sessionManager) { try { 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 = WxMpXmlOutMessage .IMAGE() diff --git a/weixin-java-osgi/pom.xml b/weixin-java-osgi/pom.xml index fbcfaba6..b44ef85d 100644 --- a/weixin-java-osgi/pom.xml +++ b/weixin-java-osgi/pom.xml @@ -6,7 +6,7 @@ com.github.binarywang weixin-java-parent - 2.5.0 + 2.5.2.BETA weixin-java-osgi diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml new file mode 100644 index 00000000..d0d50455 --- /dev/null +++ b/weixin-java-pay/pom.xml @@ -0,0 +1,47 @@ + + + + weixin-java-parent + com.github.binarywang + 2.6.0 + + 4.0.0 + + weixin-java-pay + WeiXin Java Tools - PAY + 微信支付 Java SDK + + + + com.github.binarywang + weixin-java-common + ${project.version} + + + + org.jodd + jodd-http + 3.7 + + + + com.github.binarywang + qrcode-utils + 1.0 + + + + org.testng + testng + test + + + com.google.inject + guice + test + + + + diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyCoupon.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java similarity index 93% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyCoupon.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java index 60b3f688..c72ddcb2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyCoupon.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java @@ -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 toMap(int index) { - Map 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 toMap(int index) { + Map 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); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyResponse.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java similarity index 85% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyResponse.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java index a1313ea0..9d960c08 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyResponse.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java @@ -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; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxEntPayQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java similarity index 67% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxEntPayQueryRequest.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java index df2de905..f9135c34 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxEntPayQueryRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java @@ -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 me.chanjar.weixin.common.annotation.Required; @@ -16,47 +16,25 @@ import me.chanjar.weixin.common.util.ToStringUtils; *
  • 描述 * * Created by Binary Wang on 2016/10/19. + * * @author binarywang (https://github.com/binarywang) */ @XStreamAlias("xml") public class WxEntPayQueryRequest extends WxPayBaseRequest { /** *
    -   * 商户号
    -   * mch_id
    +   * 商户订单号
    +   * partner_trade_no
        * 是
    -   * 10000098
    -   * String(32)
    -   * 微信支付分配的商户号
    +   * 10000098201411111234567890
    +   * String
    +   * 商户订单号
        * 
    - */ - @XStreamAlias("mchid") - private String mchId; - - /** - *
    -  * 商户订单号
    -  * partner_trade_no
    -  * 是
    -  * 10000098201411111234567890
    -  * String
    -  * 商户订单号
    -   * 
    - */ + */ @Required @XStreamAlias("partner_trade_no") private String partnerTradeNo; - @Override - public String getMchId() { - return this.mchId; - } - - @Override - public void setMchId(String mchId) { - this.mchId = mchId; - } - public String getPartnerTradeNo() { return this.partnerTradeNo; } @@ -65,6 +43,11 @@ public class WxEntPayQueryRequest extends WxPayBaseRequest { this.partnerTradeNo = partnerTradeNo; } + @Override + protected void checkConstraints() { + //do nothing + } + @Override public String toString() { return ToStringUtils.toSimpleString(this); diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxEntPayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java similarity index 62% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxEntPayRequest.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java index bc6ae574..fd43fb1a 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxEntPayRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java @@ -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 me.chanjar.weixin.common.util.ToStringUtils; @@ -15,144 +15,150 @@ import me.chanjar.weixin.common.util.ToStringUtils; *
  • 描述 * * Created by Binary Wang on 2016/10/02. + * * @author binarywang (https://github.com/binarywang) */ @XStreamAlias("xml") -public class WxEntPayRequest extends WxPayBaseRequest{ +public class WxEntPayRequest extends WxPayBaseRequest { /** *
    -  * 公众账号appid
    -  * mch_appid
    -  * 是
    -  * wx8888888888888888
    -  * String
    -  * 微信分配的公众账号ID(企业号corpid即为此appId)
    +   * 公众账号appid
    +   * mch_appid
    +   * 是
    +   * wx8888888888888888
    +   * String
    +   * 微信分配的公众账号ID(企业号corpid即为此appId)
        * 
    - */ + */ @XStreamAlias("mch_appid") private String mchAppid; /** *
    -  *  商户号
    -  *  mchid
    -  *  是
    -  *  1900000109
    -  *  String(32)
    -  *  微信支付分配的商户号
    +   *  商户号
    +   *  mchid
    +   *  是
    +   *  1900000109
    +   *  String(32)
    +   *  微信支付分配的商户号
        * 
    - */ + */ @XStreamAlias("mchid") private String mchId; /** *
    -  * 设备号
    -  * device_info
    -  * 否
    -  * 13467007045764
    -  * String(32)
    -  *微信支付分配的终端设备号
    +   * 设备号
    +   * device_info
    +   * 否
    +   * 13467007045764
    +   * String(32)
    +   * 微信支付分配的终端设备号
        * 
    - */ + */ @XStreamAlias("device_info") private String deviceInfo; /** *
    -  * 商户订单号
    -  * partner_trade_no
    -  * 是
    -  * 10000098201411111234567890
    -  * String
    -  * 商户订单号
    +   * 商户订单号
    +   * partner_trade_no
    +   * 是
    +   * 10000098201411111234567890
    +   * String
    +   * 商户订单号
        * 
    - */ + */ @XStreamAlias("partner_trade_no") private String partnerTradeNo; /** *
    -  * 需保持唯一性 用户openid
    -  * openid
    -  * 是
    -  * oxTWIuGaIt6gTKsQRLau2M0yL16E
    -  * String
    -  * 商户appid下,某用户的openid
    +   * 需保持唯一性 用户openid
    +   * openid
    +   * 是
    +   * oxTWIuGaIt6gTKsQRLau2M0yL16E
    +   * String
    +   * 商户appid下,某用户的openid
        * 
    - */ + */ @XStreamAlias("openid") private String openid; /** *
    -  * 校验用户姓名选项
    -  * check_name
    -  * 是
    -  * OPTION_CHECK
    -  * String
    -  * NO_CHECK:不校验真实姓名 
    -  * FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
    -  * OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
    +   * 校验用户姓名选项
    +   * check_name
    +   * 是
    +   * OPTION_CHECK
    +   * String
    +   * NO_CHECK:不校验真实姓名 
    +   * FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
    +   * OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
        * 
    - */ + */ @XStreamAlias("check_name") private String checkName; /** *
    -  * 收款用户姓名
    -  * re_user_name
    -  * 可选
    -  * 马花花
    -  * String
    -  * 收款用户真实姓名。
    -  * 如果check_name设置为FORCE_CHECK或OPTION_CHECK,  则必填用户真实姓名
    +   * 收款用户姓名
    +   * re_user_name
    +   * 可选
    +   * 马花花
    +   * String
    +   * 收款用户真实姓名。
    +   * 如果check_name设置为FORCE_CHECK或OPTION_CHECK,  则必填用户真实姓名
        * 
    - */ + */ @XStreamAlias("re_user_name") private String reUserName; /** *
    -  * 金额
    -  * amount
    -  * 是
    -  * 10099
    -  * int
    -  * 企业付款金额, 单位为分
    +   * 金额
    +   * amount
    +   * 是
    +   * 10099
    +   * int
    +   * 企业付款金额, 单位为分
        * 
    - */ + */ @XStreamAlias("amount") private Integer amount; /** *
    -  * 企业付款描述信息
    -  * desc
    -  * 是
    -  * 理赔
    -  * String
    -  * 企业付款操作说明信息。必填。
    +   * 企业付款描述信息
    +   * desc
    +   * 是
    +   * 理赔
    +   * String
    +   * 企业付款操作说明信息。必填。
        * 
    - */ + */ @XStreamAlias("desc") private String description; /** *
    -  * Ip地址
    -  * spbill_create_ip
    -  * 是
    -  * 192.168.0.1
    -  * String(32)
    -  * 调用接口的机器Ip地址
    +   * Ip地址
    +   * spbill_create_ip
    +   * 是
    +   * 192.168.0.1
    +   * String(32)
    +   * 调用接口的机器Ip地址
        * 
    - */ + */ @XStreamAlias("spbill_create_ip") private String spbillCreateIp; + @Override + protected void checkConstraints() { + + } + @Override public String getAppid() { return this.mchAppid; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java new file mode 100644 index 00000000..594fa496 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java @@ -0,0 +1,46 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
    + * 授权码查询openid接口请求对象类
    + * Created by Binary Wang on 2017-3-27.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayAuthcode2OpenidRequest extends WxPayBaseRequest { + + /** + *
    +   *     授权码
    +   *     auth_code
    +   *     是
    +   *     String(128)
    +   *     扫码支付授权码,设备读取用户微信中的条码或者二维码信息
    +   * 
    + */ + @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 + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayBaseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java similarity index 53% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayBaseRequest.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java index c13f1b8f..4d5cbbaa 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayBaseRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java @@ -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.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.xml.XStreamInitializer; +import org.apache.commons.lang3.StringUtils; import java.math.BigDecimal; @@ -23,6 +28,22 @@ import java.math.BigDecimal; * @author binarywang(Binary Wang) */ public abstract class WxPayBaseRequest { + /** + * 检查请求参数内容,包括必填参数以及特殊约束 + */ + protected void checkFields() throws WxErrorException { + //check required fields + BeanUtils.checkRequiredFields(this); + + //check other parameters + this.checkConstraints(); + } + + /** + * 检查约束情况 + */ + protected abstract void checkConstraints(); + /** *
        * 公众账号ID
    @@ -47,6 +68,32 @@ public abstract class WxPayBaseRequest {
        */
       @XStreamAlias("mch_id")
       protected String mchId;
    +
    +  /**
    +   * 
    +   * 服务商模式下的子商户公众账号ID
    +   * sub_appid
    +   * 是
    +   * String(32)
    +   * wxd678efh567hg6787
    +   * 微信分配的子商户公众账号ID
    +   * 
    + */ + @XStreamAlias("sub_appid") + protected String subAppId; + + /** + *
    +   * 服务商模式下的子商户号
    +   * sub_mch_id
    +   * 是
    +   * String(32)
    +   * 1230000109
    +   * 微信支付分配的子商户号,开发者模式下必填
    +   * 
    + */ + @XStreamAlias("sub_mch_id") + protected String subMchId; /** *
        * 随机字符串
    @@ -128,6 +175,22 @@ public abstract class WxPayBaseRequest {
         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
       public String toString() {
         return ToStringUtils.toSimpleString(this);
    @@ -138,4 +201,41 @@ public abstract class WxPayBaseRequest {
         xstream.processAnnotations(this.getClass());
         return xstream.toXML(this);
       }
    +
    +  /**
    +   * 
    +   * 检查参数,并设置签名
    +   * 1、检查参数(注意:子类实现需要检查参数的而外功能时,请在调用父类的方法前进行相应判断)
    +   * 2、补充系统参数,如果未传入则从配置里读取
    +   * 3、生成签名,并设置进去
    +   * 
    + * @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())); + } + } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayDownloadBillRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java similarity index 76% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayDownloadBillRequest.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java index 9e1ab4dc..ed02060f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayDownloadBillRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java @@ -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 me.chanjar.weixin.common.annotation.Required; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; /** *
    @@ -12,6 +16,8 @@ import me.chanjar.weixin.common.annotation.Required;
      */
     @XStreamAlias("xml")
     public class WxPayDownloadBillRequest extends WxPayBaseRequest {
    +  private static final String[] BILL_TYPE = new String[]{"ALL", "REFUND", "SUCCESS"};
    +
       /**
        * 
        * 设备号
    @@ -120,4 +126,16 @@ public class WxPayDownloadBillRequest extends WxPayBaseRequest {
       public void setTarType(String 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()));
    +    }
    +  }
     }
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
    new file mode 100644
    index 00000000..a0253378
    --- /dev/null
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
    @@ -0,0 +1,380 @@
    +package com.github.binarywang.wxpay.bean.request;
    +
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +import me.chanjar.weixin.common.annotation.Required;
    +
    +/**
    + * 
    + *  提交刷卡支付请求对象类
    + * Created by Binary Wang on 2017-3-23.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayMicropayRequest extends WxPayBaseRequest { + /** + *
    +   * 签名类型
    +   * sign_type
    +   * 否
    +   * String(32)
    +   * HMAC-SHA256
    +   * 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
    +   **/
    +  @XStreamAlias("sign_type")
    +  private String signType;
    +
    +  /**
    +   * 
    +   * 商品描述
    +   * body
    +   * 是
    +   * String(128)
    +   * image形象店-深圳腾大- QQ公仔
    +   * 商品简单描述,该字段须严格按照规范传递,具体请见参数规定
    +   **/
    +  @Required
    +  @XStreamAlias("body")
    +  private String body;
    +
    +  /**
    +   * 
    +   * 商品详情
    +   * detail
    +   * 否
    +   * String(6000)
    +   *
    +   * 单品优惠功能字段,需要接入请见详细说明
    +   **/
    +  @XStreamAlias("detail")
    +  private String detail;
    +
    +  /**
    +   * 
    +   * 附加数据
    +   * attach
    +   * 否
    +   * String(127)
    +   * 说明
    +   * 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
    +   **/
    +  @XStreamAlias("attach")
    +  private String attach;
    +
    +  /**
    +   * 
    +   * 商户订单号
    +   * out_trade_no
    +   * 是
    +   * String(32)
    +   * 1217752501201407033233368018
    +   * 商户系统内部的订单号,32个字符内、可包含字母,其他说明见商户订单号
    +   **/
    +  @Required
    +  @XStreamAlias("out_trade_no")
    +  private String outTradeNo;
    +
    +  /**
    +   * 
    +   * 订单金额
    +   * total_fee
    +   * 是
    +   * Int
    +   * 888
    +   * 订单总金额,单位为分,只能为整数,详见支付金额
    +   **/
    +  @Required
    +  @XStreamAlias("total_fee")
    +  private Integer totalFee;
    +
    +  /**
    +   * 
    +   * 货币类型
    +   * fee_type
    +   * 否
    +   * String(16)
    +   * CNY
    +   * 符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   **/
    +  @XStreamAlias("fee_type")
    +  private String feeType;
    +
    +  /**
    +   * 
    +   * 终端IP
    +   * spbill_create_ip
    +   * 是
    +   * String(16)
    +   * 8.8.8.8
    +   * 调用微信支付API的机器IP
    +   **/
    +  @Required
    +  @XStreamAlias("spbill_create_ip")
    +  private String spbillCreateIp;
    +
    +  /**
    +   * 
    +   * 商品标记
    +   * goods_tag
    +   * 否
    +   * String(32)
    +   * 1234
    +   * 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
    +   **/
    +  @XStreamAlias("goods_tag")
    +  private String goodsTag;
    +
    +  /**
    +   * 
    +   * 指定支付方式
    +   * limit_pay
    +   * 否
    +   * String(32)
    +   * no_credit
    +   * no_credit--指定不能使用信用卡支付
    +   **/
    +  @XStreamAlias("limit_pay")
    +  private String limitPay;
    +
    +  /**
    +   * 
    +   * 授权码
    +   * 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);
    +    }
    +  }
    +}
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayOrderCloseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java
    similarity index 80%
    rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayOrderCloseRequest.java
    rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java
    index 8f26750f..21968ac9 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayOrderCloseRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java
    @@ -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;
     
    @@ -10,7 +10,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias;
      * 
    */ @XStreamAlias("xml") -public class WxPayOrderCloseRequest extends WxPayBaseRequest{ +public class WxPayOrderCloseRequest extends WxPayBaseRequest { /** *
    @@ -33,4 +33,8 @@ public class WxPayOrderCloseRequest extends WxPayBaseRequest{
         this.outTradeNo = outTradeNo;
       }
     
    +  @Override
    +  protected void checkConstraints() {
    +
    +  }
     }
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayOrderQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
    similarity index 74%
    rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayOrderQueryRequest.java
    rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
    index 3cc7dd2b..49972799 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayOrderQueryRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
    @@ -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 org.apache.commons.lang3.StringUtils;
     
     /**
      * 
    @@ -14,6 +15,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias;
      * 
  • 示例值 *
  • 描述 *
  • + * * @author binarywang(Binary Wang) */ @XStreamAlias("xml") @@ -60,4 +62,12 @@ public class WxPayOrderQueryRequest extends WxPayBaseRequest { public void setOutTradeNo(String 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 不能同时存在或同时为空,必须二选一"); + } + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java new file mode 100644 index 00000000..6aef2aa0 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java @@ -0,0 +1,163 @@ +package com.github.binarywang.wxpay.bean.request; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import org.apache.commons.lang3.StringUtils; + +/** + *
    + * 撤销订单请求类
    + * Created by Binary Wang on 2017-3-23.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayOrderReverseRequest extends WxPayBaseRequest { + + /** + *
    +   * 微信订单号
    +   * transaction_id
    +   * String(28)
    +   * 1217752501201400000000000000
    +   * 微信的订单号,优先使用
    +   * 
    + */ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
    +   * 商户订单号
    +   * out_trade_no
    +   * String(32)
    +   * 1217752501201400000000000000
    +   * 商户系统内部的订单号
    +   * transaction_id、out_trade_no二选一,如果同时存在优先级:transaction_id> out_trade_no
    +   * 
    + */ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
    +   * 签名类型
    +   * 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);
    +    }
    +  }
    +}
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRedpackQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java
    similarity index 91%
    rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRedpackQueryRequest.java
    rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java
    index de7eee93..191db469 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRedpackQueryRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java
    @@ -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;
     
    @@ -54,4 +54,9 @@ public class WxPayRedpackQueryRequest extends WxPayBaseRequest {
       public void setMchBillNo(String mchBillNo) {
         this.mchBillNo = mchBillNo;
       }
    +
    +  @Override
    +  protected void checkConstraints() {
    +
    +  }
     }
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
    similarity index 81%
    rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java
    rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
    index 373b0917..1efa23db 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
    @@ -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 org.apache.commons.lang3.StringUtils;
     
     /**
      * 
    @@ -132,4 +133,15 @@ public class WxPayRefundQueryRequest extends WxPayBaseRequest {
       public void setRefundId(String 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 必须四选一");
    +    }
    +
    +  }
     }
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
    similarity index 50%
    rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundRequest.java
    rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
    index 22bc6ae2..c7fc8c26 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
    @@ -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 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;
     
     /**
      * 
    @@ -13,13 +19,25 @@ import me.chanjar.weixin.common.annotation.Required;
      * 
  • 类型 *
  • 示例值 *
  • 描述 + * Created by Binary Wang on 2016-10-08. *
  • * * @author binarywang(Binary Wang) - * Created by Binary Wang on 2016-10-08. */ @XStreamAlias("xml") 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); + } + /** *
        * 设备号
    @@ -143,6 +161,31 @@ public class WxPayRefundRequest extends WxPayBaseRequest {
       @XStreamAlias("refund_account")
       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() {
         return this.deviceInfo;
       }
    @@ -215,4 +258,117 @@ public class WxPayRefundRequest extends WxPayBaseRequest {
         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);
    +    }
    +  }
     }
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayReportRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
    similarity index 97%
    rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayReportRequest.java
    rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
    index 44b047f8..8b3f88b9 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayReportRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
    @@ -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 me.chanjar.weixin.common.annotation.Required;
    @@ -270,4 +270,9 @@ public class WxPayReportRequest extends WxPayBaseRequest {
       public void setTime(String time) {
         this.time = time;
       }
    +
    +  @Override
    +  protected void checkConstraints() {
    +    //do nothing
    +  }
     }
    diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPaySendRedpackRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java
    similarity index 81%
    rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPaySendRedpackRequest.java
    rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java
    index 95cdac22..f7082fc3 100644
    --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPaySendRedpackRequest.java
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java
    @@ -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;
     
     /**
    - *  发送红包请求参数对象
    - *  Created by Binary Wang on 2016/9/24.
    + * 发送红包请求参数对象
    + * Created by Binary Wang on 2016/9/24.
    + *
      * @author binarywang (https://github.com/binarywang)
      */
     @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")
       private String mchBillNo;
     
    @@ -25,60 +26,60 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest{
       private String sendName;
     
       /**
    -   *  re_openid
    -   *  接受红包的用户   用户在wxappid下的openid
    +   * re_openid
    +   * 接受红包的用户   用户在wxappid下的openid
        */
       @XStreamAlias("re_openid")
       private String reOpenid;
     
       /**
    -   *  total_amount
    -   *  红包总额
    +   * total_amount
    +   * 红包总额
        */
       @XStreamAlias("total_amount")
       private Integer totalAmount;
     
       /**
    -   *  total_num
    -   *  红包发放总人数
    +   * total_num
    +   * 红包发放总人数
        */
       @XStreamAlias("total_num")
       private Integer totalNum;
     
       /**
    -   *  amt_type
    -   *  红包金额设置方式
    -   *  ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额
    -   *  裂变红包必填
    +   * amt_type
    +   * 红包金额设置方式
    +   * ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额
    +   * 裂变红包必填
        */
       @XStreamAlias("amt_type")
       private String amtType;
     
       /**
    -   *  wishing
    -   *  红包祝福语
    +   * wishing
    +   * 红包祝福语
        */
       @XStreamAlias("wishing")
       private String wishing;
     
       /**
    -   *  client_ip
    -   *  服务器Ip地址
    -   *  调用接口的机器Ip地址
    +   * client_ip
    +   * 服务器Ip地址
    +   * 调用接口的机器Ip地址
        */
       @XStreamAlias("client_ip")
       private String clientIp;
     
       /**
    -   *  act_name
    -   *  活动名称
    +   * act_name
    +   * 活动名称
        */
       @XStreamAlias("act_name")
       private String actName;
     
       /**
    -   *  remark
    -   *  备注
    +   * remark
    +   * 备注
        */
       @XStreamAlias("remark")
       private String remark;
    @@ -216,6 +217,11 @@ public class WxPaySendRedpackRequest extends WxPayBaseRequest{
         this.remark = remark;
       }
     
    +  @Override
    +  protected void checkConstraints() {
    +
    +  }
    +
       @Override
       public String getAppid() {
         return this.wxAppid;
    diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java
    new file mode 100644
    index 00000000..849d9283
    --- /dev/null
    +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java
    @@ -0,0 +1,46 @@
    +package com.github.binarywang.wxpay.bean.request;
    +
    +import com.thoughtworks.xstream.annotations.XStreamAlias;
    +
    +/**
    + * 
    + * 转换短链接请求对象类
    + * Created by Binary Wang on 2017-3-27.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayShorturlRequest extends WxPayBaseRequest { + /** + *
    +   * 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
    +   * 
    + */ + @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 + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayUnifiedOrderRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java similarity index 84% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayUnifiedOrderRequest.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java index cf4c008f..48b059d3 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayUnifiedOrderRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java @@ -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 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; /** *
    @@ -17,10 +22,12 @@ import me.chanjar.weixin.common.annotation.Required;
      * 
  • 描述 *
  • * Created by Binary Wang on 2016/9/25. + * * @author binarywang (https://github.com/binarywang) */ @XStreamAlias("xml") public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { + private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"}; /** *
    @@ -56,35 +63,35 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest {
        * 否
        * String(6000)
        *  {  "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 商品描述信息
        * 
    */ @XStreamAlias("detail") @@ -267,6 +274,9 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { @XStreamAlias("openid") private String openid; + public static WxUnifiedOrderRequestBuilder builder() { + return new WxUnifiedOrderRequestBuilder(); + } public String getDeviceInfo() { return this.deviceInfo; @@ -362,6 +372,7 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** * 如果配置中已经设置,可以不设置值 + * * @param notifyURL */ public void setNotifyURL(String notifyURL) { @@ -374,6 +385,7 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { /** * 如果配置中已经设置,可以不设置值 + * * @param tradeType 交易类型 */ public void setTradeType(String tradeType) { @@ -404,8 +416,33 @@ public class WxPayUnifiedOrderRequest extends WxPayBaseRequest { 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 { diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxEntPayQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayQueryResult.java similarity index 97% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxEntPayQueryResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayQueryResult.java index 8ffbeff2..dac69e17 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxEntPayQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayQueryResult.java @@ -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; /** * 企业付款查询返回结果 * Created by Binary Wang on 2016/10/19. + * * @author binarywang (https://github.com/binarywang) */ @XStreamAlias("xml") diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxEntPayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayResult.java similarity index 96% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxEntPayResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayResult.java index 5a8c4b75..fa0a4e5f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxEntPayResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayResult.java @@ -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; /** * 企业付款返回结果 * Created by Binary Wang on 2016/10/02. + * * @author binarywang (https://github.com/binarywang) */ @XStreamAlias("xml") diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java new file mode 100644 index 00000000..f56c61a7 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java @@ -0,0 +1,40 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
    + *  授权码查询openid接口请求结果类
    + * Created by Binary Wang on 2017-3-27.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayAuthcode2OpenidResult extends WxPayBaseResult { + /** + *
    +   *   用户标识
    +   *   openid
    +   *   是
    +   *   String(128)
    +   *   用户在商户appid下的唯一标识
    +   * 
    + */ + @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; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java new file mode 100644 index 00000000..899703c8 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java @@ -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; + +/** + *
    + * 微信支付结果共用属性类
    + * Created by Binary Wang on 2016-10-24.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +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 fromXML(String xmlString, Class 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 toMap() { + if (StringUtils.isBlank(this.xmlString)) { + throw new RuntimeException("xml数据有问题,请核实!"); + } + + Map 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 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); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayCommonResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java similarity index 88% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayCommonResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java index a6e7de5f..1fc4d309 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayCommonResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java @@ -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; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java new file mode 100644 index 00000000..2f8842af --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java @@ -0,0 +1,328 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
    + * 提交刷卡支付接口响应结果对象类
    + * Created by Binary Wang on 2017-3-23.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayMicropayResult extends WxPayBaseResult { + /** + *
    +   * 用户标识
    +   * openid
    +   * 是
    +   * String(128)
    +   * Y
    +   * 用户在商户appid 下的唯一标识
    +   * 
    + **/ + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 是否关注公众账号
    +   * is_subscribe
    +   * 是
    +   * String(1)
    +   * Y
    +   * 用户是否关注公众账号,仅在公众账号类型支付有效,取值范围:Y或N;Y-关注;N-未关注
    +   * 
    + **/ + @XStreamAlias("is_subscribe") + private String isSubscribe; + + /** + *
    +   * 交易类型
    +   * trade_type
    +   * 是
    +   * String(16)
    +   * MICROPAY
    +   * 支付类型为MICROPAY(即扫码支付)
    +   * 
    + **/ + @XStreamAlias("trade_type") + private String tradeType; + + /** + *
    +   * 付款银行
    +   * bank_type
    +   * 是
    +   * String(32)
    +   * CMC
    +   * 银行类型,采用字符串类型的银行标识,值列表详见银行类型
    +   * 
    + **/ + @XStreamAlias("bank_type") + private String bankType; + + /** + *
    +   * 货币类型
    +   * fee_type
    +   * 否
    +   * String(16)
    +   * CNY
    +   * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + **/ + @XStreamAlias("fee_type") + private String feeType; + + /** + *
    +   * 订单金额
    +   * total_fee
    +   * 是
    +   * Int
    +   * 888
    +   * 订单总金额,单位为分,只能为整数,详见支付金额
    +   * 
    + **/ + @XStreamAlias("total_fee") + private String totalFee; + + /** + *
    +   * 应结订单金额
    +   * settlement_total_fee
    +   * 否
    +   * Int
    +   * 100
    +   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    +   * 
    + **/ + @XStreamAlias("settlement_total_fee") + private Integer settlementTotalFee; + + /** + *
    +   * 代金券金额
    +   * coupon_fee
    +   * 否
    +   * Int
    +   * 100
    +   * “代金券”金额<=订单金额,订单金额-“代金券”金额=现金支付金额,详见支付金额
    +   * 
    + **/ + @XStreamAlias("coupon_fee") + private Integer couponFee; + + /** + *
    +   * 现金支付货币类型
    +   * cash_fee_type
    +   * 否
    +   * String(16)
    +   * CNY
    +   * 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + **/ + @XStreamAlias("cash_fee_type") + private String cashFeeType; + + /** + *
    +   * 现金支付金额
    +   * cash_fee
    +   * 是
    +   * Int
    +   * 100
    +   * 订单现金支付金额,详见支付金额
    +   * 
    + **/ + @XStreamAlias("cash_fee") + private Integer cashFee; + + /** + *
    +   * 微信支付订单号
    +   * transaction_id
    +   * 是
    +   * String(32)
    +   * 1217752501201407033233368018
    +   * 微信支付订单号
    +   * 
    + **/ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
    +   * 商户订单号
    +   * out_trade_no
    +   * 是
    +   * String(32)
    +   * 1217752501201407033233368018
    +   * 商户系统的订单号,与请求一致。
    +   * 
    + **/ + @XStreamAlias("out_trade_no") + private String outTradeNo; + + /** + *
    +   * 商家数据包
    +   * attach
    +   * 否
    +   * String(128)
    +   * 123456
    +   * 商家数据包,原样返回
    +   * 
    + **/ + @XStreamAlias("attach") + private String attach; + + /** + *
    +   * 支付完成时间
    +   * time_end
    +   * 是
    +   * String(14)
    +   * 20141030133525
    +   * 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。详见时间规则
    +   * 
    + **/ + @XStreamAlias("time_end") + private String timeEnd; + + /** + *
    +   * 营销详情
    +   * promotion_detail
    +   * 否
    +   * String(6000)
    +   * 示例见下文
    +   * 新增返回,单品优惠功能字段,需要接入请见详细说明
    +   * 
    + **/ + @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; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderCloseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java similarity index 92% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderCloseResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java index ccfa07bf..7221251f 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderCloseResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java @@ -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; diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java new file mode 100644 index 00000000..723702a4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java @@ -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; + + /** + *
    +   * 设备号
    +   * device_info
    +   * 否
    +   * String(32)
    +   * 013467007045764
    +   * 微信支付分配的终端设备号,
    +   * 
    + */ + @XStreamAlias("device_info") + private String deviceInfo; + + /** + *
    +   * 用户标识
    +   * openid
    +   * 是
    +   * String(128)
    +   * wxd930ea5d5a258f4f
    +   * 用户在商户appid下的唯一标识
    +   * 
    + */ + @XStreamAlias("openid") + private String openid; + + /** + *
    +   * 是否关注公众账号
    +   * is_subscribe
    +   * 否
    +   * String(1)
    +   * Y
    +   * 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
    +   * 
    + */ + @XStreamAlias("is_subscribe") + private String isSubscribe; + + + /** + *
    +   * 交易类型
    +   * trade_type
    +   * 是
    +   * String(16)
    +   * JSAPI	JSAPI、NATIVE、APP
    +   * 
    + */ + @XStreamAlias("trade_type") + private String tradeType; + + + /** + *
    +   * 付款银行
    +   * bank_type
    +   * 是
    +   * String(16)
    +   * CMC
    +   * 银行类型,采用字符串类型的银行标识,银行类型见银行列表
    +   * 
    + */ + @XStreamAlias("bank_type") + private String bankType; + + + /** + *
    +   * 订单金额
    +   * total_fee
    +   * 是
    +   * Int
    +   * 100
    +   * 订单总金额,单位为分
    +   * 
    + */ + @XStreamAlias("total_fee") + private Integer totalFee; + /** + *
    +   * 应结订单金额
    +   * settlement_total_fee
    +   * 否
    +   * Int
    +   * 100
    +   * 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
    +   * 
    + */ + @XStreamAlias("settlement_total_fee") + private Integer settlementTotalFee; + /** + *
    +   * 货币种类
    +   * fee_type
    +   * 否
    +   * String(8)
    +   * CNY
    +   * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + */ + @XStreamAlias("fee_type") + private String feeType; + /** + *
    +   * 现金支付金额
    +   * cash_fee
    +   * 是
    +   * Int
    +   * 100
    +   * 现金支付金额订单现金支付金额,详见支付金额
    +   * 
    + */ + @XStreamAlias("cash_fee") + private Integer cashFee; + /** + *
    +   * 现金支付货币类型
    +   * cash_fee_type
    +   * 否
    +   * String(16)
    +   * CNY
    +   * 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
    +   * 
    + */ + @XStreamAlias("cash_fee_type") + private String cashFeeType; + /** + *
    +   * 总代金券金额
    +   * coupon_fee
    +   * 否
    +   * Int
    +   * 10
    +   * 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
    +   * 
    + */ + @XStreamAlias("coupon_fee") + private Integer couponFee; + + /** + *
    +   * 代金券使用数量
    +   * coupon_count
    +   * 否
    +   * Int
    +   * 1
    +   * 代金券使用数量
    +   * 
    + */ + @XStreamAlias("coupon_count") + private Integer couponCount; + + private List couponList; + + /** + *
    +   * 微信支付订单号
    +   * transaction_id
    +   * 是
    +   * String(32)
    +   * 1217752501201407033233368018
    +   * 微信支付订单号
    +   * 
    + */ + @XStreamAlias("transaction_id") + private String transactionId; + + /** + *
    +   * 商户订单号
    +   * out_trade_no
    +   * 是
    +   * String(32)
    +   * 1212321211201407033568112322
    +   * 商户系统的订单号,与请求一致。
    +   * 
    + */ + @XStreamAlias("out_trade_no") + private String outTradeNo; + /** + *
    +   * 商家数据包
    +   * attach
    +   * 否
    +   * String(128)
    +   * 123456
    +   * 商家数据包,原样返回
    +   * 
    + */ + @XStreamAlias("attach") + private String attach; + /** + *
    +   * 支付完成时间
    +   * time_end
    +   * 是
    +   * String(14)
    +   * 20141030133525
    +   * 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
    +   * 
    + */ + @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 getCouponList() { + return couponList; + } + + public void setCouponList(List 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 toMap() { + Map 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); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java similarity index 95% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java index 8cdf4e6d..ff087bf0 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java @@ -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.thoughtworks.xstream.annotations.XStreamAlias; -import io.restassured.path.xml.XmlPath; import java.util.List; @@ -395,11 +394,10 @@ public class WxPayOrderQueryResult extends WxPayBaseResult { public void composeCoupons() { if (this.couponCount != null && this.couponCount > 0) { 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))); } } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java new file mode 100644 index 00000000..c80b72d2 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java @@ -0,0 +1,35 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
    + * 撤销订单响应结果类
    + * Created by Binary Wang on 2017-3-23.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayOrderReverseResult extends WxPayBaseResult { + + /** + *
    +   * 是否重调
    +   * recall
    +   * 是
    +   * String(1)
    +   * Y
    +   * 是否需要继续调用撤销,Y-需要,N-不需要
    +   * 
    + **/ + @XStreamAlias("recall") + private String isRecall; + + public String getIsRecall() { + return this.isRecall; + } + + public void setIsRecall(String isRecall) { + this.isRecall = isRecall; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRedpackQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java similarity index 99% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRedpackQueryResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java index 6febaa37..e0b86732 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRedpackQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java @@ -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; diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java similarity index 89% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java index 1e569ac1..1916fdb9 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java @@ -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.thoughtworks.xstream.annotations.XStreamAlias; -import io.restassured.path.xml.XmlPath; import java.util.List; @@ -186,26 +185,25 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { /** * 组装生成退款记录属性的内容 - */ + */ public void composeRefundRecords() { if (this.refundCount != null && this.refundCount > 0) { this.refundRecords = Lists.newArrayList(); - XmlPath xmlPath = new XmlPath(this.getXmlString()); for (int i = 0; i < this.refundCount; i++) { RefundRecord refundRecord = new 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) { continue; @@ -215,8 +213,8 @@ public class WxPayRefundQueryResult extends WxPayBaseResult { for (int j = 0; j < refundRecord.getCouponRefundCount(); j++) { coupons.add( 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) ) ); } diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java similarity index 98% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java index d00e2db5..1e6126b2 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java @@ -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 java.io.Serializable; + /** *
      * 微信支付-申请退款返回结果
      * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
      * 
    - * @author liukaitj * + * @author liukaitj */ @XStreamAlias("xml") -public class WxPayRefundResult extends WxPayBaseResult implements Serializable{ +public class WxPayRefundResult extends WxPayBaseResult implements Serializable { private static final long serialVersionUID = 1L; @XStreamAlias("device_info") diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPaySendRedpackResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java similarity index 97% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPaySendRedpackResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java index 8fe009bc..868f089b 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPaySendRedpackResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java @@ -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 java.io.Serializable; + /** * 向微信用户个人发现金红包返回结果 * https://pay.weixin.qq.com/wiki/doc/api/cash_coupon.php?chapter=13_5 - * @author kane * + * @author kane */ @XStreamAlias("xml") public class WxPaySendRedpackResult extends WxPayBaseResult implements Serializable { diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java new file mode 100644 index 00000000..2f93cb81 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java @@ -0,0 +1,34 @@ +package com.github.binarywang.wxpay.bean.result; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +/** + *
    + * 转换短链接结果对象类
    + * Created by Binary Wang on 2017-3-27.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +@XStreamAlias("xml") +public class WxPayShorturlResult extends WxPayBaseResult { + /** + *
    +   * URL链接
    +   * short_url
    +   * 是
    +   * String(64)
    +   * weixin://wxpay/s/XXXXXX
    +   * 转换后的URL
    +   * 
    + */ + @XStreamAlias("short_url") + private String shortUrl; + + public String getShortUrl() { + return this.shortUrl; + } + + public void setShortUrl(String shortUrl) { + this.shortUrl = shortUrl; + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayUnifiedOrderResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java similarity index 87% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayUnifiedOrderResult.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java index 5097cd10..f6d82bd1 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayUnifiedOrderResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java @@ -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; /** *
      * 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果
    - * 统一下单(详见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)
      * 
    * * @author chanjarster diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxScanPayNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxScanPayNotifyResult.java new file mode 100644 index 00000000..5bf11068 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxScanPayNotifyResult.java @@ -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; + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java new file mode 100644 index 00000000..c03b2482 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java @@ -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; + } + + /** + * 交易类型 + *
    +   * JSAPI--公众号支付
    +   * NATIVE--原生扫码支付
    +   * APP--app支付
    +   * 
    + */ + 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); + } + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyResultConverter.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java similarity index 95% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyResultConverter.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java index bdd440b9..0e375bd6 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyResultConverter.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java @@ -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 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 fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields())); - fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields())); - Map fieldMap = getFieldMap(fields); - - List 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 getFieldMap(List fields) { - Map fieldMap = Maps.uniqueIndex(fields, new Function() { - @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 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 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 fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields())); + fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields())); + Map fieldMap = getFieldMap(fields); + + List 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 getFieldMap(List fields) { + Map fieldMap = Maps.uniqueIndex(fields, new Function() { + @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 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); + } +} diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java similarity index 65% rename from weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index 004cd5bc..e336cd21 100644 --- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -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.mp.bean.pay.request.*; -import me.chanjar.weixin.mp.bean.pay.result.*; import java.io.File; import java.util.Map; /** + *
      * 微信支付相关接口
      * Created by Binary Wang on 2016/7/28.
    + * 
    * * @author binarywang (https://github.com/binarywang) */ -public interface WxMpPayService { +public interface WxPayService { /** *
    -   * 查询订单(详见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 {
        * 
    * * @param transactionId 微信订单号 - * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 + * @param outTradeNo 商户系统内部的订单号,当没提供transactionId时需要传这个。 */ WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException; @@ -44,12 +47,12 @@ public interface WxMpPayService { * 是否需要证书: 不需要。 *
    * - * @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。 + * @param outTradeNo 商户系统内部的订单号 */ 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 * @@ -65,6 +68,16 @@ public interface WxMpPayService { */ Map getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException; + /** + * 获取配置 + */ + WxPayConfig getConfig(); + + /** + * 设置配置对象 + */ + void setConfig(WxPayConfig config); + /** *
        * 微信支付-申请退款
    @@ -81,7 +94,8 @@ public interface WxMpPayService {
        * 
        * 微信支付-查询退款
        * 应用场景:
    -   *  提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
    +   *  提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,
    +   *  银行卡支付的退款3个工作日后重新查询退款状态。
        * 详见 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
        * 接口链接:https://api.mch.weixin.qq.com/pay/refundquery
        * 
    @@ -93,92 +107,15 @@ public interface WxMpPayService { * @param refundId 微信退款单号 * @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; - /** - * 微信公众号支付签名算法(详见: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 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 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 prams); - - /** - * 校验签名是否正确 - * - * @param params 需要校验的参数Map - * @param signKey 校验的签名Key - * @return true - 签名校验成功,false - 签名校验失败 - * @see #checkSign(Map, String) - */ - boolean checkSign(Map params, String signKey); - /** * 发送微信红包给个人用户 *
    @@ -248,6 +185,20 @@ public interface WxMpPayService {
        */
       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
    +   * 其中XXXXX为商户需要填写的内容,商户将该链接生成二维码,如需要打印发布二维码,需要采用此格式。商户可调用第三方库生成二维码图片。
    +   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4
    +   * 
    + * + * @param productId 产品Id + * @return 生成的二维码URL连接 + */ + String createScanPayQrcodeMode1(String productId); + /** *
        * 扫码支付模式二生成二维码的方法
    @@ -296,4 +247,81 @@ public interface WxMpPayService {
        * @return 保存到本地的临时文件
        */
       File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException;
    +
    +  /**
    +   * 
    +   * 提交刷卡支付
    +   * 文档地址: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
    +   * 是否需要证书:不需要。
    +   * 
    + */ + WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException; + + /** + *
    +   * 撤销订单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
    +   *  是否需要证书:请求需要双向证书。
    +   * 
    + */ + WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException; + + /** + *
    +   *  转换短链接
    +   *  文档地址:
    +   *     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
    +   *  是否需要证书:否
    +   * 
    + * @param request 请求对象 + */ + String shorturl(WxPayShorturlRequest request) throws WxErrorException; + + /** + *
    +   *  转换短链接
    +   * 
    + * @see WxPayService#shorturl(WxPayShorturlRequest) + * @param longUrl 需要被压缩的网址 + */ + String shorturl(String longUrl) throws WxErrorException; + + /** + *
    +   * 授权码查询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
    +   * 
    + * @param request 请求对象 + * @return openid + */ + String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException; + + /** + *
    +   * 授权码查询OPENID接口
    +   * 
    + * @see WxPayService#authcode2Openid(WxPayAuthcode2OpenidRequest) + * @param authCode 授权码 + * @return openid + */ + String authcode2Openid(String authCode) throws WxErrorException; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java new file mode 100644 index 00000000..c26d0583 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java @@ -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 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 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 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); + } + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java new file mode 100644 index 00000000..2a6030f3 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java @@ -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; + +/** + *
    + * 签名相关工具类
    + * Created by Binary Wang on 2017-3-23.
    + * @author binarywang(Binary Wang)
    + * 
    + */ +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 params, String signKey) { +// if (this.getConfig().useSandboxForWxPay()) { +// //使用仿真测试环境 +// //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用仿真测试环境 +// signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; +// //return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456"; +// } + + SortedMap 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 params, String signKey) { + String sign = createSign(params, signKey); + return sign.equals(params.get("sign")); + } +} diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResultTest.java similarity index 74% rename from weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResultTest.java rename to weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResultTest.java index aaab37a2..d6a1f7b8 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResultTest.java @@ -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.annotations.*; @@ -12,6 +12,15 @@ import java.util.Map; *
    */ public class WxPayBaseResultTest { + + @Test + public void testGetXmlValue() throws Exception { + } + + @Test + public void testXml2Doc() throws Exception { + } + @Test public void testToMap() throws Exception { WxPayOrderQueryResult result = new WxPayOrderQueryResult(); @@ -39,11 +48,17 @@ public class WxPayBaseResultTest { Map map = result.toMap(); 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 map = result.toMap(); + System.out.println(map); } } diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResultTest.java similarity index 98% rename from weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResultTest.java rename to weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResultTest.java index e0fd9808..d847a155 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResultTest.java @@ -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.annotations.*; diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResultTest.java similarity index 97% rename from weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResultTest.java rename to weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResultTest.java index 6b8a9ae9..6ca4192a 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResultTest.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.mp.bean.pay.result; +package com.github.binarywang.wxpay.bean.result; import org.testng.*; diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPaySendRedpackResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResultTest.java similarity index 98% rename from weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPaySendRedpackResultTest.java rename to weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResultTest.java index f2aa0a94..1985c924 100644 --- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPaySendRedpackResultTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResultTest.java @@ -1,4 +1,4 @@ -package me.chanjar.weixin.mp.bean.pay.result; +package com.github.binarywang.wxpay.bean.result; import com.thoughtworks.xstream.XStream; import me.chanjar.weixin.common.util.xml.XStreamInitializer; diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java new file mode 100644 index 00000000..93b0b8b5 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImplTest.java @@ -0,0 +1,267 @@ +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.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.github.binarywang.wxpay.testbase.XmlWxPayConfig; +import com.google.inject.Inject; +import me.chanjar.weixin.common.exception.WxErrorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.*; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +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 WxPayServiceImplTest { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Inject + private WxPayService payService; + + @Test + public void testGetPayInfo() throws Exception { + Map payInfo = this.payService.getPayInfo(WxPayUnifiedOrderRequest.builder() + .body("我去") + .totalFee(1) + .spbillCreateIp("111111") + .notifyURL("111111") + .tradeType("JSAPI") + .outTradeNo("111111") + .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) + .build()); + this.logger.info(payInfo.toString()); + } + + @Test + public void testDownloadBill() throws Exception { + File file = this.payService.downloadBill("20170101", "ALL", "GZIP", "1111111"); + assertNotNull(file); + //必填字段为空时,抛出异常 + this.payService.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.payService.report(request); + } + + /** + * Test method for {@link WxPayService#refund(WxPayRefundRequest)} . + */ + @Test + public void testRefund() throws Exception { + WxPayRefundResult result = this.payService.refund( + WxPayRefundRequest.newBuilder() + .outRefundNo("aaa") + .outTradeNo("1111") + .totalFee(1222) + .refundFee(111) + .build()); + this.logger.info(result.toString()); + } + + /** + * Test method for {@link WxPayService#refundQuery(java.lang.String, java.lang.String, java.lang.String, java.lang.String)} . + */ + @Test + public void testRefundQuery() throws Exception { + WxPayRefundQueryResult result; + + result = this.payService.refundQuery("1", "", "", ""); + this.logger.info(result.toString()); + + result = this.payService.refundQuery("", "2", "", ""); + this.logger.info(result.toString()); + + result = this.payService.refundQuery("", "", "3", ""); + this.logger.info(result.toString()); + + result = this.payService.refundQuery("", "", "", "4"); + this.logger.info(result.toString()); + + //测试四个参数都填的情况,应该报异常的 + result = this.payService.refundQuery("1", "2", "3", "4"); + this.logger.info(result.toString()); + } + + /** + * Test method for {@link WxPayService#sendRedpack(WxPaySendRedpackRequest)} . + */ + @Test + public void testSendRedpack() throws Exception { + WxPaySendRedpackRequest request = new WxPaySendRedpackRequest(); + request.setActName("abc"); + request.setClientIp("aaa"); + request.setMchBillNo("aaaa"); + request.setReOpenid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()); + WxPaySendRedpackResult redpackResult = this.payService.sendRedpack(request); + this.logger.info(redpackResult.toString()); + } + + /** + * Test method for {@link WxPayService#queryRedpack(java.lang.String)}. + */ + @Test + public void testQueryRedpack() throws Exception { + WxPayRedpackQueryResult redpackResult = this.payService.queryRedpack("aaaa"); + this.logger.info(redpackResult.toString()); + } + + /** + * Test method for {@link WxPayService#unifiedOrder(WxPayUnifiedOrderRequest)}. + */ + @Test + public void testUnifiedOrder() throws WxErrorException { + WxPayUnifiedOrderResult result = this.payService + .unifiedOrder(WxPayUnifiedOrderRequest.builder() + .body("我去") + .totalFee(1) + .spbillCreateIp("111111") + .notifyURL("111111") + .tradeType("JSAPI") + .openid(((XmlWxPayConfig) this.payService.getConfig()).getOpenid()) + .outTradeNo("111111") + .build()); + this.logger.info(result.toString()); + } + + /** + * Test method for {@link WxPayService#queryOrder(java.lang.String, java.lang.String)} . + */ + @Test + public void testQueryOrder() throws WxErrorException { + this.logger.info(this.payService.queryOrder("11212121", null).toString()); + this.logger.info(this.payService.queryOrder(null, "11111").toString()); + } + + /** + * Test method for {@link WxPayService#closeOrder(java.lang.String)} . + */ + @Test + public void testCloseOrder() throws WxErrorException { + this.logger.info(this.payService.closeOrder("11212121").toString()); + } + + /** + * Test method for {@link WxPayService#entPay(WxEntPayRequest)}. + */ + @Test + public void testEntPay() throws WxErrorException { + WxEntPayRequest request = new WxEntPayRequest(); + this.logger.info(this.payService.entPay(request).toString()); + } + + /** + * Test method for {@link WxPayService#queryEntPay(java.lang.String)}. + */ + @Test + public void testQueryEntPay() throws WxErrorException { + this.logger.info(this.payService.queryEntPay("11212121").toString()); + } + + @Test + public void testCreateScanPayQrcodeMode1() throws Exception { + String productId = "abc"; + byte[] bytes = this.payService.createScanPayQrcodeMode1(productId, null, null); + Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); + Files.write(qrcodeFilePath, bytes); + String qrcodeContent = QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()); + this.logger.info(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.payService.createScanPayQrcodeMode2(qrcodeContent, null, null); + Path qrcodeFilePath = Files.createTempFile("qrcode_", ".jpg"); + Files.write(qrcodeFilePath, bytes); + assertEquals(QrcodeUtils.decodeQrcode(qrcodeFilePath.toFile()), qrcodeContent); + } + + @Test + public void testGetOrderNotifyResult() throws Exception { + } + + @Test + public void testMicropay() throws Exception { + WxPayMicropayResult result = this.payService.micropay(WxPayMicropayRequest.newBuilder() + .body("body") + .outTradeNo("aaaaa") + .totalFee(123) + .spbillCreateIp("127.0.0.1") + .authCode("aaa") + .build()); + this.logger.info(result.toString()); + } + + @Test + public void testGetConfig() throws Exception { + // no need to test + } + + @Test + public void testSetConfig() throws Exception { + // no need to test + } + + @Test + public void testReverseOrder() throws Exception { + WxPayOrderReverseResult result = this.payService.reverseOrder(WxPayOrderReverseRequest.newBuilder() + .outTradeNo("1111") + .build()); + assertNotNull(result); + this.logger.info(result.toString()); + } + + @Test + public void testShorturl() throws Exception { + String longUrl = "weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX"; + + String result = this.payService.shorturl(new WxPayShorturlRequest(longUrl)); + assertNotNull(result); + this.logger.info(result); + + result = this.payService.shorturl(longUrl); + assertNotNull(result); + this.logger.info(result); + } + + @Test + public void testAuthcode2Openid() throws Exception { + String authCode = "11111"; + + String result = this.payService.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode)); + assertNotNull(result); + this.logger.info(result); + + result = this.payService.authcode2Openid(authCode); + assertNotNull(result); + this.logger.info(result); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/ApiTestModule.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/ApiTestModule.java new file mode 100644 index 00000000..d5a028d1 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/ApiTestModule.java @@ -0,0 +1,38 @@ +package com.github.binarywang.wxpay.testbase; + +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import com.google.inject.Binder; +import com.google.inject.Module; +import com.thoughtworks.xstream.XStream; +import me.chanjar.weixin.common.util.xml.XStreamInitializer; + +import java.io.IOException; +import java.io.InputStream; + +public class ApiTestModule implements Module { + + @Override + public void configure(Binder binder) { + try (InputStream is1 = ClassLoader.getSystemResourceAsStream("test-config.xml")) { + XmlWxPayConfig config = this.fromXml(XmlWxPayConfig.class, is1); + WxPayService wxService = new WxPayServiceImpl(); + wxService.setConfig(config); + + binder.bind(WxPayService.class).toInstance(wxService); + binder.bind(WxPayConfig.class).toInstance(config); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("unchecked") + private T fromXml(Class clazz, InputStream is) { + XStream xstream = XStreamInitializer.getInstance(); + xstream.alias("xml", clazz); + xstream.processAnnotations(clazz); + return (T) xstream.fromXML(is); + } + +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java new file mode 100644 index 00000000..a10f36f3 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/testbase/XmlWxPayConfig.java @@ -0,0 +1,31 @@ +package com.github.binarywang.wxpay.testbase; + +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.http.ssl.SSLContexts; + +import javax.net.ssl.SSLContext; +import java.io.File; +import java.io.FileInputStream; +import java.security.KeyStore; + +@XStreamAlias("xml") +public class XmlWxPayConfig extends WxPayConfig { + private String openid; + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + @Override + public boolean useSandboxForWxPay() { + //沙箱环境不成熟,有问题无法使用,暂时屏蔽掉 + // return true; + return false; + } +} diff --git a/weixin-java-pay/src/test/resources/logback-test.xml b/weixin-java-pay/src/test/resources/logback-test.xml new file mode 100644 index 00000000..75de28c0 --- /dev/null +++ b/weixin-java-pay/src/test/resources/logback-test.xml @@ -0,0 +1,16 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + diff --git a/weixin-java-pay/src/test/resources/test-config.sample.xml b/weixin-java-pay/src/test/resources/test-config.sample.xml new file mode 100644 index 00000000..1e64cbe4 --- /dev/null +++ b/weixin-java-pay/src/test/resources/test-config.sample.xml @@ -0,0 +1,13 @@ + + 公众号appid + 微信商户平台ID + 商户平台设置的API密钥 + + 商户平台的证书文件地址 + 某个openId +