Bladeren bron

Merge pull request #184 from Wechat-Group/develop

合并Develop,准备发布2.6.0版本
master
Binary Wang 7 jaren geleden
committed by GitHub
bovenliggende
commit
7892f7136b
100 gewijzigde bestanden met toevoegingen van 4571 en 2443 verwijderingen
  1. +1
    -2
      build.gradle
  2. +0
    -0
      contribution.md
  3. +13
    -11
      pom.xml
  4. +9
    -8
      readme.md
  5. +1
    -11
      weixin-java-common/pom.xml
  6. +27
    -28
      weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java
  7. +70
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java
  8. +16
    -2
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java
  9. +4
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java
  10. +139
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java
  11. +49
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java
  12. +3
    -10
      weixin-java-cp/pom.xml
  13. +49
    -86
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java
  14. +2
    -2
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java
  15. +23
    -45
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java
  16. +48
    -0
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java
  17. +3
    -2
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java
  18. +26
    -1
      weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java
  19. +17
    -0
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/TestConstants.java
  20. +4
    -4
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java
  21. +53
    -15
      weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java
  22. +3
    -19
      weixin-java-mp/pom.xml
  23. +0
    -37
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java
  24. +19
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java
  25. +22
    -91
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java
  26. +90
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java
  27. +2
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java
  28. +0
    -7
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java
  29. +18
    -4
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java
  30. +0
    -521
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java
  31. +2
    -3
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java
  32. +29
    -45
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java
  33. +15
    -6
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java
  34. +12
    -10
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java
  35. +46
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java
  36. +56
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java
  37. +7
    -7
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java
  38. +0
    -235
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResult.java
  39. +0
    -392
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderNotifyResult.java
  40. +3
    -3
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java
  41. +14
    -14
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java
  42. +71
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java
  43. +2
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java
  44. +7
    -0
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java
  45. +1
    -1
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java
  46. +3
    -3
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java
  47. +7
    -2
      weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java
  48. +5
    -4
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java
  49. +5
    -4
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java
  50. +37
    -40
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java
  51. +0
    -195
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java
  52. +1
    -1
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java
  53. +1
    -2
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java
  54. +0
    -6
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java
  55. +17
    -0
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConstants.java
  56. +31
    -0
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java
  57. +2
    -1
      weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java
  58. +1
    -1
      weixin-java-osgi/pom.xml
  59. +47
    -0
      weixin-java-pay/pom.xml
  60. +63
    -64
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java
  61. +64
    -60
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java
  62. +13
    -30
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java
  63. +81
    -75
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java
  64. +46
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java
  65. +101
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java
  66. +19
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java
  67. +380
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java
  68. +6
    -2
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java
  69. +11
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java
  70. +163
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java
  71. +6
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java
  72. +13
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java
  73. +158
    -2
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java
  74. +6
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java
  75. +32
    -26
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java
  76. +46
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java
  77. +70
    -33
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java
  78. +2
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayQueryResult.java
  79. +2
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayResult.java
  80. +40
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java
  81. +347
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java
  82. +1
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java
  83. +328
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java
  84. +1
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java
  85. +392
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java
  86. +5
    -7
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java
  87. +35
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java
  88. +1
    -1
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java
  89. +14
    -16
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java
  90. +5
    -5
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java
  91. +4
    -4
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java
  92. +34
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java
  93. +2
    -2
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java
  94. +24
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxScanPayNotifyResult.java
  95. +157
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
  96. +129
    -130
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java
  97. +117
    -89
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java
  98. +416
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java
  99. +84
    -0
      weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java
  100. +20
    -5
      weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResultTest.java

+ 1
- 2
build.gradle Bestand weergeven

@@ -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'
}


CONTRIBUTION.md → contribution.md Bestand weergeven


+ 13
- 11
pom.xml Bestand weergeven

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>2.5.0</version>
<version>2.6.0</version>
<packaging>pom</packaging>
<name>WeiXin Java Tools - Parent</name>
<description>微信公众号、企业号上级POM</description>
@@ -86,7 +86,8 @@
<module>weixin-java-common</module>
<module>weixin-java-cp</module>
<module>weixin-java-mp</module>
<module>weixin-java-osgi</module>
<module>weixin-java-pay</module>
<!--module>weixin-java-osgi</module-->
</modules>

<properties>
@@ -105,6 +106,7 @@
<commons-io.version>2.5</commons-io.version>
<commons-codec.version>1.10</commons-codec.version>
<jetty.version>9.3.0.RC0</jetty.version>
<jedis.version>2.9.0</jedis.version>
</properties>

<dependencies>
@@ -113,6 +115,11 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
@@ -152,17 +159,12 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
@@ -194,9 +196,9 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
</dependencies>
</dependencyManagement>


+ 9
- 8
readme.md Bestand weergeven

@@ -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:
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>2.5.7.BETA</version>
<version>2.6.0</version>
</dependency>
```
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:
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>2.5.0</version>
<version>2.6.0</version>
</dependency>
```
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:
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-cp</artifactId>
<version>2.5.0</version>
<version>2.6.0</version>
</dependency>
```
gradle:
```groovy
compile 'com.github.binarywang:weixin-java-cp:2.5.0'
compile 'com.github.binarywang:weixin-java-cp:2.6.0'
```

+ 1
- 11
weixin-java-common/pom.xml Bestand weergeven

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>2.5.0</version>
<version>2.6.0</version>
</parent>

<artifactId>weixin-java-common</artifactId>
@@ -14,16 +14,6 @@
<description>微信公众号、企业号Java SDK Common</description>

<dependencies>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>


+ 27
- 28
weixin-java-common/src/main/java/me/chanjar/weixin/common/api/WxConsts.java Bestand weergeven

@@ -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, "涉嫌其他");
}
}

+ 70
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/bean/menu/WxMenuButton.java Bestand weergeven

@@ -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;

/**
* <pre>
* 菜单的响应动作类型:
* view表示网页类型,
* click表示点击类型,
* miniprogram表示小程序类型
* </pre>
*/
private String type;

/**
* 菜单标题,不超过16个字节,子菜单不超过60个字节
*/
private String name;

/**
* <pre>
* 菜单KEY值,用于消息接口推送,不超过128字节
* click等点击类型必须
* </pre>
*/
private String key;

/**
* <pre>
* 网页链接,用户点击菜单可打开链接,不超过1024字节。type为miniprogram时,不支持小程序的老版本客户端将打开本url。
* view、miniprogram类型必须
* </pre>
*/
private String url;

/**
* <pre>
* 调用新增永久素材接口返回的合法media_id
* media_id类型和view_limited类型必须
* </pre>
*/
@SerializedName("media_id")
private String mediaId;

/**
* <pre>
* 小程序的appid
* miniprogram类型必须
* </pre>
*/
@SerializedName("appid")
private String appId;

/**
* <pre>
* 小程序的页面路径
* miniprogram类型必须
* </pre>
*/
@SerializedName("pagepath")
private String pagePath;

@SerializedName("sub_button")
private List<WxMenuButton> subButtons = new ArrayList<>();

@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;
}
}

+ 16
- 2
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/GsonHelper.java Bestand weergeven

@@ -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<Long> result = Lists.newArrayList();
for (int i = 0; i < jsonArray.size(); i++) {
result.add(jsonArray.get(i).getAsLong());
}

return result.toArray(new Long[0]);
}

public static JsonArray getAsJsonArray(JsonElement element) {
return element == null ? null : element.getAsJsonArray();
}


+ 4
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/json/WxMenuGsonAdapter.java Bestand weergeven

@@ -46,6 +46,8 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, 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<WxMenu>, 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;
}



+ 139
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/MpnewsArticle.java Bestand weergeven

@@ -0,0 +1,139 @@
package me.chanjar.weixin.cp.bean.article;

/**
* <pre>
* Created by BinaryWang on 2017/3/27.
* </pre>
* @author Binary Wang
*/
public class MpnewsArticle {
private String title;
private String thumbMediaId;
private String author;
private String contentSourceUrl;
private String content;
private String digest;
private String showCoverPic;

private MpnewsArticle(Builder builder) {
setTitle(builder.title);
setThumbMediaId(builder.thumbMediaId);
setAuthor(builder.author);
setContentSourceUrl(builder.contentSourceUrl);
setContent(builder.content);
setDigest(builder.digest);
setShowCoverPic(builder.showCoverPic);
}

public static Builder newBuilder() {
return new Builder();
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getThumbMediaId() {
return thumbMediaId;
}

public void setThumbMediaId(String thumbMediaId) {
this.thumbMediaId = thumbMediaId;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public String getContentSourceUrl() {
return contentSourceUrl;
}

public void setContentSourceUrl(String contentSourceUrl) {
this.contentSourceUrl = contentSourceUrl;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public String getDigest() {
return digest;
}

public void setDigest(String digest) {
this.digest = digest;
}

public String getShowCoverPic() {
return showCoverPic;
}

public void setShowCoverPic(String showCoverPic) {
this.showCoverPic = showCoverPic;
}

public static final class Builder {
private String title;
private String thumbMediaId;
private String author;
private String contentSourceUrl;
private String content;
private String digest;
private String showCoverPic;

private Builder() {
}

public Builder title(String title) {
this.title = title;
return this;
}

public Builder thumbMediaId(String thumbMediaId) {
this.thumbMediaId = thumbMediaId;
return this;
}

public Builder author(String author) {
this.author = author;
return this;
}

public Builder contentSourceUrl(String contentSourceUrl) {
this.contentSourceUrl = contentSourceUrl;
return this;
}

public Builder content(String content) {
this.content = content;
return this;
}

public Builder digest(String digest) {
this.digest = digest;
return this;
}

public Builder showCoverPic(String showCoverPic) {
this.showCoverPic = showCoverPic;
return this;
}

public MpnewsArticle build() {
return new MpnewsArticle(this);
}
}
}

+ 49
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/cp/bean/article/NewArticle.java Bestand weergeven

@@ -0,0 +1,49 @@
package me.chanjar.weixin.cp.bean.article;

/**
* <pre>
* Created by BinaryWang on 2017/3/27.
* </pre>
*
* @author Binary Wang
*/
public class NewArticle {

private String title;
private String description;
private String url;
private String picUrl;

public String getTitle() {
return this.title;
}

public void setTitle(String title) {
this.title = title;
}

public String getDescription() {
return this.description;
}

public void setDescription(String description) {
this.description = description;
}

public String getUrl() {
return this.url;
}

public void setUrl(String url) {
this.url = url;
}

public String getPicUrl() {
return this.picUrl;
}

public void setPicUrl(String picUrl) {
this.picUrl = picUrl;
}

}

+ 3
- 10
weixin-java-cp/pom.xml Bestand weergeven

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>2.5.0</version>
<version>2.6.0</version>
</parent>

<artifactId>weixin-java-cp</artifactId>
@@ -20,9 +20,8 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
@@ -49,12 +48,6 @@
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<optional>true</optional>
</dependency>
</dependencies>

<build>


+ 49
- 86
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java Bestand weergeven

@@ -28,9 +28,9 @@ public interface WxCpService {
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性
* </pre>
*
* @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 {
* 企业在员工验证成功后,调用本方法告诉企业号平台该员工关注成功。
* </pre>
*
* @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 {
* </pre>
*
* @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 {
* </pre>
*
* @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=上传下载多媒体文件
* </pre>
*
* @param media_id
* @param mediaId 媒体id
* @return 保存到本地的临时文件
* @throws WxErrorException
*/
File mediaDownload(String media_id) throws WxErrorException;
File mediaDownload(String mediaId) throws WxErrorException;

/**
* <pre>
* 发送消息
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=发送消息
* 详情请见: http://qydev.weixin.qq.com/wiki/index.php?title=%E5%8F%91%E9%80%81%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
* </pre>
*
* @param message
* @throws WxErrorException
* @param message 要发送的消息对象
*/
void messageSend(WxCpMessage message) throws WxErrorException;

@@ -158,8 +150,7 @@ public interface WxCpService {
* 注意: 这个方法使用WxCpConfigStorage里的agentId
* </pre>
*
* @param menu
* @throws WxErrorException
* @param menu 菜单对象
* @see #menuCreate(Integer, WxMenu)
*/
void menuCreate(WxMenu menu) throws WxErrorException;
@@ -173,8 +164,7 @@ public interface WxCpService {
* </pre>
*
* @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
* </pre>
*
* @throws WxErrorException
* @see #menuDelete(Integer)
*/
void menuDelete() throws WxErrorException;
@@ -201,7 +190,6 @@ public interface WxCpService {
* </pre>
*
* @param agentId 企业号应用的id
* @throws WxErrorException
* @see #menuDelete()
*/
void menuDelete(Integer agentId) throws WxErrorException;
@@ -214,7 +202,6 @@ public interface WxCpService {
* 注意: 这个方法使用WxCpConfigStorage里的agentId
* </pre>
*
* @throws WxErrorException
* @see #menuGet(Integer)
*/
WxMenu menuGet() throws WxErrorException;
@@ -228,7 +215,6 @@ public interface WxCpService {
* </pre>
*
* @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=部门管理接口
* </pre>
*
* @throws WxErrorException
*/
List<WxCpDepart> departGet() throws WxErrorException;

@@ -264,7 +247,6 @@ public interface WxCpService {
* </pre>
*
* @param group 要更新的group,group的id,name必须设置
* @throws WxErrorException
*/
void departUpdate(WxCpDepart group) throws WxErrorException;

@@ -273,8 +255,7 @@ public interface WxCpService {
* 部门管理接口 - 删除部门
* </pre>
*
* @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<WxCpUser> 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<WxCpUser> 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;

/**
* <pre>
* 批量删除成员
*
* http://qydev.weixin.qq.com/wiki/index.php?title=管理成员#.E6.89.B9.E9.87.8F.E5.88.A0.E9.99.A4.E6.88.90.E5.91.98
* </pre>
*
* @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<WxCpUser> tagGetUsers(String tagId) throws WxErrorException;

/**
* 增加标签成员
*
* @param tagId
* @param userIds
* @param tagId 标签id
* @param userIds 用户ID 列表
*/
void tagAddUsers(String tagId, List<String> userIds, List<String> partyIds) throws WxErrorException;

/**
* <pre>
* <pre>
* 构造oauth2授权的url连接
* </pre>
*
* @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
* </pre>
*
* @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
* </pre>
*
* @param code
* @param code 微信oauth授权返回的代码
* @return [userid, deviceid]
* @see #oauth2getUserInfo(Integer, String)
*/
@@ -439,7 +412,7 @@ public interface WxCpService {
* </pre>
*
* @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<String> 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 {
* </pre>
*
* @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}的实现方法
* </pre>
*
* @param executor
* @param uri
* @param data
* @param <T>
* @param <E>
* @throws WxErrorException
* @param executor 执行器
* @param uri 请求地址
* @param data 参数
* @param <T> 请求值类型
* @param <E> 返回值类型
*/
<T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException;

/**
* 注入 {@link WxCpConfigStorage} 的实现
*
* @param wxConfigProvider
* @param wxConfigProvider 配置对象
*/
void setWxCpConfigStorage(WxCpConfigStorage wxConfigProvider);

@@ -525,7 +493,7 @@ public interface WxCpService {
* 默认:1000ms
* </pre>
*
* @param retrySleepMillis
* @param retrySleepMillis 重试休息时间
*/
void setRetrySleepMillis(int retrySleepMillis);

@@ -535,7 +503,7 @@ public interface WxCpService {
* 默认:5次
* </pre>
*
* @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}
* </pre>
*
* @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;
}

+ 2
- 2
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java Bestand weergeven

@@ -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);
}




+ 23
- 45
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpMessage.java Bestand weergeven

@@ -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<WxArticle> articles = new ArrayList<>();
private List<NewArticle> articles = new ArrayList<>();
private List<MpnewsArticle> mpnewsArticles = new ArrayList<>();

public List<MpnewsArticle> getMpnewsArticles() {
return mpnewsArticles;
}

public void setMpnewsArticles(List<MpnewsArticle> mpnewsArticles) {
this.mpnewsArticles = mpnewsArticles;
}

/**
* 获得文本消息builder
@@ -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}
* </pre>
*
* @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<WxArticle> getArticles() {
public List<NewArticle> getArticles() {
return this.articles;
}

public void setArticles(List<WxArticle> articles) {
public void setArticles(List<NewArticle> 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;
}

}

}

+ 48
- 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/MpnewsBuilder.java Bestand weergeven

@@ -0,0 +1,48 @@
package me.chanjar.weixin.cp.bean.messagebuilder;

import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.cp.bean.WxCpMessage;
import me.chanjar.weixin.cp.bean.article.MpnewsArticle;

import java.util.ArrayList;
import java.util.List;

/**
* mpnews类型的图文消息builder
* <pre>
* 用法:
* WxCustomMessage m = WxCustomMessage.MPNEWS().addArticle(article).toUser(...).build();
* </pre>
*
* @author Binary Wang
*/
public final class MpnewsBuilder extends BaseBuilder<MpnewsBuilder> {
private List<MpnewsArticle> articles = new ArrayList<>();

private String mediaId;

public MpnewsBuilder() {
this.msgType = WxConsts.CUSTOM_MSG_MPNEWS;
}

public MpnewsBuilder mediaId(String mediaId) {
this.mediaId = mediaId;
return this;
}

public MpnewsBuilder addArticle(MpnewsArticle article) {
this.articles.add(article);
return this;
}

@Override
public WxCpMessage build() {
WxCpMessage m = super.build();
m.setMpnewsArticles(this.articles);
if (this.mediaId != null) {
m.setMediaId(this.mediaId);
}

return m;
}
}

+ 3
- 2
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/messagebuilder/NewsBuilder.java Bestand weergeven

@@ -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<NewsBuilder> {

private List<WxCpMessage.WxArticle> articles = new ArrayList<>();
private List<NewArticle> 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;
}


+ 26
- 1
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/util/json/WxCpMessageGsonAdapter.java Bestand weergeven

@@ -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<WxCpMessage> {
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<WxCpMessage> {
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;
}



+ 17
- 0
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/TestConstants.java Bestand weergeven

@@ -0,0 +1,17 @@
package me.chanjar.weixin.cp.api;

/**
* <pre>
* 仅供测试使用的一些常量
* Created by Binary Wang on 2017-3-9.
* </pre>
*/
public class TestConstants {
///////////////////////
// 文件类型
///////////////////////
public static final String FILE_JPG = "jpeg";
public static final String FILE_MP3 = "mp3";
public static final String FILE_AMR = "amr";
public static final String FILE_MP4 = "mp4";
}

+ 4
- 4
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMediaAPITest.java Bestand weergeven

@@ -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"}
};
}



+ 53
- 15
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/WxCpMessageTest.java Bestand weergeven

@@ -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\"}}");
}

}

+ 3
- 19
weixin-java-mp/pom.xml Bestand weergeven

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>2.5.0</version>
<version>2.6.0</version>
</parent>
<artifactId>weixin-java-mp</artifactId>
<name>WeiXin Java Tools - MP</name>
@@ -18,21 +18,11 @@
<artifactId>weixin-java-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
@@ -55,14 +45,8 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>xml-path</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>qrcode-utils</artifactId>
<version>1.0</version>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>



+ 0
- 37
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java Bestand weergeven

@@ -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();

/**
* 交易类型
* <pre>
* JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
* </pre>
*
* @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();
}

+ 19
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDeviceService.java Bestand weergeven

@@ -76,5 +76,24 @@ public interface WxMpDeviceService {
*/
WxDeviceBindResult compelUnbind(WxDeviceBind wxDeviceBind) throws WxErrorException;

/**
* <pre>
* 通过device type和device id 获取设备主人的openid
* 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-11
* </pre>
* @param deviceType 设备类型,目前为"公众账号原始ID"
* @param deviceId 设备ID
* @return WxDeviceOpenIdResult
*/
WxDeviceOpenIdResult getOpenId(String deviceType,String deviceId) throws WxErrorException;

/**
* <pre>
* 通过openid获取用户在当前devicetype下绑定的deviceid列表`
* 详情请见:http://iot.weixin.qq.com/wiki/new/index.html?page=3-4-12
* </pre>
* @param openId 要查询的用户的openid
* @return WxDeviceBindDeviceResult
*/
WxDeviceBindDeviceResult getBindDevice(String openId) throws WxErrorException;
}

