Skip to content

Commit ca567ce

Browse files
authored
🆕 #3618 【微信支付】增加境外微信支付的支持
1 parent a6825a6 commit ca567ce

File tree

7 files changed

+508
-0
lines changed

7 files changed

+508
-0
lines changed

weixin-java-pay/OVERSEAS_PAY.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# 境外微信支付(Overseas WeChat Pay)支持
2+
3+
本次更新添加了境外微信支付的支持,解决了 [Issue #3618](https://github.com/binarywang/WxJava/issues/3618) 中提到的问题。
4+
5+
## 问题背景
6+
7+
境外微信支付需要使用新的API接口地址和额外的参数:
8+
- 使用不同的基础URL: `https://apihk.mch.weixin.qq.com`
9+
- 需要额外的参数: `trade_type``merchant_category_code`
10+
- 使用不同的API端点: `/global/v3/transactions/*`
11+
12+
## 新增功能
13+
14+
### 1. GlobalTradeTypeEnum
15+
新的枚举类,定义了境外支付的交易类型和对应的API端点:
16+
- `APP`: `/global/v3/transactions/app`
17+
- `JSAPI`: `/global/v3/transactions/jsapi`
18+
- `NATIVE`: `/global/v3/transactions/native`
19+
- `H5`: `/global/v3/transactions/h5`
20+
21+
### 2. WxPayUnifiedOrderV3GlobalRequest
22+
扩展的请求类,包含境外支付必需的额外字段:
23+
- `trade_type`: 交易类型 (JSAPI, APP, NATIVE, H5)
24+
- `merchant_category_code`: 商户类目代码(境外商户必填)
25+
26+
### 3. 新的服务方法
27+
- `createOrderV3Global()`: 创建境外支付订单
28+
- `unifiedOrderV3Global()`: 境外统一下单接口
29+
30+
## 使用示例
31+
32+
### JSAPI支付示例
33+
```java
34+
// 创建境外支付请求
35+
WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest();
36+
request.setOutTradeNo(RandomUtils.getRandomStr());
37+
request.setDescription("境外商品购买");
38+
request.setNotifyUrl("https://your-domain.com/notify");
39+
40+
// 设置金额
41+
WxPayUnifiedOrderV3GlobalRequest.Amount amount = new WxPayUnifiedOrderV3GlobalRequest.Amount();
42+
amount.setCurrency(WxPayConstants.CurrencyType.CNY);
43+
amount.setTotal(100); // 1元,单位为分
44+
request.setAmount(amount);
45+
46+
// 设置支付者
47+
WxPayUnifiedOrderV3GlobalRequest.Payer payer = new WxPayUnifiedOrderV3GlobalRequest.Payer();
48+
payer.setOpenid("用户的openid");
49+
request.setPayer(payer);
50+
51+
// 设置境外支付必需的参数
52+
request.setTradeType("JSAPI");
53+
request.setMerchantCategoryCode("5812"); // 商户类目代码
54+
55+
// 调用境外支付接口
56+
WxPayUnifiedOrderV3Result.JsapiResult result = payService.createOrderV3Global(
57+
GlobalTradeTypeEnum.JSAPI,
58+
request
59+
);
60+
```
61+
62+
### APP支付示例
63+
```java
64+
WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest();
65+
// ... 设置基础信息 ...
66+
67+
request.setTradeType("APP");
68+
request.setMerchantCategoryCode("5812");
69+
request.setPayer(new WxPayUnifiedOrderV3GlobalRequest.Payer()); // APP支付不需要openid
70+
71+
WxPayUnifiedOrderV3Result.AppResult result = payService.createOrderV3Global(
72+
GlobalTradeTypeEnum.APP,
73+
request
74+
);
75+
```
76+
77+
### NATIVE支付示例
78+
```java
79+
WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest();
80+
// ... 设置基础信息 ...
81+
82+
request.setTradeType("NATIVE");
83+
request.setMerchantCategoryCode("5812");
84+
request.setPayer(new WxPayUnifiedOrderV3GlobalRequest.Payer());
85+
86+
String codeUrl = payService.createOrderV3Global(
87+
GlobalTradeTypeEnum.NATIVE,
88+
request
89+
);
90+
```
91+
92+
## 配置说明
93+
94+
境外支付使用相同的 `WxPayConfig` 配置,无需特殊设置:
95+
96+
```java
97+
WxPayConfig config = new WxPayConfig();
98+
config.setAppId("你的AppId");
99+
config.setMchId("你的境外商户号");
100+
config.setMchKey("你的商户密钥");
101+
config.setNotifyUrl("https://your-domain.com/notify");
102+
103+
// V3相关配置
104+
config.setPrivateKeyPath("你的私钥文件路径");
105+
config.setCertSerialNo("你的商户证书序列号");
106+
config.setApiV3Key("你的APIv3密钥");
107+
```
108+
109+
**注意**: 境外支付会自动使用 `https://apihk.mch.weixin.qq.com` 作为基础URL,无需手动设置。
110+
111+
## 兼容性
112+
113+
- 完全向后兼容,不影响现有的国内支付功能
114+
- 使用相同的配置类和结果类
115+
- 遵循现有的代码风格和架构模式
116+
117+
## 参考文档
118+
119+
- [境外微信支付文档](https://pay.weixin.qq.com/doc/global/v3/zh/4013014223)
120+
- [原始Issue #3618](https://github.com/binarywang/WxJava/issues/3618)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.github.binarywang.wxpay.bean.request;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import lombok.Data;
5+
import lombok.EqualsAndHashCode;
6+
import lombok.NoArgsConstructor;
7+
import lombok.experimental.Accessors;
8+
9+
import java.io.Serializable;
10+
11+
/**
12+
* <pre>
13+
* 境外微信支付统一下单请求参数对象.
14+
* 参考文档:https://pay.weixin.qq.com/doc/global/v3/zh/4013014223
15+
* </pre>
16+
*
17+
* @author Binary Wang
18+
*/
19+
@Data
20+
@NoArgsConstructor
21+
@Accessors(chain = true)
22+
@EqualsAndHashCode(callSuper = true)
23+
public class WxPayUnifiedOrderV3GlobalRequest extends WxPayUnifiedOrderV3Request implements Serializable {
24+
private static final long serialVersionUID = 1L;
25+
26+
/**
27+
* <pre>
28+
* 字段名:交易类型
29+
* 变量名:trade_type
30+
* 是否必填:是
31+
* 类型:string[1,16]
32+
* 描述:
33+
* 交易类型,取值如下:
34+
* JSAPI--JSAPI支付
35+
* NATIVE--Native支付
36+
* APP--APP支付
37+
* H5--H5支付
38+
* 示例值:JSAPI
39+
* </pre>
40+
*/
41+
@SerializedName(value = "trade_type")
42+
private String tradeType;
43+
44+
/**
45+
* <pre>
46+
* 字段名:商户类目
47+
* 变量名:merchant_category_code
48+
* 是否必填:是
49+
* 类型:string[1,32]
50+
* 描述:
51+
* 商户类目,境外商户必填
52+
* 示例值:5812
53+
* </pre>
54+
*/
55+
@SerializedName(value = "merchant_category_code")
56+
private String merchantCategoryCode;
57+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.github.binarywang.wxpay.bean.result.enums;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
/**
7+
* 境外微信支付方式
8+
* Overseas WeChat Pay trade types with global endpoints
9+
*
10+
* @author Binary Wang
11+
*/
12+
@Getter
13+
@AllArgsConstructor
14+
public enum GlobalTradeTypeEnum {
15+
/**
16+
* APP
17+
*/
18+
APP("/global/v3/transactions/app"),
19+
/**
20+
* JSAPI 或 小程序
21+
*/
22+
JSAPI("/global/v3/transactions/jsapi"),
23+
/**
24+
* NATIVE
25+
*/
26+
NATIVE("/global/v3/transactions/native"),
27+
/**
28+
* H5
29+
*/
30+
H5("/global/v3/transactions/h5");
31+
32+
/**
33+
* 境外下单url
34+
*/
35+
private final String url;
36+
}

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.github.binarywang.wxpay.bean.request.*;
77
import com.github.binarywang.wxpay.bean.result.*;
88
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
9+
import com.github.binarywang.wxpay.bean.result.enums.GlobalTradeTypeEnum;
910
import com.github.binarywang.wxpay.bean.transfer.TransferBillsNotifyResult;
1011
import com.github.binarywang.wxpay.config.WxPayConfig;
1112
import com.github.binarywang.wxpay.constant.WxPayConstants;
@@ -640,6 +641,17 @@ public interface WxPayService {
640641
*/
641642
<T> T createPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException;
642643

644+
/**
645+
* 境外微信支付调用统一下单接口,并组装生成支付所需参数对象.
646+
*
647+
* @param <T> 请使用{@link WxPayUnifiedOrderV3Result}里的内部类或字段
648+
* @param tradeType the global trade type
649+
* @param request 境外统一下单请求参数
650+
* @return 返回 {@link WxPayUnifiedOrderV3Result}里的内部类或字段
651+
* @throws WxPayException the wx pay exception
652+
*/
653+
<T> T createOrderV3Global(GlobalTradeTypeEnum tradeType, WxPayUnifiedOrderV3GlobalRequest request) throws WxPayException;
654+
643655
/**
644656
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
645657
*
@@ -660,6 +672,16 @@ public interface WxPayService {
660672
*/
661673
WxPayUnifiedOrderV3Result unifiedOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request) throws WxPayException;
662674

675+
/**
676+
* 境外微信支付在发起支付前,需要调用统一下单接口,获取"预支付交易会话标识"
677+
*
678+
* @param tradeType the global trade type
679+
* @param request 境外请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
680+
* @return the wx pay unified order result
681+
* @throws WxPayException the wx pay exception
682+
*/
683+
WxPayUnifiedOrderV3Result unifiedOrderV3Global(GlobalTradeTypeEnum tradeType, WxPayUnifiedOrderV3GlobalRequest request) throws WxPayException;
684+
663685
/**
664686
* <pre>
665687
* 合单支付API(APP支付、JSAPI支付、H5支付、NATIVE支付).

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.github.binarywang.wxpay.bean.request.*;
1212
import com.github.binarywang.wxpay.bean.result.*;
1313
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
14+
import com.github.binarywang.wxpay.bean.result.enums.GlobalTradeTypeEnum;
1415
import com.github.binarywang.wxpay.bean.transfer.TransferBillsNotifyResult;
1516
import com.github.binarywang.wxpay.config.WxPayConfig;
1617
import com.github.binarywang.wxpay.config.WxPayConfigHolder;
@@ -746,6 +747,14 @@ public <T> T createPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOr
746747
return result.getPayInfo(tradeType, appId, request.getSubMchId(), this.getConfig().getPrivateKey());
747748
}
748749

750+
@Override
751+
public <T> T createOrderV3Global(GlobalTradeTypeEnum tradeType, WxPayUnifiedOrderV3GlobalRequest request) throws WxPayException {
752+
WxPayUnifiedOrderV3Result result = this.unifiedOrderV3Global(tradeType, request);
753+
// Convert GlobalTradeTypeEnum to TradeTypeEnum for getPayInfo method
754+
TradeTypeEnum domesticTradeType = TradeTypeEnum.valueOf(tradeType.name());
755+
return result.getPayInfo(domesticTradeType, request.getAppid(), request.getMchid(), this.getConfig().getPrivateKey());
756+
}
757+
749758
@Override
750759
public WxPayUnifiedOrderV3Result unifiedPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException {
751760
if (StringUtils.isBlank(request.getSpAppid())) {
@@ -790,6 +799,28 @@ public WxPayUnifiedOrderV3Result unifiedOrderV3(TradeTypeEnum tradeType, WxPayUn
790799
return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class);
791800
}
792801

802+
@Override
803+
public WxPayUnifiedOrderV3Result unifiedOrderV3Global(GlobalTradeTypeEnum tradeType, WxPayUnifiedOrderV3GlobalRequest request) throws WxPayException {
804+
if (StringUtils.isBlank(request.getAppid())) {
805+
request.setAppid(this.getConfig().getAppId());
806+
}
807+
if (StringUtils.isBlank(request.getMchid())) {
808+
request.setMchid(this.getConfig().getMchId());
809+
}
810+
if (StringUtils.isBlank(request.getNotifyUrl())) {
811+
request.setNotifyUrl(this.getConfig().getNotifyUrl());
812+
}
813+
if (StringUtils.isBlank(request.getTradeType())) {
814+
request.setTradeType(tradeType.name());
815+
}
816+
817+
// Use global WeChat Pay base URL for overseas payments
818+
String globalBaseUrl = "https://apihk.mch.weixin.qq.com";
819+
String url = globalBaseUrl + tradeType.getUrl();
820+
String response = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
821+
return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class);
822+
}
823+
793824
@Override
794825
public CombineTransactionsResult combine(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException {
795826
if (StringUtils.isBlank(request.getCombineAppid())) {

0 commit comments

Comments
 (0)