diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c703964824..0b16b4779e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,7 +28,7 @@ $ git push
* 定期使用项目仓库内容更新自己仓库内容。
```bash
-$ git remote add upstream https://github.com/Wechat-Group/WxJava
+$ git remote add upstream https://github.com/binarywang/WxJava
$ git fetch upstream
$ git checkout develop
$ git rebase upstream/develop
diff --git a/README.md b/README.md
index ab645b7c4f..e02e046b33 100644
--- a/README.md
+++ b/README.md
@@ -21,24 +21,29 @@
-
+ |
|
-
+ |
|
-
+ |
@@ -59,7 +64,7 @@
### 重要信息
1. [`WxJava` 荣获 `GitCode` 2024年度十大开源社区奖项](https://mp.weixin.qq.com/s/wM_UlMsDm3IZ1CPPDvcvQw)。
2. 项目合作洽谈请联系微信`binary0000`(在微信里自行搜索并添加好友,请注明来意,如有关于SDK问题需讨论请参考下文入群讨论,不要加此微信)。
-3. **2023-12-28 发布 [【4.6.0正式版】](https://mp.weixin.qq.com/s/9Hhc_8w-v7ogS_TEAsqfAg)**!
+3. **2024-12-30 发布 [【4.7.0正式版】](https://mp.weixin.qq.com/s/_7k-XLYBqeJJhvHWCsdT0A)**!
4. 贡献源码可以参考视频:[【贡献源码全过程(上集)】](https://mp.weixin.qq.com/s/3xUZSATWwHR_gZZm207h7Q)、[【贡献源码全过程(下集)】](https://mp.weixin.qq.com/s/nyzJwVVoYSJ4hSbwyvTx9A) ,友情提供:[程序员小山与Bug](https://space.bilibili.com/473631007)
5. 新手重要提示:本项目仅是一个SDK开发工具包,未提供Web实现,建议使用 `maven` 或 `gradle` 引用本项目即可使用本SDK提供的各种功能,详情可参考 **[【Demo项目】](demo.md)** 或本项目中的部分单元测试代码;
6. 微信开发新手请务必阅读【开发文档】([Gitee Wiki](https://gitee.com/binary/weixin-java-tools/wikis/Home) 或者 [Github Wiki](https://github.com/binarywang/WxJava/wiki))的常见问题部分,可以少走很多弯路,节省不少时间。
@@ -90,7 +95,7 @@
com.github.binarywang
(不同模块参考下文)
- 4.6.0
+ 4.7.0
```
diff --git a/pom.xml b/pom.xml
index 730f6b5809..3c660ac656 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
pom
WxJava - Weixin/Wechat Java SDK
微信开发Java SDK
diff --git a/solon-plugins/pom.xml b/solon-plugins/pom.xml
index bf956526c8..4270e7aaee 100644
--- a/solon-plugins/pom.xml
+++ b/solon-plugins/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
pom
wx-java-solon-plugins
@@ -14,7 +14,7 @@
WxJava 各个模块的 Solon Plugin
- 3.0.1
+ 3.2.0
diff --git a/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml
index aa9911e115..072019106e 100644
--- a/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-channel-multi-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-channel-solon-plugin/README.md b/solon-plugins/wx-java-channel-solon-plugin/README.md
new file mode 100644
index 0000000000..a7168a8edc
--- /dev/null
+++ b/solon-plugins/wx-java-channel-solon-plugin/README.md
@@ -0,0 +1,92 @@
+# wx-java-channel-solon-plugin
+
+## 快速开始
+1. 引入依赖
+ ```xml
+
+
+ com.github.binarywang
+ wx-java-channel-solon-plugin
+ ${version}
+
+
+
+
+ redis.clients
+ jedis
+ ${jedis.version}
+
+
+
+
+ org.redisson
+ redisson
+ ${redisson.version}
+
+
+ ```
+2. 添加配置(app.properties)
+ ```properties
+ # 视频号配置(必填)
+ ## 视频号小店的appId和secret
+ wx.channel.app-id=@appId
+ wx.channel.secret=@secret
+ # 视频号配置 选填
+ ## 设置视频号小店消息服务器配置的token
+ wx.channel.token=@token
+ ## 设置视频号小店消息服务器配置的EncodingAESKey
+ wx.channel.aes-key=
+ ## 支持JSON或者XML格式,默认JSON
+ wx.channel.msg-data-format=JSON
+ ## 是否使用稳定版 Access Token
+ wx.channel.use-stable-access-token=false
+
+
+ # ConfigStorage 配置(选填)
+ ## 配置类型: memory(默认), jedis, redisson, redis_template
+ wx.channel.config-storage.type=memory
+ ## 相关redis前缀配置: wx:channel(默认)
+ wx.channel.config-storage.key-prefix=wx:channel
+ wx.channel.config-storage.redis.host=127.0.0.1
+ wx.channel.config-storage.redis.port=6379
+ wx.channel.config-storage.redis.password=123456
+
+
+ # http 客户端配置(选填)
+ ## # http客户端类型: http_client(默认)
+ wx.channel.config-storage.http-client-type=http_client
+ wx.channel.config-storage.http-proxy-host=
+ wx.channel.config-storage.http-proxy-port=
+ wx.channel.config-storage.http-proxy-username=
+ wx.channel.config-storage.http-proxy-password=
+ ## 最大重试次数,默认:5 次,如果小于 0,则为 0
+ wx.channel.config-storage.max-retry-times=5
+ ## 重试时间间隔步进,默认:1000 毫秒,如果小于 0,则为 1000
+ wx.channel.config-storage.retry-sleep-millis=1000
+ ```
+3. 自动注入的类型
+- `WxChannelService`
+- `WxChannelConfig`
+4. 使用样例
+
+```java
+import me.chanjar.weixin.channel.api.WxChannelService;
+import me.chanjar.weixin.channel.bean.shop.ShopInfoResponse;
+import me.chanjar.weixin.channel.util.JsonUtils;
+import me.chanjar.weixin.common.error.WxErrorException;
+import org.noear.solon.annotation.Inject;
+
+@Component
+public class DemoService {
+ @Inject
+ private WxChannelService wxChannelService;
+
+ public String getShopInfo() throws WxErrorException {
+ // 获取店铺基本信息
+ ShopInfoResponse response = wxChannelService.getBasicService().getShopInfo();
+ // 此处为演示,如果要返回response的结果,建议自己封装一个VO,避免直接返回response
+ return JsonUtils.encode(response);
+ }
+}
+```
+
diff --git a/solon-plugins/wx-java-channel-solon-plugin/pom.xml b/solon-plugins/wx-java-channel-solon-plugin/pom.xml
index dda371c780..256dd4a177 100644
--- a/solon-plugins/wx-java-channel-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-channel-solon-plugin/pom.xml
@@ -3,7 +3,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
index d2218490b9..eeea99b448 100644
--- a/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-cp-multi-solon-plugin/pom.xml
@@ -4,7 +4,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-cp-solon-plugin/pom.xml b/solon-plugins/wx-java-cp-solon-plugin/pom.xml
index 05598d6b9c..1d12f05ac4 100644
--- a/solon-plugins/wx-java-cp-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-cp-solon-plugin/pom.xml
@@ -4,7 +4,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml
index deca9a2ffa..9d7b0b7282 100644
--- a/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-miniapp-multi-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml b/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
index 5075140322..416f842596 100644
--- a/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-miniapp-solon-plugin/pom.xml
@@ -4,7 +4,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
index 67f9e2da37..f01f206089 100644
--- a/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-mp-multi-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-mp-solon-plugin/pom.xml b/solon-plugins/wx-java-mp-solon-plugin/pom.xml
index 5dcea9ac93..54b49d2668 100644
--- a/solon-plugins/wx-java-mp-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-mp-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-open-solon-plugin/pom.xml b/solon-plugins/wx-java-open-solon-plugin/pom.xml
index bd8c9e3e45..88c035eba5 100644
--- a/solon-plugins/wx-java-open-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-open-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-pay-solon-plugin/pom.xml b/solon-plugins/wx-java-pay-solon-plugin/pom.xml
index 47153d8f13..032e69a53e 100644
--- a/solon-plugins/wx-java-pay-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-pay-solon-plugin/pom.xml
@@ -5,7 +5,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/solon-plugins/wx-java-qidian-solon-plugin/pom.xml b/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
index b5488655ec..c3c0d322e0 100644
--- a/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
+++ b/solon-plugins/wx-java-qidian-solon-plugin/pom.xml
@@ -3,7 +3,7 @@
wx-java-solon-plugins
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index 75f2d94865..a849cc8e40 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
pom
wx-java-spring-boot-starters
diff --git a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml
index b67cc1733e..f9ffa4cacc 100644
--- a/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-channel-multi-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
index 4a4567198c..8c1018d4f0 100644
--- a/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-channel-spring-boot-starter/pom.xml
@@ -3,7 +3,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
index 0128c7bf52..205a39ce09 100644
--- a/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-cp-multi-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
index 59c2f63f8a..f9ef3aaede 100644
--- a/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-cp-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
index c90f2b741d..e0e781cc65 100644
--- a/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-miniapp-multi-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml
index 2eaa6f1c77..c38db4802a 100644
--- a/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-miniapp-spring-boot-starter/pom.xml
@@ -4,7 +4,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
index 3ec7cf5163..40487f9bd1 100644
--- a/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-mp-multi-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
index 4bc7037c22..d87b662007 100644
--- a/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-mp-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
index cd6f25e892..31c9e158b7 100644
--- a/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-open-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
index 960556dad7..91a92769c8 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/pom.xml
@@ -5,7 +5,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java
index 9a9672de1a..e401a8cfba 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/config/WxPayAutoConfiguration.java
@@ -57,6 +57,8 @@ public WxPayService wxPayService() {
payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath()));
payConfig.setCertSerialNo(StringUtils.trimToNull(this.properties.getCertSerialNo()));
payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiv3Key()));
+ payConfig.setPublicKeyId(StringUtils.trimToNull(this.properties.getPublicKeyId()));
+ payConfig.setPublicKeyPath(StringUtils.trimToNull(this.properties.getPublicKeyPath()));
wxPayService.setConfig(payConfig);
return wxPayService;
diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java
index 232bd33c47..a1a8cc2297 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/com/binarywang/spring/starter/wxjava/pay/properties/WxPayProperties.java
@@ -73,7 +73,17 @@ public class WxPayProperties {
* apiv3 商户apiclient_cert.pem
*/
private String privateCertPath;
-
+
+ /**
+ * 公钥ID
+ */
+ private String publicKeyId;
+
+ /**
+ * pub_key.pem证书文件的绝对路径或者以classpath:开头的类路径.
+ */
+ private String publicKeyPath;
+
/**
* 微信支付是否使用仿真测试环境.
* 默认不使用
diff --git a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
index 5fe49991e1..e44dc428be 100644
--- a/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
+++ b/spring-boot-starters/wx-java-qidian-spring-boot-starter/pom.xml
@@ -3,7 +3,7 @@
wx-java-spring-boot-starters
com.github.binarywang
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/weixin-graal/pom.xml b/weixin-graal/pom.xml
index 44c7c952fe..edf9c81d56 100644
--- a/weixin-graal/pom.xml
+++ b/weixin-graal/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
weixin-graal
diff --git a/weixin-java-channel/pom.xml b/weixin-java-channel/pom.xml
index 0cb27642ab..ac1d8f4c83 100644
--- a/weixin-java-channel/pom.xml
+++ b/weixin-java-channel/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
weixin-java-channel
diff --git a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderCommissionInfo.java b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderCommissionInfo.java
index 78e391e774..f3cab1f4bf 100644
--- a/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderCommissionInfo.java
+++ b/weixin-java-channel/src/main/java/me/chanjar/weixin/channel/bean/order/OrderCommissionInfo.java
@@ -42,4 +42,8 @@ public class OrderCommissionInfo implements Serializable {
/** 达人openfinderid */
@JsonProperty("openfinderid")
private String openFinderId;
+
+ /** 新带货平台 id */
+ @JsonProperty("talent_id")
+ private String talentId;
}
diff --git a/weixin-java-common/pom.xml b/weixin-java-common/pom.xml
index b775fab23b..4afef6adee 100644
--- a/weixin-java-common/pom.xml
+++ b/weixin-java-common/pom.xml
@@ -6,7 +6,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
weixin-java-common
diff --git a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java
index 4c06f5168e..12f04ba20c 100644
--- a/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java
+++ b/weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/apache/DefaultApacheHttpClientBuilder.java
@@ -59,7 +59,7 @@ public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder {
* 设置为负数是使用系统默认设置(非3000ms的默认值,而是httpClient的默认设置).
*
*/
- private int connectionRequestTimeout = -1;
+ private int connectionRequestTimeout = 3000;
/**
* 建立链接的超时时间,默认为5000ms.由于是在链接池获取链接,此设置应该并不起什么作用
diff --git a/weixin-java-cp/pom.xml b/weixin-java-cp/pom.xml
index 85a37ba2f9..4ba0471db1 100644
--- a/weixin-java-cp/pom.xml
+++ b/weixin-java-cp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
weixin-java-cp
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentWorkBenchService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentWorkBenchService.java
index c50aa2f5fc..67c57a8a88 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentWorkBenchService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpAgentWorkBenchService.java
@@ -36,4 +36,12 @@ public interface WxCpAgentWorkBenchService {
* @throws WxErrorException the wx error exception
*/
void setWorkBenchData(WxCpAgentWorkBench wxCpAgentWorkBench) throws WxErrorException;
+
+ /**
+ * Batch sets work bench data.
+ *
+ * @param wxCpAgentWorkBench the wx cp agent work bench
+ * @throws WxErrorException the wx error exception
+ */
+ void batchSetWorkBenchData(WxCpAgentWorkBench wxCpAgentWorkBench) throws WxErrorException;
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java
index f55d2f7b93..7f3cdeab7c 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpExternalContactService.java
@@ -38,6 +38,7 @@ public interface WxCpExternalContactService {
* 用户需要妥善存储返回的config_id,config_id丢失可能导致用户无法编辑或删除「联系我」。
* 临时会话模式不占用「联系我」数量,但每日最多添加10万个,并且仅支持单人。
* 临时会话模式的二维码,添加好友完成后该二维码即刻失效。
+ * 文档地址
*
*
* @param info 客户联系「联系我」方式
@@ -59,6 +60,25 @@ public interface WxCpExternalContactService {
*/
WxCpContactWayInfo getContactWay(String configId) throws WxErrorException;
+ /**
+ * 获取企业已配置的「联系我」列表
+ *
+ *
+ * 获取企业配置的「联系我」二维码和「联系我」小程序插件列表。不包含临时会话。
+ * 注意,该接口仅可获取2021年7月10日以后创建的「联系我」
+ *
+ *
+ * 文档地址: 获取企业已配置的「联系我」列表
+ *
+ * @param startTime 「联系我」创建起始时间戳, 默认为90天前
+ * @param endTime 「联系我」创建结束时间戳, 默认为当前时间
+ * @param cursor 分页查询使用的游标,为上次请求返回的 next_cursor
+ * @param limit 每次查询的分页大小,默认为100条,最多支持1000条
+ * @return contact way configId
+ * @throws WxErrorException the wx error exception
+ */
+ WxCpContactWayList listContactWay(Long startTime, Long endTime, String cursor, Long limit) throws WxErrorException;
+
/**
* 更新企业已配置的「联系我」方式
*
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java
index 86b342f2fc..5a53829dc0 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpKfService.java
@@ -75,6 +75,19 @@ public interface WxCpKfService {
*/
WxCpKfServicerOpResp addServicer(String openKfid, List userIdList) throws WxErrorException;
+ /**
+ * 接待人员管理
+ * 添加指定客服账号的接待人员,每个客服账号目前最多可添加2000个接待人员,20个部门。
+ * userid_list和department_id_list至少需要填其中一个
+ *
+ * @param openKfid 客服帐号ID
+ * @param userIdList 接待人员userid列表。第三方应用填密文userid,即open_userid 可填充个数:1 ~ 100。超过100个需分批调用。
+ * @param departmentIdList 接待人员部门id列表 可填充个数:0 ~ 20。
+ * @return 添加客服账号结果 wx cp kf servicer op resp
+ * @throws WxErrorException 异常
+ */
+ WxCpKfServicerOpResp addServicer(String openKfid, List userIdList,List departmentIdList) throws WxErrorException;
+
/**
* 接待人员管理
* 从客服帐号删除接待人员
@@ -86,6 +99,19 @@ public interface WxCpKfService {
*/
WxCpKfServicerOpResp delServicer(String openKfid, List userIdList) throws WxErrorException;
+ /**
+ * 接待人员管理
+ * 从客服帐号删除接待人员
+ * userid_list和department_id_list至少需要填其中一个
+ *
+ * @param openKfid 客服帐号ID
+ * @param userIdList 接待人员userid列表。第三方应用填密文userid,即open_userid 可填充个数:1 ~ 100。超过100个需分批调用。
+ * @param departmentIdList 接待人员部门id列表 可填充个数:0 ~ 100。超过100个需分批调用。
+ * @return 删除客服账号结果 wx cp kf servicer op resp
+ * @throws WxErrorException 异常
+ */
+ WxCpKfServicerOpResp delServicer(String openKfid, List userIdList, List departmentIdList) throws WxErrorException;
+
/**
* 接待人员管理
* 获取某个客服帐号的接待人员列表
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
index 82f6db9178..e874b26f42 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMediaService.java
@@ -2,6 +2,8 @@
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlReq;
+import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlResult;
import java.io.File;
import java.io.IOException;
@@ -133,4 +135,21 @@ WxMediaUploadResult upload(String mediaType, String filename, String url)
* @throws WxErrorException the wx error exception
*/
String uploadImg(File file) throws WxErrorException;
+
+ /**
+ * 生成异步上传任务
+ * 跟上传临时素材拿到的media_id使用场景是不通用的,目前适配的接口如下:https://developer.work.weixin.qq.com/document/path/96488#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF%E8%AF%B4%E6%98%8E
+ * @param req 请求参数
+ * @return 返回异步任务id
+ * @throws WxErrorException the wx error exception
+ */
+ String uploadByUrl(MediaUploadByUrlReq req) throws WxErrorException;
+
+ /**
+ * 查询异步任务结果
+ * @param jobId 任务id。最长为128字节,60分钟内有效
+ * @return 返回异步任务结果
+ * @throws WxErrorException the wx error exception
+ */
+ MediaUploadByUrlResult uploadByUrl(String jobId) throws WxErrorException;
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java
index 4647e0ed3f..ee57107b5c 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpOaService.java
@@ -22,7 +22,7 @@ public interface WxCpOaService {
*
* 请求方式:POST(HTTPS)
* 请求地址: https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent?access_token=ACCESS_TOKEN
- * 文档地址:https://work.weixin.qq.com/api/doc/90000/90135/91853
+ * 文档地址
*
*
* @param request 请求
@@ -34,7 +34,7 @@ public interface WxCpOaService {
/**
*
* 获取打卡数据
- * API doc : https://work.weixin.qq.com/api/doc#90000/90135/90262
+ * 文档地址
*
*
* @param openCheckinDataType 打卡类型。1:上下班打卡;2:外出打卡;3:全部打卡
@@ -50,7 +50,7 @@ List getCheckinData(Integer openCheckinDataType, Date startTime
/**
*
* 获取打卡规则
- * API doc : https://work.weixin.qq.com/api/doc#90000/90135/90263
+ * 文档地址
*
*
* @param datetime 需要获取规则的当天日期
@@ -64,7 +64,7 @@ List getCheckinData(Integer openCheckinDataType, Date startTime
/**
*
* 获取企业所有打卡规则
- * API doc : https://work.weixin.qq.com/api/doc/90000/90135/93384
+ * 文档地址
*
*
* @return 打卡规则列表 crop checkin option
@@ -82,7 +82,7 @@ List getCheckinData(Integer openCheckinDataType, Date startTime
*
* 一次拉取调用最多拉取100个审批记录,可以通过多次拉取的方式来满足需求,但调用频率不可超过600次/分。
*
- * API doc : https://work.weixin.qq.com/api/doc/90000/90135/91816
+ * 文档地址
*
*
* @param startTime 开始时间
@@ -121,7 +121,7 @@ WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime,
*
* 一次拉取调用最多拉取100个审批记录,可以通过多次拉取的方式来满足需求,但调用频率不可超过600次/分。
*
- * API doc : https://work.weixin.qq.com/api/doc/90000/90135/91816
+ * 文档地址
*
* 1 接口频率限制 600次/分钟
* 2 请求的参数endtime需要大于startime, 起始时间跨度不能超过31天;
@@ -146,7 +146,7 @@ WxCpApprovalInfo getApprovalInfo(@NonNull Date startTime, @NonNull Date endTime,
*
* 企业可通过审批应用或自建应用Secret调用本接口,根据审批单号查询企业微信“审批应用”的审批申请详情。
*
- * API Doc : https://work.weixin.qq.com/api/doc/90000/90135/91983
+ * 文档地址
*
*
* @param spNo 审批单编号。
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolUserService.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolUserService.java
index 26cfe3a019..a92bfcc100 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolUserService.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpSchoolUserService.java
@@ -334,7 +334,7 @@ WxCpBaseResp updateStudent(@NonNull String studentUserId, String newStudentUserI
* 请求方式:GET(HTTPS)
* 请求地址:https://qyapi.weixin.qq.com/cgi-bin/school/department/list?access_token=ACCESS_TOKEN&id=ID
*
- * @param id the id
+ * @param id 部门id。获取指定部门及其下的子部门。 如果不填,默认获取全量组织架构
* @return wx cp department list
* @throws WxErrorException the wx error exception
*/
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentWorkBenchServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentWorkBenchServiceImpl.java
index bb5c191e96..b0bbb38642 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentWorkBenchServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpAgentWorkBenchServiceImpl.java
@@ -38,4 +38,10 @@ public void setWorkBenchData(WxCpAgentWorkBench wxCpAgentWorkBench) throws WxErr
final String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(WORKBENCH_DATA_SET));
this.mainService.post(url, wxCpAgentWorkBench.toUserDataString());
}
+
+ @Override
+ public void batchSetWorkBenchData(WxCpAgentWorkBench wxCpAgentWorkBench) throws WxErrorException {
+ final String url = String.format(this.mainService.getWxCpConfigStorage().getApiUrl(WORKBENCH_BATCH_DATA_SET));
+ this.mainService.post(url, wxCpAgentWorkBench.toBatchUserDataString());
+ }
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
index c2fbdfe6ef..8e3a8d7b95 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImpl.java
@@ -65,6 +65,17 @@ public WxCpContactWayInfo getContactWay(String configId) throws WxErrorException
return WxCpContactWayInfo.fromJson(this.mainService.post(url, json.toString()));
}
+ @Override
+ public WxCpContactWayList listContactWay(Long startTime, Long endTime, String cursor, Long limit) throws WxErrorException {
+ JsonObject json = new JsonObject();
+ json.addProperty("start_time", startTime);
+ json.addProperty("end_time", endTime);
+ json.addProperty("cursor", cursor);
+ json.addProperty("limit", limit);
+ final String url = this.mainService.getWxCpConfigStorage().getApiUrl(LIST_CONTACT_WAY);
+ return WxCpContactWayList.fromJson(this.mainService.post(url, json.toString()));
+ }
+
@Override
public WxCpBaseResp updateContactWay(WxCpContactWayInfo info) throws WxErrorException {
if (StringUtils.isBlank(info.getContactWay().getConfigId())) {
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java
index 29e84c516f..be4f2a5850 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpKfServiceImpl.java
@@ -70,23 +70,62 @@ public WxCpKfAccountLinkResp getAccountLink(WxCpKfAccountLink link) throws WxErr
@Override
public WxCpKfServicerOpResp addServicer(String openKfid, List userIdList) throws WxErrorException {
- return servicerOp(openKfid, userIdList, SERVICER_ADD);
+ return servicerOp(openKfid, userIdList, null, SERVICER_ADD);
+ }
+
+ @Override
+ public WxCpKfServicerOpResp addServicer(String openKfId, List userIdList, List departmentIdList) throws WxErrorException {
+ validateParameters(SERVICER_ADD, userIdList, departmentIdList);
+ return servicerOp(openKfId, userIdList, departmentIdList, SERVICER_ADD);
}
@Override
public WxCpKfServicerOpResp delServicer(String openKfid, List userIdList) throws WxErrorException {
- return servicerOp(openKfid, userIdList, SERVICER_DEL);
+ return servicerOp(openKfid, userIdList, null, SERVICER_DEL);
}
- private WxCpKfServicerOpResp servicerOp(String openKfid, List userIdList, String uri) throws WxErrorException {
+ @Override
+ public WxCpKfServicerOpResp delServicer(String openKfid, List userIdList, List departmentIdList) throws WxErrorException {
+ validateParameters(SERVICER_DEL, userIdList, departmentIdList);
+ return servicerOp(openKfid, userIdList, departmentIdList, SERVICER_DEL);
+ }
+
+ private void validateParameters(String uri, List userIdList, List departmentIdList) {
+ if ((userIdList == null || userIdList.isEmpty()) && (departmentIdList == null || departmentIdList.isEmpty())) {
+ throw new IllegalArgumentException("userid_list和department_id_list至少需要填其中一个");
+ }
+ if (SERVICER_DEL.equals(uri)) {
+ if (userIdList != null && userIdList.size() > 100) {
+ throw new IllegalArgumentException("可填充个数:0 ~ 100。超过100个需分批调用。");
+ }
+ if (departmentIdList != null && departmentIdList.size() > 100) {
+ throw new IllegalArgumentException("可填充个数:0 ~ 100。超过100个需分批调用。");
+ }
+ } else {
+ if (userIdList != null && userIdList.size() > 100) {
+ throw new IllegalArgumentException("可填充个数:0 ~ 100。超过100个需分批调用。");
+ }
+ if (departmentIdList != null && departmentIdList.size() > 20) {
+ throw new IllegalArgumentException("可填充个数:0 ~ 20。");
+ }
+ }
+ }
+
+ private WxCpKfServicerOpResp servicerOp(String openKfid, List userIdList, List departmentIdList, String uri) throws WxErrorException {
String url = cpService.getWxCpConfigStorage().getApiUrl(uri);
JsonObject json = new JsonObject();
json.addProperty("open_kfid", openKfid);
- JsonArray userIdArray = new JsonArray();
- userIdList.forEach(userIdArray::add);
- json.add("userid_list", userIdArray);
-
+ if (userIdList != null && !userIdList.isEmpty()) {
+ JsonArray userIdArray = new JsonArray();
+ userIdList.forEach(userIdArray::add);
+ json.add("userid_list", userIdArray);
+ }
+ if (departmentIdList != null && !departmentIdList.isEmpty()) {
+ JsonArray departmentIdArray = new JsonArray();
+ departmentIdList.forEach(departmentIdArray::add);
+ json.add("department_id_list", departmentIdArray);
+ }
String responseContent = cpService.post(url, json.toString());
return WxCpKfServicerOpResp.fromJson(responseContent);
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
index 863dd7c1d4..427ce9d898 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImpl.java
@@ -1,5 +1,6 @@
package me.chanjar.weixin.cp.api.impl;
+import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
@@ -9,8 +10,12 @@
import me.chanjar.weixin.common.util.http.InputStreamData;
import me.chanjar.weixin.common.util.http.MediaInputStreamUploadRequestExecutor;
import me.chanjar.weixin.common.util.http.MediaUploadRequestExecutor;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+import me.chanjar.weixin.common.util.json.GsonParser;
import me.chanjar.weixin.cp.api.WxCpMediaService;
import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlReq;
+import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlResult;
import java.io.File;
import java.io.IOException;
@@ -20,7 +25,12 @@
import java.nio.file.Files;
import java.util.UUID;
-import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.*;
+import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.GET_UPLOAD_BY_URL_RESULT;
+import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.IMG_UPLOAD;
+import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.JSSDK_MEDIA_GET;
+import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.MEDIA_GET;
+import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.MEDIA_UPLOAD;
+import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.Media.UPLOAD_BY_URL;
/**
*
@@ -119,4 +129,20 @@ public String uploadImg(File file) throws WxErrorException {
return this.mainService.execute(MediaUploadRequestExecutor.create(this.mainService.getRequestHttp()), url, file)
.getUrl();
}
+
+ @Override
+ public String uploadByUrl(MediaUploadByUrlReq req) throws WxErrorException {
+ final String url = this.mainService.getWxCpConfigStorage().getApiUrl(UPLOAD_BY_URL);
+ String responseContent = this.mainService.post(url, req.toJson());
+ return GsonHelper.getString(GsonParser.parse(responseContent), "jobid");
+ }
+
+ @Override
+ public MediaUploadByUrlResult uploadByUrl(String jobId) throws WxErrorException {
+ final String url = this.mainService.getWxCpConfigStorage().getApiUrl(GET_UPLOAD_BY_URL_RESULT);
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("jobid", jobId);
+ String post = this.mainService.post(url, jsonObject.toString());
+ return MediaUploadByUrlResult.fromJson(post);
+ }
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java
index 2a64f52bc7..d04a051c0e 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpOAuth2ServiceImpl.java
@@ -13,6 +13,8 @@
import me.chanjar.weixin.cp.bean.workbench.WxCpSecondVerificationInfo;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+import java.util.Optional;
+
import static me.chanjar.weixin.common.api.WxConsts.OAuth2Scope.*;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.OAuth2.*;
@@ -74,9 +76,9 @@ public WxCpOauth2UserInfo getUserInfo(Integer agentId, String code) throws WxErr
JsonObject jo = GsonParser.parse(responseText);
return WxCpOauth2UserInfo.builder()
- .userId(GsonHelper.getString(jo, "UserId"))
+ .userId(Optional.ofNullable(GsonHelper.getString(jo, "UserId")).orElse(GsonHelper.getString(jo, "userid")))
.deviceId(GsonHelper.getString(jo, "DeviceId"))
- .openId(GsonHelper.getString(jo, "OpenId"))
+ .openId(Optional.ofNullable(GsonHelper.getString(jo, "OpenId")).orElse(GsonHelper.getString(jo, "openid")))
.userTicket(GsonHelper.getString(jo, "user_ticket"))
.expiresIn(GsonHelper.getString(jo, "expires_in"))
.externalUserId(GsonHelper.getString(jo, "external_userid"))
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolUserServiceImpl.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolUserServiceImpl.java
index fac1689e08..58bf20873f 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolUserServiceImpl.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolUserServiceImpl.java
@@ -16,6 +16,7 @@
import org.apache.commons.lang3.StringUtils;
import java.util.List;
+import java.util.Objects;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.ExternalContact.*;
import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.School.*;
@@ -246,7 +247,7 @@ public String convertToOpenId(@NonNull String externalUserId) throws WxErrorExce
@Override
public WxCpDepartmentList listDepartment(Integer id) throws WxErrorException {
- String apiUrl = this.cpService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST) + id;
+ String apiUrl = Objects.isNull(id) ? this.cpService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST) : String.format("%s?id=%s", this.cpService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST), id);
String responseContent = this.cpService.get(apiUrl, null);
return WxCpDepartmentList.fromJson(responseContent);
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
index e74173ee3f..2a3e4448b6 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/WxCpAgentWorkBench.java
@@ -6,6 +6,7 @@
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
+import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import me.chanjar.weixin.cp.bean.workbench.WorkBenchKeyData;
import me.chanjar.weixin.cp.bean.workbench.WorkBenchList;
import me.chanjar.weixin.cp.constant.WxCpConsts;
@@ -33,6 +34,10 @@ public class WxCpAgentWorkBench implements Serializable {
* 用户的userid
*/
private String userId;
+ /**
+ * 用户的userIds
+ */
+ private List useridList;
/**
* 应用id
*/
@@ -58,6 +63,15 @@ public class WxCpAgentWorkBench implements Serializable {
* 参考示例:今日要闻
*/
private Boolean enableWebviewClick;
+ /**
+ * 高度。可以有两种选择:single_row与double_row。当为single_row时,高度为106px(如果隐藏标题则为147px)。
+ * 当为double_row时,高度固定为171px(如果隐藏标题则为212px)。默认值为double_row
+ */
+ private String height;
+ /**
+ * 是否要隐藏展示了应用名称的标题部分,默认值为false。
+ */
+ private Boolean hideTitle;
private List keyDataList;
@@ -93,6 +107,20 @@ public String toUserDataString() {
return userDataObject.toString();
}
+ /**
+ * 生成批量用户数据Json字符串
+ *
+ * @return the string
+ */
+ public String toBatchUserDataString() {
+ JsonObject userDataObject = new JsonObject();
+ userDataObject.addProperty("agentid", this.agentId);
+ JsonArray useridList = WxGsonBuilder.create().toJsonTree(this.useridList).getAsJsonArray();
+ userDataObject.add("userid_list", useridList);
+ this.handleBatch(userDataObject);
+ return userDataObject.toString();
+ }
+
/**
* 处理不用类型的工作台数据
*/
@@ -140,9 +168,9 @@ private void handle(JsonObject templateObject) {
webview.addProperty("url", this.url);
webview.addProperty("jump_url", this.jumpUrl);
webview.addProperty("pagepath", this.pagePath);
- if (null != this.enableWebviewClick) {
- webview.addProperty("enable_webview_click", this.enableWebviewClick);
- }
+ webview.addProperty("enable_webview_click", this.enableWebviewClick);
+ webview.addProperty("height", this.height);
+ webview.addProperty("hide_title", this.hideTitle);
templateObject.add("webview", webview);
break;
}
@@ -152,4 +180,75 @@ private void handle(JsonObject templateObject) {
}
}
+ /**
+ * 处理不用类型的工作台数据
+ */
+ private void handleBatch(JsonObject templateObject) {
+ switch (this.getType()) {
+ case WxCpConsts.WorkBenchType.KEYDATA: {
+ JsonArray keyDataArray = new JsonArray();
+ JsonObject itemsObject = new JsonObject();
+ for (WorkBenchKeyData keyDataItem : this.keyDataList) {
+ JsonObject keyDataObject = new JsonObject();
+ keyDataObject.addProperty("key", keyDataItem.getKey());
+ keyDataObject.addProperty("data", keyDataItem.getData());
+ keyDataObject.addProperty("jump_url", keyDataItem.getJumpUrl());
+ keyDataObject.addProperty("pagepath", keyDataItem.getPagePath());
+ keyDataArray.add(keyDataObject);
+ }
+ itemsObject.add("items", keyDataArray);
+ JsonObject dataObject = new JsonObject();
+ dataObject.addProperty("type", WxCpConsts.WorkBenchType.KEYDATA);
+ dataObject.add("keydata", itemsObject);
+ templateObject.add("data", dataObject);
+ break;
+ }
+ case WxCpConsts.WorkBenchType.IMAGE: {
+ JsonObject image = new JsonObject();
+ image.addProperty("url", this.url);
+ image.addProperty("jump_url", this.jumpUrl);
+ image.addProperty("pagepath", this.pagePath);
+ JsonObject dataObject = new JsonObject();
+ dataObject.addProperty("type", WxCpConsts.WorkBenchType.IMAGE);
+ dataObject.add("image", image);
+ templateObject.add("data", dataObject);
+ break;
+ }
+ case WxCpConsts.WorkBenchType.LIST: {
+ JsonArray listArray = new JsonArray();
+ JsonObject itemsObject = new JsonObject();
+ for (WorkBenchList listItem : this.lists) {
+ JsonObject listObject = new JsonObject();
+ listObject.addProperty("title", listItem.getTitle());
+ listObject.addProperty("jump_url", listItem.getJumpUrl());
+ listObject.addProperty("pagepath", listItem.getPagePath());
+ listArray.add(listObject);
+ }
+ itemsObject.add("items", listArray);
+ JsonObject dataObject = new JsonObject();
+ dataObject.addProperty("type", WxCpConsts.WorkBenchType.LIST);
+ dataObject.add("list", itemsObject);
+ templateObject.add("data", dataObject);
+ break;
+ }
+ case WxCpConsts.WorkBenchType.WEBVIEW: {
+ JsonObject webview = new JsonObject();
+ webview.addProperty("url", this.url);
+ webview.addProperty("jump_url", this.jumpUrl);
+ webview.addProperty("pagepath", this.pagePath);
+ webview.addProperty("enable_webview_click", this.enableWebviewClick);
+ webview.addProperty("height", this.height);
+ webview.addProperty("hide_title", this.hideTitle);
+ JsonObject dataObject = new JsonObject();
+ dataObject.addProperty("type", WxCpConsts.WorkBenchType.WEBVIEW);
+ dataObject.add("webview", webview);
+ templateObject.add("data", dataObject);
+ break;
+ }
+ default: {
+ //do nothing
+ }
+ }
+ }
+
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java
index 3a6a61902c..5da6a8fd5a 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayInfo.java
@@ -152,6 +152,13 @@ public static class ContactWay implements Serializable {
@SerializedName("unionid")
private String unionId;
+
+ /**
+ *非必填,是否开启同一外部企业客户只能添加同一个员工,默认为否,开启后,同一个企业的客户会优先添加到同一个跟进人
+ */
+ @SerializedName("is_exclusive")
+ private boolean isExclusive;
+
/**
*
* 非必填
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayList.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayList.java
new file mode 100644
index 0000000000..04918f64e4
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/external/WxCpContactWayList.java
@@ -0,0 +1,63 @@
+package me.chanjar.weixin.cp.bean.external;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 「联系我」方式 列表返回对象
+ *
+ * @author imyzt
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@NoArgsConstructor
+public class WxCpContactWayList extends WxCpBaseResp implements Serializable {
+ private static final long serialVersionUID = -8697184659526210472L;
+
+ @SerializedName("contact_way")
+ private List contactWay;
+
+ /**
+ * The type Contact way.
+ */
+ @Getter
+ @Setter
+ public static class ContactWay implements Serializable {
+ private static final long serialVersionUID = -8697184659526210472L;
+
+ /**
+ * 联系方式的配置id
+ */
+ @SerializedName("config_id")
+ private String configId;
+ }
+
+ /**
+ * From json wx cp contact way list.
+ *
+ * @param json the json
+ * @return the wx cp contact way list
+ */
+ public static WxCpContactWayList fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, WxCpContactWayList.class);
+ }
+
+ /**
+ * To json string.
+ *
+ * @return the string
+ */
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlReq.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlReq.java
new file mode 100644
index 0000000000..c5cb21bde5
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlReq.java
@@ -0,0 +1,58 @@
+package me.chanjar.weixin.cp.bean.media;
+
+import lombok.Data;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+/**
+ * 生成异步上传任务
+ * @author imyzt
+ * @date 2025/04/27
+ */
+@Data
+public class MediaUploadByUrlReq {
+
+ /**
+ * 场景值。1-客户联系入群欢迎语素材(目前仅支持1)。 注意:每个场景值有对应的使用范围,详见上面的「使用场景说明」
+ */
+ private Integer scene;
+
+ /**
+ * 媒体文件类型。目前仅支持video-视频,file-普通文件 不超过32字节。
+ */
+ private String type;
+
+ /**
+ * 文件名,标识文件展示的名称。比如,使用该media_id发消息时,展示的文件名由该字段控制。 不超过128字节。
+ */
+ private String filename;
+
+ /**
+ * 文件cdn url。url要求支持Range分块下载 不超过1024字节。 如果为腾讯云cos链接,则需要设置为「公有读」权限。
+ */
+ private String url;
+
+ /**
+ * 文件md5。对比从url下载下来的文件md5是否一致。 不超过32字节。
+ */
+ private String md5;
+
+ /**
+ * From json wx cp base resp.
+ *
+ * @param json the json
+ * @return the wx cp base resp
+ */
+ public static MediaUploadByUrlReq fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, MediaUploadByUrlReq.class);
+ }
+
+ /**
+ * To json string.
+ *
+ * @return the string
+ */
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlResult.java
new file mode 100644
index 0000000000..cc931eed39
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/media/MediaUploadByUrlResult.java
@@ -0,0 +1,82 @@
+package me.chanjar.weixin.cp.bean.media;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import me.chanjar.weixin.cp.bean.WxCpBaseResp;
+import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
+
+import java.io.Serializable;
+
+/**
+ * 异步上传企微素材
+ * @author imyzt
+ * @date 2025/4/27
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class MediaUploadByUrlResult extends WxCpBaseResp implements Serializable {
+
+ private static final long serialVersionUID = 330834334738622341L;
+
+ /**
+ * 任务状态。1-处理中,2-完成,3-异常失败
+ */
+ @SerializedName("status")
+ private Integer status;
+
+ @SerializedName("detail")
+ private Detail detail;
+
+ @Data
+ public static class Detail {
+
+ /**
+ * 任务失败返回码。当status为3时返回非0,其他返回0
+ * 830001 url非法 确认url是否支持Range分块下载
+ * 830003 url下载数据失败 确认url本身是否能正常访问
+ * 45001 文件大小超过限制 确认文件在5字节~200M范围内
+ * 301019 文件MD5不匹配 确认url对应的文件内容md5,跟所填的md5参数是否一致
+ * 注意: status=2时,此处微信并未返回任何值
+ */
+ @SerializedName("errcode")
+ private Integer errCode;
+
+ /**
+ * 注意: status=2时,此处微信并未返回任何值
+ */
+ @SerializedName("errmsg")
+ private String errMsg;
+
+ /**
+ * 媒体文件上传后获取的唯一标识,3天内有效。当status为2时返回。
+ */
+ @SerializedName("media_id")
+ private String mediaId;
+
+ /**
+ * 媒体文件创建的时间戳。当status为2时返回。
+ */
+ @SerializedName("created_at")
+ private String createdAt;
+ }
+
+ /**
+ * From json wx cp media upload by url result.
+ *
+ * @param json the json
+ * @return the wx cp media upload by url result
+ */
+ public static MediaUploadByUrlResult fromJson(String json) {
+ return WxCpGsonBuilder.create().fromJson(json, MediaUploadByUrlResult.class);
+ }
+
+ /**
+ * To json string.
+ *
+ * @return the string
+ */
+ public String toJson() {
+ return WxCpGsonBuilder.create().toJson(this);
+ }
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java
index 8b6b0689a7..e26b152daf 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessage.java
@@ -403,7 +403,7 @@ public class WxCpTpXmlMessage implements Serializable {
* The Agent id.
*/
@XStreamAlias("AgentID")
- protected String agentID;
+ protected Integer agentID;
/**
* The Pic url.
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlApprovalInfo.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlApprovalInfo.java
index 7193c7cf6f..798a5c8b00 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlApprovalInfo.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlApprovalInfo.java
@@ -118,7 +118,7 @@ public static class NotifyNode implements Serializable {
/**
* 抄送人userid
*/
- @XStreamAlias("ItemUserid")
+ @XStreamAlias("ItemUserId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String itemUserId;
@@ -190,7 +190,7 @@ public static class Item implements Serializable {
/**
* 分支审批人userid
*/
- @XStreamAlias("ItemUserid")
+ @XStreamAlias("ItemUserId")
@XStreamConverter(value = XStreamCDataConverter.class)
private String itemUserId;
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
index fb4213f504..2313bcb516 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessage.java
@@ -155,6 +155,10 @@ public class WxCpXmlMessage implements Serializable {
@XStreamConverter(value = XStreamCDataConverter.class)
private String memChangeCnt;
+ @XStreamAlias("MemChangeList")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String MemChangeList;
+
@XStreamAlias("Source")
@XStreamConverter(value = XStreamCDataConverter.class)
private String source;
@@ -198,6 +202,13 @@ public class WxCpXmlMessage implements Serializable {
@XStreamAlias("SelectedItems")
private List selectedItems;
+ /**
+ * 异步任务id
+ */
+ @XStreamAlias("JobId")
+ @XStreamConverter(value = XStreamCDataConverter.class)
+ private String jobId;
+
/**
* 微信客服
* 调用拉取消息接口时,需要传此token,用于校验请求的合法性
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java
index d843cad6cf..c88cb7b9be 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/msgaudit/WxCpChatModel.java
@@ -25,9 +25,6 @@ public class WxCpChatModel implements Serializable {
@SerializedName("action")
private String action;
- @SerializedName("send")
- private String send;
-
@SerializedName("from")
private String from;
@@ -606,7 +603,7 @@ public static class File implements Serializable {
private String sdkFileId;
@SerializedName("filesize")
- private Integer fileSize;
+ private Long fileSize;
/**
* From json file.
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApprovalTemplateResult.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApprovalTemplateResult.java
index 2a497d15fc..d10594a546 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApprovalTemplateResult.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/WxCpOaApprovalTemplateResult.java
@@ -6,6 +6,7 @@
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
+import me.chanjar.weixin.cp.bean.oa.templatedata.TemplateTips;
import me.chanjar.weixin.cp.bean.oa.templatedata.TemplateTitle;
import me.chanjar.weixin.cp.bean.oa.templatedata.control.*;
import me.chanjar.weixin.cp.util.json.WxCpGsonBuilder;
@@ -94,6 +95,9 @@ public static class TemplateConfig implements Serializable {
@SerializedName("vacation_list")
private TemplateVacation vacationList;
+ @SerializedName("tips")
+ private TemplateTips tips;
+
}
@Data
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java
index 1a00baad0f..91ee8b7cde 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateConfig.java
@@ -37,4 +37,6 @@ public class TemplateConfig implements Serializable {
@SerializedName("vacation_list")
private TemplateVacation vacationList;
+ private TemplateTips tips;
+
}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTips.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTips.java
new file mode 100644
index 0000000000..58daeb007c
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTips.java
@@ -0,0 +1,18 @@
+package me.chanjar.weixin.cp.bean.oa.templatedata;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author mrsiu@msn.com
+ * @version 1.0
+ * @date 2025/1/16 09:40
+ */
+@Data
+public class TemplateTips {
+
+ @SerializedName("tips_content")
+ private List tipsContent;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsContent.java
new file mode 100644
index 0000000000..939e6819a0
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsContent.java
@@ -0,0 +1,15 @@
+package me.chanjar.weixin.cp.bean.oa.templatedata;
+
+import lombok.Data;
+
+/**
+ * @author mrsiu@msn.com
+ * @version 1.0
+ * @date 2025/1/16 09:42
+ */
+@Data
+public class TemplateTipsContent {
+
+ private TemplateTipsText text;
+ private String lang;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubText.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubText.java
new file mode 100644
index 0000000000..ac4681038c
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubText.java
@@ -0,0 +1,14 @@
+package me.chanjar.weixin.cp.bean.oa.templatedata;
+
+import lombok.Data;
+
+/**
+ * @author mrsiu@msn.com
+ * @version 1.0
+ * @date 2025/1/16 09:45
+ */
+@Data
+public class TemplateTipsSubText {
+ private Integer type;
+ private TemplateTipsSubTextContent content;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContent.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContent.java
new file mode 100644
index 0000000000..9c99b2688e
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContent.java
@@ -0,0 +1,16 @@
+package me.chanjar.weixin.cp.bean.oa.templatedata;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+/**
+ * @author mrsiu@msn.com
+ * @version 1.0
+ * @date 2025/1/16 09:46
+ */
+@Data
+public class TemplateTipsSubTextContent {
+ @SerializedName("plain_text")
+ private TemplateTipsSubTextContentPlainText plainText;
+ private TemplateTipsSubTextContentLink link;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentLink.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentLink.java
new file mode 100644
index 0000000000..4cd198409a
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentLink.java
@@ -0,0 +1,14 @@
+package me.chanjar.weixin.cp.bean.oa.templatedata;
+
+import lombok.Data;
+
+/**
+ * @author mrsiu@msn.com
+ * @version 1.0
+ * @date 2025/1/16 09:49
+ */
+@Data
+public class TemplateTipsSubTextContentLink {
+ private String title;
+ private String url;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentPlainText.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentPlainText.java
new file mode 100644
index 0000000000..12969cdcdb
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsSubTextContentPlainText.java
@@ -0,0 +1,13 @@
+package me.chanjar.weixin.cp.bean.oa.templatedata;
+
+import lombok.Data;
+
+/**
+ * @author mrsiu@msn.com
+ * @date 2025/1/16 09:47
+ * @version 1.0
+ */
+@Data
+public class TemplateTipsSubTextContentPlainText {
+ private String content;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsText.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsText.java
new file mode 100644
index 0000000000..100c5bb137
--- /dev/null
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/bean/oa/templatedata/TemplateTipsText.java
@@ -0,0 +1,17 @@
+package me.chanjar.weixin.cp.bean.oa.templatedata;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @author mrsiu@msn.com
+ * @date 2025/1/16 09:43
+ * @version 1.0
+ */
+@Data
+public class TemplateTipsText {
+ @SerializedName("sub_text")
+ private List subText;
+}
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
index b53f7549d7..093d386e6a 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpApiPathConsts.java
@@ -130,6 +130,10 @@ interface WorkBench {
* The constant WORKBENCH_DATA_SET.
*/
String WORKBENCH_DATA_SET = "/cgi-bin/agent/set_workbench_data";
+ /**
+ * The constant WORKBENCH_BATCH_DATA_SET.
+ */
+ String WORKBENCH_BATCH_DATA_SET = "/cgi-bin/agent/batch_set_workbench_data";
}
/**
@@ -234,6 +238,12 @@ interface Media {
* The constant JSSDK_MEDIA_GET.
*/
String JSSDK_MEDIA_GET = "/cgi-bin/media/get/jssdk";
+
+ /** The constant GET_UPLOAD_BY_URL_RESULT. */
+ String GET_UPLOAD_BY_URL_RESULT = "/cgi-bin/media/get_upload_by_url_result";
+
+ /** The constant UPLOAD_BY_URL. */
+ String UPLOAD_BY_URL = "/cgi-bin/media/upload_by_url";
}
/**
@@ -691,7 +701,7 @@ interface School {
/**
* The constant DEPARTMENT_LIST.
*/
- String DEPARTMENT_LIST = "/cgi-bin/school/department/list?id=";
+ String DEPARTMENT_LIST = "/cgi-bin/school/department/list";
/**
* The constant GET_PAYMENT_RESULT.
@@ -1085,6 +1095,10 @@ interface ExternalContact {
* The constant GET_CONTACT_WAY.
*/
String GET_CONTACT_WAY = "/cgi-bin/externalcontact/get_contact_way";
+ /**
+ * The constant LIST_CONTACT_WAY.
+ */
+ String LIST_CONTACT_WAY = "/cgi-bin/externalcontact/list_contact_way";
/**
* The constant UPDATE_CONTACT_WAY.
*/
diff --git a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
index 606dcea6d2..3d51c9e2c9 100644
--- a/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
+++ b/weixin-java-cp/src/main/java/me/chanjar/weixin/cp/constant/WxCpConsts.java
@@ -219,6 +219,11 @@ public static class EventType {
*/
public static final String CUSTOMER_ACQUISITION = "customer_acquisition";
+ /**
+ * 异步上传临时素材结果回调通知
+ */
+ public static final String UPLOAD_MEDIA_JOB_FINISH = "upload_media_job_finish";
+
}
/**
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
index c629165ca4..4bd80928bd 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpExternalContactServiceImplTest.java
@@ -4,6 +4,9 @@
import com.google.common.collect.Lists;
import com.google.inject.Inject;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -90,6 +93,20 @@ public void testGetContactWay() throws WxErrorException {
assertNotNull(contactWayInfo);
}
+ /**
+ * Test list contact way.
+ *
+ * @throws WxErrorException the wx error exception
+ */
+ @Test
+ public void testListContactWay() throws WxErrorException {
+ long startTime = LocalDateTime.now().minusDays(1).toEpochSecond(ZoneOffset.of("+8"));
+ long endTime = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
+ WxCpContactWayList wxCpContactWayList = this.wxCpService.getExternalContactService().listContactWay(startTime, endTime, null, 100L);
+ System.out.println(wxCpContactWayList.toJson());
+ assertNotNull(wxCpContactWayList);
+ }
+
/**
* Test update contact way.
*
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java
index b964aad513..381a4c1454 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpMediaServiceImplTest.java
@@ -7,6 +7,8 @@
import me.chanjar.weixin.cp.api.ApiTestModule;
import me.chanjar.weixin.cp.api.TestConstants;
import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlReq;
+import me.chanjar.weixin.cp.bean.media.MediaUploadByUrlResult;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
@@ -127,4 +129,38 @@ public void testGetJssdkFile() throws WxErrorException {
assertThat(file).isNotNull();
System.out.println(file);
}
+
+ /**
+ * Test upload media by url.
+ *
+ * @throws WxErrorException the wx error exception
+ */
+ @Test
+ public void testUploadMediaByUrl() throws WxErrorException {
+ MediaUploadByUrlReq req = new MediaUploadByUrlReq();
+ req.setScene(1);
+ req.setType("video");
+ req.setFilename("mov_bbb");
+ req.setUrl("https://www.w3school.com.cn/example/html5/mov_bbb.mp4");
+ req.setMd5("198918f40ecc7cab0fc4231adaf67c96");
+ String jobId = this.wxService.getMediaService().uploadByUrl(req);
+ System.out.println(jobId);
+ }
+
+ /**
+ * Test upload media by url.
+ *
+ * @throws WxErrorException the wx error exception
+ */
+ @Test
+ public void testUploadMediaByUrlResult() throws WxErrorException, InterruptedException {
+ String jobId = "job1745801375_5GIKWuFF3M7hcIkeSNMqs_W26xy5VeSWjLaLFTEdSfQ";
+ MediaUploadByUrlResult result = this.wxService.getMediaService().uploadByUrl(jobId);
+ System.out.println(result);
+ }
+
+ @Test
+ public void testUploadMediaJobFinishEvent() throws WxErrorException {
+ File file = this.wxService.getMediaService().getJssdkFile("....");
+ }
}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java
index a37a42ee68..0cb1e8e5e6 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpOaServiceImplTest.java
@@ -162,8 +162,6 @@ public void testGetCheckinOption() throws WxErrorException {
*/
@Test
public void testGetCropCheckinOption() throws WxErrorException {
-
- Date now = new Date();
List results = wxService.getOaService().getCropCheckinOption();
assertThat(results).isNotNull();
System.out.println("results ");
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolUserServiceImplTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolUserServiceImplTest.java
new file mode 100644
index 0000000000..da1cc25542
--- /dev/null
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/impl/WxCpSchoolUserServiceImplTest.java
@@ -0,0 +1,139 @@
+package me.chanjar.weixin.cp.api.impl;
+
+import com.google.gson.Gson;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.cp.api.WxCpService;
+import me.chanjar.weixin.cp.bean.school.user.WxCpDepartmentList;
+import me.chanjar.weixin.cp.config.impl.WxCpDefaultConfigImpl;
+import org.mockito.Mockito;
+import org.testng.annotations.Test;
+
+import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.School.DEPARTMENT_LIST;
+import static org.testng.Assert.assertEquals;
+
+public class WxCpSchoolUserServiceImplTest {
+
+
+ String allDeptListJson = "{\n" +
+ "\t\"errcode\": 0,\n" +
+ "\t\"errmsg\": \"ok\",\n" +
+ "\t\"departments\": [\n" +
+ "\t\t{\n" +
+ "\t\t\t\"name\": \"一年级\",\n" +
+ "\t\t\t\"parentid\": 1,\n" +
+ "\t\t\t\"id\": 2,\n" +
+ "\t\t\t\"type\":2,\n" +
+ "\t\t\t\"register_year\":2018,\n" +
+ "\t\t\t\"standard_grade\":1,\n" +
+ "\t\t\t\"order\":1,\n" +
+ "\t\t\t\"department_admins\": [\n" +
+ "\t\t\t\t{\n" +
+ "\t\t\t\t\t\"userid\": \"zhangsan\",\n" +
+ "\t\t\t\t\t\"type\": 1\n" +
+ "\t\t\t\t},\n" +
+ "\t\t\t\t{\n" +
+ "\t\t\t\t\t\"userid\": \"lisi\",\n" +
+ "\t\t\t\t\t\"type\": 2\n" +
+ "\t\t\t\t}\n" +
+ "\t\t\t],\n" +
+ " \"is_graduated\": 0\n" +
+ "\t\t},\n" +
+ "\t\t{\n" +
+ "\t\t\t\"name\": \"一年级一班\",\n" +
+ "\t\t\t\"parentid\": 1,\n" +
+ "\t\t\t\"id\": 3,\n" +
+ "\t\t\t\"type\": 1,\n" +
+ "\t\t\t\"department_admins\": [\n" +
+ "\t\t\t\t{\n" +
+ "\t\t\t\t\t\"userid\": \"zhangsan\",\n" +
+ "\t\t\t\t\t\"type\": 3,\n" +
+ "\t\t\t\t\t\"subject\":\"语文\"\n" +
+ "\t\t\t\t},\n" +
+ "\t\t\t\t{\n" +
+ "\t\t\t\t\t\"userid\": \"lisi\",\n" +
+ "\t\t\t\t\t\"type\": 4,\n" +
+ "\t\t\t\t\t\"subject\":\"数学\"\n" +
+ "\t\t\t\t}\n" +
+ "\t\t\t],\n" +
+ "\t\t\t\"open_group_chat\": 1,\n" +
+ " \"group_chat_id\": \"group_chat_id\"\n" +
+ "\t\t}\n" +
+ "\t]\n" +
+ "}\n";
+
+ String deptId3Json = "{\n" +
+ " \"errcode\": 0,\n" +
+ " \"errmsg\": \"ok\",\n" +
+ " \"departments\": [\n" +
+ " {\n" +
+ " \"name\": \"一年级一班\",\n" +
+ " \"parentid\": 1,\n" +
+ " \"id\": 3,\n" +
+ " \"type\": 1,\n" +
+ " \"department_admins\": [\n" +
+ " {\n" +
+ " \"userid\": \"zhangsan\",\n" +
+ " \"type\": 3,\n" +
+ " \"subject\":\"语文\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"userid\": \"lisi\",\n" +
+ " \"type\": 4,\n" +
+ " \"subject\":\"数学\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"open_group_chat\": 1,\n" +
+ " \"group_chat_id\": \"group_chat_id\"\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+
+ String deptId2Json = "{\n" +
+ " \"errcode\": 0,\n" +
+ " \"errmsg\": \"ok\",\n" +
+ " \"departments\": []\n" +
+ "}\n";
+
+
+ @Test
+ public void testListDepartmentWhenIdIsNull() throws WxErrorException {
+
+ WxCpService mockCpService = Mockito.mock(WxCpService.class);
+ WxCpSchoolUserServiceImpl wxCpSchoolUserService = new WxCpSchoolUserServiceImpl(mockCpService);
+ Mockito.when(mockCpService.getWxCpConfigStorage()).thenReturn(new WxCpDefaultConfigImpl());
+ Mockito.when(mockCpService.get(mockCpService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST), null)).thenReturn(allDeptListJson);
+ WxCpDepartmentList wxCpDepartmentList = wxCpSchoolUserService.listDepartment(null);
+ //WxCpDepartmentList没有重写Equals和Hashcode,不能直接比较
+ Gson gson = new Gson();
+ assertEquals(gson.toJson(wxCpDepartmentList), gson.toJson(gson.fromJson(allDeptListJson, WxCpDepartmentList.class)), "should be equal");
+
+ }
+
+ @Test
+ public void testListDepartmentWhenIdIs2() throws WxErrorException {
+
+ WxCpService mockCpService = Mockito.mock(WxCpService.class);
+ WxCpSchoolUserServiceImpl wxCpSchoolUserService = new WxCpSchoolUserServiceImpl(mockCpService);
+ Mockito.when(mockCpService.getWxCpConfigStorage()).thenReturn(new WxCpDefaultConfigImpl());
+ Gson gson = new Gson();
+ int deptId = 2;
+ Mockito.when(mockCpService.get(String.format("%s?id=%s", mockCpService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST), deptId), null)).thenReturn(deptId2Json);
+ //WxCpDepartmentList没有重写Equals和Hashcode,不能直接比较
+ assertEquals(gson.toJson(wxCpSchoolUserService.listDepartment(deptId)), gson.toJson(gson.fromJson(deptId2Json, WxCpDepartmentList.class)), "should be equal");
+
+ }
+
+ @Test
+ public void testListDepartmentWhenIdIs3() throws WxErrorException {
+
+ WxCpService mockCpService = Mockito.mock(WxCpService.class);
+ WxCpSchoolUserServiceImpl wxCpSchoolUserService = new WxCpSchoolUserServiceImpl(mockCpService);
+ Mockito.when(mockCpService.getWxCpConfigStorage()).thenReturn(new WxCpDefaultConfigImpl());
+ Gson gson = new Gson();
+ int deptId = 3;
+ Mockito.when(mockCpService.get(String.format("%s?id=%s", mockCpService.getWxCpConfigStorage().getApiUrl(DEPARTMENT_LIST), deptId), null)).thenReturn(deptId3Json);
+ //WxCpDepartmentList没有重写Equals和Hashcode,不能直接比较
+ assertEquals(gson.toJson(wxCpSchoolUserService.listDepartment(deptId)), gson.toJson(gson.fromJson(deptId3Json, WxCpDepartmentList.class)), "should be equal");
+
+ }
+}
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessageTest.java
index d6cd827630..28246cf00b 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpTpXmlMessageTest.java
@@ -152,7 +152,7 @@ public void enterAppTest() {
assertEquals(wxXmlMessage.getCreateTime(), Long.valueOf(1408091189));
assertEquals(wxXmlMessage.getEvent(), "enter_agent");
assertEquals(wxXmlMessage.getEventKey(), "");
- assertEquals(wxXmlMessage.getAgentID(), Integer.valueOf(1));
+ assertEquals(wxXmlMessage.getAgentID(), 1);
}
/**
diff --git a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
index a760a17ff6..5bcfe9698a 100644
--- a/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
+++ b/weixin-java-cp/src/test/java/me/chanjar/weixin/cp/bean/message/WxCpXmlMessageTest.java
@@ -6,6 +6,7 @@
import org.testng.annotations.Test;
import static me.chanjar.weixin.cp.constant.WxCpConsts.EventType.TASKCARD_CLICK;
+import static me.chanjar.weixin.cp.constant.WxCpConsts.EventType.UPLOAD_MEDIA_JOB_FINISH;
import static org.assertj.core.api.Assertions.assertThat;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
@@ -421,4 +422,24 @@ public void testOpenApprovalChange() {
assertThat(wxCpXmlMessage.getApprovalInfo().getApprovalNodes().get(0).getItems().get(0).getItemName()).isNotEmpty();
assertThat(wxCpXmlMessage.getApprovalInfo().getNotifyNodes().get(0).getItemName()).isNotEmpty();
}
+
+ /**
+ * Test open approval change.
+ */
+ public void testUploadMediaJobFinishEvent() {
+ String xml = "\n" +
+ "\t\n" +
+ "\t\n" +
+ "\t1425284517\n" +
+ "\t\n" +
+ "\t\n" +
+ "\t\n" +
+ "";
+
+ WxCpXmlMessage wxCpXmlMessage = WxCpXmlMessage.fromXml(xml);
+ assertThat(wxCpXmlMessage).isNotNull();
+ assertThat(wxCpXmlMessage.getJobId()).isNotEmpty();
+ assertThat(wxCpXmlMessage.getJobId()).isEqualTo("jobid_S0MrnndvRG5fadSlLwiBqiDDbM143UqTmKP3152FZk4");
+ assertThat(wxCpXmlMessage.getEvent()).isEqualTo(UPLOAD_MEDIA_JOB_FINISH);
+ }
}
diff --git a/weixin-java-miniapp/pom.xml b/weixin-java-miniapp/pom.xml
index a9bb5f37dc..3c3898f3f3 100644
--- a/weixin-java-miniapp/pom.xml
+++ b/weixin-java-miniapp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
weixin-java-miniapp
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaOrderManagementService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaOrderManagementService.java
new file mode 100644
index 0000000000..d82cfd19cc
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaOrderManagementService.java
@@ -0,0 +1,40 @@
+package cn.binarywang.wx.miniapp.api;
+
+import cn.binarywang.wx.miniapp.bean.order.WxMaOrderManagementGetOrderDetailPath;
+import cn.binarywang.wx.miniapp.bean.order.WxMaOrderManagementResult;
+import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoBaseResponse;
+import me.chanjar.weixin.common.error.WxErrorException;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2025/01/16 15:20
+ */
+public interface WxMaOrderManagementService {
+
+ /**
+ * 查询订单详情路径
+ * 注意事项
+ * 如果没有配置过订单详情路径,会返回成功,其中path为''。
+ *
+ * @return WxMaOrderManagementGetOrderDetailPath
+ * @throws WxErrorException e
+ */
+ WxMaOrderManagementGetOrderDetailPath getOrderDetailPath()
+ throws WxErrorException;
+
+
+ /**
+ * 配置订单详情路径
+ * 注意事项
+ * 调用接口前需要先完成订单中心授权协议签署。
+ * 请确保配置的path可正常跳转到小程序,并且path必须包含字符串“${商品订单号}”。
+ *
+ * @param path 订单详情路径
+ * @return WxMaOrderManagementResult
+ * @throws WxErrorException e
+ */
+ WxMaOrderManagementResult updateOrderDetailPath(String path)
+ throws WxErrorException;
+
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
index 9d55df3797..a5446361a3 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/WxMaService.java
@@ -550,6 +550,12 @@ WxMaApiResponse execute(
* @return getWxMaOrderShippingService
*/
WxMaOrderShippingService getWxMaOrderShippingService();
+ /**
+ * 小程序订单管理服务
+ *
+ * @return WxMaOrderManagementService
+ */
+ WxMaOrderManagementService getWxMaOrderManagementService();
/**
* 小程序openApi管理
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
index 8af0626b92..aa7b061fb1 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
@@ -153,6 +153,9 @@ public abstract class BaseWxMaServiceImpl implements WxMaService, RequestH
private final WxMaOrderShippingService wxMaOrderShippingService =
new WxMaOrderShippingServiceImpl(this);
+ private final WxMaOrderManagementService wxMaOrderManagementService =
+ new WxMaOrderManagementServiceImpl(this);
+
private final WxMaOpenApiService wxMaOpenApiService = new WxMaOpenApiServiceImpl(this);
private final WxMaVodService wxMaVodService = new WxMaVodServiceImpl(this);
private final WxMaXPayService wxMaXPayService = new WxMaXPayServiceImpl(this);
@@ -499,7 +502,10 @@ public WxMaConfig getWxMaConfig() {
@Override
public void setWxMaConfig(WxMaConfig maConfig) {
final String appid = maConfig.getAppid();
- this.setMultiConfigs(ImmutableMap.of(appid, maConfig), appid);
+ Map map = new HashMap<>();
+ map.put(appid, maConfig);
+ Map configMap = Collections.unmodifiableMap(map);
+ this.setMultiConfigs(configMap, appid);
}
@Override
@@ -814,6 +820,16 @@ public WxMaOrderShippingService getWxMaOrderShippingService() {
return this.wxMaOrderShippingService;
}
+ /**
+ * 小程序订单管理服务
+ *
+ * @return WxMaOrderManagementService
+ */
+ @Override
+ public WxMaOrderManagementService getWxMaOrderManagementService() {
+ return this.wxMaOrderManagementService;
+ }
+
@Override
public WxMaOpenApiService getWxMaOpenApiService() {
return this.wxMaOpenApiService;
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java
index 05e8f2e0a7..342224effb 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaImmediateDeliveryServiceImpl.java
@@ -207,7 +207,7 @@ public GetDeliveryListResponse getDeliveryList() throws WxErrorException {
@Override
public WxMaBaseResponse updateWaybillGoods(UpdateWaybillGoodsRequest request) throws WxErrorException {
- String responseContent = this.wxMaService.post(InstantDelivery.GET_DELIVERY_LIST_URL,request);
+ String responseContent = this.wxMaService.post(InstantDelivery.UPDATE_WAYBILL_GOODS_URL,request);
WxMaBaseResponse response = WxMaGsonBuilder.create().fromJson(responseContent, WxMaBaseResponse.class);
if (response.getErrcode() == -1) {
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOrderManagementServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOrderManagementServiceImpl.java
new file mode 100644
index 0000000000..7fcf73f5a3
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/WxMaOrderManagementServiceImpl.java
@@ -0,0 +1,72 @@
+package cn.binarywang.wx.miniapp.api.impl;
+
+import cn.binarywang.wx.miniapp.api.WxMaOrderManagementService;
+import cn.binarywang.wx.miniapp.api.WxMaService;
+import cn.binarywang.wx.miniapp.bean.order.WxMaOrderManagementGetOrderDetailPath;
+import cn.binarywang.wx.miniapp.bean.order.WxMaOrderManagementResult;
+import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoBaseResponse;
+import cn.binarywang.wx.miniapp.json.WxMaGsonBuilder;
+import com.google.gson.JsonObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.api.WxConsts;
+import me.chanjar.weixin.common.enums.WxType;
+import me.chanjar.weixin.common.error.WxError;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.common.util.json.GsonHelper;
+import me.chanjar.weixin.common.util.json.GsonParser;
+
+import static cn.binarywang.wx.miniapp.constant.WxMaApiUrlConstants.OrderManagement.*;
+
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2025/01/16 15:31
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class WxMaOrderManagementServiceImpl implements WxMaOrderManagementService {
+
+ private final WxMaService wxMaService;
+
+ /**
+ * 查询订单详情路径
+ * 注意事项
+ * 如果没有配置过订单详情路径,会返回成功,其中path为''。
+ *
+ * @return WxMaOrderManagementGetOrderDetailPath
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxMaOrderManagementGetOrderDetailPath getOrderDetailPath() throws WxErrorException {
+ return request(GET_ORDER_DETAIL_PATH, new Object(), WxMaOrderManagementGetOrderDetailPath.class);
+
+ }
+
+ /**
+ * 配置订单详情路径
+ * 注意事项
+ * 调用接口前需要先完成订单中心授权协议签署。
+ * 请确保配置的path可正常跳转到小程序,并且path必须包含字符串“${商品订单号}”。
+ *
+ * @param path 订单详情路径
+ * @return WxMaOrderManagementResult
+ * @throws WxErrorException e
+ */
+ @Override
+ public WxMaOrderManagementResult updateOrderDetailPath(String path) throws WxErrorException {
+ JsonObject jsonObject = GsonHelper.buildJsonObject("path", path);
+ return request(UPDATE_ORDER_DETAIL_PATH, jsonObject, WxMaOrderManagementResult.class);
+
+ }
+
+ private T request(String url, Object request, Class resultT) throws WxErrorException {
+ String responseContent = this.wxMaService.post(url, request);
+ JsonObject jsonObject = GsonParser.parse(responseContent);
+ if (jsonObject.get(WxConsts.ERR_CODE).getAsInt() != 0) {
+ throw new WxErrorException(WxError.fromJson(responseContent, WxType.MiniApp));
+ }
+ return WxMaGsonBuilder.create().fromJson(responseContent, resultT);
+ }
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
index 7a004b845c..75d8174caf 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/WxMaMessage.java
@@ -31,7 +31,7 @@ public class WxMaMessage implements Serializable {
private static final long serialVersionUID = -3586245291677274914L;
/**
- * 使用dom4j解析的存放所有xml属性和值的map.
+ * 使用dom4j解析的存放所有xml或json属性和值的map.
*/
private Map allFieldsMap;
@@ -287,6 +287,7 @@ public static WxMaMessage fromJson(String json) {
}
message.setUselessMsg(null);
}
+ message.setAllFieldsMap(WxMaGsonBuilder.create().fromJson(json, Map.class));
return message;
}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/order/WxMaOrderManagementGetOrderDetailPath.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/order/WxMaOrderManagementGetOrderDetailPath.java
new file mode 100644
index 0000000000..02c53a53f8
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/order/WxMaOrderManagementGetOrderDetailPath.java
@@ -0,0 +1,24 @@
+package cn.binarywang.wx.miniapp.bean.order;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serializable;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2025/01/16 15:27
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class WxMaOrderManagementGetOrderDetailPath extends WxMaOrderManagementResult {
+ private static final long serialVersionUID = -5288666524298706169L;
+
+ /**
+ * 订单详情路径
+ */
+ @SerializedName("path")
+ private String path;
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/order/WxMaOrderManagementResult.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/order/WxMaOrderManagementResult.java
new file mode 100644
index 0000000000..5a903b8980
--- /dev/null
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/bean/order/WxMaOrderManagementResult.java
@@ -0,0 +1,27 @@
+package cn.binarywang.wx.miniapp.bean.order;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author xzh
+ * @Description
+ * @createTime 2025/01/16 15:27
+ */
+@Data
+public class WxMaOrderManagementResult implements Serializable {
+ private static final long serialVersionUID = 1468925151935770503L;
+ /**
+ * 错误码
+ */
+ @SerializedName("errcode")
+ private Integer errCode;
+
+ /**
+ * 错误原因
+ */
+ @SerializedName("errmsg")
+ private String errMsg;
+}
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
index 5908385790..30d52b17cf 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/constant/WxMaApiUrlConstants.java
@@ -626,7 +626,7 @@ public interface InstantDelivery {
String GET_DELIVERY_LIST_URL =
"https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/get_delivery_list";
- /** 获取运力id列表get_delivery_list 商户使用此接口获取所有运力id的列表 */
+ /** 物流服务-查询组件-更新物品信息接口 update_waybill_goods 更新物品信息 */
String UPDATE_WAYBILL_GOODS_URL =
"https://api.weixin.qq.com/cgi-bin/express/delivery/open_msg/update_waybill_goods";
@@ -751,7 +751,7 @@ public interface OrderShipping {
*
*/
String UPLOAD_COMBINED_SHIPPING_INFO =
- "https://api.weixin.qq.com/wxa/sec/order/upload_combined_shipping_info";
+ "https://api.weixin.qq.com/wxa/sec/order/upload_combined_shipping_info";
/**
* 查询订单发货状态.
@@ -779,7 +779,7 @@ public interface OrderShipping {
*
*/
String NOTIFY_CONFIRM_RECEIVE =
- "https://api.weixin.qq.com/wxa/sec/order/notify_confirm_receive";
+ "https://api.weixin.qq.com/wxa/sec/order/notify_confirm_receive";
/**
* 消息跳转路径设置接口.
@@ -809,6 +809,35 @@ public interface OrderShipping {
}
+ /**
+ * 小程序订单管理
+ *
+ *
+ * 文档地址: https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html
+ *
+ */
+ public interface OrderManagement {
+
+ /**
+ * 配置订单详情路径.
+ *
+ *
+ * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html
+ *
+ */
+ String UPDATE_ORDER_DETAIL_PATH = "https://api.weixin.qq.com/wxa/sec/order/update_order_detail_path";
+
+ /**
+ * 查询订单详情路径.
+ *
+ *
+ * 文档地址:https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/business-capabilities/order_center/order_center.html
+ *
+ */
+ String GET_ORDER_DETAIL_PATH = "https://api.weixin.qq.com/wxa/sec/order/get_order_detail_path";
+
+ }
+
public interface Vod {
String LIST_MEDIA_URL = "https://api.weixin.qq.com/wxa/sec/vod/listmedia";
String GET_MEDIA_URL = "https://api.weixin.qq.com/wxa/sec/vod/getmedia";
diff --git a/weixin-java-mp/pom.xml b/weixin-java-mp/pom.xml
index c8c3f298c8..22ec60f381 100644
--- a/weixin-java-mp/pom.xml
+++ b/weixin-java-mp/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
weixin-java-mp
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java
index 6d4869b6a1..86555aa4a1 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java
@@ -50,12 +50,12 @@ public void initHttp() {
clientBuilder.proxy(getRequestHttpProxy().getProxy());
//设置授权
- clientBuilder.authenticator(new Authenticator() {
+ clientBuilder.proxyAuthenticator(new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword());
return response.request().newBuilder()
- .header("Authorization", credential)
+ .header("Proxy-Authorization", credential)
.build();
}
});
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
index 27b7eaecc7..3d5f4ac3a0 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/message/WxMpXmlMessage.java
@@ -186,6 +186,22 @@ public class WxMpXmlMessage implements Serializable {
@JacksonXmlCData
private String unionId;
+ @XStreamAlias("ret")
+ @JacksonXmlProperty(localName = "ret")
+ private Integer ret;
+
+ @XStreamAlias("nickname")
+ @JacksonXmlProperty(localName = "nickname")
+ private String nickname;
+
+ @XStreamAlias("first")
+ @JacksonXmlProperty(localName = "first")
+ private String first;
+
+ @XStreamAlias("second")
+ @JacksonXmlProperty(localName = "second")
+ private String second;
+
///////////////////////////////////////
// 群发消息返回的结果
///////////////////////////////////////
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessage.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessage.java
index d2695959e8..6820d103b8 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessage.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessage.java
@@ -17,7 +17,7 @@
@NoArgsConstructor
@Builder
@AllArgsConstructor
-public class WxMpSubscribeMessage {
+public class WxMpSubscribeMessage implements Serializable {
/**
* 接收者openid.
diff --git a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpMapConfigImpl.java b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpMapConfigImpl.java
index cd701d1efc..72e6e615f7 100644
--- a/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpMapConfigImpl.java
+++ b/weixin-java-mp/src/main/java/me/chanjar/weixin/mp/config/impl/WxMpMapConfigImpl.java
@@ -15,7 +15,7 @@ public class WxMpMapConfigImpl extends WxMpDefaultConfigImpl {
private static final long serialVersionUID = 5311395137835650104L;
- private static final ConcurrentHashMap CONCURRENT_HASH_MAP = new ConcurrentHashMap<>(1);
+ private final ConcurrentHashMap CONCURRENT_HASH_MAP = new ConcurrentHashMap<>(1);
private static final String MAP_KEY = "access_token";
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMapConfigImplTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMapConfigImplTest.java
new file mode 100644
index 0000000000..167c0e019c
--- /dev/null
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/impl/WxMpMapConfigImplTest.java
@@ -0,0 +1,58 @@
+package me.chanjar.weixin.mp.api.impl;
+
+import com.google.inject.Inject;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.test.ApiTestModule;
+import me.chanjar.weixin.mp.config.impl.WxMpMapConfigImpl;
+import me.chanjar.weixin.mp.util.WxMpConfigStorageHolder;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * 测试 ConcurrentHashMap 保存配置信息
+ * @author jimmyjimmy-sw
+ */
+@Test
+@Guice(modules = ApiTestModule.class)
+public class WxMpMapConfigImplTest {
+
+ @Inject
+ private WxMpService wxService;
+
+ /**
+ * 测试多租户保存 WxMpMapConfigImpl 到 WxMpService,切换之后能获取到租户各自AppId对应的token
+ * @throws WxErrorException
+ */
+ @Test
+ public void testAppidSwitch() throws WxErrorException {
+ // 保存租户A的配置信息,并获取token
+ WxMpMapConfigImpl configAppA = new WxMpMapConfigImpl();
+ String appidA = "APPID_A";
+ configAppA.setAppId(appidA);
+ configAppA.setSecret("APP_SECRET_A");
+ configAppA.useStableAccessToken(true);
+ String tokenA = "TOKEN_A";
+ configAppA.updateAccessToken(tokenA,60 * 60 * 1);
+ wxService.addConfigStorage(appidA, configAppA);
+ WxMpConfigStorageHolder.set(appidA);
+ assertEquals(this.wxService.getAccessToken(),tokenA);
+
+ // 保存租户B的配置信息,并获取token
+ WxMpMapConfigImpl configAppB = new WxMpMapConfigImpl();
+ String appidB = "APPID_B";
+ configAppB.setAppId(appidB);
+ configAppB.setSecret("APP_SECRET_B");
+ configAppB.useStableAccessToken(true);
+ String tokenB = "TOKEN_B";
+ configAppB.updateAccessToken(tokenB,60 * 60 * 1);
+ wxService.addConfigStorage(appidB, configAppB);
+ WxMpConfigStorageHolder.set(appidB);
+ assertEquals(this.wxService.getAccessToken(),tokenB);
+
+ // 上下文切换到租户A 获取租户A的token
+ WxMpConfigStorageHolder.set(appidA);
+ assertEquals(this.wxService.getAccessToken(),tokenA);
+ }
+}
diff --git a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessageTest.java b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessageTest.java
index 078ad51570..684211659f 100644
--- a/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessageTest.java
+++ b/weixin-java-mp/src/test/java/me/chanjar/weixin/mp/bean/subscribe/WxMpSubscribeMessageTest.java
@@ -2,6 +2,9 @@
import org.testng.annotations.*;
+import java.io.Serializable;
+import java.util.Arrays;
+
import static org.testng.AssertJUnit.*;
/**
@@ -43,4 +46,9 @@ public void testToJson() {
assertEquals(message.toJson(), actual);
}
+
+ @Test
+ void testWxMpSubscribeMessageIsSerializable() {
+ assertTrue(Arrays.stream(WxMpSubscribeMessage.class.getInterfaces()).anyMatch(anInterface -> anInterface.isInstance(Serializable.class)));
+ }
}
diff --git a/weixin-java-open/pom.xml b/weixin-java-open/pom.xml
index 0146f516ad..1588287bc5 100644
--- a/weixin-java-open/pom.xml
+++ b/weixin-java-open/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
weixin-java-open
diff --git a/weixin-java-pay/pom.xml b/weixin-java-pay/pom.xml
index cba2ede006..c396980a50 100644
--- a/weixin-java-pay/pom.xml
+++ b/weixin-java-pay/pom.xml
@@ -5,7 +5,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
4.0.0
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyconfirm/ApplySubjectConfirmCreateRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyconfirm/ApplySubjectConfirmCreateRequest.java
index b95061461c..d77522ecce 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyconfirm/ApplySubjectConfirmCreateRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/applyconfirm/ApplySubjectConfirmCreateRequest.java
@@ -119,8 +119,8 @@ public static class ApplySubConfirmIdentificationInfo implements Serializable {
* IDENTIFICATION_TYPE_TAIWAN_RESIDENT:台湾居民证
* 示例值:IDENTIFICATION_TYPE_IDCARD
*/
- @SerializedName("id_doc_type")
- private IdTypeEnum idDocType;
+ @SerializedName("identification_type")
+ private IdTypeEnum identificationType;
/**
* 法定代表人说明函
@@ -414,13 +414,13 @@ public static class ApplySubConfirmSubjectInfo implements Serializable {
* 若未传入将默认填写:false。
* 示例值:true
*/
- @SerializedName("finance_institution")
+ @SerializedName("is_finance_institution")
private Boolean financeInstitution;
/**
* 营业执照
*/
- @SerializedName("business_license_info")
+ @SerializedName("business_licence_info")
private ApplySubConfirmBusinessLicenseInfo businessLicenseInfo;
/**
* 登记证书
@@ -736,8 +736,8 @@ public static class ApplySubConfirmSpecialOperationList implements Serializable
* 参看微信支付提供的特殊行业id对照表
* 示例值:100
*/
- @SerializedName("finance_type")
- private Integer financeType;
+ @SerializedName("category_id")
+ private Integer categoryId;
/**
* 行业经营许可证资质照片
@@ -791,29 +791,10 @@ public static class ApplySubConfirmAdditionInfo implements Serializable {
private static final long serialVersionUID = 1L;
/**
- * 法人开户承诺函
- */
- @SerializedName("legal_person_commitment")
- private String legalPersonCommitment;
-
- /**
- * 法人开户意愿视频
+ * 待确认商户号列表
*/
- @SerializedName("legal_person_video")
- private String legalPersonVideo;
-
- /**
- * 补充材料
- */
- @SerializedName("business_addition_pics")
- private List businessAdditionPics;
-
- /**
- * 补充说明
- */
- @SerializedName("business_addition_msg")
- private String businessAdditionMsg;
-
+ @SerializedName("confirm_mchid_list")
+ private List confirmMchidList;
}
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailResult.java
index 157e095bba..f8562dce39 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ComplaintDetailResult.java
@@ -327,4 +327,141 @@ public static class ServiceOrder implements Serializable {
*/
@SerializedName("user_tag_list")
private String[] userTagList;
+
+ /**
+ *
+ * 字段名:补充信息
+ * 是否必填:否
+ * 描述: 用在特定行业或场景下返回的补充信息
+ *
+ */
+ @SerializedName("additional_info")
+ private AdditionalInfo additionalInfo;
+
+ @Data
+ public static class AdditionalInfo implements Serializable {
+ private static final long serialVersionUID = 7917816070738944147L;
+
+ /**
+ *
+ * 字段名:补充信息类型
+ * 是否必填:否
+ * 描述: 补充信息类型
+ * 示例值:SHARE_POWER_TYPE: 充电宝投诉相关行业
+ *
+ */
+ @SerializedName("type")
+ private String type;
+
+ /**
+ *
+ * 字段名:充电宝投诉相关信息
+ * 是否必填:否
+ * 描述:当type为充电宝投诉相关时有值
+ *
+ */
+ @SerializedName("share_power_info")
+ private SharePowerInfo sharePowerInfo;
+
+ /**
+ * 充电宝投诉相关信息
+ */
+ @Data
+ public static class SharePowerInfo implements Serializable {
+ private static final long serialVersionUID = -2878382307459369354L;
+
+ /**
+ *
+ * 字段名:归还时间
+ * 是否必填:否
+ * 描述:遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE,
+ * yyyy-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,
+ * HH:mm:ss表示时分秒,
+ * TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。
+ * 示例值:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日 13点29分35秒
+ *
+ */
+ @SerializedName("return_time")
+ private String returnTime;
+
+ /**
+ *
+ * 字段名:归还地点信息
+ * 是否必填:否
+ * 描述: 归还地点信息
+ *
+ */
+ @SerializedName("return_address_info")
+ private ReturnAddressInfo returnAddressInfo;
+
+ @Data
+ public static class ReturnAddressInfo implements Serializable {
+ private static final long serialVersionUID = -7649986542568217256L;
+
+ /**
+ *
+ * 字段名:归还地点
+ * 是否必填:否 string(256)
+ * 描述:归还地点
+ *
+ */
+ @SerializedName("return_address")
+ private String returnAddress;
+
+ /**
+ *
+ * 字段名:归还地点经度
+ * 是否必填:否 string(32)
+ * 描述:经度,字符串,范围为-180~180,负数表示西经。使用GCJ-02坐标系
+ *
+ */
+ @SerializedName("longitude")
+ private String longitude;
+
+ /**
+ *
+ * 字段名:归还地点纬度
+ * 是否必填:否 string(32)
+ * 描述:纬度,字符串,范围为-90~90,负数表示南纬。使用GCJ-02坐标系
+ *
+ */
+ @SerializedName("latitude")
+ private String latitude;
+ }
+
+ /**
+ *
+ * 字段名:是否归还同一柜机
+ * 是否必填:否
+ * 描述:用户声明是否将充电宝归还至与借取时同一柜机
+ *
+ */
+ @SerializedName("is_returned_to_same_machine")
+ private Boolean isReturnedToSameMachine;
+ }
+ }
+
+ /**
+ *
+ * 字段名:是否在平台协助中
+ * 是否必填:否
+ * 描述:标识当前投诉单是否正处在平台协助流程中。
+ * 注:在协助期间由微信支付客服为用户服务,期间商户向用户发送的留言用户不可见
+ *
+ */
+ @SerializedName("in_platform_service")
+ private Boolean inPlatformService;
+
+ /**
+ *
+ * 字段名:是否需即时服务用户
+ * 是否必填:否
+ * 描述:因用户诉求紧急度、用户界面差异等因素,部分投诉单建议商户更即时地响应用户诉求。
+ * 如此处标识为“是”,建议商户提升服务时效,给用户带来更好的体验
+ *
+ */
+ @SerializedName("need_immediate_service")
+ private Boolean needImmediateService;
+
+
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryResult.java
index 2da216446d..7c8738fe29 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/NegotiationHistoryResult.java
@@ -142,20 +142,30 @@ public static class ComplaintMedia implements Serializable {
* 是否必填:是
* 描述:
* 当前投诉协商记录的操作类型,对应枚举:
- * USER_CREATE_COMPLAINT:用户提交投诉
- * USER_CONTINUE_COMPLAINT:用户继续投诉
- * USER_RESPONSE:用户留言
- * PLATFORM_RESPONSE:平台留言
- * MERCHANT_RESPONSE:商户留言
- * MERCHANT_CONFIRM_COMPLETE:商户申请结单
- * COMPLAINT_FULL_REFUNDED:投诉单全额退款
- * USER_CREATE_COMPLAINT_SYSTEM_MESSAGE:用户提交投诉系统通知
- * COMPLAINT_FULL_REFUNDED_SYSTEM_MESSAGE:投诉单全额退款系统通知
- * USER_CONTINUE_COMPLAINT_SYSTEM_MESSAGE:用户继续投诉系统通知
- * MERCHANT_CONFIRM_COMPLETE_SYSTEM_MESSAGE:商户申请结单系统通知
- * USER_REVOKE_COMPLAINT:用户主动撤诉(只存在于历史投诉单的协商历史中)
- * PLATFORM_HELP_APPLICATION:平台问询
- * USER_APPLY_PLATFORM_HELP:申请协助
+ * USER_CREATE_COMPLAINT: 用户提交投诉
+ * USER_CONTINUE_COMPLAINT: 用户继续投诉
+ * USER_RESPONSE: 用户留言
+ * PLATFORM_RESPONSE: 平台留言
+ * MERCHANT_RESPONSE: 商户留言
+ * MERCHANT_CONFIRM_COMPLETE: 商户申请结单
+ * USER_CREATE_COMPLAINT_SYSTEM_MESSAGE: 用户提交投诉系统通知
+ * COMPLAINT_FULL_REFUNDED_SYSTEM_MESSAGE: 投诉单发起全额退款系统通知
+ * USER_CONTINUE_COMPLAINT_SYSTEM_MESSAGE: 用户继续投诉系统通知
+ * USER_REVOKE_COMPLAINT: 用户主动撤诉(只存在于历史投诉单的协商历史中)
+ * USER_COMFIRM_COMPLAINT: 用户确认投诉解决(只存在于历史投诉单的协商历史中)
+ * PLATFORM_HELP_APPLICATION: 平台催办
+ * USER_APPLY_PLATFORM_HELP: 用户申请平台协助
+ * MERCHANT_APPROVE_REFUND: 商户同意退款申请
+ * MERCHANT_REFUSE_RERUND: 商户拒绝退款申请, 此时操作内容里展示拒绝原因
+ * USER_SUBMIT_SATISFACTION: 用户提交满意度调查结果,此时操作内容里会展示满意度分数
+ * SERVICE_ORDER_CANCEL: 服务订单已取消
+ * SERVICE_ORDER_COMPLETE: 服务订单已完成
+ * COMPLAINT_PARTIAL_REFUNDED_SYSTEM_MESSAGE: 投诉单发起部分退款系统通知
+ * COMPLAINT_REFUND_RECEIVED_SYSTEM_MESSAGE: 投诉单退款到账系统通知
+ * COMPLAINT_ENTRUSTED_REFUND_SYSTEM_MESSAGE: 投诉单受托退款系统通知
+ * USER_APPLY_PLATFORM_SERVICE: 用户申请平台协助
+ * USER_CANCEL_PLATFORM_SERVICE: 用户取消平台协助
+ * PLATFORM_SERVICE_FINISHED: 客服结束平台协助
*
*/
@SerializedName("operate_type")
@@ -179,11 +189,32 @@ public static class ComplaintMedia implements Serializable {
* 描述:
* 当前投诉协商记录提交的图片凭证(url格式),最多返回4张图片,url有效时间为1小时。如未查询到协商历史图片凭证,则返回空数组。
* 注:本字段包含商户、微信支付客服在协商解决投诉时上传的图片凭证,若希望查看用户图片,请使用complaint_media_list字段并联系微信支付客服
+ * 注:此字段不包含用户提交的图片凭证,建议统一使用complaint_media_list字段接收和请求资料凭证,未来该字段将废弃
*
*/
@SerializedName("image_list")
private List imageList;
+ /**
+ *
+ * 字段名:用户申请平台协助原因
+ * 是否必填:否
+ * 描述:用户此次申请平台协助时选择的申请协助原因
+ *
+ */
+ @SerializedName("user_appy_platform_service_reason")
+ private String userApplyPlatformServiceReason;
+
+ /**
+ *
+ * 字段名:用户申请平台协助原因描述
+ * 是否必填:否
+ * 描述:用户此次申请平台协助时填写的具体申请协助原因描述
+ *
+ */
+ @SerializedName("user_appy_platform_service_reason_description")
+ private String userApplyPlatformServiceReasonDescription;
+
}
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ResponseRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ResponseRequest.java
index 6f582b9301..470f2bed11 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ResponseRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/ResponseRequest.java
@@ -94,4 +94,57 @@ public class ResponseRequest implements Serializable {
@SerializedName("jump_url_text")
private String jumpUrlText;
+ /**
+ *
+ * 字段名:跳转小程序信息
+ * 是否必填:否
+ * 描述:商户可在回复中附加小程序信息,引导用户跳转至商户客诉处理小程序。
+ * 注:配置小程序属于灰度功能,若有需要请联系对接的行业运营进行咨询。
+ *
+ */
+ @SerializedName("mini_program_jump_info")
+ private MiniProgramJumpInfo miniProgramJumpInfo;
+
+
+ /**
+ * 跳转小程序信息
+ */
+ @Data
+ public static class MiniProgramJumpInfo implements Serializable {
+ private static final long serialVersionUID = 1169503275787468380L;
+
+ /**
+ *
+ * 字段名:跳转小程序APPID
+ * 是否必填:是
+ * 描述:商户可在回复中附加小程序页面路径,引导用户跳转至商户服务工具页面。
+ * 该字段为小程序APPID。
+ *
+ */
+ @SerializedName("appid")
+ private String appId;
+
+ /**
+ *
+ * 字段名:跳转小程序页面PATH
+ * 是否必填:是
+ * 描述:商户可在回复中附加小程序页面路径,引导用户跳转至商户服务工具页面。
+ * 该字段为小程序路径。
+ *
+ */
+ @SerializedName("path")
+ private String path;
+
+ /**
+ *
+ * 字段名:跳转小程序页面名称
+ * 是否必填:是
+ * 描述:商户可在回复中附加小程序页面路径,引导用户跳转至商户服务工具页面。
+ * 该字段为商户可自定义的页面名称。
+ *
+ */
+ @SerializedName("text")
+ private String text;
+ }
+
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/UpdateRefundProgressRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/UpdateRefundProgressRequest.java
index f7715c522e..79668bd0ce 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/UpdateRefundProgressRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/complaint/UpdateRefundProgressRequest.java
@@ -61,7 +61,7 @@ public class UpdateRefundProgressRequest implements Serializable {
/**
*
* 字段名:拒绝退款原因
- * 是否必填:否
+ * 是否必填:否 string(200)
* 描述:在拒绝退款时返回拒绝退款的原因
*
*/
@@ -72,7 +72,9 @@ public class UpdateRefundProgressRequest implements Serializable {
*
* 字段名:拒绝退款的举证图片列表
* 是否必填:否
- * 描述:在拒绝退款时,如果有拒绝的图片举证,可以提供 最多上传4张图片, 传入调用“商户上传反馈图片”接口返回的media_id,最多上传4张图片凭证
+ * 描述:在拒绝退款时,如果有拒绝的图片举证,可以提供 最多上传4张图片,
+ * 传入调用“商户上传反馈图片”接口返回的media_id,最多上传4张图片凭证
+ *
*
*/
@SerializedName("reject_media_list")
@@ -81,7 +83,7 @@ public class UpdateRefundProgressRequest implements Serializable {
/**
*
* 字段名:备注
- * 是否必填:否
+ * 是否必填:否 string(200)
* 描述:任何需要向微信支付客服反馈的信息
*
*/
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/ComplaintNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/ComplaintNotifyResult.java
index a5d18df6df..9464144c1d 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/ComplaintNotifyResult.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/ComplaintNotifyResult.java
@@ -47,13 +47,23 @@ public static class DecryptNotifyResult implements Serializable {
* 是否必填:是
* 描述:
* 触发本次投诉通知回调的具体动作类型,枚举如下:
+ * 常规通知:
* CREATE_COMPLAINT:用户提交投诉
* CONTINUE_COMPLAINT:用户继续投诉
* USER_RESPONSE:用户新留言
* RESPONSE_BY_PLATFORM:平台新留言
- * SELLER_REFUND:收款方全额退款
+ * SELLER_REFUND:商户发起全额退款
* MERCHANT_RESPONSE:商户新回复
* MERCHANT_CONFIRM_COMPLETE:商户反馈处理完成
+ * USER_APPLY_PLATFORM_SERVICE:用户申请平台协助
+ * USER_CANCEL_PLATFORM_SERVICE:用户取消平台协助
+ * PLATFORM_SERVICE_FINISHED:客服结束平台协助
+ *
+ * 申请退款单的附加通知:
+ * 以下通知会更新投诉单状态,建议收到后查询投诉单详情。
+ * MERCHANT_APPROVE_REFUND:商户同意退款
+ * MERCHANT_REJECT_REFUND:商户驳回退款
+ * REFUND_SUCCESS:退款到账
*
*/
@SerializedName(value = "action_type")
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java
index 98dae388ef..8ac588de81 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3Request.java
@@ -250,6 +250,12 @@ public static class Payer implements Serializable {
*/
@SerializedName(value = "openid")
private String openid;
+
+ /**
+ * 实名支付用户身份标识
+ */
+ @SerializedName(value = "identity")
+ private Identity identity;
}
@Data
@@ -572,4 +578,36 @@ public static class SettleInfo implements Serializable {
@SerializedName(value = "profit_sharing")
private Boolean profitSharing;
}
+
+
+ @Data
+ @NoArgsConstructor
+ public static class Identity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 证件类型
+ * IDCARD:身份证
+ * HONGKONG_MACAO:港澳回乡证
+ * HONGKONG_MACAO_RESIDENT:港澳居住证
+ * TAIWAN_RESIDENT:台湾居住证
+ * FOREIGN_RESIDENT:外国人永居证
+ * OVERSEA_PASSPORT:护照
+ */
+ @SerializedName(value = "type")
+ private String type;
+ /**
+ * 证件号
+ * 证件号,如身份证号。
+ * 示例值:43102119910910512X
+ */
+ @SerializedName(value = "number")
+ private String number;
+ /**
+ * 证件姓名。
+ * 示例值:周星星
+ */
+ @SerializedName(value = "name")
+ private String name;
+ }
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsCancelResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsCancelResult.java
new file mode 100644
index 0000000000..9e59fecf73
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsCancelResult.java
@@ -0,0 +1,48 @@
+package com.github.binarywang.wxpay.bean.transfer;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 商家转账到零钱撤销转账接口
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012716458
+ *
+ *
+ * @author Nor
+ * @date 2025/1/17
+ */
+@Data
+@NoArgsConstructor
+public class TransferBillsCancelResult implements Serializable {
+ private static final long serialVersionUID = -4935840810530008418L;
+
+ /**
+ * 【商户单号】 商户系统内部的商家单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
+ */
+ @SerializedName("out_bill_no")
+ private String outBillNo;
+
+ /**
+ * 【微信转账单号】 微信转账单号,微信商家转账系统返回的唯一标识
+ */
+ @SerializedName("transfer_bill_no")
+ private String transferBillNo;
+
+ /**
+ * 【单据状态】 商家转账订单状态
+ * 可选取值
+ * CANCELING: 商户撤销请求受理成功,该笔转账正在撤销中
+ * CANCELLED: 转账撤销完成
+ */
+ private String state;
+
+ /**
+ * 【最后一次单据状态变更时间】 按照使用rfc3339所定义的格式,格式为yyyy-MM-DDThh:mm:ss+TIMEZONE
+ */
+ @SerializedName("update_time")
+ private String updateTime;
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsGetResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsGetResult.java
new file mode 100644
index 0000000000..2e24a4a3c6
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsGetResult.java
@@ -0,0 +1,103 @@
+package com.github.binarywang.wxpay.bean.transfer;
+
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 商家转账到零钱查询转账单接口
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012716457 https://pay.weixin.qq.com/doc/v3/merchant/4012716437
+ *
+ *
+ * @author Nor
+ * @date 2025/1/17
+ */
+@Data
+@NoArgsConstructor
+public class TransferBillsGetResult implements Serializable {
+ private static final long serialVersionUID = -6376955113492371763L;
+
+ /**
+ * 【商户号】 微信支付分配的商户号
+ */
+ @SerializedName("mch_id")
+ private String mchId;
+
+ /**
+ * 【商户单号】 商户系统内部的商家单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
+ */
+ @SerializedName("out_bill_no")
+ private String outBillNo;
+
+ /**
+ * 【商家转账订单号】 商家转账订单的主键,唯一定义此资源的标识
+ */
+ @SerializedName("transfer_bill_no")
+ private String transferBillNo;
+
+ /**
+ * 【商户AppID】 申请商户号的AppID或商户号绑定的AppID(企业号corpid即为此AppID)
+ */
+ private String appid;
+
+ /**
+ * 【单据状态】
+ * 可选取值
+ * ACCEPTED: 转账已受理
+ * PROCESSING: 转账处理中,转账结果尚未明确,如一直处于此状态,建议检查账户余额是否足够
+ * WAIT_USER_CONFIRM: 待收款用户确认,可拉起微信收款确认页面进行收款确认
+ * TRANSFERING: 转账结果尚未明确,可拉起微信收款确认页面再次重试确认收款
+ * SUCCESS: 转账成功
+ * FAIL: 转账失败
+ * CANCELING: 商户撤销请求受理成功,该笔转账正在撤销中
+ * CANCELLED: 转账撤销完成
+ *
+ * @see WxPayConstants.TransformBillState
+ */
+ private String state;
+
+ /**
+ * 【转账金额】 转账金额单位为“分”。
+ */
+ @SerializedName("transfer_amount")
+ private String transferAmount;
+
+ /**
+ * 【转账备注】 单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符
+ */
+ @SerializedName("transfer_remark")
+ private String transferRemark;
+
+ /**
+ * 【失败原因】 订单已失败或者已退资金时,返回失败原因
+ */
+ @SerializedName("fail_reason")
+ private String failReason;
+
+ /**
+ * 【收款用户OpenID】 商户AppID下,某用户的OpenID
+ */
+ private String openid;
+
+ /**
+ * 【收款用户姓名】 收款方真实姓名。支持标准RSA算法和国密算法,公钥由微信侧提供转账金额 >= 2,000元时,该笔明细必须填写若商户传入收款用户姓名,微信支付会校验用户OpenID与姓名是否一致,并提供电子回单
+ */
+ @SerializedName("user_name")
+ private String userName;
+
+ /**
+ * 【单据创建时间】 单据受理成功时返回,按照使用rfc3339所定义的格式,格式为yyyy-MM-DDThh:mm:ss+TIMEZONE
+ */
+ @SerializedName("create_time")
+ private String createTime;
+
+ /**
+ * 【最后一次状态变更时间】 单据最后更新时间,按照使用rfc3339所定义的格式,格式为yyyy-MM-DDThh:mm:ss+TIMEZONE
+ */
+ @SerializedName("update_time")
+ private String updateTime;
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsNotifyResult.java
new file mode 100644
index 0000000000..80709a1022
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsNotifyResult.java
@@ -0,0 +1,79 @@
+package com.github.binarywang.wxpay.bean.transfer;
+
+import com.github.binarywang.wxpay.bean.notify.OriginNotifyResponse;
+import com.github.binarywang.wxpay.bean.notify.WxPayBaseNotifyV3Result;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 商家转账到零钱接口将转账结果通知用户
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012716434
+ *
+ */
+@Data
+public class TransferBillsNotifyResult implements Serializable, WxPayBaseNotifyV3Result {
+ /**
+ * 源数据
+ */
+ private OriginNotifyResponse rawData;
+ /**
+ * 解密后的数据
+ */
+ private TransferBillsNotifyResult.DecryptNotifyResult result;
+
+ @Data
+ @NoArgsConstructor
+ public static class DecryptNotifyResult implements Serializable {
+ /**
+ * 商户号
+ */
+ @SerializedName(value = "mch_id")
+ private String mchId;
+ /**
+ * 商家批次单号
+ */
+ @SerializedName(value = "out_bill_no")
+ private String outBillNo;
+ /**
+ * 微信批次单号
+ */
+ @SerializedName(value = "transfer_bill_no")
+ private String transferBillNo;
+ /**
+ * 批次状态
+ */
+ @SerializedName(value = "state")
+ private String state;
+ /**
+ * 转账金额
+ */
+ @SerializedName(value = "transfer_amount")
+ private Integer transferAmount;
+
+ /**
+ * 批次状态
+ */
+ @SerializedName(value = "openid")
+ private String openid;
+
+ /**
+ * 单据创建时间
+ */
+ @SerializedName(value = "create_time")
+ private String createTime;
+ /**
+ * 最后一次状态变更时间
+ */
+ @SerializedName(value = "update_time")
+ private String updateTime;
+ /**
+ * 错误原因
+ */
+ @SerializedName(value = "fail_reason")
+ private String failReason;
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsRequest.java
new file mode 100644
index 0000000000..230e564e4b
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsRequest.java
@@ -0,0 +1,108 @@
+package com.github.binarywang.wxpay.bean.transfer;
+
+import com.github.binarywang.wxpay.v3.SpecEncrypt;
+import com.google.gson.annotations.SerializedName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 发起商家转账API参数
+ *
+ * @author allovine
+ * created on 2025/1/15
+ **/
+@Data
+@Builder(builderMethodName = "newBuilder")
+@NoArgsConstructor
+@AllArgsConstructor
+public class TransferBillsRequest implements Serializable {
+ private static final long serialVersionUID = -2175582517588397437L;
+
+ /**
+ * 直连商户的appid
+ */
+ @SerializedName("appid")
+ private String appid;
+
+ /**
+ * 商户系统内部的商家单号
+ */
+ @SerializedName("out_bill_no")
+ private String outBillNo;
+
+ /**
+ * 转账场景ID
+ * 商户平台-产品中心-商家转账 申请
+ * 佣金报酬 ID:1005
+ */
+ @SerializedName("transfer_scene_id")
+ private String transferSceneId;
+
+ /**
+ * 用户在直连商户应用下的用户标示
+ */
+ @SerializedName("openid")
+ private String openid;
+
+ /**
+ * 收款用户姓名
+ */
+ @SpecEncrypt
+ @SerializedName("user_name")
+ private String userName;
+
+ /**
+ * 转账金额
+ */
+ @SerializedName("transfer_amount")
+ private Integer transferAmount;
+
+ /**
+ * 转账备注
+ */
+ @SerializedName("transfer_remark")
+ private String transferRemark;
+
+ /**
+ * 异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的url,必须为https,不能携带参数
+ */
+ @SerializedName("notify_url")
+ private String notifyUrl;
+
+ /**
+ * 用户收款感知
+ */
+ @SerializedName("user_recv_perception")
+ private String userRecvPerception;
+
+
+ /**
+ * 转账场景报备信息
+ */
+ @SerializedName("transfer_scene_report_infos")
+ private List transferSceneReportInfos;
+
+
+ @Data
+ @Builder(builderMethodName = "newBuilder")
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class TransferSceneReportInfo {
+ /**
+ * 信息类型
+ */
+ @SerializedName("info_type")
+ private String infoType;
+
+ /**
+ * 信息内容
+ */
+ @SerializedName("info_content")
+ private String infoContent;
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsResult.java
new file mode 100644
index 0000000000..78e0aec6ab
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBillsResult.java
@@ -0,0 +1,58 @@
+package com.github.binarywang.wxpay.bean.transfer;
+
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 商家转账结果
+ *
+ * @author allovine
+ * created on 2025/1/15
+ **/
+@Data
+@NoArgsConstructor
+public class TransferBillsResult implements Serializable {
+ private static final long serialVersionUID = -2175582517588397437L;
+
+ /**
+ * 商户单号
+ */
+ @SerializedName("out_bill_no")
+ private String outBillNo;
+
+ /**
+ * 微信转账单号
+ */
+ @SerializedName("transfer_bill_no")
+ private String transferBillNo;
+
+ /**
+ * 单据创建时间
+ */
+ @SerializedName("create_time")
+ private String createTime;
+
+ /**
+ * 单据状态
+ *
+ * @see WxPayConstants.TransformBillState
+ */
+ @SerializedName("state")
+ private String state;
+
+ /**
+ * 失败原因
+ */
+ @SerializedName("fail_reason")
+ private String failReason;
+
+ /**
+ * 跳转领取页面的package信息
+ */
+ @SerializedName("package_info")
+ private String packageInfo;
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/VerifierBuilder.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/VerifierBuilder.java
new file mode 100644
index 0000000000..c7bc14f526
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/VerifierBuilder.java
@@ -0,0 +1,131 @@
+package com.github.binarywang.wxpay.config;
+
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.v3.auth.*;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+/**
+ * 验证器构建.
+ *
+ * @author holy
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+class VerifierBuilder {
+ /**
+ * 构建验证器.
+ *
+ * 场景
+ *
+ * 1. 老商户号,只有平台证书,未开通公钥 (已验证)
+ * 2. 新商户号,被强制开通公钥,没有平台证书 (已验证)
+ * 3. 老商户号,有平台证书,主动开通公钥 (未验证,具备条件的朋友,可以帮忙验证下)
+ * ...
+ *
+ *
+ * @param certSerialNo c
+ * @param mchId m
+ * @param apiV3Key a
+ * @param merchantPrivateKey m
+ * @param wxPayHttpProxy w
+ * @param certAutoUpdateTime c
+ * @param payBaseUrl p
+ * @param publicKeyId p
+ * @param publicKey p
+ * @return v
+ * @throws WxPayException e
+ */
+ @SuppressWarnings("java:S107")
+ static Verifier build(
+ // 平台证书 - 依赖参数
+ String certSerialNo,
+ String mchId,
+ String apiV3Key,
+ PrivateKey merchantPrivateKey,
+ WxPayHttpProxy wxPayHttpProxy,
+ int certAutoUpdateTime,
+ String payBaseUrl,
+ // 公钥 - 依赖参数
+ String publicKeyId,
+ PublicKey publicKey
+ ) throws WxPayException {
+ Verifier certificatesVerifier = null;
+ Exception ex = null;
+
+ // 构建平台证书验证器
+ // (沿用旧逻辑)优先构建平台证书验证器,因为公钥验证器需要平台证书验证器 (见以下 .setOtherVerifier )
+ // 新商户号默认无平台证书,已确认无法构建平台证书验证器,会抛出异常;老商户号,有平台证书主动开通公钥的情况,待具备条件的朋友验证
+ // 建议公钥模式稳定后,优先构建公钥验证器,以免每次都尝试构建平台证书验证器,且失败 {@link com.github.binarywang.wxpay.v3.auth.PublicCertificateVerifier.verify}
+ if (merchantPrivateKey != null && StringUtils.isNoneBlank(certSerialNo, apiV3Key)) {
+ try {
+ certificatesVerifier = getCertificatesVerifier(
+ certSerialNo, mchId, apiV3Key, merchantPrivateKey, wxPayHttpProxy, certAutoUpdateTime, payBaseUrl
+ );
+ } catch (Exception e) {
+ ex = e;
+ }
+ }
+
+ // 构建公钥验证器
+ if (publicKey != null && StringUtils.isNotBlank(publicKeyId)) {
+ try {
+ certificatesVerifier = getPublicCertVerifier(publicKeyId, publicKey, certificatesVerifier);
+ } catch (Exception e) {
+ ex = e;
+ }
+ }
+ if (certificatesVerifier != null) {
+ return certificatesVerifier;
+ }
+
+ // 有异常时抛出
+ if (ex != null) {
+ throw new WxPayException(ex.getMessage(), ex);
+ }
+
+ // 没有证书验证器时。不确定是否抛出异常,沿用之前逻辑,返回 null
+ return null;
+ }
+
+ /**
+ * 获取证书验证器.
+ *
+ * @param certSerialNo certSerialNo
+ * @param mchId mchId
+ * @param apiV3Key apiV3Key
+ * @param merchantPrivateKey merchantPrivateKey
+ * @param wxPayHttpProxy wxPayHttpProxy
+ * @param certAutoUpdateTime certAutoUpdateTime
+ * @param payBaseUrl payBaseUrl
+ * @return verifier
+ */
+ private static AutoUpdateCertificatesVerifier getCertificatesVerifier(
+ String certSerialNo, String mchId, String apiV3Key, PrivateKey merchantPrivateKey,
+ WxPayHttpProxy wxPayHttpProxy, int certAutoUpdateTime, String payBaseUrl
+ ) {
+ return new AutoUpdateCertificatesVerifier(
+ new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)),
+ apiV3Key.getBytes(StandardCharsets.UTF_8), certAutoUpdateTime,
+ payBaseUrl, wxPayHttpProxy);
+ }
+
+ /**
+ * 获取公钥验证器.
+ *
+ * @param publicKeyId id
+ * @param publicKey key
+ * @param certificatesVerifier verifier
+ * @return verifier
+ */
+ private static Verifier getPublicCertVerifier(String publicKeyId, PublicKey publicKey, Verifier certificatesVerifier) {
+ Verifier publicCertificatesVerifier = new PublicCertificateVerifier(publicKey, publicKeyId);
+ publicCertificatesVerifier.setOtherVerifier(certificatesVerifier);
+ certificatesVerifier = publicCertificatesVerifier;
+ return certificatesVerifier;
+ }
+}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
index 293c52eac6..75db10a070 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java
@@ -4,18 +4,9 @@
import com.github.binarywang.wxpay.util.HttpProxyUtils;
import com.github.binarywang.wxpay.util.ResourcesUtils;
import com.github.binarywang.wxpay.v3.WxPayV3HttpClientBuilder;
-import com.github.binarywang.wxpay.v3.auth.*;
+import com.github.binarywang.wxpay.v3.auth.Verifier;
+import com.github.binarywang.wxpay.v3.auth.WxPayValidator;
import com.github.binarywang.wxpay.v3.util.PemUtils;
-import java.io.*;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.security.KeyStore;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.Optional;
-import javax.net.ssl.SSLContext;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
@@ -26,6 +17,17 @@
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.ssl.SSLContexts;
+import javax.net.ssl.SSLContext;
+import java.io.*;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.Optional;
+
/**
* 微信支付配置
*
@@ -225,6 +227,11 @@ public class WxPayConfig {
*/
private Verifier verifier;
+ /**
+ * 是否将全部v3接口的请求都添加Wechatpay-Serial请求头,默认不添加
+ */
+ private boolean strictlyNeedWechatPaySerial = false;
+
/**
* 返回所设置的微信支付接口请求地址域名.
*
@@ -293,13 +300,13 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
}
try {
- if (merchantPrivateKey == null) {
+ if (merchantPrivateKey == null && StringUtils.isNotBlank(this.getPrivateKeyPath())) {
try (InputStream keyInputStream = this.loadConfigInputStream(this.getPrivateKeyString(), this.getPrivateKeyPath(),
this.privateKeyContent, "privateKeyPath")) {
merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
}
}
- if (certificate == null && StringUtils.isBlank(this.getCertSerialNo())) {
+ if (certificate == null && StringUtils.isBlank(this.getCertSerialNo()) && StringUtils.isNotBlank(this.getPrivateCertPath())) {
try (InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(),
this.privateCertContent, "privateCertPath")) {
certificate = PemUtils.loadCertificate(certInputStream);
@@ -309,8 +316,8 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
PublicKey publicKey = null;
if (this.getPublicKeyString() != null || this.getPublicKeyPath() != null || this.publicKeyContent != null) {
try (InputStream pubInputStream =
- this.loadConfigInputStream(this.getPublicKeyString(), this.getPublicKeyPath(),
- this.publicKeyContent, "publicKeyPath")) {
+ this.loadConfigInputStream(this.getPublicKeyString(), this.getPublicKeyPath(),
+ this.publicKeyContent, "publicKeyPath")) {
publicKey = PemUtils.loadPublicKey(pubInputStream);
}
}
@@ -318,16 +325,12 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
//构造Http Proxy正向代理
WxPayHttpProxy wxPayHttpProxy = getWxPayHttpProxy();
- Verifier certificatesVerifier;
- if (publicKey == null) {
- certificatesVerifier =
- new AutoUpdateCertificatesVerifier(
- new WxPayCredentials(mchId, new PrivateKeySigner(certSerialNo, merchantPrivateKey)),
- this.getApiV3Key().getBytes(StandardCharsets.UTF_8), this.getCertAutoUpdateTime(),
- this.getPayBaseUrl(), wxPayHttpProxy);
- } else {
- certificatesVerifier = new PublicCertificateVerifier(publicKey, publicKeyId);
- }
+ // 构造证书验签器
+ Verifier certificatesVerifier = VerifierBuilder.build(
+ this.getCertSerialNo(), this.getMchId(), this.getApiV3Key(), merchantPrivateKey, wxPayHttpProxy,
+ this.getCertAutoUpdateTime(), this.getPayBaseUrl(),
+ this.getPublicKeyId(), publicKey
+ );
WxPayV3HttpClientBuilder wxPayV3HttpClientBuilder = WxPayV3HttpClientBuilder.create()
.withMerchant(mchId, certSerialNo, merchantPrivateKey)
@@ -365,21 +368,32 @@ private WxPayHttpProxy getWxPayHttpProxy() {
return null;
}
+ /**
+ * 从指定参数加载输入流
+ *
+ * @param configString 证书内容进行Base64加密后的字符串
+ * @param configPath 证书路径
+ * @param configContent 证书内容的字节数组
+ * @param certName 证书的标识
+ * @return 输入流
+ * @throws WxPayException 异常
+ */
private InputStream loadConfigInputStream(String configString, String configPath, byte[] configContent,
- String fileName) throws WxPayException {
- InputStream inputStream;
+ String certName) throws WxPayException {
if (configContent != null) {
- inputStream = new ByteArrayInputStream(configContent);
- } else if (StringUtils.isNotEmpty(configString)) {
- configContent = configString.getBytes(StandardCharsets.UTF_8);
- inputStream = new ByteArrayInputStream(configContent);
- } else {
- if (StringUtils.isBlank(configPath)) {
- throw new WxPayException("请确保证书文件地址【" + fileName + "】或者内容已配置");
- }
- inputStream = this.loadConfigInputStream(configPath);
+ return new ByteArrayInputStream(configContent);
+ }
+
+ if (StringUtils.isNotEmpty(configString)) {
+ configContent = Base64.getDecoder().decode(configString);
+ return new ByteArrayInputStream(configContent);
+ }
+
+ if (StringUtils.isBlank(configPath)) {
+ throw new WxPayException(String.format("请确保【%s】的文件地址【%s】存在", certName, configPath));
}
- return inputStream;
+
+ return this.loadConfigInputStream(configPath);
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
index 819cdfe731..e8a6b6acb3 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java
@@ -6,6 +6,7 @@
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
import com.google.common.collect.Lists;
+import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.time.FastDateFormat;
import java.text.Format;
@@ -353,4 +354,86 @@ public static class ReceiverType {
public static final String PERSONAL_SUB_OPENID = "PERSONAL_SUB_OPENID";
}
+ /**
+ * 微信商户转账订单状态
+ */
+ @UtilityClass
+ public static class TransformBillState {
+ /**
+ * 转账已受理
+ */
+ public static final String ACCEPTED = "ACCEPTED";
+
+ /**
+ * 转账处理中,转账结果尚未明确,如一直处于此状态,建议检查账户余额是否足够
+ */
+ public static final String PROCESSING = "PROCESSING";
+
+ /**
+ * 待收款用户确认,可拉起微信收款确认页面进行收款确认
+ */
+ public static final String WAIT_USER_CONFIRM = "WAIT_USER_CONFIRM";
+
+ /**
+ * 转账结果尚未明确,可拉起微信收款确认页面再次重试确认收款
+ */
+ public static final String TRANSFERING = "TRANSFERING";
+
+ /**
+ * 转账成功
+ */
+ public static final String SUCCESS = "SUCCESS";
+
+ /**
+ * 转账失败
+ */
+ public static final String FAIL = "FAIL";
+
+ /**
+ * 商户撤销请求受理成功,该笔转账正在撤销中
+ */
+ public static final String CANCELING = "CANCELING";
+
+ /**
+ * 转账撤销完成
+ */
+ public static final String CANCELLED = "CANCELLED";
+
+ }
+
+ /**
+ * 【转账场景ID】 该笔转账使用的转账场景,可前往“商户平台-产品中心-商家转账”中申请。
+ */
+ @UtilityClass
+ public static class TransformSceneId {
+ /**
+ * 现金营销
+ */
+ public static final String CASH_MARKETING = "1001";
+ }
+
+ /**
+ * 用户收款感知
+ *
+ * @see 官方文档
+ */
+ @UtilityClass
+ public static class UserRecvPerception {
+ /**
+ * 转账场景 现金营销
+ * 场景介绍 向参与营销活动的用户发放现金奖励
+ */
+ public static class CASH_MARKETING {
+ /**
+ * 默认展示
+ */
+ public static final String ACTIVITY = "活动奖励";
+
+ /**
+ * 需在发起转账时,“用户收款感知”字段主动传入“现金奖励”才可展示
+ */
+ public static final String CASH = "现金奖励";
+ }
+
+ }
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ComplaintService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ComplaintService.java
index 66de1458a3..6fc1367cf4 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ComplaintService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ComplaintService.java
@@ -152,7 +152,7 @@ public interface ComplaintService {
/**
*
* 商户上传反馈图片API
- * 文档详见: ...
+ * 文档详见: ...
* 接口链接:https://api.mch.weixin.qq.com/v3/merchant-service/images/upload
*
*
@@ -165,7 +165,7 @@ public interface ComplaintService {
/**
*
* 商户上传反馈图片API
- * 文档详见: ...
+ * 文档详见: ...
* 接口链接:https://api.mch.weixin.qq.com/v3/merchant-service/images/upload
*
*
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java
index ebf746214d..01113c9506 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java
@@ -111,4 +111,82 @@ public interface TransferService {
*/
TransferBatchDetailResult transferBatchesOutBatchNoDetail(String outBatchNo, String outDetailNo) throws WxPayException;
+ /**
+ *
+ *
+ * 2025.1.15 开始新接口 发起商家转账API
+ *
+ * 请求方式:POST(HTTPS)
+ * 请求地址:请求地址
+ *
+ * 文档地址:发起商家转账API
+ *
+ *
+ * @param request 转账请求参数
+ * @return TransferBillsResult 转账结果
+ * @throws WxPayException .
+ */
+ TransferBillsResult transferBills(TransferBillsRequest request) throws WxPayException;
+
+ /**
+ *
+ *
+ * 2025.1.15 开始新接口 撤销转账API
+ *
+ * 请求方式:POST(HTTPS)
+ * 请求地址:请求地址
+ *
+ * 文档地址:商户撤销转账API
+ *
+ *
+ * @param outBillNo 【商户单号】 商户系统内部的商家单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
+ * @return TransformBillsGetResult 转账单
+ * @throws WxPayException .
+ */
+ TransferBillsCancelResult transformBillsCancel(String outBillNo) throws WxPayException;
+
+ /**
+ *
+ *
+ * 2025.1.15 开始新接口 发起商家转账API
+ *
+ * 请求方式:GET(HTTPS)
+ * 请求地址:请求地址
+ *
+ * 文档地址:商户单号查询转账单API
+ *
+ *
+ * @param outBillNo 【商户单号】 商户系统内部的商家单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
+ * @return TransformBillsGetResult 转账单
+ * @throws WxPayException .
+ */
+ TransferBillsGetResult getBillsByOutBillNo(String outBillNo) throws WxPayException;
+
+ /**
+ *
+ *
+ * 2025.1.15 开始新接口 微信单号查询转账单API
+ *
+ * 请求方式:GET(HTTPS)
+ * 请求地址:请求地址
+ *
+ * 文档地址:商户单号查询转账单API
+ *
+ *
+ * @param transferBillNo 【微信转账单号】 微信转账单号,微信商家转账系统返回的唯一标识
+ * @return TransformBillsGetResult 转账单
+ * @throws WxPayException .
+ */
+ TransferBillsGetResult getBillsByTransferBillNo(String transferBillNo) throws WxPayException;
+
+ /**
+ * 2025.1.15 开始新接口 解析商家转账结果
+ * 详见
+ *
+ * @param notifyData 通知数据
+ * @param header 通知头部数据,不传则表示不校验头
+ * @return the wx transfer notify result
+ * @throws WxPayException the wx pay exception
+ */
+ TransferBillsNotifyResult parseTransferBillsNotifyResult(String notifyData, SignatureHeader header) throws WxPayException;
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java
index 57c2937c62..8ceac2b6ba 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java
@@ -6,6 +6,7 @@
import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.bean.transfer.TransferBillsNotifyResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
@@ -38,16 +39,18 @@ public interface WxPayService {
* Map里 加入新的 {@link WxPayConfig},适用于动态添加新的微信商户配置.
*
* @param mchId 商户id
+ * @param appId 微信应用id
* @param wxPayConfig 新的微信配置
*/
- void addConfig(String mchId, WxPayConfig wxPayConfig);
+ void addConfig(String mchId, String appId, WxPayConfig wxPayConfig);
/**
- * 从 Map中 移除 {@link String mchId} 所对应的 {@link WxPayConfig},适用于动态移除微信商户配置.
+ * 从 Map中 移除 {@link String mchId} 和 {@link String appId} 所对应的 {@link WxPayConfig},适用于动态移除微信商户配置.
*
* @param mchId 对应商户的标识
+ * @param appId 微信应用id
*/
- void removeConfig(String mchId);
+ void removeConfig(String mchId, String appId);
/**
* 注入多个 {@link WxPayConfig} 的实现. 并为每个 {@link WxPayConfig} 赋予不同的 {@link String mchId} 值
@@ -69,17 +72,19 @@ public interface WxPayService {
* 进行相应的商户切换.
*
* @param mchId 商户标识
+ * @param appId 微信应用id
* @return 切换是否成功 boolean
*/
- boolean switchover(String mchId);
+ boolean switchover(String mchId, String appId);
/**
* 进行相应的商户切换.
*
* @param mchId 商户标识
+ * @param appId 微信应用id
* @return 切换成功 ,则返回当前对象,方便链式调用,否则抛出异常
*/
- WxPayService switchoverTo(String mchId);
+ WxPayService switchoverTo(String mchId, String appId);
/**
* 发送post请求,得到响应字节数组.
@@ -616,10 +621,10 @@ public interface WxPayService {
/**
* 调用统一下单接口,并组装生成支付所需参数对象.
*
- * @param 请使用{@link com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result}里的内部类或字段
+ * @param 请使用{@link WxPayUnifiedOrderV3Result}里的内部类或字段
* @param tradeType the trade type
* @param request 统一下单请求参数
- * @return 返回 {@link com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result}里的内部类或字段
+ * @return 返回 {@link WxPayUnifiedOrderV3Result}里的内部类或字段
* @throws WxPayException the wx pay exception
*/
T createOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request) throws WxPayException;
@@ -627,10 +632,10 @@ public interface WxPayService {
/**
* 服务商模式调用统一下单接口,并组装生成支付所需参数对象.
*
- * @param 请使用{@link com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result}里的内部类或字段
+ * @param 请使用{@link WxPayUnifiedOrderV3Result}里的内部类或字段
* @param tradeType the trade type
* @param request 统一下单请求参数
- * @return 返回 {@link com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result}里的内部类或字段
+ * @return 返回 {@link WxPayUnifiedOrderV3Result}里的内部类或字段
* @throws WxPayException the wx pay exception
*/
T createPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException;
@@ -991,6 +996,17 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
*/
WxPayTransferBatchesNotifyV3Result parseTransferBatchesNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException;
+ /**
+ * 解析商家转账批次回调通知
+ * https://pay.weixin.qq.com/doc/v3/merchant/4012712115
+ *
+ * @param notifyData
+ * @param header
+ * @return
+ * @throws WxPayException
+ */
+ TransferBillsNotifyResult parseTransferBillsNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException;
+
/**
* 解析服务商模式退款结果通知
* 详见https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_11.shtml
@@ -1603,7 +1619,8 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
/**
* 获取服务商直股份签约计划服务类
- * @return the partner pay score sign plan service
+ *
+ * @return the partner pay score sign plan service
*/
PartnerPayScoreSignPlanService getPartnerPayScoreSignPlanService();
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
index c9fc1e7bd2..3a63f0d7fd 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java
@@ -11,9 +11,9 @@
import com.github.binarywang.wxpay.bean.request.*;
import com.github.binarywang.wxpay.bean.result.*;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
+import com.github.binarywang.wxpay.bean.transfer.TransferBillsNotifyResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.config.WxPayConfigHolder;
-import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.constant.WxPayConstants.SignType;
import com.github.binarywang.wxpay.constant.WxPayConstants.TradeType;
import com.github.binarywang.wxpay.exception.WxPayException;
@@ -30,6 +30,7 @@
import com.google.gson.GsonBuilder;
import lombok.Getter;
import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.ConstructorUtils;
@@ -45,6 +46,7 @@
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.ZipException;
import static com.github.binarywang.wxpay.constant.WxPayConstants.QUERY_COMMENT_DATE_FORMAT;
@@ -58,13 +60,12 @@
*
* @author Binary Wang
*/
+@Slf4j
public abstract class BaseWxPayServiceImpl implements WxPayService {
private static final String TOTAL_FUND_COUNT = "资金流水总笔数";
private static final Gson GSON = new GsonBuilder().create();
- final Logger log = LoggerFactory.getLogger(this.getClass());
-
static ThreadLocal wxApiData = new ThreadLocal<>();
@@ -121,7 +122,7 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
private final PartnerPayScoreService partnerPayScoreService = new PartnerPayScoreServiceImpl(this);
@Getter
- private final PartnerPayScoreSignPlanService partnerPayScoreSignPlanService=new PartnerPayScoreSignPlanServiceImpl(this);
+ private final PartnerPayScoreSignPlanService partnerPayScoreSignPlanService = new PartnerPayScoreSignPlanServiceImpl(this);
@Getter
private final MerchantTransferService merchantTransferService = new MerchantTransferServiceImpl(this);
@@ -129,7 +130,7 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
@Getter
private final BrandMerchantTransferService brandMerchantTransferService = new BrandMerchantTransferServiceImpl(this);
- protected Map configMap = new HashMap<>();
+ protected Map configMap = new ConcurrentHashMap<>();
@Override
public WxPayConfig getConfig() {
@@ -142,38 +143,37 @@ public WxPayConfig getConfig() {
@Override
public void setConfig(WxPayConfig config) {
- final String defaultMchId = config.getMchId();
- this.setMultiConfig(ImmutableMap.of(defaultMchId, config), defaultMchId);
+ final String defaultKey = this.getConfigKey(config.getMchId(), config.getAppId());
+ this.setMultiConfig(ImmutableMap.of(defaultKey, config), defaultKey);
}
@Override
- public void addConfig(String mchId, WxPayConfig wxPayConfig) {
+ public void addConfig(String mchId, String appId, WxPayConfig wxPayConfig) {
synchronized (this) {
if (this.configMap == null) {
this.setConfig(wxPayConfig);
} else {
- WxPayConfigHolder.set(mchId);
- this.configMap.put(mchId, wxPayConfig);
+ String configKey = this.getConfigKey(mchId, appId);
+ WxPayConfigHolder.set(configKey);
+ this.configMap.put(configKey, wxPayConfig);
}
}
}
@Override
- public void removeConfig(String mchId) {
+ public void removeConfig(String mchId, String appId) {
synchronized (this) {
- if (this.configMap.size() == 1) {
- this.configMap.remove(mchId);
- log.warn("已删除最后一个商户号配置:{},须立即使用setConfig或setMultiConfig添加配置", mchId);
+ String configKey = this.getConfigKey(mchId, appId);
+ this.configMap.remove(configKey);
+ if (this.configMap.isEmpty()) {
+ log.warn("已删除最后一个商户号配置:mchId[{}],appid[{}],须立即使用setConfig或setMultiConfig添加配置", mchId, appId);
return;
}
- if (WxPayConfigHolder.get().equals(mchId)) {
- this.configMap.remove(mchId);
- final String defaultMpId = this.configMap.keySet().iterator().next();
- WxPayConfigHolder.set(defaultMpId);
- log.warn("已删除默认商户号配置,商户号【{}】被设为默认配置", defaultMpId);
- return;
+ if (WxPayConfigHolder.get().equals(configKey)) {
+ final String nextConfigKey = this.configMap.keySet().iterator().next();
+ WxPayConfigHolder.set(nextConfigKey);
+ log.warn("已删除默认商户号配置,商户号【{}】被设为默认配置", nextConfigKey);
}
- this.configMap.remove(mchId);
}
}
@@ -183,28 +183,34 @@ public void setMultiConfig(Map wxPayConfigs) {
}
@Override
- public void setMultiConfig(Map wxPayConfigs, String defaultMchId) {
+ public void setMultiConfig(Map wxPayConfigs, String defaultConfigKey) {
this.configMap = Maps.newHashMap(wxPayConfigs);
- WxPayConfigHolder.set(defaultMchId);
+ WxPayConfigHolder.set(defaultConfigKey);
}
@Override
- public boolean switchover(String mchId) {
- if (this.configMap.containsKey(mchId)) {
- WxPayConfigHolder.set(mchId);
+ public boolean switchover(String mchId, String appId) {
+ String configKey = this.getConfigKey(mchId, appId);
+ if (this.configMap.containsKey(configKey)) {
+ WxPayConfigHolder.set(configKey);
return true;
}
- log.error("无法找到对应【{}】的商户号配置信息,请核实!", mchId);
+ log.error("无法找到对应mchId=【{}】,appId=【{}】的商户号配置信息,请核实!", mchId, appId);
return false;
}
@Override
- public WxPayService switchoverTo(String mchId) {
- if (this.configMap.containsKey(mchId)) {
- WxPayConfigHolder.set(mchId);
+ public WxPayService switchoverTo(String mchId, String appId) {
+ String configKey = this.getConfigKey(mchId, appId);
+ if (this.configMap.containsKey(configKey)) {
+ WxPayConfigHolder.set(configKey);
return this;
}
- throw new WxRuntimeException(String.format("无法找到对应【%s】的商户号配置信息,请核实!", mchId));
+ throw new WxRuntimeException(String.format("无法找到对应mchId=【%s】,appId=【%s】的商户号配置信息,请核实!", mchId, appId));
+ }
+
+ private String getConfigKey(String mchId, String appId) {
+ return mchId + "_" + appId;
}
@Override
@@ -245,7 +251,7 @@ public WxPayRefundResult refundV2(WxPayRefundRequest request) throws WxPayExcept
@Override
public WxPayRefundV3Result refundV3(WxPayRefundV3Request request) throws WxPayException {
String url = String.format("%s/v3/refund/domestic/refunds", this.getPayBaseUrl());
- String response = this.postV3(url, GSON.toJson(request));
+ String response = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
return GSON.fromJson(response, WxPayRefundV3Result.class);
}
@@ -288,21 +294,21 @@ public WxPayRefundQueryResult refundQueryV2(WxPayRefundQueryRequest request) thr
@Override
public WxPayRefundQueryV3Result refundQueryV3(String outRefundNo) throws WxPayException {
String url = String.format("%s/v3/refund/domestic/refunds/%s", this.getPayBaseUrl(), outRefundNo);
- String response = this.getV3(url);
+ String response = this.getV3WithWechatPaySerial(url);
return GSON.fromJson(response, WxPayRefundQueryV3Result.class);
}
@Override
public WxPayRefundQueryV3Result refundQueryV3(WxPayRefundQueryV3Request request) throws WxPayException {
String url = String.format("%s/v3/refund/domestic/refunds/%s", this.getPayBaseUrl(), request.getOutRefundNo());
- String response = this.getV3(url);
+ String response = this.getV3WithWechatPaySerial(url);
return GSON.fromJson(response, WxPayRefundQueryV3Result.class);
}
@Override
public WxPayRefundQueryV3Result refundPartnerQueryV3(WxPayRefundQueryV3Request request) throws WxPayException {
- String url = String.format("%s/v3/refund/domestic/refunds/%s?sub_mchid=%s", this.getPayBaseUrl(), request.getOutRefundNo(),request.getSubMchid());
- String response = this.getV3(url);
+ String url = String.format("%s/v3/refund/domestic/refunds/%s?sub_mchid=%s", this.getPayBaseUrl(), request.getOutRefundNo(), request.getSubMchid());
+ String response = this.getV3WithWechatPaySerial(url);
return GSON.fromJson(response, WxPayRefundQueryV3Result.class);
}
@@ -317,13 +323,13 @@ public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData, String sign
log.debug("微信支付异步通知请求参数:{}", xmlData);
WxPayOrderNotifyResult result = WxPayOrderNotifyResult.fromXML(xmlData);
if (signType == null) {
+ this.switchover(result.getMchId(), result.getAppid());
if (result.getSignType() != null) {
// 如果解析的通知对象中signType有值,则使用它进行验签
signType = result.getSignType();
- } else if (configMap.get(result.getMchId()).getSignType() != null) {
+ } else if (this.getConfig().getSignType() != null) {
// 如果配置中signType有值,则使用它进行验签
- signType = configMap.get(result.getMchId()).getSignType();
- this.switchover(result.getMchId());
+ signType = this.getConfig().getSignType();
}
}
@@ -346,7 +352,7 @@ public WxPayOrderNotifyResult parseOrderNotifyResult(String xmlData, String sign
*/
private boolean verifyNotifySign(SignatureHeader header, String data) throws WxSignTestException {
String wxPaySign = header.getSignature();
- if(wxPaySign.startsWith("WECHATPAY/SIGNTEST/")){
+ if (wxPaySign.startsWith("WECHATPAY/SIGNTEST/")) {
throw new WxSignTestException("微信支付签名探测流量");
}
String beforeSign = String.format("%s\n%s\n%s\n",
@@ -420,7 +426,7 @@ public WxPayRefundNotifyResult parseRefundNotifyResult(String xmlData) throws Wx
WxPayRefundNotifyResult result;
if (XmlConfig.fastMode) {
result = BaseWxPayResult.fromXML(xmlData, WxPayRefundNotifyResult.class);
- this.switchover(result.getMchId());
+ this.switchover(result.getMchId(), result.getAppid());
result.decryptReqInfo(this.getConfig().getMchKey());
} else {
result = WxPayRefundNotifyResult.fromXML(xmlData, this.getConfig().getMchKey());
@@ -442,6 +448,11 @@ public WxPayTransferBatchesNotifyV3Result parseTransferBatchesNotifyV3Result(Str
return this.baseParseOrderNotifyV3Result(notifyData, header, WxPayTransferBatchesNotifyV3Result.class, WxPayTransferBatchesNotifyV3Result.DecryptNotifyResult.class);
}
+ @Override
+ public TransferBillsNotifyResult parseTransferBillsNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException {
+ return this.baseParseOrderNotifyV3Result(notifyData, header, TransferBillsNotifyResult.class, TransferBillsNotifyResult.DecryptNotifyResult.class);
+ }
+
@Override
public WxPayPartnerRefundNotifyV3Result parsePartnerRefundNotifyV3Result(String notifyData, SignatureHeader header) throws WxPayException {
return this.baseParseOrderNotifyV3Result(notifyData, header, WxPayPartnerRefundNotifyV3Result.class, WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult.class);
@@ -452,7 +463,7 @@ public WxScanPayNotifyResult parseScanPayNotifyResult(String xmlData, @Deprecate
try {
log.debug("扫码支付回调通知请求参数:{}", xmlData);
WxScanPayNotifyResult result = BaseWxPayResult.fromXML(xmlData, WxScanPayNotifyResult.class);
- this.switchover(result.getMchId());
+ this.switchover(result.getMchId(), result.getAppid());
log.debug("扫码支付回调通知解析后的对象:{}", result);
result.checkResult(this, this.getConfig().getSignType(), false);
return result;
@@ -512,7 +523,7 @@ public WxPayOrderQueryV3Result queryOrderV3(WxPayOrderQueryV3Request request) th
url = String.format("%s/v3/pay/transactions/id/%s", this.getPayBaseUrl(), request.getTransactionId());
}
String query = String.format("?mchid=%s", request.getMchid());
- String response = this.getV3(url + query);
+ String response = this.getV3WithWechatPaySerial(url + query);
return GSON.fromJson(response, WxPayOrderQueryV3Result.class);
}
@@ -537,14 +548,14 @@ public WxPayPartnerOrderQueryV3Result queryPartnerOrderV3(WxPayPartnerOrderQuery
url = String.format("%s/v3/pay/partner/transactions/id/%s", this.getPayBaseUrl(), request.getTransactionId());
}
String query = String.format("?sp_mchid=%s&sub_mchid=%s", request.getSpMchId(), request.getSubMchId());
- String response = this.getV3(url + query);
+ String response = this.getV3WithWechatPaySerial(url + query);
return GSON.fromJson(response, WxPayPartnerOrderQueryV3Result.class);
}
@Override
public CombineQueryResult queryCombine(String combineOutTradeNo) throws WxPayException {
String url = String.format("%s/v3/combine-transactions/out-trade-no/%s", this.getPayBaseUrl(), combineOutTradeNo);
- String response = this.getV3(url);
+ String response = this.getV3WithWechatPaySerial(url);
return GSON.fromJson(response, CombineQueryResult.class);
}
@@ -598,7 +609,7 @@ public void closeOrderV3(WxPayOrderCloseV3Request request) throws WxPayException
request.setMchid(this.getConfig().getMchId());
}
String url = String.format("%s/v3/pay/transactions/out-trade-no/%s/close", this.getPayBaseUrl(), request.getOutTradeNo());
- this.postV3(url, GSON.toJson(request));
+ this.postV3WithWechatpaySerial(url, GSON.toJson(request));
}
@Override
@@ -610,13 +621,13 @@ public void closePartnerOrderV3(WxPayPartnerOrderCloseV3Request request) throws
request.setSubMchId(this.getConfig().getSubMchId());
}
String url = String.format("%s/v3/pay/partner/transactions/out-trade-no/%s/close", this.getPayBaseUrl(), request.getOutTradeNo());
- this.postV3(url, GSON.toJson(request));
+ this.postV3WithWechatpaySerial(url, GSON.toJson(request));
}
@Override
public void closeCombine(CombineCloseRequest request) throws WxPayException {
String url = String.format("%s/v3/combine-transactions/out-trade-no/%s/close", this.getPayBaseUrl(), request.getCombineOutTradeNo());
- this.postV3(url, GSON.toJson(request));
+ this.postV3WithWechatpaySerial(url, GSON.toJson(request));
}
@Override
@@ -760,7 +771,7 @@ public WxPayUnifiedOrderV3Result unifiedPartnerOrderV3(TradeTypeEnum tradeType,
}
String url = this.getPayBaseUrl() + tradeType.getBasePartnerUrl();
- String response = this.postV3(url, GSON.toJson(request));
+ String response = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class);
}
@@ -777,7 +788,7 @@ public WxPayUnifiedOrderV3Result unifiedOrderV3(TradeTypeEnum tradeType, WxPayUn
}
String url = this.getPayBaseUrl() + tradeType.getPartnerUrl();
- String response = this.postV3(url, GSON.toJson(request));
+ String response = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class);
}
@@ -790,14 +801,14 @@ public CombineTransactionsResult combine(TradeTypeEnum tradeType, CombineTransac
request.setCombineMchid(this.getConfig().getMchId());
}
String url = this.getPayBaseUrl() + tradeType.getCombineUrl();
- String response = this.postV3(url, GSON.toJson(request));
+ String response = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
return GSON.fromJson(response, CombineTransactionsResult.class);
}
@Override
public T combineTransactions(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException {
CombineTransactionsResult result = this.combine(tradeType, request);
- return result.getPayInfo(tradeType, request.getCombineAppid(), request.getCombineAppid(), this.getConfig().getPrivateKey());
+ return result.getPayInfo(tradeType, request.getCombineAppid(), request.getCombineMchid(), this.getConfig().getPrivateKey());
}
@Override
@@ -1103,7 +1114,7 @@ public WxPayApplyBillV3Result applyTradeBill(WxPayApplyTradeBillV3Request reques
} else {
url = String.format("%s/v3/bill/tradebill?bill_date=%s&bill_type=%s&tar_type=%s", this.getPayBaseUrl(), request.getBillDate(), request.getBillType(), request.getTarType());
}
- String response = this.getV3(url);
+ String response = this.getV3WithWechatPaySerial(url);
return GSON.fromJson(response, WxPayApplyBillV3Result.class);
}
@@ -1115,7 +1126,7 @@ public WxPayApplyBillV3Result applyFundFlowBill(WxPayApplyFundFlowBillV3Request
} else {
url = String.format("%s/v3/bill/fundflowbill?bill_date=%s&account_type=%s&tar_type=%s", this.getPayBaseUrl(), request.getBillDate(), request.getAccountType(), request.getTarType());
}
- String response = this.getV3(url);
+ String response = this.getV3WithWechatPaySerial(url);
return GSON.fromJson(response, WxPayApplyBillV3Result.class);
}
@@ -1144,7 +1155,7 @@ public WxPayCodepayResult codepay(WxPayCodepayRequest request) throws WxPayExcep
request.setMchid(this.getConfig().getMchId());
}
String url = String.format("%s/v3/pay/transactions/codepay", this.getPayBaseUrl());
- String body = this.postV3(url, GSON.toJson(request));
+ String body = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
return GSON.fromJson(body, WxPayCodepayResult.class);
}
@@ -1170,7 +1181,7 @@ public WxPayOrderReverseV3Result reverseOrderV3(WxPayOrderReverseV3Request reque
}
// 拼接参数请求路径并发送
String url = String.format("%s/v3/pay/transactions/out-trade-no/%s/reverse", this.getPayBaseUrl(), request.getOutTradeNo());
- String response = this.postV3(url, GSON.toJson(request));
+ String response = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
return GSON.fromJson(response, WxPayOrderReverseV3Result.class);
}
@@ -1300,7 +1311,7 @@ public String queryComment(WxPayQueryCommentRequest request) throws WxPayExcepti
@Override
public WxPayFaceAuthInfoResult getWxPayFaceAuthInfo(WxPayFaceAuthInfoRequest request) throws WxPayException {
if (StringUtils.isEmpty(request.getSignType())) {
- request.setSignType(WxPayConstants.SignType.MD5);
+ request.setSignType(SignType.MD5);
}
request.checkAndSign(this.getConfig());
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
index e62dc9c053..f43c887c16 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java
@@ -48,8 +48,7 @@ public QueryTransferBatchesResult transferBatchesBatchId(QueryTransferBatchesReq
if (request.getNeedQueryDetail()) {
url = String.format("%s/v3/transfer/batches/batch-id/%s?need_query_detail=true&offset=%s&limit=%s&detail_status=%s",
this.payService.getPayBaseUrl(), request.getBatchId(), request.getOffset(), request.getLimit(), request.getDetailStatus());
- }
- else {
+ } else {
url = String.format("%s/v3/transfer/batches/batch-id/%s?need_query_detail=false",
this.payService.getPayBaseUrl(), request.getBatchId());
}
@@ -70,8 +69,7 @@ public QueryTransferBatchesResult transferBatchesOutBatchNo(QueryTransferBatches
if (request.getNeedQueryDetail()) {
url = String.format("%s/v3/transfer/batches/out-batch-no/%s?need_query_detail=true&offset=%s&limit=%s&detail_status=%s",
this.payService.getPayBaseUrl(), request.getOutBatchNo(), request.getOffset(), request.getLimit(), request.getDetailStatus());
- }
- else {
+ } else {
url = String.format("%s/v3/transfer/batches/out-batch-no/%s?need_query_detail=false",
this.payService.getPayBaseUrl(), request.getOutBatchNo());
}
@@ -85,4 +83,45 @@ public TransferBatchDetailResult transferBatchesOutBatchNoDetail(String outBatch
String result = this.payService.getV3(url);
return GSON.fromJson(result, TransferBatchDetailResult.class);
}
+
+ @Override
+ public TransferBillsResult transferBills(TransferBillsRequest request) throws WxPayException {
+ String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills", this.payService.getPayBaseUrl());
+ if (request.getUserName() != null && request.getUserName().length() > 0) {
+ X509Certificate validCertificate = this.payService.getConfig().getVerifier().getValidCertificate();
+ RsaCryptoUtil.encryptFields(request, validCertificate);
+ }
+ String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request));
+ return GSON.fromJson(result, TransferBillsResult.class);
+ }
+
+ @Override
+ public TransferBillsCancelResult transformBillsCancel(String outBillNo) throws WxPayException {
+ String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/%s/cancel",
+ this.payService.getPayBaseUrl(), outBillNo);
+ String result = this.payService.postV3(url, "");
+
+ return GSON.fromJson(result, TransferBillsCancelResult.class);
+ }
+
+ @Override
+ public TransferBillsGetResult getBillsByOutBillNo(String outBillNo) throws WxPayException {
+ String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/%s",
+ this.payService.getPayBaseUrl(), outBillNo);
+ String result = this.payService.getV3(url);
+ return GSON.fromJson(result, TransferBillsGetResult.class);
+ }
+
+ @Override
+ public TransferBillsGetResult getBillsByTransferBillNo(String transferBillNo) throws WxPayException {
+ String url = String.format("%s/v3/fund-app/mch-transfer/transfer-bills/transfer-bill-no/%s",
+ this.payService.getPayBaseUrl(), transferBillNo);
+ String result = this.payService.getV3(url);
+ return GSON.fromJson(result, TransferBillsGetResult.class);
+ }
+
+ @Override
+ public TransferBillsNotifyResult parseTransferBillsNotifyResult(String notifyData, SignatureHeader header) throws WxPayException {
+ return this.payService.baseParseOrderNotifyV3Result(notifyData, header, TransferBillsNotifyResult.class, TransferBillsNotifyResult.DecryptNotifyResult.class);
+ }
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
index e2b6d43fa1..dcd70b5239 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceApacheHttpImpl.java
@@ -1,10 +1,12 @@
package com.github.binarywang.wxpay.service.impl;
import com.github.binarywang.wxpay.bean.WxPayApiData;
+import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.v3.WxPayV3DownloadHttpGet;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.json.GsonParser;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
@@ -38,11 +40,13 @@
*
* @author Binary Wang
*/
+@Slf4j
public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
private static final String ACCEPT = "Accept";
private static final String CONTENT_TYPE = "Content-Type";
private static final String APPLICATION_JSON = "application/json";
+ private static final String WECHAT_PAY_SERIAL = "Wechatpay-Serial";
@Override
public byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException {
@@ -53,7 +57,7 @@ public byte[] postForBytes(String url, String requestStr, boolean useKey) throws
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
final byte[] bytes = EntityUtils.toByteArray(response.getEntity());
final String responseData = Base64.getEncoder().encodeToString(bytes);
- this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseData);
+ this.logRequestAndResponse(url, requestStr, responseData);
wxApiData.set(new WxPayApiData(url, requestStr, responseData, null));
return bytes;
}
@@ -61,7 +65,7 @@ public byte[] postForBytes(String url, String requestStr, boolean useKey) throws
httpPost.releaseConnection();
}
} catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
+ this.logError( url, requestStr, e);
wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage()));
throw new WxPayException(e.getMessage(), e);
}
@@ -75,7 +79,7 @@ public String post(String url, String requestStr, boolean useKey) throws WxPayEx
try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
String responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
- this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString);
+ this.logRequestAndResponse(url, requestStr, responseString);
if (this.getConfig().isIfSaveApiData()) {
wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
}
@@ -85,7 +89,7 @@ public String post(String url, String requestStr, boolean useKey) throws WxPayEx
httpPost.releaseConnection();
}
} catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
+ this.logError(url, requestStr, e);
if (this.getConfig().isIfSaveApiData()) {
wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage()));
}
@@ -95,11 +99,14 @@ public String post(String url, String requestStr, boolean useKey) throws WxPayEx
@Override
public String postV3(String url, String requestStr) throws WxPayException {
- CloseableHttpClient httpClient = this.createApiV3HttpClient();
HttpPost httpPost = this.createHttpPost(url, requestStr);
- httpPost.addHeader(ACCEPT, APPLICATION_JSON);
- httpPost.addHeader(CONTENT_TYPE, APPLICATION_JSON);
- try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
+ this.configureRequest(httpPost);
+ return this.requestV3(url, requestStr, httpPost);
+ }
+
+ private String requestV3(String url, String requestStr, HttpRequestBase httpRequestBase) throws WxPayException {
+ try (CloseableHttpClient httpClient = this.createApiV3HttpClient();
+ CloseableHttpResponse response = httpClient.execute(httpRequestBase)) {
//v3已经改为通过状态码判断200 204 成功
int statusCode = response.getStatusLine().getStatusCode();
//post方法有可能会没有返回值的情况
@@ -109,7 +116,7 @@ public String postV3(String url, String requestStr) throws WxPayException {
}
if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) {
- this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString);
+ this.logRequestAndResponse(url, requestStr, responseString);
return responseString;
}
@@ -117,63 +124,26 @@ public String postV3(String url, String requestStr) throws WxPayException {
JsonObject jsonObject = GsonParser.parse(responseString);
throw convertException(jsonObject);
} catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
+ this.logError(url, requestStr, e);
throw (e instanceof WxPayException) ? (WxPayException) e : new WxPayException(e.getMessage(), e);
} finally {
- httpPost.releaseConnection();
+ httpRequestBase.releaseConnection();
}
-
-
}
@Override
public String patchV3(String url, String requestStr) throws WxPayException {
- CloseableHttpClient httpClient = this.createApiV3HttpClient();
HttpPatch httpPatch = new HttpPatch(url);
httpPatch.setEntity(this.createEntry(requestStr));
-
- httpPatch.setConfig(RequestConfig.custom()
- .setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout())
- .setConnectTimeout(this.getConfig().getHttpConnectionTimeout())
- .setSocketTimeout(this.getConfig().getHttpTimeout())
- .build());
-
- httpPatch.addHeader(ACCEPT, APPLICATION_JSON);
- httpPatch.addHeader(CONTENT_TYPE, APPLICATION_JSON);
- try (CloseableHttpResponse response = httpClient.execute(httpPatch)) {
- //v3已经改为通过状态码判断200 204 成功
- int statusCode = response.getStatusLine().getStatusCode();
- //post方法有可能会没有返回值的情况
- String responseString = null;
- if (response.getEntity() != null) {
- responseString = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
- }
-
- if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) {
- this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString);
- return responseString;
- }
-
- //有错误提示信息返回
- JsonObject jsonObject = GsonParser.parse(responseString);
- throw convertException(jsonObject);
- } catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
- throw (e instanceof WxPayException) ? (WxPayException) e : new WxPayException(e.getMessage(), e);
- } finally {
- httpPatch.releaseConnection();
- }
+ return this.requestV3(url, requestStr, httpPatch);
}
@Override
public String postV3WithWechatpaySerial(String url, String requestStr) throws WxPayException {
- CloseableHttpClient httpClient = this.createApiV3HttpClient();
HttpPost httpPost = this.createHttpPost(url, requestStr);
- httpPost.addHeader(ACCEPT, APPLICATION_JSON);
- httpPost.addHeader(CONTENT_TYPE, APPLICATION_JSON);
- String serialNumber = getConfig().getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
- httpPost.addHeader("Wechatpay-Serial", serialNumber);
- try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
+ this.configureRequest(httpPost);
+ try (CloseableHttpClient httpClient = this.createApiV3HttpClient();
+ CloseableHttpResponse response = httpClient.execute(httpPost)) {
//v3已经改为通过状态码判断200 204 成功
int statusCode = response.getStatusLine().getStatusCode();
String responseString = "{}";
@@ -183,7 +153,7 @@ public String postV3WithWechatpaySerial(String url, String requestStr) throws Wx
}
if (HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) {
- this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString);
+ this.logRequestAndResponse(url, requestStr, responseString);
return responseString;
}
@@ -191,8 +161,7 @@ public String postV3WithWechatpaySerial(String url, String requestStr) throws Wx
JsonObject jsonObject = GsonParser.parse(responseString);
throw convertException(jsonObject);
} catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
- e.printStackTrace();
+ this.logError(url, requestStr, e);
throw (e instanceof WxPayException) ? (WxPayException) e : new WxPayException(e.getMessage(), e);
} finally {
httpPost.releaseConnection();
@@ -201,19 +170,17 @@ public String postV3WithWechatpaySerial(String url, String requestStr) throws Wx
@Override
public String postV3(String url, HttpPost httpPost) throws WxPayException {
+ String serialNumber = getWechatPaySerial(getConfig());
+ httpPost.addHeader(WECHAT_PAY_SERIAL, serialNumber);
return this.requestV3(url, httpPost);
}
@Override
public String requestV3(String url, HttpRequestBase httpRequest) throws WxPayException {
- httpRequest.setConfig(RequestConfig.custom()
- .setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout())
- .setConnectTimeout(this.getConfig().getHttpConnectionTimeout())
- .setSocketTimeout(this.getConfig().getHttpTimeout())
- .build());
+ this.configureRequest(httpRequest);
- CloseableHttpClient httpClient = this.createApiV3HttpClient();
- try (CloseableHttpResponse response = httpClient.execute(httpRequest)) {
+ try (CloseableHttpClient httpClient = this.createApiV3HttpClient();
+ CloseableHttpResponse response = httpClient.execute(httpRequest)) {
//v3已经改为通过状态码判断200 204 成功
int statusCode = response.getStatusLine().getStatusCode();
//post方法有可能会没有返回值的情况
@@ -231,7 +198,7 @@ public String requestV3(String url, HttpRequestBase httpRequest) throws WxPayExc
JsonObject jsonObject = GsonParser.parse(responseString);
throw convertException(jsonObject);
} catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【异常信息】:{}", url, e.getMessage());
+ log.error("\n【请求地址】:{}\n【异常信息】:{}", url, e.getMessage());
throw (e instanceof WxPayException) ? (WxPayException) e : new WxPayException(e.getMessage(), e);
} finally {
httpRequest.releaseConnection();
@@ -240,35 +207,34 @@ public String requestV3(String url, HttpRequestBase httpRequest) throws WxPayExc
@Override
public String getV3(String url) throws WxPayException {
+ if (this.getConfig().isStrictlyNeedWechatPaySerial()) {
+ return getV3WithWechatPaySerial(url);
+ }
HttpGet httpGet = new HttpGet(url);
- httpGet.addHeader(ACCEPT, APPLICATION_JSON);
- httpGet.addHeader(CONTENT_TYPE, APPLICATION_JSON);
return this.requestV3(url, httpGet);
}
@Override
public String getV3WithWechatPaySerial(String url) throws WxPayException {
HttpGet httpGet = new HttpGet(url);
- httpGet.addHeader(ACCEPT, APPLICATION_JSON);
- httpGet.addHeader(CONTENT_TYPE, APPLICATION_JSON);
- String serialNumber = getConfig().getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
- httpGet.addHeader("Wechatpay-Serial", serialNumber);
return this.requestV3(url, httpGet);
}
@Override
public InputStream downloadV3(String url) throws WxPayException {
- CloseableHttpClient httpClient = this.createApiV3HttpClient();
HttpGet httpGet = new WxPayV3DownloadHttpGet(url);
httpGet.addHeader(ACCEPT, ContentType.WILDCARD.getMimeType());
- try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+ String serialNumber = getWechatPaySerial(getConfig());
+ httpGet.addHeader(WECHAT_PAY_SERIAL, serialNumber);
+ try (CloseableHttpClient httpClient = this.createApiV3HttpClient();
+ CloseableHttpResponse response = httpClient.execute(httpGet)) {
//v3已经改为通过状态码判断200 204 成功
int statusCode = response.getStatusLine().getStatusCode();
Header contentType = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
boolean isJsonContentType = Objects.nonNull(contentType) && ContentType.APPLICATION_JSON.getMimeType()
.equals(ContentType.parse(String.valueOf(contentType.getValue())).getMimeType());
if ((HttpStatus.SC_OK == statusCode || HttpStatus.SC_NO_CONTENT == statusCode) && !isJsonContentType) {
- this.log.info("\n【请求地址】:{}\n", url);
+ log.info("\n【请求地址】:{}\n", url);
return response.getEntity().getContent();
}
@@ -278,7 +244,7 @@ public InputStream downloadV3(String url) throws WxPayException {
JsonObject jsonObject = GsonParser.parse(responseString);
throw convertException(jsonObject);
} catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【异常信息】:{}", url, e.getMessage());
+ log.error("\n【请求地址】:{}\n【异常信息】:{}", url, e.getMessage());
throw (e instanceof WxPayException) ? (WxPayException) e : new WxPayException(e.getMessage(), e);
} finally {
httpGet.releaseConnection();
@@ -290,19 +256,28 @@ public String putV3(String url, String requestStr) throws WxPayException {
HttpPut httpPut = new HttpPut(url);
StringEntity entity = this.createEntry(requestStr);
httpPut.setEntity(entity);
- httpPut.addHeader(ACCEPT, APPLICATION_JSON);
- httpPut.addHeader(CONTENT_TYPE, APPLICATION_JSON);
return requestV3(url, httpPut);
}
@Override
public String deleteV3(String url) throws WxPayException {
HttpDelete httpDelete = new HttpDelete(url);
- httpDelete.addHeader(ACCEPT, APPLICATION_JSON);
- httpDelete.addHeader(CONTENT_TYPE, APPLICATION_JSON);
return requestV3(url, httpDelete);
}
+ private void configureRequest(HttpRequestBase request) {
+ String serialNumber = getWechatPaySerial(getConfig());
+ request.addHeader(ACCEPT, APPLICATION_JSON);
+ request.addHeader(CONTENT_TYPE, APPLICATION_JSON);
+ request.addHeader(WECHAT_PAY_SERIAL, serialNumber);
+
+ request.setConfig(RequestConfig.custom()
+ .setConnectionRequestTimeout(this.getConfig().getHttpConnectionTimeout())
+ .setConnectTimeout(this.getConfig().getHttpConnectionTimeout())
+ .setSocketTimeout(this.getConfig().getHttpTimeout())
+ .build());
+ }
+
private CloseableHttpClient createApiV3HttpClient() throws WxPayException {
CloseableHttpClient apiV3HttpClient = this.getConfig().getApiV3HttpClient();
if (null == apiV3HttpClient) {
@@ -368,9 +343,8 @@ private void initSSLContext(HttpClientBuilder httpClientBuilder) throws WxPayExc
new DefaultHostnameVerifier()));
}
-
private WxPayException convertException(JsonObject jsonObject) {
- //todo 这里考虑使用新的适用于V3的异常
+ //TODO 这里考虑使用新的适用于V3的异常
JsonElement codeElement = jsonObject.get("code");
String code = codeElement == null ? null : codeElement.getAsString();
String message = jsonObject.get("message").getAsString();
@@ -380,4 +354,22 @@ private WxPayException convertException(JsonObject jsonObject) {
return wxPayException;
}
+ /**
+ * 兼容微信支付公钥模式
+ */
+ private String getWechatPaySerial(WxPayConfig wxPayConfig) {
+ if (StringUtils.isNotBlank(wxPayConfig.getPublicKeyId())) {
+ return wxPayConfig.getPublicKeyId();
+ }
+
+ return wxPayConfig.getVerifier().getValidCertificate().getSerialNumber().toString(16).toUpperCase();
+ }
+
+ private void logRequestAndResponse(String url, String requestStr, String responseStr) {
+ log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseStr);
+ }
+
+ private void logError(String url, String requestStr, Exception e) {
+ log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
+ }
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
index 5e6d23eac9..7c2f1e82c0 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/WxPayServiceJoddHttpImpl.java
@@ -9,6 +9,7 @@
import jodd.http.ProxyInfo.ProxyType;
import jodd.http.net.SSLSocketHttpConnectionProvider;
import jodd.http.net.SocketHttpConnectionProvider;
+import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
@@ -24,6 +25,7 @@
*
* @author Binary Wang
*/
+@Slf4j
public class WxPayServiceJoddHttpImpl extends BaseWxPayServiceImpl {
@Override
public byte[] postForBytes(String url, String requestStr, boolean useKey) throws WxPayException {
@@ -31,13 +33,13 @@ public byte[] postForBytes(String url, String requestStr, boolean useKey) throws
HttpRequest request = this.buildHttpRequest(url, requestStr, useKey);
byte[] responseBytes = request.send().bodyBytes();
final String responseString = Base64.getEncoder().encodeToString(responseBytes);
- this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseString);
+ log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据(Base64编码后)】:{}", url, requestStr, responseString);
if (this.getConfig().isIfSaveApiData()) {
wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
}
return responseBytes;
} catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
+ log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage()));
throw new WxPayException(e.getMessage(), e);
}
@@ -49,13 +51,13 @@ public String post(String url, String requestStr, boolean useKey) throws WxPayEx
HttpRequest request = this.buildHttpRequest(url, requestStr, useKey);
String responseString = this.getResponseString(request.send());
- this.log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString);
+ log.info("\n【请求地址】:{}\n【请求数据】:{}\n【响应数据】:{}", url, requestStr, responseString);
if (this.getConfig().isIfSaveApiData()) {
wxApiData.set(new WxPayApiData(url, requestStr, responseString, null));
}
return responseString;
} catch (Exception e) {
- this.log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
+ log.error("\n【请求地址】:{}\n【请求数据】:{}\n【异常信息】:{}", url, requestStr, e.getMessage());
wxApiData.set(new WxPayApiData(url, requestStr, null, e.getMessage()));
throw new WxPayException(e.getMessage(), e);
}
@@ -146,9 +148,9 @@ private HttpRequest buildHttpRequest(String url, String requestStr, boolean useK
private String getResponseString(HttpResponse response) throws WxPayException {
try {
- this.log.debug("【微信服务器响应头信息】:\n{}", response.toString(false));
+ log.debug("【微信服务器响应头信息】:\n{}", response.toString(false));
} catch (NullPointerException e) {
- this.log.warn("HttpResponse.toString() 居然抛出空指针异常了", e);
+ log.warn("HttpResponse.toString() 居然抛出空指针异常了", e);
}
String responseString = response.bodyText();
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/PublicCertificateVerifier.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/PublicCertificateVerifier.java
index 9344fc6f83..8c9c4f3569 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/PublicCertificateVerifier.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/PublicCertificateVerifier.java
@@ -9,6 +9,8 @@ public class PublicCertificateVerifier implements Verifier{
private final PublicKey publicKey;
+ private Verifier certificateVerifier;
+
private final X509PublicCertificate publicCertificate;
public PublicCertificateVerifier(PublicKey publicKey, String publicId) {
@@ -16,8 +18,15 @@ public PublicCertificateVerifier(PublicKey publicKey, String publicId) {
this.publicCertificate = new X509PublicCertificate(publicKey, publicId);
}
+ public void setOtherVerifier(Verifier verifier) {
+ this.certificateVerifier = verifier;
+ }
+
@Override
public boolean verify(String serialNumber, byte[] message, String signature) {
+ if (!serialNumber.contains("PUB_KEY_ID") && this.certificateVerifier != null) {
+ return this.certificateVerifier.verify(serialNumber, message, signature);
+ }
try {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initVerify(publicKey);
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Verifier.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Verifier.java
index 49f92e2f5b..22676601c0 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Verifier.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/Verifier.java
@@ -7,4 +7,6 @@ public interface Verifier {
X509Certificate getValidCertificate();
+
+ default void setOtherVerifier(Verifier verifier) {};
}
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/TransferServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/TransferServiceImplTest.java
index 7f89bd4721..10c2a5da66 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/TransferServiceImplTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/TransferServiceImplTest.java
@@ -2,6 +2,7 @@
import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest;
import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest;
+import com.github.binarywang.wxpay.bean.transfer.TransferBillsRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.testbase.ApiTestModule;
@@ -73,4 +74,32 @@ public void testTransferBatchesOutBatchNo() throws WxPayException {
public void testTransferBatchesOutBatchNoDetail() throws WxPayException {
log.info("商家明细单号查询明细单:{}", this.payService.getTransferService().transferBatchesOutBatchNoDetail("1655447999520", "1655447989156"));
}
+
+ @Test
+ public void testTransferBills() throws WxPayException {
+ TransferBillsRequest transferBillsRequest = TransferBillsRequest.newBuilder()
+ .appid("wxf636efh5xxxxx")
+ .outBillNo("1655447989156")
+ .transferSceneId("1005")
+ .transferAmount(100)
+ .transferRemark("测试转账")
+ .openid("oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI")
+ .userName("测试用户").build();
+ log.info("发起商家转账:{}", this.payService.getTransferService().transferBills(transferBillsRequest));
+ }
+
+ @Test
+ public void testTransformBillsCancel() throws WxPayException {
+ log.info("撤销商家转账:{}", this.payService.getTransferService().transformBillsCancel("123456"));
+ }
+
+ @Test
+ public void testGetBillsByOutBillNo() throws WxPayException {
+ log.info("商户单号查询转账单:{}", this.payService.getTransferService().getBillsByOutBillNo("123456"));
+ }
+
+ @Test
+ public void testGetBillsByTransferBillNo() throws WxPayException {
+ log.info("微信单号查询转账单:{}", this.payService.getTransferService().getBillsByTransferBillNo("123456"));
+ }
}
diff --git a/weixin-java-qidian/pom.xml b/weixin-java-qidian/pom.xml
index 31a0c21dc0..08bdfa0af6 100644
--- a/weixin-java-qidian/pom.xml
+++ b/weixin-java-qidian/pom.xml
@@ -7,7 +7,7 @@
com.github.binarywang
wx-java
- 4.7.0
+ 4.7.5.B
weixin-java-qidian
|