+ 22
- 91
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java Bestand weergeven

@@ -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;
}
}

+ 90
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInRedisConfigStorage.java Bestand weergeven

@@ -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;
}
}

+ 2
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpQrcodeService.java Bestand weergeven

@@ -6,8 +6,10 @@ import me.chanjar.weixin.mp.bean.result.WxMpQrCodeTicket;
import java.io.File;

/**
* <pre>
* 二维码相关操作接口
* 文档地址:https://mp.weixin.qq.com/wiki?action=doc&id=mp1443433542&t=0.9274944716856435
* </pre>
* @author Binary Wang
*/
public interface WxMpQrcodeService {


+ 0
- 7
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java Bestand weergeven

@@ -311,13 +311,6 @@ public interface WxMpService {
*/
WxMpCardService getCardService();

/**
* 返回微信支付相关接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMpPayService
*/
WxMpPayService getPayService();

/**
* 返回数据分析统计相关接口方法的实现类对象,以方便调用其各个接口
*


+ 18
- 4
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpDeviceServiceImpl.java Bestand weergeven

@@ -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);
}
}


+ 0
- 521
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImpl.java Bestand weergeven

@@ -1,521 +0,0 @@
package me.chanjar.weixin.mp.api.impl;

import com.github.binarywang.utils.qrcode.QrcodeUtils;
import com.google.common.collect.Maps;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.BeanUtils;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpPayService;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.pay.request.*;
import me.chanjar.weixin.mp.bean.pay.result.*;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
* Created by Binary Wang on 2016/7/28.
*
* @author binarywang (https://github.com/binarywang)
*/
public class WxMpPayServiceImpl implements WxMpPayService {

private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com";
private static final String[] TRADE_TYPES = new String[]{"JSAPI", "NATIVE", "APP"};
private static final String[] REFUND_ACCOUNT = new String[]{"REFUND_SOURCE_RECHARGE_FUNDS", "REFUND_SOURCE_UNSETTLED_FUNDS"};
private static final String[] BILL_TYPE = new String[]{"ALL","REFUND","SUCCESS"};;
private final Logger log = LoggerFactory.getLogger(this.getClass());
private WxMpService wxMpService;

public WxMpPayServiceImpl(WxMpService wxMpService) {
this.wxMpService = wxMpService;
}

private WxMpConfigStorage getConfig() {
return wxMpService.getWxMpConfigStorage();
}

private String getPayBaseUrl(){
if(this.getConfig().useSandboxForWxPay()){
return PAY_BASE_URL + "/sandboxnew";
}

return PAY_BASE_URL;
}

@Override
public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException {
this.initRequest(request);
if (StringUtils.isBlank(request.getOpUserId())) {
request.setOpUserId(this.getConfig().getPartnerId());
}
this.checkParameters(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/secapi/pay/refund";
String responseContent = this.executeWithKey(url, request.toXML());
WxPayRefundResult result = WxPayRefundResult.fromXML(responseContent, WxPayRefundResult.class);
this.checkResult(result);
return result;
}

@Override
public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId) throws WxErrorException {
if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo) && StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) ||
(StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo) && StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) {
throw new IllegalArgumentException("transaction_id , out_trade_no,out_refund_no, refund_id 必须四选一");
}

WxPayRefundQueryRequest request = new WxPayRefundQueryRequest();
this.initRequest(request);
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo));
request.setTransactionId(StringUtils.trimToNull(transactionId));
request.setOutRefundNo(StringUtils.trimToNull(outRefundNo));
request.setRefundId(StringUtils.trimToNull(refundId));
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/pay/refundquery";
String responseContent = this.executeRequest(url, request.toXML());
WxPayRefundQueryResult result = WxPayRefundQueryResult.fromXML(responseContent, WxPayRefundQueryResult.class);
result.composeRefundRecords();
this.checkResult(result);
return result;
}


private void checkResult(WxPayBaseResult result) throws WxErrorException {
//校验返回结果签名
Map<String, String> map = result.toMap();
if (result.getSign() != null &&!this.checkSign(map)) {
log.debug("校验结果签名失败,参数:{}", map);
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build());
}

//校验结果是否成功
if (!"SUCCESS".equalsIgnoreCase(result.getReturnCode())
|| !"SUCCESS".equalsIgnoreCase(result.getResultCode())) {
WxError error = WxError.newBuilder().setErrorCode(-1)
.setErrorMsg("返回代码: " + result.getReturnCode() + ", 返回信息: "
+ result.getReturnMsg() + ", 结果代码: " + result.getResultCode() + ", 错误代码: "
+ result.getErrCode() + ", 错误详情: " + result.getErrCodeDes())
.build();
log.error("结果业务代码异常,参数:{},详细:{}", map, error);
throw new WxErrorException(error);
}
}

private void checkParameters(WxPayDownloadBillRequest request) throws WxErrorException {
BeanUtils.checkRequiredFields(request);

if (StringUtils.isNotBlank(request.getTarType()) && !"GZIP".equals(request.getTarType())) {
throw new IllegalArgumentException("tar_type值如果存在,只能为GZIP");
}

if ( !ArrayUtils.contains(BILL_TYPE, request.getBillType())) {
throw new IllegalArgumentException("bill_tpye目前必须为" + Arrays.toString(BILL_TYPE) + "其中之一,实际值:" + request.getBillType());
}

}

private void checkParameters(WxPayRefundRequest request) throws WxErrorException {
BeanUtils.checkRequiredFields(request);

if (StringUtils.isNotBlank(request.getRefundAccount())) {
if (!ArrayUtils.contains(REFUND_ACCOUNT, request.getRefundAccount())) {
throw new IllegalArgumentException("refund_account目前必须为" + Arrays.toString(REFUND_ACCOUNT) + "其中之一,实际值:" + request.getRefundAccount());
}
}

if (StringUtils.isBlank(request.getOutTradeNo()) && StringUtils.isBlank(request.getTransactionId())) {
throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时为空,必须提供一个");
}
}

@Override
public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException {
try {
log.trace("微信支付回调参数详细:{}", xmlData);
WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData);
log.debug("微信支付回调结果对象:{}", result);
this.checkResult(result);
return result;
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
throw e;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new WxErrorException(WxError.newBuilder().setErrorMsg("发生异常" + e.getMessage()).build());
}
}

@Override
public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request)
throws WxErrorException {
this.initRequest(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack";
if (request.getAmtType() != null) {
//裂变红包
url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack";
}
String responseContent = this.executeWithKey(url, request.toXML());
WxPaySendRedpackResult result = WxPaySendRedpackResult.fromXML(responseContent, WxPaySendRedpackResult.class);
this.checkResult(result);
return result;
}

@Override
public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException {
WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest();
request.setMchBillNo(mchBillNo);
request.setBillType("MCHT");
initRequest(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo";
String responseContent = this.executeWithKey(url, request.toXML());
WxPayRedpackQueryResult result = WxPayRedpackQueryResult.fromXML(responseContent, WxPayRedpackQueryResult.class);
this.checkResult(result);
return result;
}

@Override
public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException {
if ((StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) ||
(StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo))) {
throw new IllegalArgumentException("transaction_id 和 out_trade_no 不能同时存在或同时为空,必须二选一");
}

WxPayOrderQueryRequest request = new WxPayOrderQueryRequest();
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo));
request.setTransactionId(StringUtils.trimToNull(transactionId));
initRequest(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/pay/orderquery";
String responseContent = this.executeRequest(url, request.toXML());
WxPayOrderQueryResult result = WxPayOrderQueryResult.fromXML(responseContent, WxPayOrderQueryResult.class);
result.composeCoupons();
this.checkResult(result);
return result;
}

@Override
public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException {
if (StringUtils.isBlank(outTradeNo)) {
throw new IllegalArgumentException("out_trade_no 不能为空");
}

WxPayOrderCloseRequest request = new WxPayOrderCloseRequest();
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo));
initRequest(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/pay/closeorder";
String responseContent = this.executeRequest(url, request.toXML());
WxPayOrderCloseResult result = WxPayOrderCloseResult.fromXML(responseContent, WxPayOrderCloseResult.class);
this.checkResult(result);

return result;
}

@Override
public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request)
throws WxErrorException {

this.initRequest(request);
if (StringUtils.isBlank(request.getNotifyURL())) {
request.setNotifyURL(getConfig().getNotifyURL());
}
if (StringUtils.isBlank(request.getTradeType())) {
request.setTradeType(getConfig().getTradeType());
}
checkParameters(request);//校验参数
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/pay/unifiedorder";
String xmlParam = request.toXML();
log.debug("微信统一下单接口,URL:{},参数:{}", url, xmlParam);

String responseContent = this.executeRequest(url, xmlParam);
log.debug("微信统一下单接口,URL:{},结果:{}", url, responseContent);
WxPayUnifiedOrderResult result = WxPayUnifiedOrderResult.fromXML(responseContent, WxPayUnifiedOrderResult.class);
this.checkResult(result);
return result;
}

private void initRequest(WxPayBaseRequest request) {
if (StringUtils.isBlank(request.getAppid())) {
request.setAppid(getConfig().getAppId());
}
if (StringUtils.isBlank(request.getMchId())) {
request.setMchId(getConfig().getPartnerId());
}
if (StringUtils.isBlank(request.getNonceStr())) {
request.setNonceStr(String.valueOf(System.currentTimeMillis()));
}
}

private void checkParameters(WxPayUnifiedOrderRequest request) throws WxErrorException {
BeanUtils.checkRequiredFields(request);

if (!ArrayUtils.contains(TRADE_TYPES, request.getTradeType())) {
throw new IllegalArgumentException("trade_type目前必须为" + Arrays.toString(TRADE_TYPES) + "其中之一,实际值:" + request.getTradeType());
}

if ("JSAPI".equals(request.getTradeType()) && request.getOpenid() == null) {
throw new IllegalArgumentException("当 trade_type是'JSAPI'时未指定openid");
}

if ("NATIVE".equals(request.getTradeType()) && request.getProductId() == null) {
throw new IllegalArgumentException("当 trade_type是'NATIVE'时未指定product_id");
}
}

@Override
public Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException {
WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request);
String prepayId = unifiedOrderResult.getPrepayId();
if (StringUtils.isBlank(prepayId)) {
throw new RuntimeException(String.format("Failed to get prepay id due to error code '%s'(%s).",
unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes()));
}

Map<String, String> payInfo = new HashMap<>();
payInfo.put("appId", getConfig().getAppId());
// 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis()));
payInfo.put("package", "prepay_id=" + prepayId);
payInfo.put("signType", "MD5");
if ("NATIVE".equals(request.getTradeType())) {
payInfo.put("codeUrl", unifiedOrderResult.getCodeURL());
}
payInfo.put("paySign", this.createSign(payInfo));
return payInfo;
}

@Override
public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException {
this.initRequest(request);
BeanUtils.checkRequiredFields(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers";

String responseContent = this.executeWithKey(url, request.toXML());
WxEntPayResult result = WxEntPayResult.fromXML(responseContent, WxEntPayResult.class);
this.checkResult(result);
return result;
}

@Override
public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException {
WxEntPayQueryRequest request = new WxEntPayQueryRequest();
this.initRequest(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo";
String responseContent = this.executeWithKey(url, request.toXML());
WxEntPayQueryResult result = WxEntPayQueryResult.fromXML(responseContent, WxEntPayQueryResult.class);
this.checkResult(result);
return result;
}

@Override
public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) {
//weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?");
Map<String, String> params = Maps.newHashMap();
params.put("appid", this.getConfig().getAppId());
params.put("mch_id", this.getConfig().getPartnerId());
params.put("product_id", productId);
params.put("time_stamp", String.valueOf(System.currentTimeMillis()));
params.put("nonce_str", String.valueOf(System.currentTimeMillis()));

String sign = this.createSign(params);
params.put("sign", sign);

for (String key : params.keySet()) {
codeUrl.append(key + "=" + params.get(key) + "&");
}

String content = codeUrl.toString().substring(0, codeUrl.length() - 1);
if (sideLength == null || sideLength < 1) {
return QrcodeUtils.createQrcode(content, logoFile);
}

return QrcodeUtils.createQrcode(content, sideLength, logoFile);
}

@Override
public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) {
if (sideLength == null || sideLength < 1) {
return QrcodeUtils.createQrcode(codeUrl, logoFile);
}

return QrcodeUtils.createQrcode(codeUrl, sideLength, logoFile);
}

public void report(WxPayReportRequest request) throws WxErrorException {
BeanUtils.checkRequiredFields(request);
this.initRequest(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/payitil/report";
String responseContent = this.wxMpService.post(url, request.toXML());
WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class);
this.checkResult(result);
}

@Override
public File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException {
WxPayDownloadBillRequest request = new WxPayDownloadBillRequest();
this.initRequest(request);
request.setBillType(billType);
request.setBillDate(billDate);
request.setTarType(tarType);
request.setDeviceInfo(deviceInfo);
this.checkParameters(request);
request.setSign(this.createSign(request));

String url = this.getPayBaseUrl() + "/pay/downloadbill";
//TODO 返回的内容可能是文件流,也有可能是xml,需要区分对待
String responseContent = this.wxMpService.post(url, request.toXML());

WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class);
this.checkResult(result);
return null;
}

private String executeRequest(String url, String requestStr) throws WxErrorException {
HttpPost httpPost = new HttpPost(url);
if (this.wxMpService.getHttpProxy() != null) {
httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build());
}

try (CloseableHttpClient httpclient = HttpClients.custom().build()) {
httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1")));

try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result);
return result;
}
} catch (IOException e) {
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage());
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e);
} finally {
httpPost.releaseConnection();
}
}

