From 14c343599b51929766acdbe2dc8188bddf190ead Mon Sep 17 00:00:00 2001 From: mmj781050866 <781050866@qq.com> Date: Wed, 16 Aug 2023 11:04:35 +0800 Subject: [PATCH 1/2] =?UTF-8?q?:art:=20#3111=20=E3=80=90=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E5=88=86=E8=B4=A6=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0=E5=AD=90=E5=95=86=E6=88=B7?= =?UTF-8?q?=E5=BA=94=E7=94=A8ID=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bean/profitsharing/v3/ProfitSharingReceiver.java | 10 ++++++++++ .../bean/profitsharing/v3/ProfitSharingRequest.java | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/v3/ProfitSharingReceiver.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/v3/ProfitSharingReceiver.java index 0ed3c4ecfb..10ba3821b8 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/v3/ProfitSharingReceiver.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/v3/ProfitSharingReceiver.java @@ -44,6 +44,16 @@ public class ProfitSharingReceiver implements Serializable { @SerializedName("appid") private String appid; + /** + *
+   * 字段名:子商户应用ID
+   * 是否必填:否
+   * 描述:子商户的公众账号ID,分账接收方类型包含PERSONAL_SUB_OPENID时必填
+   * 
+ */ + @SerializedName("sub_appid") + private String subAppid; + /** *
    * 字段名:分账接收方类型
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/v3/ProfitSharingRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/v3/ProfitSharingRequest.java
index 5bcf30f8bc..44e72dec2c 100644
--- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/v3/ProfitSharingRequest.java
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/v3/ProfitSharingRequest.java
@@ -44,6 +44,16 @@ public class ProfitSharingRequest implements Serializable {
   @SerializedName("appid")
   private String appid;
 
+  /**
+   * 
+   * 字段名:子商户应用ID
+   * 是否必填:否
+   * 描述:子商户的公众账号ID,分账接收方类型包含PERSONAL_SUB_OPENID时必填
+   * 
+ */ + @SerializedName("sub_appid") + private String subAppid; + /** *
    * 字段名:微信订单号

From c3b623372d6e4ce598f6feec83904ece42ef1774 Mon Sep 17 00:00:00 2001
From: wangkai 
Date: Wed, 16 Aug 2023 17:53:55 +0800
Subject: [PATCH 2/2] =?UTF-8?q?=E3=80=90=E5=BE=AE=E4=BF=A1=E6=94=AF?=
 =?UTF-8?q?=E4=BB=98=E3=80=91=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1=E6=94=AF?=
 =?UTF-8?q?=E4=BB=98=E6=9C=8D=E5=8A=A1=E5=95=86V3=E7=89=88=E6=9C=AC?=
 =?UTF-8?q?=E8=AF=B4=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 others/weixin-java-config/README.md           | 424 ++++++++++++++++++
 .../wx-java-pay-spring-boot-starter/README.md |  22 +-
 2 files changed, 437 insertions(+), 9 deletions(-)
 create mode 100644 others/weixin-java-config/README.md

diff --git a/others/weixin-java-config/README.md b/others/weixin-java-config/README.md
new file mode 100644
index 0000000000..aa70de9579
--- /dev/null
+++ b/others/weixin-java-config/README.md
@@ -0,0 +1,424 @@
+# weixin-java-config
+1.目录说明:多配置文件目录
+
+2.项目多配置集锦
+```yml
+wechat:
+  pay: #微信服务商支付
+    configs:
+    - appId: wxe97b2x9c2b3d #spAppId
+      mchId: 16486610 #服务商商户
+      subAppId: wx118cexxe3c07679 #子appId
+      subMchId: 16496705 #子商户
+      apiV3Key: Dc1DBwSc094jAKDGR5aqqb7PTHr #apiV3密钥
+      privateKeyPath: classpath:cert/apiclient_key.pem #服务商证书文件,apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径(可以配置绝对路径)
+      privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
+  miniapp: #小程序
+    configs:
+    - appid: wx118ce3xxc76ccg
+      secret: 8a132a276ee2f8fb58b1ed8f2
+      token: #微信小程序消息服务器配置的token
+      aesKey: #微信小程序消息服务器配置的EncodingAESKey
+      msgDataFormat: JSON
+  cp: #企业微信
+    corpId: wwa3be8efd2addfgj
+    appConfigs:
+    - agentId: 10001 #客户联系
+      secret: T5fTj1n-sBAT4rKNW5c9IYNfPdXZ8-oGol5tX
+      token: 2bSNqTcLtFYBUa1u2
+      aesKey: AXazu2Xyw44SNY1x8go2phn9p9B2O9oiEfqPN
+    - agentId: 10003 #会话内容存档
+      secret: xIpum7Yt4NMXcyxdzcQ2l_46BG4QIQDR57MhA
+      token:
+      aesKey:
+    - agentId: 3010011 #打卡
+      secret: 3i2Mhfusifaw_-04bMYI8OoKGxPe9mDALbUxV
+      token:
+      aesKey:
+    - agentId: 19998 #通讯录同步
+      secret: rNyDae0Pg-3d-wqTd_ozMSJfF0DEjTCz3b_pr
+      token: xUke8yZciAZqImGZ
+      aesKey: EUTVyArqJcfnpFiudxjRpuOexNqBoPbwrNG3R
+    - agentId: 20000 #微盘
+      secret: D-TVMvUji7PZZdjhZOSgiy2MTuBd0OCdvI_zi
+      token:
+      aesKey:
+```
+
+3.主要代码
+###### 1)微信服务商支付
+```java
+@Data
+@ConfigurationProperties(prefix = "wechat.pay")
+public class WxPayProperties {
+
+    private List configs;
+
+    @Getter
+    @Setter
+    public static class Config {
+
+        private String appId;
+        private String mchId;
+        private String subAppId;
+        private String subMchId;
+        private String apiV3Key;
+        private String privateKeyPath;
+        private String privateCertPath;
+
+    }
+
+}
+```
+```java
+@Configuration
+@EnableConfigurationProperties(WxPayProperties.class)
+@AllArgsConstructor
+public class WxPayConfiguration {
+
+    private WxPayProperties properties;
+
+    @Bean
+    public WxPayService wxPayService() {
+
+        // 多配置
+        WxPayService wxPayService = new WxPayServiceImpl();
+        Map payConfigs = this.properties.getConfigs().stream().map(config -> {
+            WxPayConfig payConfig = new WxPayConfig();
+            payConfig.setAppId(StringUtils.trimToNull(config.getAppId()));
+            payConfig.setMchId(StringUtils.trimToNull(config.getMchId()));
+            payConfig.setSubAppId(StringUtils.trimToNull(config.getSubAppId()));
+            payConfig.setSubMchId(StringUtils.trimToNull(config.getSubMchId()));
+            payConfig.setApiV3Key(StringUtils.trimToNull(config.getApiV3Key()));
+            payConfig.setPrivateKeyPath(StringUtils.trimToNull(config.getPrivateKeyPath()));
+            payConfig.setPrivateCertPath(StringUtils.trimToNull(config.getPrivateCertPath()));
+
+            // 可以指定是否使用沙箱环境
+            payConfig.setUseSandboxEnv(false);
+            return payConfig;
+        }).collect(Collectors.toMap(config -> config.getSubMchId(), a -> a));
+
+        wxPayService.setMultiConfig(payConfigs);
+        return wxPayService;
+    }
+
+}
+```
+###### 2)微信小程序
+```java
+@Setter
+@Getter
+@ConfigurationProperties(prefix = "wechat.miniapp")
+public class WxMaProperties {
+
+    private List configs;
+
+    @Data
+    public static class Config {
+
+        /**
+         * 设置微信小程序的appid
+         */
+        private String appid;
+
+        /**
+         * 设置微信小程序的Secret
+         */
+        private String secret;
+
+        /**
+         * 设置微信小程序消息服务器配置的token
+         */
+        private String token;
+
+        /**
+         * 设置微信小程序消息服务器配置的EncodingAESKey
+         */
+        private String aesKey;
+
+        /**
+         * 消息格式,XML或者JSON
+         */
+        private String msgDataFormat;
+
+    }
+
+}
+```
+```java
+@Configuration
+@EnableConfigurationProperties(WxMaProperties.class)
+public class WxMaConfiguration {
+
+    private WxMaProperties properties;
+    private static Map maServices;
+    private static final Map routers = Maps.newHashMap();
+
+    @Autowired
+    public WxMaConfiguration(WxMaProperties properties) {
+        this.properties = properties;
+    }
+
+    public static WxMaService getMaService(String appId) {
+        WxMaService wxService = maServices.get(appId);
+        Optional.ofNullable(wxService).orElseThrow(() -> new RuntimeException("没有配置appId"));
+        return wxService;
+    }
+
+    public static WxMaMessageRouter getRouter(String appId) {
+        return routers.get(appId);
+    }
+
+    @PostConstruct
+    public void init() {
+        List configs = this.properties.getConfigs();
+        if (configs == null) {
+            return;
+        }
+
+        maServices = configs.stream().map(a -> {
+                    // 多配置
+                    WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
+                    config.setAppid(a.getAppid());
+                    config.setSecret(a.getSecret());
+                    config.setToken(a.getToken());
+                    config.setAesKey(a.getAesKey());
+                    config.setMsgDataFormat(a.getMsgDataFormat());
+
+                    WxMaService service = new WxMaServiceImpl();
+                    service.setWxMaConfig(config);
+
+                    routers.put(a.getAppid(), this.newRouter(service));
+                    return service;
+                }).collect(Collectors.toMap(s -> s.getWxMaConfig().getAppid(), a -> a));
+    }
+
+    private WxMaMessageRouter newRouter(WxMaService service) {
+        final WxMaMessageRouter router = new WxMaMessageRouter(service);
+        router
+                .rule().handler(logHandler).next()
+                .rule().async(false).content("订阅消息").handler(subscribeMsgHandler).end()
+                .rule().async(false).content("文本").handler(textHandler).end()
+                .rule().async(false).content("图片").handler(picHandler).end()
+                .rule().async(false).content("二维码").handler(qrcodeHandler).end();
+        return router;
+    }
+
+    private final WxMaMessageHandler subscribeMsgHandler = (wxMessage, context, service, sessionManager) -> {
+        service.getMsgService().sendSubscribeMsg(WxMaSubscribeMessage.builder()
+                .templateId("此处更换为自己的模板id")
+                .data(Lists.newArrayList(
+                        new WxMaSubscribeMessage.MsgData("keyword1", "339208499")))
+                .toUser(wxMessage.getFromUser())
+                .build());
+        return null;
+    };
+
+    private final WxMaMessageHandler logHandler = (wxMessage, context, service, sessionManager) -> {
+        log.info("收到logHandler消息:" + wxMessage.toString());
+        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("收到信息为:" + wxMessage.toJson())
+                .toUser(wxMessage.getFromUser()).build());
+        return null;
+    };
+
+    private final WxMaMessageHandler textHandler = (wxMessage, context, service, sessionManager) -> {
+        log.info("收到textHandler消息:" + wxMessage.toString());
+        service.getMsgService().sendKefuMsg(WxMaKefuMessage.newTextBuilder().content("回复文本消息")
+                .toUser(wxMessage.getFromUser()).build());
+        return null;
+    };
+
+    private final WxMaMessageHandler picHandler = (wxMessage, context, service, sessionManager) -> {
+        log.info("收到picHandler消息:" + wxMessage.toString());
+        try {
+            WxMediaUploadResult uploadResult = service.getMediaService()
+                    .uploadMedia("image", "png",
+                            ClassLoader.getSystemResourceAsStream("tmp.png"));
+            service.getMsgService().sendKefuMsg(
+                    WxMaKefuMessage
+                            .newImageBuilder()
+                            .mediaId(uploadResult.getMediaId())
+                            .toUser(wxMessage.getFromUser())
+                            .build());
+        } catch (WxErrorException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    };
+
+    private final WxMaMessageHandler qrcodeHandler = (wxMessage, context, service, sessionManager) -> {
+        log.info("收到qrcodeHandler消息:" + wxMessage.toString());
+        try {
+            final File file = service.getQrcodeService().createQrcode("123", 430);
+            WxMediaUploadResult uploadResult = service.getMediaService().uploadMedia("image", file);
+            service.getMsgService().sendKefuMsg(
+                    WxMaKefuMessage
+                            .newImageBuilder()
+                            .mediaId(uploadResult.getMediaId())
+                            .toUser(wxMessage.getFromUser())
+                            .build());
+        } catch (WxErrorException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    };
+
+}
+```
+###### 3)企业微信
+```java
+@Getter
+@Setter
+@ConfigurationProperties(prefix = "wechat.cp")
+public class WxCpProperties {
+
+  /**
+   * 设置企业微信的corpId
+   */
+  private String corpId;
+
+  private List appConfigs;
+
+  @Getter
+  @Setter
+  public static class AppConfig {
+    /**
+     * 设置企业微信应用的AgentId
+     */
+    private Integer agentId;
+
+    /**
+     * 设置企业微信应用的Secret
+     */
+    private String secret;
+
+    /**
+     * 设置企业微信应用的token
+     */
+    private String token;
+
+    /**
+     * 设置企业微信应用的EncodingAESKey
+     */
+    private String aesKey;
+
+  }
+
+}
+```
+```java
+@Configuration
+@EnableConfigurationProperties(WxCpProperties.class)
+public class WxCpConfiguration {
+
+    private LogHandler logHandler;
+    private NullHandler nullHandler;
+    private LocationHandler locationHandler;
+    private MenuHandler menuHandler;
+    private MsgHandler msgHandler;
+    private UnsubscribeHandler unsubscribeHandler;
+    private SubscribeHandler subscribeHandler;
+
+    private WxCpProperties properties;
+
+    private static Map routers = Maps.newHashMap();
+    private static Map cpServices = Maps.newHashMap();
+
+    @Autowired
+    public WxCpConfiguration(LogHandler logHandler, NullHandler nullHandler, LocationHandler locationHandler,
+                             MenuHandler menuHandler, MsgHandler msgHandler, UnsubscribeHandler unsubscribeHandler,
+                             SubscribeHandler subscribeHandler, WxCpProperties properties) {
+        this.logHandler = logHandler;
+        this.nullHandler = nullHandler;
+        this.locationHandler = locationHandler;
+        this.menuHandler = menuHandler;
+        this.msgHandler = msgHandler;
+        this.unsubscribeHandler = unsubscribeHandler;
+        this.subscribeHandler = subscribeHandler;
+        this.properties = properties;
+    }
+
+
+    public static Map getRouters() {
+        return routers;
+    }
+
+
+    public static WxCpService getCpService(Integer agentId) {
+        WxCpService cpService = cpServices.get(agentId);
+        Optional.ofNullable(cpService).orElseThrow(() -> new RuntimeException("cpService不能为空"));
+        return cpService;
+    }
+
+    @PostConstruct
+    public void initServices() {
+        cpServices = this.properties.getAppConfigs().stream().map(a -> {
+            val configStorage = new WxCpDefaultConfigImpl();
+            configStorage.setCorpId(this.properties.getCorpId());
+            configStorage.setAgentId(a.getAgentId());
+            configStorage.setCorpSecret(a.getSecret());
+            configStorage.setToken(a.getToken());
+            configStorage.setAesKey(a.getAesKey());
+
+            val service = new WxCpServiceImpl();
+            service.setWxCpConfigStorage(configStorage);
+
+            routers.put(a.getAgentId(), this.newRouter(service));
+            return service;
+        }).collect(Collectors.toMap(service -> service.getWxCpConfigStorage().getAgentId(), a -> a));
+    }
+
+    private WxCpMessageRouter newRouter(WxCpService wxCpService) {
+        final val newRouter = new WxCpMessageRouter(wxCpService);
+
+        // 记录所有事件的日志 (异步执行)
+        newRouter.rule().handler(this.logHandler).next();
+
+        // 自定义菜单事件
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.MenuButtonType.CLICK).handler(this.menuHandler).end();
+
+        // 点击菜单链接事件(这里使用了一个空的处理器,可以根据自己需要进行扩展)
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler).end();
+
+        // 关注事件
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler)
+            .end();
+
+        // 取消关注事件
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.EventType.UNSUBSCRIBE)
+            .handler(this.unsubscribeHandler).end();
+
+        // 上报地理位置事件
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.EventType.LOCATION).handler(this.locationHandler)
+            .end();
+
+        // 接收地理位置消息
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
+            .handler(this.locationHandler).end();
+
+        // 扫码事件(这里使用了一个空的处理器,可以根据自己需要进行扩展)
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxConsts.EventType.SCAN).handler(this.nullHandler).end();
+
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxCpConsts.EventType.CHANGE_CONTACT).handler(new ContactChangeHandler()).end();
+
+        newRouter.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
+            .event(WxCpConsts.EventType.ENTER_AGENT).handler(new EnterAgentHandler()).end();
+
+        // 默认
+        newRouter.rule().async(false).handler(this.msgHandler).end();
+
+        return newRouter;
+    }
+
+}
+```
+4.其他请移步wiki:[GitHub wiki](https://github.com/Wechat-Group/WxJava/wiki)
diff --git a/spring-boot-starters/wx-java-pay-spring-boot-starter/README.md b/spring-boot-starters/wx-java-pay-spring-boot-starter/README.md
index 8d96901f24..d87a38fb9c 100644
--- a/spring-boot-starters/wx-java-pay-spring-boot-starter/README.md
+++ b/spring-boot-starters/wx-java-pay-spring-boot-starter/README.md
@@ -15,8 +15,6 @@ wx:
     appId: 
     mchId: 
     mchKey: 
-    subAppId:
-    subMchId:
     keyPath:
 ```
 ###### 2)V3版本
@@ -30,10 +28,16 @@ wx:
     privateKeyPath: classpath:cert/apiclient_key.pem #apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径
     privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
 ```
-
-
-
-
-
-
-
+###### 3)V3服务商版本
+```yml
+wx:
+  pay: #微信服务商支付
+    configs:
+    - appId: wxe97b2x9c2b3d #spAppId
+      mchId: 16486610 #服务商商户
+      subAppId: wx118cexxe3c07679 #子appId
+      subMchId: 16496705 #子商户
+      apiV3Key: Dc1DBwSc094jAKDGR5aqqb7PTHr #apiV3密钥
+      privateKeyPath: classpath:cert/apiclient_key.pem #服务商证书文件,apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径(可以配置绝对路径)
+      privateCertPath: classpath:cert/apiclient_cert.pem #apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径
+```