|
| 1 | +# wx-java-pay-multi-spring-boot-starter |
| 2 | + |
| 3 | +## 快速开始 |
| 4 | + |
| 5 | +本starter支持微信支付多公众号关联配置,适用于以下场景: |
| 6 | +- 一个服务商需要为多个公众号提供支付服务 |
| 7 | +- 一个系统需要支持多个公众号的支付业务 |
| 8 | +- 需要根据不同的appId动态切换支付配置 |
| 9 | + |
| 10 | +## 使用说明 |
| 11 | + |
| 12 | +### 1. 引入依赖 |
| 13 | + |
| 14 | +在项目的 `pom.xml` 中添加以下依赖: |
| 15 | + |
| 16 | +```xml |
| 17 | +<dependency> |
| 18 | + <groupId>com.github.binarywang</groupId> |
| 19 | + <artifactId>wx-java-pay-multi-spring-boot-starter</artifactId> |
| 20 | + <version>${version}</version> |
| 21 | +</dependency> |
| 22 | +``` |
| 23 | + |
| 24 | +### 2. 添加配置 |
| 25 | + |
| 26 | +在 `application.yml` 或 `application.properties` 中配置多个公众号的支付信息。 |
| 27 | + |
| 28 | +#### 配置示例(application.yml) |
| 29 | + |
| 30 | +##### V2版本配置 |
| 31 | +```yml |
| 32 | +wx: |
| 33 | + pay: |
| 34 | + configs: |
| 35 | + # 配置1 - 可以使用appId作为key |
| 36 | + wx1234567890abcdef: |
| 37 | + appId: wx1234567890abcdef |
| 38 | + mchId: 1234567890 |
| 39 | + mchKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| 40 | + keyPath: classpath:cert/app1/apiclient_cert.p12 |
| 41 | + notifyUrl: https://example.com/pay/notify |
| 42 | + # 配置2 - 也可以使用自定义标识作为key |
| 43 | + config2: |
| 44 | + appId: wx9876543210fedcba |
| 45 | + mchId: 9876543210 |
| 46 | + mchKey: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy |
| 47 | + keyPath: classpath:cert/app2/apiclient_cert.p12 |
| 48 | + notifyUrl: https://example.com/pay/notify |
| 49 | +``` |
| 50 | +
|
| 51 | +##### V3版本配置 |
| 52 | +```yml |
| 53 | +wx: |
| 54 | + pay: |
| 55 | + configs: |
| 56 | + # 公众号1配置 |
| 57 | + wx1234567890abcdef: |
| 58 | + appId: wx1234567890abcdef |
| 59 | + mchId: 1234567890 |
| 60 | + apiV3Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| 61 | + certSerialNo: 62C6CEAA360BCxxxxxxxxxxxxxxx |
| 62 | + privateKeyPath: classpath:cert/app1/apiclient_key.pem |
| 63 | + privateCertPath: classpath:cert/app1/apiclient_cert.pem |
| 64 | + notifyUrl: https://example.com/pay/notify |
| 65 | + # 公众号2配置 |
| 66 | + wx9876543210fedcba: |
| 67 | + appId: wx9876543210fedcba |
| 68 | + mchId: 9876543210 |
| 69 | + apiV3Key: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy |
| 70 | + certSerialNo: 73D7DFBB471CDxxxxxxxxxxxxxxx |
| 71 | + privateKeyPath: classpath:cert/app2/apiclient_key.pem |
| 72 | + privateCertPath: classpath:cert/app2/apiclient_cert.pem |
| 73 | + notifyUrl: https://example.com/pay/notify |
| 74 | +``` |
| 75 | +
|
| 76 | +##### V3服务商版本配置 |
| 77 | +```yml |
| 78 | +wx: |
| 79 | + pay: |
| 80 | + configs: |
| 81 | + # 服务商为公众号1提供服务 |
| 82 | + config1: |
| 83 | + appId: wxe97b2x9c2b3d # 服务商appId |
| 84 | + mchId: 16486610 # 服务商商户号 |
| 85 | + subAppId: wx118cexxe3c07679 # 子商户公众号appId |
| 86 | + subMchId: 16496705 # 子商户号 |
| 87 | + apiV3Key: Dc1DBwSc094jAKDGR5aqqb7PTHr |
| 88 | + privateKeyPath: classpath:cert/apiclient_key.pem |
| 89 | + privateCertPath: classpath:cert/apiclient_cert.pem |
| 90 | + # 服务商为公众号2提供服务 |
| 91 | + config2: |
| 92 | + appId: wxe97b2x9c2b3d # 服务商appId(可以相同) |
| 93 | + mchId: 16486610 # 服务商商户号(可以相同) |
| 94 | + subAppId: wx228dexxf4d18890 # 子商户公众号appId(不同) |
| 95 | + subMchId: 16496706 # 子商户号(不同) |
| 96 | + apiV3Key: Dc1DBwSc094jAKDGR5aqqb7PTHr |
| 97 | + privateKeyPath: classpath:cert/apiclient_key.pem |
| 98 | + privateCertPath: classpath:cert/apiclient_cert.pem |
| 99 | +``` |
| 100 | +
|
| 101 | +#### 配置示例(application.properties) |
| 102 | +
|
| 103 | +```properties |
| 104 | +# 公众号1配置 |
| 105 | +wx.pay.configs.wx1234567890abcdef.app-id=wx1234567890abcdef |
| 106 | +wx.pay.configs.wx1234567890abcdef.mch-id=1234567890 |
| 107 | +wx.pay.configs.wx1234567890abcdef.apiv3-key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
| 108 | +wx.pay.configs.wx1234567890abcdef.cert-serial-no=62C6CEAA360BCxxxxxxxxxxxxxxx |
| 109 | +wx.pay.configs.wx1234567890abcdef.private-key-path=classpath:cert/app1/apiclient_key.pem |
| 110 | +wx.pay.configs.wx1234567890abcdef.private-cert-path=classpath:cert/app1/apiclient_cert.pem |
| 111 | +wx.pay.configs.wx1234567890abcdef.notify-url=https://example.com/pay/notify |
| 112 | + |
| 113 | +# 公众号2配置 |
| 114 | +wx.pay.configs.wx9876543210fedcba.app-id=wx9876543210fedcba |
| 115 | +wx.pay.configs.wx9876543210fedcba.mch-id=9876543210 |
| 116 | +wx.pay.configs.wx9876543210fedcba.apiv3-key=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy |
| 117 | +wx.pay.configs.wx9876543210fedcba.cert-serial-no=73D7DFBB471CDxxxxxxxxxxxxxxx |
| 118 | +wx.pay.configs.wx9876543210fedcba.private-key-path=classpath:cert/app2/apiclient_key.pem |
| 119 | +wx.pay.configs.wx9876543210fedcba.private-cert-path=classpath:cert/app2/apiclient_cert.pem |
| 120 | +wx.pay.configs.wx9876543210fedcba.notify-url=https://example.com/pay/notify |
| 121 | +``` |
| 122 | + |
| 123 | +### 3. 使用示例 |
| 124 | + |
| 125 | +自动注入的类型:`WxPayMultiServices` |
| 126 | + |
| 127 | +```java |
| 128 | +import com.binarywang.spring.starter.wxjava.pay.service.WxPayMultiServices; |
| 129 | +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; |
| 130 | +import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result; |
| 131 | +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; |
| 132 | +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; |
| 133 | +import com.github.binarywang.wxpay.service.WxPayService; |
| 134 | +import org.springframework.beans.factory.annotation.Autowired; |
| 135 | +import org.springframework.stereotype.Service; |
| 136 | + |
| 137 | +@Service |
| 138 | +public class PayService { |
| 139 | + @Autowired |
| 140 | + private WxPayMultiServices wxPayMultiServices; |
| 141 | + |
| 142 | + /** |
| 143 | + * 为不同的公众号创建支付订单 |
| 144 | + * |
| 145 | + * @param configKey 配置标识(即 wx.pay.configs.<configKey> 中的 key,可以是 appId 或自定义标识) |
| 146 | + */ |
| 147 | + public void createOrder(String configKey, String openId, Integer totalFee, String body) throws Exception { |
| 148 | + // 根据配置标识获取对应的WxPayService |
| 149 | + WxPayService wxPayService = wxPayMultiServices.getWxPayService(configKey); |
| 150 | + |
| 151 | + if (wxPayService == null) { |
| 152 | + throw new IllegalArgumentException("未找到配置标识对应的微信支付配置: " + configKey); |
| 153 | + } |
| 154 | + |
| 155 | + // 使用WxPayService进行支付操作 |
| 156 | + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); |
| 157 | + request.setOutTradeNo(generateOutTradeNo()); |
| 158 | + request.setDescription(body); |
| 159 | + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(totalFee)); |
| 160 | + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(openId)); |
| 161 | + request.setNotifyUrl(wxPayService.getConfig().getNotifyUrl()); |
| 162 | + |
| 163 | + // V3统一下单 |
| 164 | + WxPayUnifiedOrderV3Result.JsapiResult result = |
| 165 | + wxPayService.createOrderV3(TradeTypeEnum.JSAPI, request); |
| 166 | + |
| 167 | + // 返回给前端用于调起支付 |
| 168 | + // ... |
| 169 | + } |
| 170 | + |
| 171 | + /** |
| 172 | + * 服务商模式示例 |
| 173 | + */ |
| 174 | + public void serviceProviderExample(String configKey) throws Exception { |
| 175 | + // 使用配置标识获取WxPayService |
| 176 | + WxPayService wxPayService = wxPayMultiServices.getWxPayService(configKey); |
| 177 | + |
| 178 | + if (wxPayService == null) { |
| 179 | + throw new IllegalArgumentException("未找到配置: " + configKey); |
| 180 | + } |
| 181 | + |
| 182 | + // 获取子商户的配置信息 |
| 183 | + String subAppId = wxPayService.getConfig().getSubAppId(); |
| 184 | + String subMchId = wxPayService.getConfig().getSubMchId(); |
| 185 | + |
| 186 | + // 进行支付操作 |
| 187 | + // ... |
| 188 | + } |
| 189 | + |
| 190 | + /** |
| 191 | + * 查询订单示例 |
| 192 | + * |
| 193 | + * @param configKey 配置标识(即 wx.pay.configs.<configKey> 中的 key) |
| 194 | + */ |
| 195 | + public void queryOrder(String configKey, String outTradeNo) throws Exception { |
| 196 | + WxPayService wxPayService = wxPayMultiServices.getWxPayService(configKey); |
| 197 | + |
| 198 | + if (wxPayService == null) { |
| 199 | + throw new IllegalArgumentException("未找到配置标识对应的微信支付配置: " + configKey); |
| 200 | + } |
| 201 | + |
| 202 | + // 查询订单 |
| 203 | + WxPayOrderQueryV3Result result = wxPayService.queryOrderV3(null, outTradeNo); |
| 204 | + // 处理查询结果 |
| 205 | + // ... |
| 206 | + } |
| 207 | + |
| 208 | + private String generateOutTradeNo() { |
| 209 | + // 生成商户订单号 |
| 210 | + return "ORDER_" + System.currentTimeMillis(); |
| 211 | + } |
| 212 | +} |
| 213 | +``` |
| 214 | + |
| 215 | +### 4. 配置说明 |
| 216 | + |
| 217 | +#### 必填配置项 |
| 218 | + |
| 219 | +| 配置项 | 说明 | 示例 | |
| 220 | +|--------|------|------| |
| 221 | +| appId | 公众号或小程序的appId | wx1234567890abcdef | |
| 222 | +| mchId | 商户号 | 1234567890 | |
| 223 | + |
| 224 | +#### V2版本配置项 |
| 225 | + |
| 226 | +| 配置项 | 说明 | 是否必填 | |
| 227 | +|--------|------|----------| |
| 228 | +| mchKey | 商户密钥 | 是(V2) | |
| 229 | +| keyPath | p12证书文件路径 | 部分接口需要 | |
| 230 | + |
| 231 | +#### V3版本配置项 |
| 232 | + |
| 233 | +| 配置项 | 说明 | 是否必填 | |
| 234 | +|--------|------|----------| |
| 235 | +| apiV3Key | API V3密钥 | 是(V3) | |
| 236 | +| certSerialNo | 证书序列号 | 是(V3) | |
| 237 | +| privateKeyPath | apiclient_key.pem路径 | 是(V3) | |
| 238 | +| privateCertPath | apiclient_cert.pem路径 | 是(V3) | |
| 239 | + |
| 240 | +#### 服务商模式配置项 |
| 241 | + |
| 242 | +| 配置项 | 说明 | 是否必填 | |
| 243 | +|--------|------|----------| |
| 244 | +| subAppId | 子商户公众号appId | 服务商模式必填 | |
| 245 | +| subMchId | 子商户号 | 服务商模式必填 | |
| 246 | + |
| 247 | +#### 可选配置项 |
| 248 | + |
| 249 | +| 配置项 | 说明 | 默认值 | |
| 250 | +|--------|------|--------| |
| 251 | +| notifyUrl | 支付结果通知URL | 无 | |
| 252 | +| refundNotifyUrl | 退款结果通知URL | 无 | |
| 253 | +| serviceId | 微信支付分serviceId | 无 | |
| 254 | +| payScoreNotifyUrl | 支付分回调地址 | 无 | |
| 255 | +| payScorePermissionNotifyUrl | 支付分授权回调地址 | 无 | |
| 256 | +| useSandboxEnv | 是否使用沙箱环境 | false | |
| 257 | +| apiHostUrl | 自定义API主机地址 | https://api.mch.weixin.qq.com | |
| 258 | +| strictlyNeedWechatPaySerial | 是否所有V3请求都添加序列号头 | false | |
| 259 | +| fullPublicKeyModel | 是否完全使用公钥模式 | false | |
| 260 | +| publicKeyId | 公钥ID | 无 | |
| 261 | +| publicKeyPath | 公钥文件路径 | 无 | |
| 262 | + |
| 263 | +## 常见问题 |
| 264 | + |
| 265 | +### 1. 如何选择配置的key? |
| 266 | + |
| 267 | +配置的key(即 `wx.pay.configs.<configKey>` 中的 `<configKey>` 部分)可以自由选择: |
| 268 | +- 可以使用appId作为key(如 `wx.pay.configs.wx1234567890abcdef`),这样调用 `getWxPayService("wx1234567890abcdef")` 时就像直接用 appId 获取服务 |
| 269 | +- 可以使用自定义标识(如 `wx.pay.configs.config1`),调用时使用 `getWxPayService("config1")` |
| 270 | + |
| 271 | +**注意**:`getWxPayService(configKey)` 方法的参数是配置文件中定义的 key,而不是 appId。只有当你使用 appId 作为配置 key 时,才能直接传入 appId。 |
| 272 | + |
| 273 | +### 2. V2和V3配置可以混用吗? |
| 274 | + |
| 275 | +可以。不同的配置可以使用不同的版本,例如: |
| 276 | +```yml |
| 277 | +wx: |
| 278 | + pay: |
| 279 | + configs: |
| 280 | + app1: # V2配置 |
| 281 | + appId: wx111 |
| 282 | + mchId: 111 |
| 283 | + mchKey: xxx |
| 284 | + app2: # V3配置 |
| 285 | + appId: wx222 |
| 286 | + mchId: 222 |
| 287 | + apiV3Key: yyy |
| 288 | + privateKeyPath: xxx |
| 289 | +``` |
| 290 | +
|
| 291 | +### 3. 证书文件如何放置? |
| 292 | +
|
| 293 | +证书文件可以放在以下位置: |
| 294 | +- `src/main/resources` 目录下,使用 `classpath:` 前缀 |
| 295 | +- 服务器绝对路径,直接填写完整路径 |
| 296 | +- 建议为不同配置使用不同的目录组织证书 |
| 297 | + |
| 298 | +### 4. 服务商模式如何配置? |
| 299 | + |
| 300 | +服务商模式需要同时配置服务商信息和子商户信息: |
| 301 | +- `appId` 和 `mchId` 填写服务商的信息 |
| 302 | +- `subAppId` 和 `subMchId` 填写子商户的信息 |
| 303 | + |
| 304 | +## 注意事项 |
| 305 | + |
| 306 | +1. **配置安全**:生产环境中的密钥、证书等敏感信息,建议使用配置中心或环境变量管理 |
| 307 | +2. **证书管理**:不同公众号的证书文件要分开存放,避免混淆 |
| 308 | +3. **懒加载**:WxPayService 实例采用懒加载策略,只有在首次调用时才会创建 |
| 309 | +4. **线程安全**:WxPayMultiServices 的实现是线程安全的 |
| 310 | +5. **配置更新**:如需动态更新配置,可调用 `removeWxPayService(configKey)` 方法移除缓存的实例 |
| 311 | + |
| 312 | +## 更多信息 |
| 313 | + |
| 314 | +- [WxJava 项目首页](https://github.com/Wechat-Group/WxJava) |
| 315 | +- [微信支付官方文档](https://pay.weixin.qq.com/wiki/doc/api/) |
| 316 | +- [微信支付V3接口文档](https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml) |
0 commit comments