private String executeWithKey(String url, String requestStr) throws WxErrorException {
try {
SSLContext sslContext = getConfig().getSslContext();
if (null == sslContext) {
throw new IllegalArgumentException("请先初始化配置类(即WxMpConfigStorage的实现类)中的SSLContext!");
}

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null,
new DefaultHostnameVerifier());

HttpPost httpPost = new HttpPost(url);
if (this.wxMpService.getHttpProxy() != null) {
httpPost.setConfig(RequestConfig.custom().setProxy(this.wxMpService.getHttpProxy()).build());
}

try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) {
httpPost.setEntity(new StringEntity(new String(requestStr.getBytes("UTF-8"), "ISO-8859-1")));
try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result);
return result;
}
} finally {
httpPost.releaseConnection();
}
} catch (Exception e) {
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage());
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e);
}
}

@Override
public String createSign(Object xmlBean) {
return this.createSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey());
}

@Override
public String createSign(Object xmlBean, String signKey) {
return this.createSign(BeanUtils.xmlBean2Map(xmlBean), signKey);
}

@Override
public String createSign(Map<String, String> params) {
return this.createSign(params, this.getConfig().getPartnerKey());
}

@Override
public String createSign(Map<String, String> params, String signKey) {
if(this.getConfig().useSandboxForWxPay()){
//使用仿真测试环境
//TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用这个仿真测试环境
signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
//return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
}

SortedMap<String, String> sortedMap = new TreeMap<>(params);

StringBuilder toSign = new StringBuilder();
for (String key : sortedMap.keySet()) {
String value = params.get(key);
if (StringUtils.isNotEmpty(value) && !"sign".equals(key) && !"key".equals(key)) {
toSign.append(key + "=" + value + "&");
}
}

toSign.append("key=" + signKey);
return DigestUtils.md5Hex(toSign.toString()).toUpperCase();
}

@Override
public boolean checkSign(Object xmlBean) {
return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), getConfig().getPartnerKey());
}

@Override
public boolean checkSign(Object xmlBean, String signKey) {
return this.checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey);
}

@Override
public boolean checkSign(Map<String, String> params) {
return this.checkSign(params, getConfig().getPartnerKey());
}

@Override
public boolean checkSign(Map<String, String> params, String signKey) {
String sign = this.createSign(params, signKey);
return sign.equals(params.get("sign"));
}
}

+ 2
- 3
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpQrcodeServiceImpl.java Bestand weergeven

@@ -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);


+ 29
- 45
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceImpl.java Bestand weergeven

@@ -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;


+ 15
- 6
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/WxMpMassPreviewMessage.java Bestand weergeven

@@ -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不匹配的话,会返回系统繁忙的错误
* </pre>
*
* @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);
}


+ 12
- 10
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/BaseResp.java Bestand weergeven

@@ -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;
}
}
}

+ 46
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceBindDeviceResult.java Bestand weergeven

@@ -0,0 +1,46 @@
package me.chanjar.weixin.mp.bean.device;

import com.google.gson.annotations.SerializedName;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;

import java.util.List;

/**
* Created by keungtung on 16/12/2016.
*/
public class WxDeviceBindDeviceResult extends AbstractDeviceBean {
@SerializedName("resp_msg")
private RespMsg respMsg;
@SerializedName("openid")
private String openId;
@SerializedName("device_list")
private List<Device> devices;

public static WxDeviceBindDeviceResult fromJson(String json) {
return WxMpGsonBuilder.create().fromJson(json, WxDeviceBindDeviceResult.class);
}

private class Device {
@SerializedName("device_type")
private String deviceType;
@SerializedName("device_id")
private String deviceId;

public String getDeviceType() {
return deviceType;
}

public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}

public String getDeviceId() {
return deviceId;
}

public void setDeviceId(String deviceId) {
this.deviceId = deviceId;
}
}

}

+ 56
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceOpenIdResult.java Bestand weergeven

@@ -0,0 +1,56 @@
package me.chanjar.weixin.mp.bean.device;

import com.google.gson.annotations.SerializedName;
import me.chanjar.weixin.mp.util.json.WxMpGsonBuilder;

import java.util.List;

/**
* Created by keungtung on 16/12/2016.
*/
public class WxDeviceOpenIdResult extends AbstractDeviceBean {
@SerializedName("errcode")
private Integer errCode;
@SerializedName("errmsg")
private String errMsg;
@SerializedName("open_id")
private List<String> openIds;
@SerializedName("resp_msg")
private RespMsg respMsg;

public static WxDeviceOpenIdResult fromJson(String json) {
return WxMpGsonBuilder.create().fromJson(json, WxDeviceOpenIdResult.class);
}

public Integer getErrCode() {
return errCode;
}

public void setErrCode(Integer errCode) {
this.errCode = errCode;
}

public String getErrMsg() {
return errMsg;
}

public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}

public List<String> getOpenIds() {
return openIds;
}

public void setOpenIds(List<String> openIds) {
this.openIds = openIds;
}

public RespMsg getRespMsg() {
return respMsg;
}

public void setRespMsg(RespMsg respMsg) {
this.respMsg = respMsg;
}
}

+ 7
- 7
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/device/WxDeviceQrCodeResult.java Bestand weergeven

@@ -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() {


+ 0
- 235
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResult.java Bestand weergeven

@@ -1,235 +0,0 @@
package me.chanjar.weixin.mp.bean.pay.result;

import com.google.common.collect.Maps;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import io.restassured.internal.path.xml.NodeChildrenImpl;
import io.restassured.internal.path.xml.NodeImpl;
import io.restassured.path.xml.XmlPath;
import io.restassured.path.xml.element.Node;
import io.restassured.path.xml.exception.XmlPathException;
import me.chanjar.weixin.common.util.ToStringUtils;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.Iterator;
import java.util.Map;

/**
* <pre>
* 微信支付结果共用属性类
* Created by Binary Wang on 2016-10-24.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
public abstract class WxPayBaseResult {
/**
* 返回状态码
*/
@XStreamAlias("return_code")
protected String returnCode;
/**
* 返回信息
*/
@XStreamAlias("return_msg")
protected String returnMsg;
private String xmlString;
/**
* 业务结果
*/
@XStreamAlias("result_code")
private String resultCode;
/**
* 错误代码
*/
@XStreamAlias("err_code")
private String errCode;
/**
* 错误代码描述
*/
@XStreamAlias("err_code_des")
private String errCodeDes;
/**
* 公众账号ID
*/
@XStreamAlias("appid")
private String appid;
/**
* 商户号
*/
@XStreamAlias("mch_id")
private String mchId;
/**
* 随机字符串
*/
@XStreamAlias("nonce_str")
private String nonceStr;
/**
* 签名
*/
@XStreamAlias("sign")
private String sign;

/**
* 将单位分转换成单位圆
*
* @param fee 将要被转换为元的分的数值
*/
public static String feeToYuan(Integer fee) {
return new BigDecimal(Double.valueOf(fee) / 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
}

/**
* 从xml字符串创建bean对象
*/
public static <T extends WxPayBaseResult> T fromXML(String xmlString, Class<T> clz) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(clz);
T result = (T) xstream.fromXML(xmlString);
result.setXmlString(xmlString);
return result;
}

public String getXmlString() {
return this.xmlString;
}

public void setXmlString(String xmlString) {
this.xmlString = xmlString;
}

protected Logger getLogger() {
return LoggerFactory.getLogger(this.getClass());
}

@Override
public String toString() {
return ToStringUtils.toSimpleString(this);
}

public String getReturnCode() {
return this.returnCode;
}

public void setReturnCode(String returnCode) {
this.returnCode = returnCode;
}

public String getReturnMsg() {
return this.returnMsg;
}

public void setReturnMsg(String returnMsg) {
this.returnMsg = returnMsg;
}

public String getResultCode() {
return this.resultCode;
}

public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}

public String getErrCode() {
return this.errCode;
}

public void setErrCode(String errCode) {
this.errCode = errCode;
}

public String getErrCodeDes() {
return this.errCodeDes;
}

public void setErrCodeDes(String errCodeDes) {
this.errCodeDes = errCodeDes;
}

public String getAppid() {
return this.appid;
}

public void setAppid(String appid) {
this.appid = appid;
}

public String getMchId() {
return this.mchId;
}

public void setMchId(String mchId) {
this.mchId = mchId;
}

public String getNonceStr() {
return this.nonceStr;
}

public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}

public String getSign() {
return this.sign;
}

public void setSign(String sign) {
this.sign = sign;
}

/**
* 将bean通过保存的xml字符串转换成map
*/
public Map<String, String> toMap() {
Map<String, String> result = Maps.newHashMap();
XmlPath xmlPath = new XmlPath(this.xmlString);
NodeImpl rootNode = null;
try {
rootNode = xmlPath.get("xml");
} catch (XmlPathException e) {
throw new RuntimeException("xml数据有问题,请核实!");
}

if (rootNode == null) {
throw new RuntimeException("xml数据有问题,请核实!");
}

Iterator<Node> iterator = rootNode.children().nodeIterator();
while (iterator.hasNext()) {
Node node = iterator.next();
result.put(node.name(), node.value());
}

return result;
}

private String getXmlValue(XmlPath xmlPath, String path) {
if (xmlPath.get(path) instanceof NodeChildrenImpl) {
if (((NodeChildrenImpl) xmlPath.get(path)).size() == 0) {
return null;
}
}

return xmlPath.getString(path);
}

protected <T> T getXmlValue(XmlPath xmlPath, String path, Class<T> clz) {
String value = this.getXmlValue(xmlPath, path);
if (value == null) {
return null;
}

switch (clz.getSimpleName()) {
case "String":
return (T) value;
case "Integer":
return (T) Integer.valueOf(value);
}

throw new UnsupportedOperationException("暂时不支持此种类型的数据");
}
}

+ 0
- 392
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderNotifyResult.java Bestand weergeven

@@ -1,392 +0,0 @@
package me.chanjar.weixin.mp.bean.pay.result;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import me.chanjar.weixin.common.util.BeanUtils;
import me.chanjar.weixin.common.util.ToStringUtils;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyCoupon;
import me.chanjar.weixin.mp.bean.pay.WxPayOrderNotifyResultConverter;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
* 支付结果通用通知 ,文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
* @author aimilin6688
* @since 2.5.0
*/
@XStreamAlias("xml")
public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializable {

private static final long serialVersionUID = 5389718115223345496L;


/**
* <pre>
* 设备号
* device_info
* 否
* String(32)
* 013467007045764
* 微信支付分配的终端设备号,
* </pre>
*/
@XStreamAlias("device_info")
private String deviceInfo;

/**
* <pre>
* 用户标识
* openid
* 是
* String(128)
* wxd930ea5d5a258f4f
* 用户在商户appid下的唯一标识
* </pre>
*/
@XStreamAlias("openid")
private String openid;

/**
* <pre>
* 是否关注公众账号
* is_subscribe
* 否
* String(1)
* Y
* 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
* </pre>
*/
@XStreamAlias("is_subscribe")
private String isSubscribe;


/**
* <pre>
* 交易类型
* trade_type
* 是
* String(16)
* JSAPI JSAPI、NATIVE、APP
* </pre>
*/
@XStreamAlias("trade_type")
private String tradeType;


/**
* <pre>
* 付款银行
* bank_type
* 是
* String(16)
* CMC
* 银行类型,采用字符串类型的银行标识,银行类型见银行列表
* </pre>
*/
@XStreamAlias("bank_type")
private String bankType;


/**
* <pre>
* 订单金额
* total_fee
* 是
* Int
* 100
* 订单总金额,单位为分
* </pre>
*/
@XStreamAlias("total_fee")
private Integer totalFee;
/**
* <pre>
* 应结订单金额
* settlement_total_fee
* 否
* Int
* 100
* 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
* </pre>
*/
@XStreamAlias("settlement_total_fee")
private Integer settlementTotalFee;
/**
* <pre>
* 货币种类
* fee_type
* 否
* String(8)
* CNY
* 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
* </pre>
*/
@XStreamAlias("fee_type")
private String feeType;
/**
* <pre>
* 现金支付金额
* cash_fee
* 是
* Int
* 100
* 现金支付金额订单现金支付金额,详见支付金额
* </pre>
*/
@XStreamAlias("cash_fee")
private Integer cashFee;
/**
* <pre>
* 现金支付货币类型
* cash_fee_type
* 否
* String(16)
* CNY
* 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
* </pre>
*/
@XStreamAlias("cash_fee_type")
private String cashFeeType;
/**
* <pre>
* 总代金券金额
* coupon_fee
* 否
* Int
* 10
* 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
* </pre>
*/
@XStreamAlias("coupon_fee")
private Integer couponFee;

/**
* <pre>
* 代金券使用数量
* coupon_count
* 否
* Int
* 1
* 代金券使用数量
* </pre>
*/
@XStreamAlias("coupon_count")
private Integer couponCount;

private List<WxPayOrderNotifyCoupon> couponList;

/**
* <pre>
* 微信支付订单号
* transaction_id
* 是
* String(32)
* 1217752501201407033233368018
* 微信支付订单号
* </pre>
*/
@XStreamAlias("transaction_id")
private String transactionId;

/**
* <pre>
* 商户订单号
* out_trade_no
* 是
* String(32)
* 1212321211201407033568112322
* 商户系统的订单号,与请求一致。
* </pre>
*/
@XStreamAlias("out_trade_no")
private String outTradeNo;
/**
* <pre>
* 商家数据包
* attach
* 否
* String(128)
* 123456
* 商家数据包,原样返回
* </pre>
*/
@XStreamAlias("attach")
private String attach;
/**
* <pre>
* 支付完成时间
* time_end
* 是
* String(14)
* 20141030133525
* 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
* </pre>
*/
@XStreamAlias("time_end")
private String timeEnd;

public Integer getCouponCount() {
return couponCount;
}

public void setCouponCount(Integer couponCount) {
this.couponCount = couponCount;
}

public List<WxPayOrderNotifyCoupon> getCouponList() {
return couponList;
}

public void setCouponList(List<WxPayOrderNotifyCoupon> couponList) {
this.couponList = couponList;
}

public String getDeviceInfo() {
return deviceInfo;
}

public void setDeviceInfo(String deviceInfo) {
this.deviceInfo = deviceInfo;
}

public String getOpenid() {
return openid;
}

public void setOpenid(String openid) {
this.openid = openid;
}

public String getIsSubscribe() {
return isSubscribe;
}

public void setIsSubscribe(String isSubscribe) {
this.isSubscribe = isSubscribe;
}

public String getTradeType() {
return tradeType;
}

public void setTradeType(String tradeType) {
this.tradeType = tradeType;
}

public String getBankType() {
return bankType;
}

public void setBankType(String bankType) {
this.bankType = bankType;
}

public Integer getTotalFee() {
return totalFee;
}

public void setTotalFee(Integer totalFee) {
this.totalFee = totalFee;
}

public Integer getSettlementTotalFee() {
return settlementTotalFee;
}

public void setSettlementTotalFee(Integer settlementTotalFee) {
this.settlementTotalFee = settlementTotalFee;
}

public String getFeeType() {
return feeType;
}

public void setFeeType(String feeType) {
this.feeType = feeType;
}

public Integer getCashFee() {
return cashFee;
}

public void setCashFee(Integer cashFee) {
this.cashFee = cashFee;
}

public String getCashFeeType() {
return cashFeeType;
}

public void setCashFeeType(String cashFeeType) {
this.cashFeeType = cashFeeType;
}

public Integer getCouponFee() {
return couponFee;
}

public void setCouponFee(Integer couponFee) {
this.couponFee = couponFee;
}

public String getTransactionId() {
return transactionId;
}

public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}

public String getOutTradeNo() {
return outTradeNo;
}

public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}

public String getAttach() {
return attach;
}

public void setAttach(String attach) {
this.attach = attach;
}

public String getTimeEnd() {
return timeEnd;
}

public void setTimeEnd(String timeEnd) {
this.timeEnd = timeEnd;
}

@Override
public Map<String,String> toMap(){
Map<String,String> resultMap = BeanUtils.xmlBean2Map(this);
if(this.getCouponCount() != null && this.getCouponCount() > 0){
for (int i = 0; i < this.getCouponCount(); i++) {
WxPayOrderNotifyCoupon coupon = couponList.get(i);
resultMap.putAll(coupon.toMap(i));
}
}
return resultMap;
}

@Override
public String toString() {
return ToStringUtils.toSimpleString(this);
}

public static WxPayOrderNotifyResult fromXML(String xmlString) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(WxPayOrderNotifyResult.class);
xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider()));
WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString);
result.setXmlString(xmlString);
return result;
}
}

+ 3
- 3
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUser.java Bestand weergeven

@@ -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;
}



+ 14
- 14
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpUserList.java Bestand weergeven

@@ -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<String> openIds = new ArrayList<>();
protected String nextOpenId;
public int getTotal() {
protected List<String> openids = new ArrayList<>();
protected String nextOpenid;
public long getTotal() {
return this.total;
}
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<String> getOpenIds() {
return this.openIds;
public List<String> getOpenids() {
return this.openids;
}
public void setOpenIds(List<String> openIds) {
this.openIds = openIds;
public void setOpenids(List<String> openids) {
this.openids = openids;
}
public String getNextOpenId() {
return this.nextOpenId;
public String getNextOpenid() {
return this.nextOpenid;
}
public void setNextOpenId(String nextOpenId) {
this.nextOpenId = nextOpenId;
public void setNextOpenid(String nextOpenid) {
this.nextOpenid = nextOpenid;
}
public static WxMpUserList fromJson(String json) {
return WxMpGsonBuilder.INSTANCE.create().fromJson(json, WxMpUserList.class);
}


+ 71
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessage.java Bestand weergeven

@@ -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;

/**
* <pre>
* 跳小程序所需数据,不需跳小程序可不用传该数据
* url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。
* 开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。
* </pre>
*/
private String url;
/**
* 模板跳转链接
* @see #url
*/
private MiniProgram miniProgram;

/**
* 模板数据
*/
private List<WxMpTemplateData> 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<WxMpTemplateData> 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;
}
}


+ 2
- 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpMassPreviewMessageGsonAdapter.java Bestand weergeven

@@ -16,7 +16,8 @@ public class WxMpMassPreviewMessageGsonAdapter implements JsonSerializer<WxMpMas
@Override
public JsonElement serialize(WxMpMassPreviewMessage wxMpMassPreviewMessage, Type type, JsonSerializationContext jsonSerializationContext) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("towxname", wxMpMassPreviewMessage.getToWxUsername());
jsonObject.addProperty("towxname", wxMpMassPreviewMessage.getToWxUserName());
jsonObject.addProperty("touser", wxMpMassPreviewMessage.getToWxUserOpenid());
if (WxConsts.MASS_MSG_NEWS.equals(wxMpMassPreviewMessage.getMsgType())) {
JsonObject news = new JsonObject();
news.addProperty("media_id", wxMpMassPreviewMessage.getMediaId());


+ 7
- 0
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpTemplateMessageGsonAdapter.java Bestand weergeven

@@ -28,6 +28,13 @@ public class WxMpTemplateMessageGsonAdapter implements JsonSerializer<WxMpTempla
messageJson.addProperty("url", message.getUrl());
}

if(message.getMiniProgram() !=null){
JsonObject miniProgramJson = new JsonObject();
miniProgramJson.addProperty("appid", message.getMiniProgram().getAppid());
miniProgramJson.addProperty("pagepath", message.getMiniProgram().getPagePath());
messageJson.add("miniprogram", miniProgramJson);
}

JsonObject data = new JsonObject();
messageJson.add("data", data);



+ 1
- 1
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxMpUserGsonAdapter.java Bestand weergeven

@@ -36,7 +36,7 @@ public class WxMpUserGsonAdapter implements JsonDeserializer<WxMpUser> {
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("男");


+ 3
- 3
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/json/WxUserListGsonAdapter.java Bestand weergeven

@@ -20,13 +20,13 @@ public class WxUserListGsonAdapter implements JsonDeserializer<WxMpUserList> {
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;


+ 7
- 2
weixin-java-mp/src/main/java/me/chanjar/weixin/mp/util/xml/XStreamTransformer.java Bestand weergeven

@@ -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);
}



+ 5
- 4
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpMassMessageAPITest.java Bestand weergeven

@@ -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());



+ 5
- 4
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMaterialServiceImplTest.java Bestand weergeven

@@ -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"}
};
}



+ 37
- 40
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMenuServiceImplTest.java Bestand weergeven

@@ -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("菜单");


+ 0
- 195
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpPayServiceImplTest.java Bestand weergeven

@@ -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);
}


}

+ 1
- 1
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpUserServiceImplTest.java Bestand weergeven

@@ -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);
}



+ 1
- 2
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/ApiTestModule.java Bestand weergeven

@@ -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);


+ 0
- 6
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConfigStorage.java Bestand weergeven

@@ -64,10 +64,4 @@ public class TestConfigStorage extends WxMpInMemoryConfigStorage {
super.accessTokenLock = lock;
}

@Override
public boolean useSandboxForWxPay() {
return false;
//沙箱环境不成熟,有问题无法使用,暂时屏蔽掉
// return true;
}
}

+ 17
- 0
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/test/TestConstants.java Bestand weergeven

@@ -0,0 +1,17 @@
package me.chanjar.weixin.mp.api.test;

/**
* <pre>
* 仅供测试使用的一些常量
* Created by Binary Wang on 2017-3-9.
* </pre>
*/
public class TestConstants {
///////////////////////
// 文件类型
///////////////////////
public static final String FILE_JPG = "jpeg";
public static final String FILE_MP3 = "mp3";
public static final String FILE_AMR = "amr";
public static final String FILE_MP4 = "mp4";
}

+ 31
- 0
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/template/WxMpTemplateMessageTest.java Bestand weergeven

@@ -0,0 +1,31 @@
package me.chanjar.weixin.mp.bean.template;

import org.testng.annotations.*;

import static org.testng.AssertJUnit.*;

/**
* <pre>
* Created by Binary Wang on 2017-3-30.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
public class WxMpTemplateMessageTest {
@Test
public void testToJson() throws Exception {
WxMpTemplateMessage tm = WxMpTemplateMessage.builder()
.toUser("OPENID")
.templateId("ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY")
.miniProgram(new WxMpTemplateMessage.MiniProgram("xiaochengxuappid12345", "index?foo=bar"))
.url("http://weixin.qq.com/download")
.build();

tm.addWxMpTemplateData(
new WxMpTemplateData("first", "haahah", "#FF00FF"));
tm.addWxMpTemplateData(
new WxMpTemplateData("remark", "heihei", "#FF00FF"));

assertEquals(tm.toJson(), "{\"touser\":\"OPENID\",\"template_id\":\"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY\",\"url\":\"http://weixin.qq.com/download\",\"miniprogram\":{\"appid\":\"xiaochengxuappid12345\",\"pagepath\":\"index?foo=bar\"},\"data\":{\"first\":{\"value\":\"haahah\",\"color\":\"#FF00FF\"},\"remark\":{\"value\":\"heihei\",\"color\":\"#FF00FF\"}}}");
}

}

+ 2
- 1
weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/DemoImageHandler.java Bestand weergeven

@@ -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()


+ 1
- 1
weixin-java-osgi/pom.xml Bestand weergeven

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-parent</artifactId>
<version>2.5.0</version>
<version>2.5.2.BETA</version>
</parent>

<artifactId>weixin-java-osgi</artifactId>


+ 47
- 0
weixin-java-pay/pom.xml Bestand weergeven

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>weixin-java-parent</artifactId>
<groupId>com.github.binarywang</groupId>
<version>2.6.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>weixin-java-pay</artifactId>
<name>WeiXin Java Tools - PAY</name>
<description>微信支付 Java SDK</description>

<dependencies>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-common</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<version>3.7</version>
<!-- 由于较新的3.8版本需要jdk8,故而此处采用较低版本 -->
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>qrcode-utils</artifactId>
<version>1.0</version>
</dependency>

<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyCoupon.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyCoupon.java Bestand weergeven

@@ -1,64 +1,63 @@
package me.chanjar.weixin.mp.bean.pay;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 支付异步通知代金券详细
*/
public class WxPayOrderNotifyCoupon implements Serializable {
/**
* @fields serialVersionUID
*/
private static final long serialVersionUID = -4165343733538156097L;
@XStreamAlias("coupon_id")
private String couponId;
@XStreamAlias("coupon_type")
private String couponType;
@XStreamAlias("coupon_fee")
private Integer couponFee;
public String getCouponId() {
return couponId;
}
public void setCouponId(String couponId) {
this.couponId = couponId;
}
public String getCouponType() {
return couponType;
}
public void setCouponType(String couponType) {
this.couponType = couponType;
}
public Integer getCouponFee() {
return couponFee;
}
public void setCouponFee(Integer couponFee) {
this.couponFee = couponFee;
}
public Map<String, String> toMap(int index) {
Map<String, String> map = new HashMap<>();
map.put("coupon_id_" + index, this.getCouponId());
map.put("coupon_type_" + index, this.getCouponType());
map.put("coupon_fee_" + index, this.getCouponFee() + "");
return map;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}
package com.github.binarywang.wxpay.bean;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
* 支付异步通知代金券详细
*/
public class WxPayOrderNotifyCoupon implements Serializable {
/**
* @fields serialVersionUID
*/
private static final long serialVersionUID = -4165343733538156097L;

@XStreamAlias("coupon_id")
private String couponId;
@XStreamAlias("coupon_type")
private String couponType;
@XStreamAlias("coupon_fee")
private Integer couponFee;

public String getCouponId() {
return couponId;
}

public void setCouponId(String couponId) {
this.couponId = couponId;
}

public String getCouponType() {
return couponType;
}

public void setCouponType(String couponType) {
this.couponType = couponType;
}

public Integer getCouponFee() {
return couponFee;
}

public void setCouponFee(Integer couponFee) {
this.couponFee = couponFee;
}

public Map<String, String> toMap(int index) {
Map<String, String> map = new HashMap<>();
map.put("coupon_id_" + index, this.getCouponId());
map.put("coupon_type_" + index, this.getCouponType());
map.put("coupon_fee_" + index, this.getCouponFee() + "");
return map;
}

@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyResponse.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/WxPayOrderNotifyResponse.java Bestand weergeven

@@ -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;
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxEntPayQueryRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayQueryRequest.java Bestand weergeven

@@ -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;
* <li>描述
* </pre>
* Created by Binary Wang on 2016/10/19.
*
* @author binarywang (https://github.com/binarywang)
*/
@XStreamAlias("xml")
public class WxEntPayQueryRequest extends WxPayBaseRequest {
/**
* <pre>
* 商户号
* mch_id
* 商户订单
* partner_trade_no
* 是
* 10000098
* String(32)
* 微信支付分配的商户号
* 10000098201411111234567890
* String
* 商户订单
* </pre>
*/
@XStreamAlias("mchid")
private String mchId;

/**
* <pre>
* 商户订单号
* partner_trade_no
* 是
* 10000098201411111234567890
* String
* 商户订单号
* </pre>
*/
*/
@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);

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxEntPayRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxEntPayRequest.java Bestand weergeven

@@ -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;
* <li>描述
* </pre>
* 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 {
/**
* <pre>
* 公众账号appid
* mch_appid
* 是
* wx8888888888888888
* String
* 微信分配的公众账号ID(企业号corpid即为此appId)
* 公众账号appid
* mch_appid
* 是
* wx8888888888888888
* String
* 微信分配的公众账号ID(企业号corpid即为此appId)
* </pre>
*/
*/
@XStreamAlias("mch_appid")
private String mchAppid;

/**
* <pre>
* 商户号
* mchid
* 是
* 1900000109
* String(32)
* 微信支付分配的商户号
* 商户号
* mchid
* 是
* 1900000109
* String(32)
* 微信支付分配的商户号
* </pre>
*/
*/
@XStreamAlias("mchid")
private String mchId;

/**
* <pre>
* 设备号
* device_info
* 否
* 13467007045764
* String(32)
*微信支付分配的终端设备号
* 设备号
* device_info
* 否
* 13467007045764
* String(32)
* 微信支付分配的终端设备号
* </pre>
*/
*/
@XStreamAlias("device_info")
private String deviceInfo;

/**
* <pre>
* 商户订单号
* partner_trade_no
* 是
* 10000098201411111234567890
* String
* 商户订单号
* 商户订单号
* partner_trade_no
* 是
* 10000098201411111234567890
* String
* 商户订单号
* </pre>
*/
*/
@XStreamAlias("partner_trade_no")
private String partnerTradeNo;

/**
* <pre>
* 需保持唯一性 用户openid
* openid
* 是
* oxTWIuGaIt6gTKsQRLau2M0yL16E
* String
* 商户appid下,某用户的openid
* 需保持唯一性 用户openid
* openid
* 是
* oxTWIuGaIt6gTKsQRLau2M0yL16E
* String
* 商户appid下,某用户的openid
* </pre>
*/
*/
@XStreamAlias("openid")
private String openid;

/**
* <pre>
* 校验用户姓名选项
* check_name
* 是
* OPTION_CHECK
* String
* NO_CHECK:不校验真实姓名 
* FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
* OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
* 校验用户姓名选项
* check_name
* 是
* OPTION_CHECK
* String
* NO_CHECK:不校验真实姓名 
* FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账) 
* OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
* </pre>
*/
*/
@XStreamAlias("check_name")
private String checkName;

/**
* <pre>
* 收款用户姓名
* re_user_name
* 可选
* 马花花
* String
* 收款用户真实姓名。
* 如果check_name设置为FORCE_CHECK或OPTION_CHECK, 则必填用户真实姓名
* 收款用户姓名
* re_user_name
* 可选
* 马花花
* String
* 收款用户真实姓名。
* 如果check_name设置为FORCE_CHECK或OPTION_CHECK, 则必填用户真实姓名
* </pre>
*/
*/
@XStreamAlias("re_user_name")
private String reUserName;

/**
* <pre>
* 金额
* amount
* 是
* 10099
* int
* 企业付款金额, 单位为分
* 金额
* amount
* 是
* 10099
* int
* 企业付款金额, 单位为分
* </pre>
*/
*/
@XStreamAlias("amount")
private Integer amount;

/**
* <pre>
* 企业付款描述信息
* desc
* 是
* 理赔
* String
* 企业付款操作说明信息。必填。
* 企业付款描述信息
* desc
* 是
* 理赔
* String
* 企业付款操作说明信息。必填。
* </pre>
*/
*/
@XStreamAlias("desc")
private String description;

/**
* <pre>
* Ip地址
* spbill_create_ip
* 是
* 192.168.0.1
* String(32)
* 调用接口的机器Ip地址
* Ip地址
* spbill_create_ip
* 是
* 192.168.0.1
* String(32)
* 调用接口的机器Ip地址
* </pre>
*/
*/
@XStreamAlias("spbill_create_ip")
private String spbillCreateIp;


@Override
protected void checkConstraints() {

}

@Override
public String getAppid() {
return this.mchAppid;

+ 46
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayAuthcode2OpenidRequest.java Bestand weergeven

@@ -0,0 +1,46 @@
package com.github.binarywang.wxpay.bean.request;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* <pre>
* 授权码查询openid接口请求对象类
* Created by Binary Wang on 2017-3-27.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayAuthcode2OpenidRequest extends WxPayBaseRequest {

/**
* <pre>
* 授权码
* auth_code
* 是
* String(128)
* 扫码支付授权码,设备读取用户微信中的条码或者二维码信息
* </pre>
*/
@XStreamAlias("auth_code")
private String authCode;

public WxPayAuthcode2OpenidRequest() {
}

public WxPayAuthcode2OpenidRequest(String authCode) {
this.authCode = authCode;
}

public String getAuthCode() {
return this.authCode;
}

public void setAuthCode(String authCode) {
this.authCode = authCode;
}

@Override
protected void checkConstraints() {
// nothing to do
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayBaseRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayBaseRequest.java Bestand weergeven

@@ -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 <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
*/
public abstract class WxPayBaseRequest {
/**
* 检查请求参数内容,包括必填参数以及特殊约束
*/
protected void checkFields() throws WxErrorException {
//check required fields
BeanUtils.checkRequiredFields(this);

//check other parameters
this.checkConstraints();
}

/**
* 检查约束情况
*/
protected abstract void checkConstraints();

/**
* <pre>
* 公众账号ID
@@ -47,6 +68,32 @@ public abstract class WxPayBaseRequest {
*/
@XStreamAlias("mch_id")
protected String mchId;

/**
* <pre>
* 服务商模式下的子商户公众账号ID
* sub_appid
* 是
* String(32)
* wxd678efh567hg6787
* 微信分配的子商户公众账号ID
* </pre>
*/
@XStreamAlias("sub_appid")
protected String subAppId;

/**
* <pre>
* 服务商模式下的子商户号
* sub_mch_id
* 是
* String(32)
* 1230000109
* 微信支付分配的子商户号,开发者模式下必填
* </pre>
*/
@XStreamAlias("sub_mch_id")
protected String subMchId;
/**
* <pre>
* 随机字符串
@@ -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);
}

/**
* <pre>
* 检查参数,并设置签名
* 1、检查参数(注意:子类实现需要检查参数的而外功能时,请在调用父类的方法前进行相应判断)
* 2、补充系统参数,如果未传入则从配置里读取
* 3、生成签名,并设置进去
* </pre>
* @param config 支付配置对象,用于读取相应系统配置信息
*/
public void checkAndSign(WxPayConfig config) throws WxErrorException {
this.checkFields();

if (StringUtils.isBlank(getAppid())) {
this.setAppid(config.getAppId());
}

if (StringUtils.isBlank(getMchId())) {
this.setMchId(config.getMchId());
}

if (StringUtils.isBlank(getSubAppId())) {
this.setSubAppId(config.getSubAppId());
}

if (StringUtils.isBlank(getSubMchId())) {
this. setSubMchId(config.getSubMchId());
}

if (StringUtils.isBlank(getNonceStr())) {
this.setNonceStr(String.valueOf(System.currentTimeMillis()));
}

//设置签名字段的值
this.setSign(SignUtils.createSign(this, config.getMchKey()));
}

}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayDownloadBillRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayDownloadBillRequest.java Bestand weergeven

@@ -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;

/**
* <pre>
@@ -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"};

/**
* <pre>
* 设备号
@@ -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()));
}
}
}

+ 380
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayMicropayRequest.java Bestand weergeven

@@ -0,0 +1,380 @@
package com.github.binarywang.wxpay.bean.request;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import me.chanjar.weixin.common.annotation.Required;

/**
* <pre>
* 提交刷卡支付请求对象类
* Created by Binary Wang on 2017-3-23.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayMicropayRequest extends WxPayBaseRequest {
/**
* <pre>
* 签名类型
* sign_type
* 否
* String(32)
* HMAC-SHA256
* 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
**/
@XStreamAlias("sign_type")
private String signType;

/**
* <pre>
* 商品描述
* body
* 是
* String(128)
* image形象店-深圳腾大- QQ公仔
* 商品简单描述,该字段须严格按照规范传递,具体请见参数规定
**/
@Required
@XStreamAlias("body")
private String body;

/**
* <pre>
* 商品详情
* detail
* 否
* String(6000)
*
* 单品优惠功能字段,需要接入请见详细说明
**/
@XStreamAlias("detail")
private String detail;

/**
* <pre>
* 附加数据
* attach
* 否
* String(127)
* 说明
* 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
**/
@XStreamAlias("attach")
private String attach;

/**
* <pre>
* 商户订单号
* out_trade_no
* 是
* String(32)
* 1217752501201407033233368018
* 商户系统内部的订单号,32个字符内、可包含字母,其他说明见商户订单号
**/
@Required
@XStreamAlias("out_trade_no")
private String outTradeNo;

/**
* <pre>
* 订单金额
* total_fee
* 是
* Int
* 888
* 订单总金额,单位为分,只能为整数,详见支付金额
**/
@Required
@XStreamAlias("total_fee")
private Integer totalFee;

/**
* <pre>
* 货币类型
* fee_type
* 否
* String(16)
* CNY
* 符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
**/
@XStreamAlias("fee_type")
private String feeType;

/**
* <pre>
* 终端IP
* spbill_create_ip
* 是
* String(16)
* 8.8.8.8
* 调用微信支付API的机器IP
**/
@Required
@XStreamAlias("spbill_create_ip")
private String spbillCreateIp;

/**
* <pre>
* 商品标记
* goods_tag
* 否
* String(32)
* 1234
* 商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠
**/
@XStreamAlias("goods_tag")
private String goodsTag;

/**
* <pre>
* 指定支付方式
* limit_pay
* 否
* String(32)
* no_credit
* no_credit--指定不能使用信用卡支付
**/
@XStreamAlias("limit_pay")
private String limitPay;

/**
* <pre>
* 授权码
* auth_code
* 是
* String(128)
* 120061098828009406
* 扫码支付授权码,设备读取用户微信中的条码或者二维码信息注:用户刷卡条形码规则:18位纯数字,以10、11、12、13、14、15开头)
**/
@Required
@XStreamAlias("auth_code")
private String authCode;

private WxPayMicropayRequest(Builder builder) {
setSignType(builder.signType);
setBody(builder.body);
setAppid(builder.appid);
setDetail(builder.detail);
setMchId(builder.mchId);
setAttach(builder.attach);
setSubAppId(builder.subAppId);
setOutTradeNo(builder.outTradeNo);
setSubMchId(builder.subMchId);
setTotalFee(builder.totalFee);
setNonceStr(builder.nonceStr);
setFeeType(builder.feeType);
setSign(builder.sign);
setSpbillCreateIp(builder.spbillCreateIp);
setGoodsTag(builder.goodsTag);
setLimitPay(builder.limitPay);
setAuthCode(builder.authCode);
}

public static Builder newBuilder() {
return new Builder();
}

public String getSignType() {
return this.signType;
}

public void setSignType(String signType) {
this.signType = signType;
}

public String getBody() {
return this.body;
}

public void setBody(String body) {
this.body = body;
}

public String getDetail() {
return this.detail;
}

public void setDetail(String detail) {
this.detail = detail;
}

public String getAttach() {
return this.attach;
}

public void setAttach(String attach) {
this.attach = attach;
}

public String getOutTradeNo() {
return this.outTradeNo;
}

public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}

public Integer getTotalFee() {
return this.totalFee;
}

public void setTotalFee(Integer totalFee) {
this.totalFee = totalFee;
}

public String getFeeType() {
return this.feeType;
}

public void setFeeType(String feeType) {
this.feeType = feeType;
}

public String getSpbillCreateIp() {
return this.spbillCreateIp;
}

public void setSpbillCreateIp(String spbillCreateIp) {
this.spbillCreateIp = spbillCreateIp;
}

public String getGoodsTag() {
return this.goodsTag;
}

public void setGoodsTag(String goodsTag) {
this.goodsTag = goodsTag;
}

public String getLimitPay() {
return this.limitPay;
}

public void setLimitPay(String limitPay) {
this.limitPay = limitPay;
}

public String getAuthCode() {
return this.authCode;
}

public void setAuthCode(String authCode) {
this.authCode = authCode;
}

@Override
protected void checkConstraints() {
//do nothing
}

public static final class Builder {
private String signType;
private String body;
private String appid;
private String detail;
private String mchId;
private String attach;
private String subAppId;
private String outTradeNo;
private String subMchId;
private Integer totalFee;
private String nonceStr;
private String feeType;
private String sign;
private String spbillCreateIp;
private String goodsTag;
private String limitPay;
private String authCode;

private Builder() {
}

public Builder signType(String signType) {
this.signType = signType;
return this;
}

public Builder body(String body) {
this.body = body;
return this;
}

public Builder appid(String appid) {
this.appid = appid;
return this;
}

public Builder detail(String detail) {
this.detail = detail;
return this;
}

public Builder mchId(String mchId) {
this.mchId = mchId;
return this;
}

public Builder attach(String attach) {
this.attach = attach;
return this;
}

public Builder subAppId(String subAppId) {
this.subAppId = subAppId;
return this;
}

public Builder outTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
return this;
}

public Builder subMchId(String subMchId) {
this.subMchId = subMchId;
return this;
}

public Builder totalFee(Integer totalFee) {
this.totalFee = totalFee;
return this;
}

public Builder nonceStr(String nonceStr) {
this.nonceStr = nonceStr;
return this;
}

public Builder feeType(String feeType) {
this.feeType = feeType;
return this;
}

public Builder sign(String sign) {
this.sign = sign;
return this;
}

public Builder spbillCreateIp(String spbillCreateIp) {
this.spbillCreateIp = spbillCreateIp;
return this;
}

public Builder goodsTag(String goodsTag) {
this.goodsTag = goodsTag;
return this;
}

public Builder limitPay(String limitPay) {
this.limitPay = limitPay;
return this;
}

public Builder authCode(String authCode) {
this.authCode = authCode;
return this;
}

public WxPayMicropayRequest build() {
return new WxPayMicropayRequest(this);
}
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayOrderCloseRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderCloseRequest.java Bestand weergeven

@@ -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;
* </pre>
*/
@XStreamAlias("xml")
public class WxPayOrderCloseRequest extends WxPayBaseRequest{
public class WxPayOrderCloseRequest extends WxPayBaseRequest {

/**
* <pre>
@@ -33,4 +33,8 @@ public class WxPayOrderCloseRequest extends WxPayBaseRequest{
this.outTradeNo = outTradeNo;
}

@Override
protected void checkConstraints() {

}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayOrderQueryRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderQueryRequest.java Bestand weergeven

@@ -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;

/**
* <pre>
@@ -14,6 +15,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias;
* <li>示例值
* <li>描述
* </pre>
*
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
*/
@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 不能同时存在或同时为空,必须二选一");
}
}
}

+ 163
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayOrderReverseRequest.java Bestand weergeven

@@ -0,0 +1,163 @@
package com.github.binarywang.wxpay.bean.request;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.apache.commons.lang3.StringUtils;

/**
* <pre>
* 撤销订单请求类
* Created by Binary Wang on 2017-3-23.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayOrderReverseRequest extends WxPayBaseRequest {

/**
* <pre>
* 微信订单号
* transaction_id
* String(28)
* 1217752501201400000000000000
* 微信的订单号,优先使用
* </pre>
*/
@XStreamAlias("transaction_id")
private String transactionId;

/**
* <pre>
* 商户订单号
* out_trade_no
* String(32)
* 1217752501201400000000000000
* 商户系统内部的订单号
* transaction_id、out_trade_no二选一,如果同时存在优先级:transaction_id> out_trade_no
* </pre>
*/
@XStreamAlias("out_trade_no")
private String outTradeNo;

/**
* <pre>
* 签名类型
* sign_type
* 否
* String(32)
* HMAC-SHA256
* 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
**/
@XStreamAlias("sign_type")
private String signType;

private WxPayOrderReverseRequest(Builder builder) {
setTransactionId(builder.transactionId);
setAppid(builder.appid);
setOutTradeNo(builder.outTradeNo);
setMchId(builder.mchId);
setSignType(builder.signType);
setSubAppId(builder.subAppId);
setSubMchId(builder.subMchId);
setNonceStr(builder.nonceStr);
setSign(builder.sign);
}

public static Builder newBuilder() {
return new Builder();
}

public String getTransactionId() {
return this.transactionId;
}

public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}

public String getOutTradeNo() {
return this.outTradeNo;
}

public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}

public String getSignType() {
return this.signType;
}

public void setSignType(String signType) {
this.signType = signType;
}

@Override
protected void checkConstraints() {
if (StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) {
throw new IllegalArgumentException("transaction_id 和 out_trade_no不能同时为空!");
}
}

public static final class Builder {
private String transactionId;
private String appid;
private String outTradeNo;
private String mchId;
private String signType;
private String subAppId;
private String subMchId;
private String nonceStr;
private String sign;

private Builder() {
}

public Builder transactionId(String transactionId) {
this.transactionId = transactionId;
return this;
}

public Builder appid(String appid) {
this.appid = appid;
return this;
}

public Builder outTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
return this;
}

public Builder mchId(String mchId) {
this.mchId = mchId;
return this;
}

public Builder signType(String signType) {
this.signType = signType;
return this;
}

public Builder subAppId(String subAppId) {
this.subAppId = subAppId;
return this;
}

public Builder subMchId(String subMchId) {
this.subMchId = subMchId;
return this;
}

public Builder nonceStr(String nonceStr) {
this.nonceStr = nonceStr;
return this;
}

public Builder sign(String sign) {
this.sign = sign;
return this;
}

public WxPayOrderReverseRequest build() {
return new WxPayOrderReverseRequest(this);
}
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRedpackQueryRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRedpackQueryRequest.java Bestand weergeven

@@ -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() {

}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundQueryRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java Bestand weergeven

@@ -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;

/**
* <pre>
@@ -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 必须四选一");
}

}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayRefundRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java Bestand weergeven

@@ -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;

/**
* <pre>
@@ -13,13 +19,25 @@ import me.chanjar.weixin.common.annotation.Required;
* <li>类型
* <li>示例值
* <li>描述
* Created by Binary Wang on 2016-10-08.
* </pre>
*
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* 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);
}

/**
* <pre>
* 设备号
@@ -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);
}
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayReportRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayReportRequest.java Bestand weergeven

@@ -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
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPaySendRedpackRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPaySendRedpackRequest.java Bestand weergeven

@@ -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;

+ 46
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayShorturlRequest.java Bestand weergeven

@@ -0,0 +1,46 @@
package com.github.binarywang.wxpay.bean.request;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* <pre>
* 转换短链接请求对象类
* Created by Binary Wang on 2017-3-27.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayShorturlRequest extends WxPayBaseRequest {
/**
* <pre>
* URL链接
* long_url
* 是
* String(512)
* weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
* 需要转换的URL,签名用原串,传输需URLencode
* </pre>
*/
@XStreamAlias("long_url")
private String longUrl;

public String getLongUrl() {
return this.longUrl;
}

public void setLongUrl(String longUrl) {
this.longUrl = longUrl;
}

public WxPayShorturlRequest() {
}

public WxPayShorturlRequest(String longUrl) {
this.longUrl = longUrl;
}

@Override
protected void checkConstraints() {
//do nothing
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/request/WxPayUnifiedOrderRequest.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java Bestand weergeven

@@ -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;

/**
* <pre>
@@ -17,10 +22,12 @@ import me.chanjar.weixin.common.annotation.Required;
* <li>描述
* </pre>
* 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"};

/**
* <pre>
@@ -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 商品描述信息
* </pre>
*/
@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 {

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxEntPayQueryResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayQueryResult.java Bestand weergeven

@@ -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")

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxEntPayResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxEntPayResult.java Bestand weergeven

@@ -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")

+ 40
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayAuthcode2OpenidResult.java Bestand weergeven

@@ -0,0 +1,40 @@
package com.github.binarywang.wxpay.bean.result;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* <pre>
* 授权码查询openid接口请求结果类
* Created by Binary Wang on 2017-3-27.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayAuthcode2OpenidResult extends WxPayBaseResult {
/**
* <pre>
* 用户标识
* openid
* 是
* String(128)
* 用户在商户appid下的唯一标识
* </pre>
*/
@XStreamAlias("openid")
private String openid;

public WxPayAuthcode2OpenidResult() {
}

public WxPayAuthcode2OpenidResult(String openid) {
this.openid = openid;
}

public String getOpenid() {
return this.openid;
}

public void setOpenid(String openid) {
this.openid = openid;
}
}

+ 347
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResult.java Bestand weergeven

@@ -0,0 +1,347 @@
package com.github.binarywang.wxpay.bean.result;

import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.github.binarywang.wxpay.util.SignUtils;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.ToStringUtils;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;

/**
* <pre>
* 微信支付结果共用属性类
* Created by Binary Wang on 2016-10-24.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
public abstract class WxPayBaseResult {
/**
* 返回状态码
*/
@XStreamAlias("return_code")
protected String returnCode;
/**
* 返回信息
*/
@XStreamAlias("return_msg")
protected String returnMsg;

//当return_code为SUCCESS的时候,还会包括以下字段:

/**
* 业务结果
*/
@XStreamAlias("result_code")
private String resultCode;
/**
* 错误代码
*/
@XStreamAlias("err_code")
private String errCode;
/**
* 错误代码描述
*/
@XStreamAlias("err_code_des")
private String errCodeDes;
/**
* 公众账号ID
*/
@XStreamAlias("appid")
private String appid;
/**
* 商户号
*/
@XStreamAlias("mch_id")
private String mchId;
/**
* 服务商模式下的子公众账号ID
*/
@XStreamAlias("sub_appid")
private String subAppId;
/**
* 服务商模式下的子商户号
*/
@XStreamAlias("sub_mch_id")
private String subMchId;
/**
* 随机字符串
*/
@XStreamAlias("nonce_str")
private String nonceStr;
/**
* 签名
*/
@XStreamAlias("sign")
private String sign;

//以下为辅助属性
/**
* xml字符串
*/
private String xmlString;

/**
* xml的Document对象,用于解析xml文本
*/
private Document xmlDoc;

/**
* 将单位分转换成单位圆
*
* @param fee 将要被转换为元的分的数值
*/
public static String feeToYuan(Integer fee) {
return new BigDecimal(Double.valueOf(fee) / 100).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString();
}

/**
* 从xml字符串创建bean对象
*/
public static <T extends WxPayBaseResult> T fromXML(String xmlString, Class<T> clz) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(clz);
T result = (T) xstream.fromXML(xmlString);
result.setXmlString(xmlString);
return result;
}

public String getXmlString() {
return this.xmlString;
}

public void setXmlString(String xmlString) {
this.xmlString = xmlString;
}

protected Logger getLogger() {
return LoggerFactory.getLogger(this.getClass());
}

@Override
public String toString() {
return ToStringUtils.toSimpleString(this);
}

public String getReturnCode() {
return this.returnCode;
}

public void setReturnCode(String returnCode) {
this.returnCode = returnCode;
}

public String getReturnMsg() {
return this.returnMsg;
}

public void setReturnMsg(String returnMsg) {
this.returnMsg = returnMsg;
}

public String getResultCode() {
return this.resultCode;
}

public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}

public String getErrCode() {
return this.errCode;
}

public void setErrCode(String errCode) {
this.errCode = errCode;
}

public String getErrCodeDes() {
return this.errCodeDes;
}

public void setErrCodeDes(String errCodeDes) {
this.errCodeDes = errCodeDes;
}

public String getAppid() {
return this.appid;
}

public void setAppid(String appid) {
this.appid = appid;
}

public String getMchId() {
return this.mchId;
}

public void setMchId(String mchId) {
this.mchId = mchId;
}

public String getNonceStr() {
return this.nonceStr;
}

public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}

public String getSign() {
return this.sign;
}

public void setSign(String sign) {
this.sign = sign;
}

public String getSubAppId() {
return subAppId;
}

public void setSubAppId(String subAppId) {
this.subAppId = subAppId;
}

public String getSubMchId() {
return subMchId;
}

public void setSubMchId(String subMchId) {
this.subMchId = subMchId;
}

/**
* 将bean通过保存的xml字符串转换成map
*/
public Map<String, String> toMap() {
if (StringUtils.isBlank(this.xmlString)) {
throw new RuntimeException("xml数据有问题,请核实!");
}

Map<String, String> result = Maps.newHashMap();
Document doc = this.getXmlDoc();

try {
NodeList list = (NodeList) XPathFactory.newInstance().newXPath()
.compile("/xml/*")
.evaluate(doc, XPathConstants.NODESET);
int len = list.getLength();
for (int i = 0; i < len; i++) {
result.put(list.item(i).getNodeName(), list.item(i).getTextContent());
}
} catch (XPathExpressionException e) {
throw new RuntimeException("非法的xml文本内容:" + xmlString);
}

return result;
}

/**
* 将xml字符串转换成Document对象,以便读取其元素值
*/
protected Document getXmlDoc() {
if (this.xmlDoc != null) {
return this.xmlDoc;
}

try {
this.xmlDoc = DocumentBuilderFactory
.newInstance()
.newDocumentBuilder()
.parse(new ByteArrayInputStream(this.xmlString.getBytes("UTF-8")));
return xmlDoc;
} catch (SAXException | IOException | ParserConfigurationException e) {
throw new RuntimeException("非法的xml文本内容:" + this.xmlString);
}

}

/**
* 获取xml中元素的值
*/
protected String getXmlValue(String... path) {
Document doc = this.getXmlDoc();
String expression = String.format("/%s//text()", Joiner.on("/").join(path));
try {
return (String) XPathFactory
.newInstance()
.newXPath()
.compile(expression)
.evaluate(doc, XPathConstants.STRING);
} catch (XPathExpressionException e) {
throw new RuntimeException("未找到相应路径的文本:" + expression);
}
}

/**
* 获取xml中元素的值,作为int值返回
*/
protected Integer getXmlValueAsInt(String... path) {
String result = this.getXmlValue(path);
if (StringUtils.isBlank(result)) {
return null;
}

return Integer.valueOf(result);
}

/**
* 校验返回结果签名
*/
public void checkResult(WxPayServiceImpl wxPayService) throws WxErrorException {
//校验返回结果签名
Map<String, String> map = toMap();
if (getSign() != null && !SignUtils.checkSign(map, wxPayService.getConfig().getMchKey())) {
this.getLogger().debug("校验结果签名失败,参数:{}", map);
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg("参数格式校验错误!").build());
}

//校验结果是否成功
if (!"SUCCESS".equalsIgnoreCase(getReturnCode())
|| !"SUCCESS".equalsIgnoreCase(getResultCode())) {
StringBuilder errorMsg = new StringBuilder();
if (getReturnCode() != null) {
errorMsg.append("返回代码:").append(getReturnCode());
}
if (getReturnMsg() != null) {
errorMsg.append(",返回信息:").append(getReturnMsg());
}
if (getResultCode() != null) {
errorMsg.append(",结果代码:").append(getResultCode());
}
if (getErrCode() != null) {
errorMsg.append(",错误代码:").append(getErrCode());
}
if (getErrCodeDes() != null) {
errorMsg.append(",错误详情:").append(getErrCodeDes());
}

WxError error = WxError.newBuilder()
.setErrorCode(-1)
.setErrorMsg(errorMsg.toString())
.build();
this.getLogger().error("\n结果业务代码异常,返回結果:{},\n{}", map, error);
throw new WxErrorException(error);
}
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayCommonResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayCommonResult.java Bestand weergeven

@@ -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;


+ 328
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayMicropayResult.java Bestand weergeven

@@ -0,0 +1,328 @@
package com.github.binarywang.wxpay.bean.result;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* <pre>
* 提交刷卡支付接口响应结果对象类
* Created by Binary Wang on 2017-3-23.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayMicropayResult extends WxPayBaseResult {
/**
* <pre>
* 用户标识
* openid
* 是
* String(128)
* Y
* 用户在商户appid 下的唯一标识
* </pre>
**/
@XStreamAlias("openid")
private String openid;

/**
* <pre>
* 是否关注公众账号
* is_subscribe
* 是
* String(1)
* Y
* 用户是否关注公众账号,仅在公众账号类型支付有效,取值范围:Y或N;Y-关注;N-未关注
* </pre>
**/
@XStreamAlias("is_subscribe")
private String isSubscribe;

/**
* <pre>
* 交易类型
* trade_type
* 是
* String(16)
* MICROPAY
* 支付类型为MICROPAY(即扫码支付)
* </pre>
**/
@XStreamAlias("trade_type")
private String tradeType;

/**
* <pre>
* 付款银行
* bank_type
* 是
* String(32)
* CMC
* 银行类型,采用字符串类型的银行标识,值列表详见银行类型
* </pre>
**/
@XStreamAlias("bank_type")
private String bankType;

/**
* <pre>
* 货币类型
* fee_type
* 否
* String(16)
* CNY
* 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
* </pre>
**/
@XStreamAlias("fee_type")
private String feeType;

/**
* <pre>
* 订单金额
* total_fee
* 是
* Int
* 888
* 订单总金额,单位为分,只能为整数,详见支付金额
* </pre>
**/
@XStreamAlias("total_fee")
private String totalFee;

/**
* <pre>
* 应结订单金额
* settlement_total_fee
* 否
* Int
* 100
* 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
* </pre>
**/
@XStreamAlias("settlement_total_fee")
private Integer settlementTotalFee;

/**
* <pre>
* 代金券金额
* coupon_fee
* 否
* Int
* 100
* “代金券”金额<=订单金额,订单金额-“代金券”金额=现金支付金额,详见支付金额
* </pre>
**/
@XStreamAlias("coupon_fee")
private Integer couponFee;

/**
* <pre>
* 现金支付货币类型
* cash_fee_type
* 否
* String(16)
* CNY
* 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
* </pre>
**/
@XStreamAlias("cash_fee_type")
private String cashFeeType;

/**
* <pre>
* 现金支付金额
* cash_fee
* 是
* Int
* 100
* 订单现金支付金额,详见支付金额
* </pre>
**/
@XStreamAlias("cash_fee")
private Integer cashFee;

/**
* <pre>
* 微信支付订单号
* transaction_id
* 是
* String(32)
* 1217752501201407033233368018
* 微信支付订单号
* </pre>
**/
@XStreamAlias("transaction_id")
private String transactionId;

/**
* <pre>
* 商户订单号
* out_trade_no
* 是
* String(32)
* 1217752501201407033233368018
* 商户系统的订单号,与请求一致。
* </pre>
**/
@XStreamAlias("out_trade_no")
private String outTradeNo;

/**
* <pre>
* 商家数据包
* attach
* 否
* String(128)
* 123456
* 商家数据包,原样返回
* </pre>
**/
@XStreamAlias("attach")
private String attach;

/**
* <pre>
* 支付完成时间
* time_end
* 是
* String(14)
* 20141030133525
* 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。详见时间规则
* </pre>
**/
@XStreamAlias("time_end")
private String timeEnd;

/**
* <pre>
* 营销详情
* promotion_detail
* 否
* String(6000)
* 示例见下文
* 新增返回,单品优惠功能字段,需要接入请见详细说明
* </pre>
**/
@XStreamAlias("promotion_detail")
private String promotionDetail;

public String getOpenid() {
return this.openid;
}

public void setOpenid(String openid) {
this.openid = openid;
}

public String getIsSubscribe() {
return this.isSubscribe;
}

public void setIsSubscribe(String isSubscribe) {
this.isSubscribe = isSubscribe;
}

public String getTradeType() {
return this.tradeType;
}

public void setTradeType(String tradeType) {
this.tradeType = tradeType;
}

public String getBankType() {
return this.bankType;
}

public void setBankType(String bankType) {
this.bankType = bankType;
}

public String getFeeType() {
return this.feeType;
}

public void setFeeType(String feeType) {
this.feeType = feeType;
}

public String getTotalFee() {
return this.totalFee;
}

public void setTotalFee(String totalFee) {
this.totalFee = totalFee;
}

public Integer getSettlementTotalFee() {
return this.settlementTotalFee;
}

public void setSettlementTotalFee(Integer settlementTotalFee) {
this.settlementTotalFee = settlementTotalFee;
}

public Integer getCouponFee() {
return this.couponFee;
}

public void setCouponFee(Integer couponFee) {
this.couponFee = couponFee;
}

public String getCashFeeType() {
return this.cashFeeType;
}

public void setCashFeeType(String cashFeeType) {
this.cashFeeType = cashFeeType;
}

public Integer getCashFee() {
return this.cashFee;
}

public void setCashFee(Integer cashFee) {
this.cashFee = cashFee;
}

public String getTransactionId() {
return this.transactionId;
}

public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}

public String getOutTradeNo() {
return this.outTradeNo;
}

public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}

public String getAttach() {
return this.attach;
}

public void setAttach(String attach) {
this.attach = attach;
}

public String getTimeEnd() {
return this.timeEnd;
}

public void setTimeEnd(String timeEnd) {
this.timeEnd = timeEnd;
}

public String getPromotionDetail() {
return this.promotionDetail;
}

public void setPromotionDetail(String promotionDetail) {
this.promotionDetail = promotionDetail;
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderCloseResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderCloseResult.java Bestand weergeven

@@ -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;


+ 392
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderNotifyResult.java Bestand weergeven

@@ -0,0 +1,392 @@
package com.github.binarywang.wxpay.bean.result;

import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon;
import com.github.binarywang.wxpay.converter.WxPayOrderNotifyResultConverter;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import me.chanjar.weixin.common.util.BeanUtils;
import me.chanjar.weixin.common.util.ToStringUtils;
import me.chanjar.weixin.common.util.xml.XStreamInitializer;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
* 支付结果通用通知 ,文档见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
*
* @author aimilin6688
* @since 2.5.0
*/
@XStreamAlias("xml")
public class WxPayOrderNotifyResult extends WxPayBaseResult implements Serializable {

private static final long serialVersionUID = 5389718115223345496L;

/**
* <pre>
* 设备号
* device_info
* 否
* String(32)
* 013467007045764
* 微信支付分配的终端设备号,
* </pre>
*/
@XStreamAlias("device_info")
private String deviceInfo;

/**
* <pre>
* 用户标识
* openid
* 是
* String(128)
* wxd930ea5d5a258f4f
* 用户在商户appid下的唯一标识
* </pre>
*/
@XStreamAlias("openid")
private String openid;

/**
* <pre>
* 是否关注公众账号
* is_subscribe
* 否
* String(1)
* Y
* 用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
* </pre>
*/
@XStreamAlias("is_subscribe")
private String isSubscribe;


/**
* <pre>
* 交易类型
* trade_type
* 是
* String(16)
* JSAPI JSAPI、NATIVE、APP
* </pre>
*/
@XStreamAlias("trade_type")
private String tradeType;


/**
* <pre>
* 付款银行
* bank_type
* 是
* String(16)
* CMC
* 银行类型,采用字符串类型的银行标识,银行类型见银行列表
* </pre>
*/
@XStreamAlias("bank_type")
private String bankType;


/**
* <pre>
* 订单金额
* total_fee
* 是
* Int
* 100
* 订单总金额,单位为分
* </pre>
*/
@XStreamAlias("total_fee")
private Integer totalFee;
/**
* <pre>
* 应结订单金额
* settlement_total_fee
* 否
* Int
* 100
* 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
* </pre>
*/
@XStreamAlias("settlement_total_fee")
private Integer settlementTotalFee;
/**
* <pre>
* 货币种类
* fee_type
* 否
* String(8)
* CNY
* 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
* </pre>
*/
@XStreamAlias("fee_type")
private String feeType;
/**
* <pre>
* 现金支付金额
* cash_fee
* 是
* Int
* 100
* 现金支付金额订单现金支付金额,详见支付金额
* </pre>
*/
@XStreamAlias("cash_fee")
private Integer cashFee;
/**
* <pre>
* 现金支付货币类型
* cash_fee_type
* 否
* String(16)
* CNY
* 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
* </pre>
*/
@XStreamAlias("cash_fee_type")
private String cashFeeType;
/**
* <pre>
* 总代金券金额
* coupon_fee
* 否
* Int
* 10
* 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
* </pre>
*/
@XStreamAlias("coupon_fee")
private Integer couponFee;

/**
* <pre>
* 代金券使用数量
* coupon_count
* 否
* Int
* 1
* 代金券使用数量
* </pre>
*/
@XStreamAlias("coupon_count")
private Integer couponCount;

private List<WxPayOrderNotifyCoupon> couponList;

/**
* <pre>
* 微信支付订单号
* transaction_id
* 是
* String(32)
* 1217752501201407033233368018
* 微信支付订单号
* </pre>
*/
@XStreamAlias("transaction_id")
private String transactionId;

/**
* <pre>
* 商户订单号
* out_trade_no
* 是
* String(32)
* 1212321211201407033568112322
* 商户系统的订单号,与请求一致。
* </pre>
*/
@XStreamAlias("out_trade_no")
private String outTradeNo;
/**
* <pre>
* 商家数据包
* attach
* 否
* String(128)
* 123456
* 商家数据包,原样返回
* </pre>
*/
@XStreamAlias("attach")
private String attach;
/**
* <pre>
* 支付完成时间
* time_end
* 是
* String(14)
* 20141030133525
* 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
* </pre>
*/
@XStreamAlias("time_end")
private String timeEnd;

public static WxPayOrderNotifyResult fromXML(String xmlString) {
XStream xstream = XStreamInitializer.getInstance();
xstream.processAnnotations(WxPayOrderNotifyResult.class);
xstream.registerConverter(new WxPayOrderNotifyResultConverter(xstream.getMapper(), xstream.getReflectionProvider()));
WxPayOrderNotifyResult result = (WxPayOrderNotifyResult) xstream.fromXML(xmlString);
result.setXmlString(xmlString);
return result;
}

public Integer getCouponCount() {
return couponCount;
}

public void setCouponCount(Integer couponCount) {
this.couponCount = couponCount;
}

public List<WxPayOrderNotifyCoupon> getCouponList() {
return couponList;
}

public void setCouponList(List<WxPayOrderNotifyCoupon> couponList) {
this.couponList = couponList;
}

public String getDeviceInfo() {
return deviceInfo;
}

public void setDeviceInfo(String deviceInfo) {
this.deviceInfo = deviceInfo;
}

public String getOpenid() {
return openid;
}

public void setOpenid(String openid) {
this.openid = openid;
}

public String getIsSubscribe() {
return isSubscribe;
}

public void setIsSubscribe(String isSubscribe) {
this.isSubscribe = isSubscribe;
}

public String getTradeType() {
return tradeType;
}

public void setTradeType(String tradeType) {
this.tradeType = tradeType;
}

public String getBankType() {
return bankType;
}

public void setBankType(String bankType) {
this.bankType = bankType;
}

public Integer getTotalFee() {
return totalFee;
}

public void setTotalFee(Integer totalFee) {
this.totalFee = totalFee;
}

public Integer getSettlementTotalFee() {
return settlementTotalFee;
}

public void setSettlementTotalFee(Integer settlementTotalFee) {
this.settlementTotalFee = settlementTotalFee;
}

public String getFeeType() {
return feeType;
}

public void setFeeType(String feeType) {
this.feeType = feeType;
}

public Integer getCashFee() {
return cashFee;
}

public void setCashFee(Integer cashFee) {
this.cashFee = cashFee;
}

public String getCashFeeType() {
return cashFeeType;
}

public void setCashFeeType(String cashFeeType) {
this.cashFeeType = cashFeeType;
}

public Integer getCouponFee() {
return couponFee;
}

public void setCouponFee(Integer couponFee) {
this.couponFee = couponFee;
}

public String getTransactionId() {
return transactionId;
}

public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}

public String getOutTradeNo() {
return outTradeNo;
}

public void setOutTradeNo(String outTradeNo) {
this.outTradeNo = outTradeNo;
}

public String getAttach() {
return attach;
}

public void setAttach(String attach) {
this.attach = attach;
}

public String getTimeEnd() {
return timeEnd;
}

public void setTimeEnd(String timeEnd) {
this.timeEnd = timeEnd;
}

@Override
public Map<String, String> toMap() {
Map<String, String> resultMap = BeanUtils.xmlBean2Map(this);
if (this.getCouponCount() != null && this.getCouponCount() > 0) {
for (int i = 0; i < this.getCouponCount(); i++) {
WxPayOrderNotifyCoupon coupon = couponList.get(i);
resultMap.putAll(coupon.toMap(i));
}
}
return resultMap;
}

@Override
public String toString() {
return ToStringUtils.toSimpleString(this);
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayOrderQueryResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderQueryResult.java Bestand weergeven

@@ -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)));
}
}
}

+ 35
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayOrderReverseResult.java Bestand weergeven

@@ -0,0 +1,35 @@
package com.github.binarywang.wxpay.bean.result;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* <pre>
* 撤销订单响应结果类
* Created by Binary Wang on 2017-3-23.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayOrderReverseResult extends WxPayBaseResult {

/**
* <pre>
* 是否重调
* recall
* 是
* String(1)
* Y
* 是否需要继续调用撤销,Y-需要,N-不需要
* </pre>
**/
@XStreamAlias("recall")
private String isRecall;

public String getIsRecall() {
return this.isRecall;
}

public void setIsRecall(String isRecall) {
this.isRecall = isRecall;
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRedpackQueryResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRedpackQueryResult.java Bestand weergeven

@@ -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;


weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundQueryResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java Bestand weergeven

@@ -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)
)
);
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayRefundResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java Bestand weergeven

@@ -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;

/**
* <pre>
* 微信支付-申请退款返回结果
* https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
* </pre>
* @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")

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPaySendRedpackResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPaySendRedpackResult.java Bestand weergeven

@@ -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 {

+ 34
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayShorturlResult.java Bestand weergeven

@@ -0,0 +1,34 @@
package com.github.binarywang.wxpay.bean.result;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* <pre>
* 转换短链接结果对象类
* Created by Binary Wang on 2017-3-27.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
@XStreamAlias("xml")
public class WxPayShorturlResult extends WxPayBaseResult {
/**
* <pre>
* URL链接
* short_url
* 是
* String(64)
* weixin://wxpay/s/XXXXXX
* 转换后的URL
* </pre>
*/
@XStreamAlias("short_url")
private String shortUrl;

public String getShortUrl() {
return this.shortUrl;
}

public void setShortUrl(String shortUrl) {
this.shortUrl = shortUrl;
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/result/WxPayUnifiedOrderResult.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayUnifiedOrderResult.java Bestand weergeven

@@ -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;

/**
* <pre>
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"返回的结果
* 统一下单(详见http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
* 统一下单(详见http://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)
* </pre>
*
* @author chanjarster

+ 24
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxScanPayNotifyResult.java Bestand weergeven

@@ -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;
}
}

+ 157
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java Bestand weergeven

@@ -0,0 +1,157 @@
package com.github.binarywang.wxpay.config;

import org.apache.http.ssl.SSLContexts;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;

/**
* 微信支付配置
*
* @author Binary Wang (https://github.com/binarywang)
*/
public class WxPayConfig {
private String appId;
private String subAppId;
private String mchId;
private String mchKey;
private String subMchId;
private String notifyUrl;
private String tradeType;
private SSLContext sslContext;
private String keyPath;

public void setNotifyUrl(String notifyUrl) {
this.notifyUrl = notifyUrl;
}

public void setTradeType(String tradeType) {
this.tradeType = tradeType;
}

/**
* 设置证书
* @param keyPath apiclient_cert.p12的文件的绝对路径
*/
public void setKeyPath(String keyPath) {
this.keyPath = keyPath;
}

public String getKeyPath() {
return keyPath;
}

public void setAppId(String appId) {
this.appId = appId;
}

public void setSubAppId(String subAppId) {
this.subAppId = subAppId;
}

public void setMchId(String mchId) {
this.mchId = mchId;
}

public void setMchKey(String mchKey) {
this.mchKey = mchKey;
}

public void setSubMchId(String subMchId) {
this.subMchId = subMchId;
}

public void setSslContext(SSLContext sslContext) {
this.sslContext = sslContext;
}

/**
* 商户号
*/
public String getMchId() {
return this.mchId;
}

/**
* 商户密钥
*/
public String getMchKey() {
return this.mchKey;
}

/**
* 公众号appid
*/
public String getAppId() {
return this.appId;
}

/**
* 服务商模式下的子商户公众账号ID
*/
public String getSubAppId() {
return this.subAppId;
}

/**
* 服务商模式下的子商户号
*/
public String getSubMchId() {
return this.subMchId;
}

/**
* 微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数。
*/
public String getNotifyUrl() {
return this.notifyUrl;
}

/**
* 交易类型
* <pre>
* JSAPI--公众号支付
* NATIVE--原生扫码支付
* APP--app支付
* </pre>
*/
public String getTradeType() {
return this.tradeType;
}

public SSLContext getSslContext() {
return this.sslContext;
}

/**
* 微信支付是否使用仿真测试环境
* 默认不使用
*/
public boolean useSandboxForWxPay() {
return false;
}

public SSLContext initSSLContext() {
if (null == mchId) {
throw new IllegalArgumentException("请确保商户号mch_id已设置");
}

File file = new File(this.keyPath);
if (!file.exists()) {
throw new RuntimeException("证书文件:【" + file.getPath() + "】不存在!");
}

try {
FileInputStream inputStream = new FileInputStream(file);
KeyStore keystore = KeyStore.getInstance("PKCS12");
char[] partnerId2charArray = mchId.toCharArray();
keystore.load(inputStream, partnerId2charArray);
this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();
return this.sslContext;
} catch (Exception e) {
throw new RuntimeException("证书文件有问题,请核实!", e);
}
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/pay/WxPayOrderNotifyResultConverter.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/converter/WxPayOrderNotifyResultConverter.java Bestand weergeven

@@ -1,130 +1,129 @@
package me.chanjar.weixin.mp.bean.pay;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
import me.chanjar.weixin.mp.bean.pay.result.WxPayOrderNotifyResult;
public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter {
public WxPayOrderNotifyResultConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
super(mapper, reflectionProvider);
}
@Override
@SuppressWarnings("rawtypes")
public boolean canConvert(Class type) {
return type.equals(WxPayOrderNotifyResult.class);
}
@Override
public void marshal(Object original, HierarchicalStreamWriter writer, MarshallingContext context) {
super.marshal(original, writer, context);
WxPayOrderNotifyResult obj = (WxPayOrderNotifyResult) original;
List<WxPayOrderNotifyCoupon> list = obj.getCouponList();
if (list == null || list.size() == 0) {
return;
}
for (int i = 0; i < list.size(); i++) {
WxPayOrderNotifyCoupon coupon = list.get(i);
writer.startNode("coupon_id_" + i);
writer.setValue(coupon.getCouponId());
writer.endNode();
writer.startNode("coupon_type_" + i);
writer.setValue(coupon.getCouponType());
writer.endNode();
writer.startNode("coupon_fee_" + i);
writer.setValue(coupon.getCouponFee() + "");
writer.endNode();
}
}
@Override
protected void marshallField(MarshallingContext context, Object newObj, Field field) {
if (field.getName().equals("couponList")) {
return;
} else {
super.marshallField(context, newObj, field);
}
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
WxPayOrderNotifyResult obj = new WxPayOrderNotifyResult();
List<Field> fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields()));
fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields()));
Map<String, Field> fieldMap = getFieldMap(fields);
List<WxPayOrderNotifyCoupon> coupons = new ArrayList<>(10);
while (reader.hasMoreChildren()) {
reader.moveDown();
if (fieldMap.containsKey(reader.getNodeName())) {
Field field = fieldMap.get(reader.getNodeName());
setFieldValue(context, obj, field);
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_id_")) {
String id = (String) context.convertAnother(obj, String.class);
getIndex(coupons, reader.getNodeName()).setCouponId(id);
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_type_")) {
String type = (String) context.convertAnother(obj, String.class);
getIndex(coupons, reader.getNodeName()).setCouponType(type);
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_fee_")) {
Integer fee = (Integer) context.convertAnother(obj, Integer.class);
getIndex(coupons, reader.getNodeName()).setCouponFee(fee);
}
reader.moveUp();
}
obj.setCouponList(coupons);
return obj;
}
private void setFieldValue(UnmarshallingContext context, WxPayOrderNotifyResult obj, Field field) {
Object val = context.convertAnother(obj, field.getType());
try {
if (val != null) {
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), obj.getClass());
pd.getWriteMethod().invoke(obj, val);
}
} catch (Exception e) {
}
}
private Map<String, Field> getFieldMap(List<Field> fields) {
Map<String, Field> fieldMap = Maps.uniqueIndex(fields, new Function<Field, String>() {
@Override
public String apply(Field field) {
if (field.isAnnotationPresent(XStreamAlias.class)) {
return field.getAnnotation(XStreamAlias.class).value();
}
return field.getName();
}
});
return fieldMap;
}
private WxPayOrderNotifyCoupon getIndex(List<WxPayOrderNotifyCoupon> coupons, String nodeName) {
Integer index = Integer.valueOf(StringUtils.substring(nodeName, nodeName.lastIndexOf("_") + 1));
if (index >= coupons.size() || coupons.get(index) == null) {
coupons.add(index, new WxPayOrderNotifyCoupon());
}
return coupons.get(index);
}
}
package com.github.binarywang.wxpay.converter;

import com.github.binarywang.wxpay.bean.WxPayOrderNotifyCoupon;
import com.github.binarywang.wxpay.bean.result.WxPayOrderNotifyResult;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class WxPayOrderNotifyResultConverter extends AbstractReflectionConverter {

public WxPayOrderNotifyResultConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
super(mapper, reflectionProvider);
}

@Override
@SuppressWarnings("rawtypes")
public boolean canConvert(Class type) {
return type.equals(WxPayOrderNotifyResult.class);
}

@Override
public void marshal(Object original, HierarchicalStreamWriter writer, MarshallingContext context) {
super.marshal(original, writer, context);
WxPayOrderNotifyResult obj = (WxPayOrderNotifyResult) original;
List<WxPayOrderNotifyCoupon> list = obj.getCouponList();
if (list == null || list.size() == 0) {
return;
}
for (int i = 0; i < list.size(); i++) {
WxPayOrderNotifyCoupon coupon = list.get(i);
writer.startNode("coupon_id_" + i);
writer.setValue(coupon.getCouponId());
writer.endNode();
writer.startNode("coupon_type_" + i);
writer.setValue(coupon.getCouponType());
writer.endNode();
writer.startNode("coupon_fee_" + i);
writer.setValue(coupon.getCouponFee() + "");
writer.endNode();
}
}

@Override
protected void marshallField(MarshallingContext context, Object newObj, Field field) {
if (field.getName().equals("couponList")) {
return;
} else {
super.marshallField(context, newObj, field);
}
}

@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
WxPayOrderNotifyResult obj = new WxPayOrderNotifyResult();

List<Field> fields = new ArrayList<>(Arrays.asList(obj.getClass().getDeclaredFields()));
fields.addAll(Arrays.asList(obj.getClass().getSuperclass().getDeclaredFields()));
Map<String, Field> fieldMap = getFieldMap(fields);

List<WxPayOrderNotifyCoupon> coupons = new ArrayList<>(10);
while (reader.hasMoreChildren()) {
reader.moveDown();
if (fieldMap.containsKey(reader.getNodeName())) {
Field field = fieldMap.get(reader.getNodeName());
setFieldValue(context, obj, field);
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_id_")) {
String id = (String) context.convertAnother(obj, String.class);
getIndex(coupons, reader.getNodeName()).setCouponId(id);
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_type_")) {
String type = (String) context.convertAnother(obj, String.class);
getIndex(coupons, reader.getNodeName()).setCouponType(type);
} else if (StringUtils.startsWith(reader.getNodeName(), "coupon_fee_")) {
Integer fee = (Integer) context.convertAnother(obj, Integer.class);
getIndex(coupons, reader.getNodeName()).setCouponFee(fee);
}
reader.moveUp();
}

obj.setCouponList(coupons);
return obj;
}

private void setFieldValue(UnmarshallingContext context, WxPayOrderNotifyResult obj, Field field) {
Object val = context.convertAnother(obj, field.getType());
try {
if (val != null) {
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), obj.getClass());
pd.getWriteMethod().invoke(obj, val);
}
} catch (Exception e) {
}
}

private Map<String, Field> getFieldMap(List<Field> fields) {
Map<String, Field> fieldMap = Maps.uniqueIndex(fields, new Function<Field, String>() {
@Override
public String apply(Field field) {
if (field.isAnnotationPresent(XStreamAlias.class)) {
return field.getAnnotation(XStreamAlias.class).value();
}
return field.getName();
}
});
return fieldMap;
}

private WxPayOrderNotifyCoupon getIndex(List<WxPayOrderNotifyCoupon> coupons, String nodeName) {
Integer index = Integer.valueOf(StringUtils.substring(nodeName, nodeName.lastIndexOf("_") + 1));
if (index >= coupons.size() || coupons.get(index) == null) {
coupons.add(index, new WxPayOrderNotifyCoupon());
}
return coupons.get(index);
}
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpPayService.java → weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java Bestand weergeven

@@ -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;

/**
* <pre>
* 微信支付相关接口
* Created by Binary Wang on 2016/7/28.
* </pre>
*
* @author binarywang (https://github.com/binarywang)
*/
public interface WxMpPayService {
public interface WxPayService {

/**
* <pre>
* 查询订单(详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2)
* 查询订单(详见https://com.github.binarywang.wechat.pay.bean.pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2)
* 该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。
* 需要调用查询接口的情况:
* ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知;
@@ -28,7 +31,7 @@ public interface WxMpPayService {
* </pre>
*
* @param transactionId 微信订单号
* @param outTradeNo 商户系统内部的订单号,当没提供transaction_id时需要传这个。
* @param outTradeNo 商户系统内部的订单号,当没提供transactionId时需要传这个。
*/
WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException;

@@ -44,12 +47,12 @@ public interface WxMpPayService {
* 是否需要证书: 不需要。
* </pre>
*
* @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<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException;

/**
* 获取配置
*/
WxPayConfig getConfig();

/**
* 设置配置对象
*/
void setConfig(WxPayConfig config);

/**
* <pre>
* 微信支付-申请退款
@@ -81,7 +94,8 @@ public interface WxMpPayService {
* <pre>
* 微信支付-查询退款
* 应用场景:
* 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款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
* </pre>
@@ -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<String, String> prams);


/**
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3)
*
* @param prams 参数信息
* @param signKey 签名Key
* @return 签名字符串
*/
String createSign(Map<String, String> prams, String signKey);


/**
* 校验签名是否正确,默认使用配置中的PartnerKey进行签名
*
* @param xmlbean Bean需要标记有XML注解
* @return true - 签名校验成功,false - 签名校验失败
* @see #checkSign(Map, String)
*/
boolean checkSign(Object xmlbean);

/**
* 校验签名是否正确
*
* @param xmlbean Bean需要标记有XML注解
* @param signKey 校验的签名Key
* @return true - 签名校验成功,false - 签名校验失败
* @see #checkSign(Map, String)
*/
boolean checkSign(Object xmlbean, String signKey);

/**
* 校验签名是否正确,默认使用配置中的PartnerKey进行签名
*
* @param prams 需要校验的参数Map
* @return true - 签名校验成功,false - 签名校验失败
* @see #checkSign(Map, String)
*/
boolean checkSign(Map<String, String> prams);

/**
* 校验签名是否正确
*
* @param params 需要校验的参数Map
* @param signKey 校验的签名Key
* @return true - 签名校验成功,false - 签名校验失败
* @see #checkSign(Map, String)
*/
boolean checkSign(Map<String, String> params, String signKey);

/**
* 发送微信红包给个人用户
* <pre>
@@ -248,6 +185,20 @@ public interface WxMpPayService {
*/
byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength);

/**
* <pre>
* 扫码支付模式一生成二维码的方法
* 二维码中的内容为链接,形式为:
* weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
* 其中XXXXX为商户需要填写的内容,商户将该链接生成二维码,如需要打印发布二维码,需要采用此格式。商户可调用第三方库生成二维码图片。
* 文档详见: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4
* </pre>
*
* @param productId 产品Id
* @return 生成的二维码URL连接
*/
String createScanPayQrcodeMode1(String productId);

/**
* <pre>
* 扫码支付模式二生成二维码的方法
@@ -296,4 +247,81 @@ public interface WxMpPayService {
* @return 保存到本地的临时文件
*/
File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException;

/**
* <pre>
* 提交刷卡支付
* 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
* 应用场景:
* 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
* 提醒1:提交支付请求后微信会同步返回支付结果。当返回结果为“系统错误”时,商户系统等待5秒后调用【查询订单API】,查询支付实际交易结果;当返回结果为“USERPAYING”时,商户系统可设置间隔时间(建议10秒)重新查询支付结果,直到支付成功或超时(建议30秒);
* 提醒2:在调用查询接口返回后,如果交易状况不明晰,请调用【撤销订单API】,此时如果交易失败则关闭订单,该单不能再支付成功;如果交易成功,则将扣款退回到用户账户。当撤销无返回或错误时,请再次调用。注意:请勿扣款后立即调用【撤销订单API】,建议至少15秒后再调用。撤销订单API需要双向证书。
* 接口地址: https://api.mch.weixin.qq.com/pay/micropay
* 是否需要证书:不需要。
* </pre>
*/
WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException;

/**
* <pre>
* 撤销订单API
* 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3
* 应用场景:
* 支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,微信支付系统会将此订单关闭;
* 如果用户支付成功,微信支付系统会将此订单资金退还给用户。
* 注意:7天以内的交易单可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。
* 提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】。
* 调用支付接口后请勿立即调用撤销订单API,建议支付后至少15s后再调用撤销订单接口。
* 接口链接 :https://api.mch.weixin.qq.com/secapi/pay/reverse
* 是否需要证书:请求需要双向证书。
* </pre>
*/
WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException;

/**
* <pre>
* 转换短链接
* 文档地址:
* https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_9&index=8
* 应用场景:
* 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),减小二维码数据量,提升扫描速度和精确度。
* 接口地址:https://api.mch.weixin.qq.com/tools/shorturl
* 是否需要证书:否
* </pre>
* @param request 请求对象
*/
String shorturl(WxPayShorturlRequest request) throws WxErrorException;

/**
* <pre>
* 转换短链接
* </pre>
* @see WxPayService#shorturl(WxPayShorturlRequest)
* @param longUrl 需要被压缩的网址
*/
String shorturl(String longUrl) throws WxErrorException;

/**
* <pre>
* 授权码查询OPENID接口
* 通过授权码查询公众号Openid,调用查询后,该授权码只能由此商户号发起扣款,直至授权码更新。
* 文档地址:
* https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_13&index=9
* 接口链接:
* https://api.mch.weixin.qq.com/tools/authcodetoopenid
* </pre>
* @param request 请求对象
* @return openid
*/
String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException;

/**
* <pre>
* 授权码查询OPENID接口
* </pre>
* @see WxPayService#authcode2Openid(WxPayAuthcode2OpenidRequest)
* @param authCode 授权码
* @return openid
*/
String authcode2Openid(String authCode) throws WxErrorException;
}

+ 416
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceImpl.java Bestand weergeven

@@ -0,0 +1,416 @@
package com.github.binarywang.wxpay.service.impl;

import com.github.binarywang.utils.qrcode.QrcodeUtils;
import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;
import com.google.common.collect.Maps;
import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

/**
* Created by Binary Wang on 2016/7/28.
*
* @author binarywang (https://github.com/binarywang)
*/
public class WxPayServiceImpl implements WxPayService {
private static final String PAY_BASE_URL = "https://api.mch.weixin.qq.com";
private final Logger log = LoggerFactory.getLogger(this.getClass());

private WxPayConfig config;

@Override
public WxPayConfig getConfig() {
return this.config;
}

@Override
public void setConfig(WxPayConfig config) {
this.config = config;
}

private String getPayBaseUrl() {
if (this.getConfig().useSandboxForWxPay()) {
return PAY_BASE_URL + "/sandboxnew";
}

return PAY_BASE_URL;
}

@Override
public WxPayRefundResult refund(WxPayRefundRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/secapi/pay/refund";
String responseContent = this.postWithKey(url, request.toXML());
WxPayRefundResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundResult.class);
result.checkResult(this);
return result;
}

@Override
public WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, String outRefundNo, String refundId)
throws WxErrorException {
WxPayRefundQueryRequest request = new WxPayRefundQueryRequest();
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo));
request.setTransactionId(StringUtils.trimToNull(transactionId));
request.setOutRefundNo(StringUtils.trimToNull(outRefundNo));
request.setRefundId(StringUtils.trimToNull(refundId));

request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/pay/refundquery";
String responseContent = this.post(url, request.toXML());
WxPayRefundQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRefundQueryResult.class);
result.composeRefundRecords();
result.checkResult(this);
return result;
}

@Override
public WxPayOrderNotifyResult getOrderNotifyResult(String xmlData) throws WxErrorException {
try {
log.debug("微信支付回调参数详细:{}", xmlData);
WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData);
log.debug("微信支付回调结果对象:{}", result);
result.checkResult(this);
return result;
} catch (WxErrorException e) {
log.error(e.getMessage(), e);
throw e;
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new WxErrorException(WxError.newBuilder().setErrorMsg("发生异常" + e.getMessage()).build());
}
}

@Override
public WxPaySendRedpackResult sendRedpack(WxPaySendRedpackRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendredpack";
if (request.getAmtType() != null) {
//裂变红包
url = this.getPayBaseUrl() + "/mmpaymkttransfers/sendgroupredpack";
}

String responseContent = this.postWithKey(url, request.toXML());
WxPaySendRedpackResult result = WxPayBaseResult.fromXML(responseContent, WxPaySendRedpackResult.class);
//毋须校验,因为没有返回签名信息
// this.checkResult(result);
return result;
}

@Override
public WxPayRedpackQueryResult queryRedpack(String mchBillNo) throws WxErrorException {
WxPayRedpackQueryRequest request = new WxPayRedpackQueryRequest();
request.setMchBillNo(mchBillNo);
request.setBillType("MCHT");
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gethbinfo";
String responseContent = this.postWithKey(url, request.toXML());
WxPayRedpackQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayRedpackQueryResult.class);
result.checkResult(this);
return result;
}

@Override
public WxPayOrderQueryResult queryOrder(String transactionId, String outTradeNo) throws WxErrorException {
WxPayOrderQueryRequest request = new WxPayOrderQueryRequest();
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo));
request.setTransactionId(StringUtils.trimToNull(transactionId));
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/pay/orderquery";
String responseContent = this.post(url, request.toXML());
if (StringUtils.isBlank(responseContent)) {
throw new WxErrorException(WxError.newBuilder().setErrorMsg("无响应结果").build());
}

WxPayOrderQueryResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderQueryResult.class);
result.composeCoupons();
result.checkResult(this);
return result;
}

@Override
public WxPayOrderCloseResult closeOrder(String outTradeNo) throws WxErrorException {
if (StringUtils.isBlank(outTradeNo)) {
throw new IllegalArgumentException("out_trade_no不能为空");
}

WxPayOrderCloseRequest request = new WxPayOrderCloseRequest();
request.setOutTradeNo(StringUtils.trimToNull(outTradeNo));
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/pay/closeorder";
String responseContent = this.post(url, request.toXML());
WxPayOrderCloseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderCloseResult.class);
result.checkResult(this);

return result;
}

@Override
public WxPayUnifiedOrderResult unifiedOrder(WxPayUnifiedOrderRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/pay/unifiedorder";
String responseContent = this.post(url, request.toXML());
WxPayUnifiedOrderResult result = WxPayBaseResult.fromXML(responseContent, WxPayUnifiedOrderResult.class);
result.checkResult(this);
return result;
}

@Override
public Map<String, String> getPayInfo(WxPayUnifiedOrderRequest request) throws WxErrorException {
WxPayUnifiedOrderResult unifiedOrderResult = this.unifiedOrder(request);
String prepayId = unifiedOrderResult.getPrepayId();
if (StringUtils.isBlank(prepayId)) {
throw new RuntimeException(String.format("无法获取prepay id,错误代码: '%s',信息:%s。",
unifiedOrderResult.getErrCode(), unifiedOrderResult.getErrCodeDes()));
}

Map<String, String> payInfo = new HashMap<>();
payInfo.put("appId", getConfig().getAppId());
// 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。
// 但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
payInfo.put("nonceStr", String.valueOf(System.currentTimeMillis()));
payInfo.put("package", "prepay_id=" + prepayId);
payInfo.put("signType", "MD5");
if ("NATIVE".equals(request.getTradeType())) {
payInfo.put("codeUrl", unifiedOrderResult.getCodeURL());
}
payInfo.put("paySign", SignUtils.createSign(payInfo, this.getConfig().getMchKey()));
return payInfo;
}

@Override
public WxEntPayResult entPay(WxEntPayRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());
String url = this.getPayBaseUrl() + "/mmpaymkttransfers/promotion/transfers";

String responseContent = this.postWithKey(url, request.toXML());
WxEntPayResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayResult.class);
result.checkResult(this);
return result;
}

@Override
public WxEntPayQueryResult queryEntPay(String partnerTradeNo) throws WxErrorException {
WxEntPayQueryRequest request = new WxEntPayQueryRequest();
request.setPartnerTradeNo(partnerTradeNo);
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/mmpaymkttransfers/gettransferinfo";
String responseContent = this.postWithKey(url, request.toXML());
WxEntPayQueryResult result = WxPayBaseResult.fromXML(responseContent, WxEntPayQueryResult.class);
result.checkResult(this);
return result;
}

@Override
public byte[] createScanPayQrcodeMode1(String productId, File logoFile, Integer sideLength) {
String content = this.createScanPayQrcodeMode1(productId);
return this.createQrcode(content, logoFile, sideLength);
}

@Override
public String createScanPayQrcodeMode1(String productId) {
//weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
StringBuilder codeUrl = new StringBuilder("weixin://wxpay/bizpayurl?");
Map<String, String> params = Maps.newHashMap();
params.put("appid", this.getConfig().getAppId());
params.put("mch_id", this.getConfig().getMchId());
params.put("product_id", productId);
params.put("time_stamp", String.valueOf(System.currentTimeMillis() / 1000));//这里需要秒,10位数字
params.put("nonce_str", String.valueOf(System.currentTimeMillis()));

String sign = SignUtils.createSign(params, this.getConfig().getMchKey());
params.put("sign", sign);


for (String key : params.keySet()) {
codeUrl.append(key + "=" + params.get(key) + "&");
}

String content = codeUrl.toString().substring(0, codeUrl.length() - 1);
log.debug("扫码支付模式一生成二维码的URL:{}", content);
return content;
}

@Override
public byte[] createScanPayQrcodeMode2(String codeUrl, File logoFile, Integer sideLength) {
return this.createQrcode(codeUrl, logoFile, sideLength);
}

private byte[] createQrcode(String content, File logoFile, Integer sideLength) {
if (sideLength == null || sideLength < 1) {
return QrcodeUtils.createQrcode(content, logoFile);
}

return QrcodeUtils.createQrcode(content, sideLength, logoFile);
}

public void report(WxPayReportRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/payitil/report";
String responseContent = this.post(url, request.toXML());
WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class);
result.checkResult(this);
}

@Override
public File downloadBill(String billDate, String billType, String tarType, String deviceInfo) throws WxErrorException {
WxPayDownloadBillRequest request = new WxPayDownloadBillRequest();
request.setBillType(billType);
request.setBillDate(billDate);
request.setTarType(tarType);
request.setDeviceInfo(deviceInfo);

request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/pay/downloadbill";
//TODO 返回的内容可能是文件流,也有可能是xml,需要区分对待
String responseContent = this.post(url, request.toXML());

WxPayCommonResult result = WxPayBaseResult.fromXML(responseContent, WxPayCommonResult.class);
result.checkResult(this);
//TODO 待实现,暂时无测试帐号,无法调试
return null;
}

@Override
public WxPayMicropayResult micropay(WxPayMicropayRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/pay/micropay";
String responseContent = this.post(url, request.toXML());
WxPayMicropayResult result = WxPayBaseResult.fromXML(responseContent, WxPayMicropayResult.class);
result.checkResult(this);
return result;
}

@Override
public WxPayOrderReverseResult reverseOrder(WxPayOrderReverseRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/secapi/pay/reverse";
String responseContent = this.postWithKey(url, request.toXML());
WxPayOrderReverseResult result = WxPayBaseResult.fromXML(responseContent, WxPayOrderReverseResult.class);
result.checkResult(this);
return result;
}

@Override
public String shorturl(WxPayShorturlRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/tools/shorturl";
String responseContent = this.post(url, request.toXML());
WxPayShorturlResult result = WxPayBaseResult.fromXML(responseContent, WxPayShorturlResult.class);
result.checkResult(this);
return result.getShortUrl();
}

@Override
public String shorturl(String longUrl) throws WxErrorException {
return this.shorturl(new WxPayShorturlRequest(longUrl));
}

@Override
public String authcode2Openid(WxPayAuthcode2OpenidRequest request) throws WxErrorException {
request.checkAndSign(this.getConfig());

String url = this.getPayBaseUrl() + "/tools/authcodetoopenid";
String responseContent = this.post(url, request.toXML());
WxPayAuthcode2OpenidResult result = WxPayBaseResult.fromXML(responseContent, WxPayAuthcode2OpenidResult.class);
result.checkResult(this);
return result.getOpenid();
}

@Override
public String authcode2Openid(String authCode) throws WxErrorException {
return this.authcode2Openid(new WxPayAuthcode2OpenidRequest(authCode));
}

private String post(String url, String xmlParam) {
String requestString = xmlParam;
try {
requestString = new String(xmlParam.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1);
} catch (UnsupportedEncodingException e) {
//实际上不会发生该异常
e.printStackTrace();
}

HttpRequest request = HttpRequest.post(url).body(requestString);
HttpResponse response = request.send();
String responseString = null;
try {
responseString = new String(response.bodyText().getBytes(CharEncoding.ISO_8859_1), CharEncoding.UTF_8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}

this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, xmlParam, responseString);
return responseString;
}

/**
* 由于暂时未找到使用jodd-http实现证书配置的办法,故而暂时使用httpclient
*/
private String postWithKey(String url, String requestStr) throws WxErrorException {
try {
SSLContext sslContext = this.getConfig().getSslContext();
if (null == sslContext) {
sslContext = this.getConfig().initSSLContext();
}

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
new String[]{"TLSv1"}, null, new DefaultHostnameVerifier());

HttpPost httpPost = new HttpPost(url);

try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build()) {
httpPost.setEntity(new StringEntity(new String(requestStr.getBytes(CharEncoding.UTF_8), CharEncoding.ISO_8859_1)));
try (CloseableHttpResponse response = httpclient.execute(httpPost)) {
String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
this.log.debug("\n[URL]: {}\n[PARAMS]: {}\n[RESPONSE]: {}", url, requestStr, result);
return result;
}
} finally {
httpPost.releaseConnection();
}
} catch (Exception e) {
this.log.error("\n[URL]: {}\n[PARAMS]: {}\n[EXCEPTION]: {}", url, requestStr, e.getMessage());
throw new WxErrorException(WxError.newBuilder().setErrorCode(-1).setErrorMsg(e.getMessage()).build(), e);
}
}

}

+ 84
- 0
weixin-java-pay/src/main/java/com/github/binarywang/wxpay/util/SignUtils.java Bestand weergeven

@@ -0,0 +1,84 @@
package com.github.binarywang.wxpay.util;

import me.chanjar.weixin.common.util.BeanUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
* <pre>
* 签名相关工具类
* Created by Binary Wang on 2017-3-23.
* @author <a href="https://github.com/binarywang">binarywang(Binary Wang)</a>
* </pre>
*/
public class SignUtils {

/**
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3)
*
* @param xmlBean Bean需要标记有XML注解
* @param signKey 签名Key
* @return 签名字符串
*/
public static String createSign(Object xmlBean, String signKey) {
return createSign(BeanUtils.xmlBean2Map(xmlBean), signKey);
}

/**
* 微信公众号支付签名算法(详见:https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=4_3)
*
* @param params 参数信息
* @param signKey 签名Key
* @return 签名字符串
*/
public static String createSign(Map<String, String> params, String signKey) {
// if (this.getConfig().useSandboxForWxPay()) {
// //使用仿真测试环境
// //TODO 目前测试发现,以下两行代码都会出问题,所以暂不建议使用仿真测试环境
// signKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
// //return "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
// }

SortedMap<String, String> sortedMap = new TreeMap<>(params);

StringBuilder toSign = new StringBuilder();
for (String key : sortedMap.keySet()) {
String value = params.get(key);
if (StringUtils.isNotEmpty(value) && !"sign".equals(key) && !"key".equals(key)) {
toSign.append(key + "=" + value + "&");
}
}

toSign.append("key=" + signKey);
return DigestUtils.md5Hex(toSign.toString()).toUpperCase();
}

/**
* 校验签名是否正确
*
* @param xmlBean Bean需要标记有XML注解
* @param signKey 校验的签名Key
* @return true - 签名校验成功,false - 签名校验失败
* @see #checkSign(Map, String)
*/
public static boolean checkSign(Object xmlBean, String signKey) {
return checkSign(BeanUtils.xmlBean2Map(xmlBean), signKey);
}

/**
* 校验签名是否正确
*
* @param params 需要校验的参数Map
* @param signKey 校验的签名Key
* @return true - 签名校验成功,false - 签名校验失败
* @see #checkSign(Map, String)
*/
public static boolean checkSign(Map<String, String> params, String signKey) {
String sign = createSign(params, signKey);
return sign.equals(params.get("sign"));
}
}

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/pay/result/WxPayBaseResultTest.java → weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/result/WxPayBaseResultTest.java Bestand weergeven

@@ -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;
* </pre>
*/
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<String, String> 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<String, String> map = result.toMap();
System.out.println(map);
}

}

Some files were not shown because too many files changed in this diff

Laden…
Annuleren
Opslaan