diff --git a/weixin-java-pay/OVERSEAS_PAY.md b/weixin-java-pay/OVERSEAS_PAY.md new file mode 100644 index 0000000000..b9da9814f9 --- /dev/null +++ b/weixin-java-pay/OVERSEAS_PAY.md @@ -0,0 +1,120 @@ +# 境外微信支付(Overseas WeChat Pay)支持 + +本次更新添加了境外微信支付的支持,解决了 [Issue #3618](https://github.com/binarywang/WxJava/issues/3618) 中提到的问题。 + +## 问题背景 + +境外微信支付需要使用新的API接口地址和额外的参数: +- 使用不同的基础URL: `https://apihk.mch.weixin.qq.com` +- 需要额外的参数: `trade_type` 和 `merchant_category_code` +- 使用不同的API端点: `/global/v3/transactions/*` + +## 新增功能 + +### 1. GlobalTradeTypeEnum +新的枚举类,定义了境外支付的交易类型和对应的API端点: +- `APP`: `/global/v3/transactions/app` +- `JSAPI`: `/global/v3/transactions/jsapi` +- `NATIVE`: `/global/v3/transactions/native` +- `H5`: `/global/v3/transactions/h5` + +### 2. WxPayUnifiedOrderV3GlobalRequest +扩展的请求类,包含境外支付必需的额外字段: +- `trade_type`: 交易类型 (JSAPI, APP, NATIVE, H5) +- `merchant_category_code`: 商户类目代码(境外商户必填) + +### 3. 新的服务方法 +- `createOrderV3Global()`: 创建境外支付订单 +- `unifiedOrderV3Global()`: 境外统一下单接口 + +## 使用示例 + +### JSAPI支付示例 +```java +// 创建境外支付请求 +WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest(); +request.setOutTradeNo(RandomUtils.getRandomStr()); +request.setDescription("境外商品购买"); +request.setNotifyUrl("https://your-domain.com/notify"); + +// 设置金额 +WxPayUnifiedOrderV3GlobalRequest.Amount amount = new WxPayUnifiedOrderV3GlobalRequest.Amount(); +amount.setCurrency(WxPayConstants.CurrencyType.CNY); +amount.setTotal(100); // 1元,单位为分 +request.setAmount(amount); + +// 设置支付者 +WxPayUnifiedOrderV3GlobalRequest.Payer payer = new WxPayUnifiedOrderV3GlobalRequest.Payer(); +payer.setOpenid("用户的openid"); +request.setPayer(payer); + +// 设置境外支付必需的参数 +request.setTradeType("JSAPI"); +request.setMerchantCategoryCode("5812"); // 商户类目代码 + +// 调用境外支付接口 +WxPayUnifiedOrderV3Result.JsapiResult result = payService.createOrderV3Global( + GlobalTradeTypeEnum.JSAPI, + request +); +``` + +### APP支付示例 +```java +WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest(); +// ... 设置基础信息 ... + +request.setTradeType("APP"); +request.setMerchantCategoryCode("5812"); +request.setPayer(new WxPayUnifiedOrderV3GlobalRequest.Payer()); // APP支付不需要openid + +WxPayUnifiedOrderV3Result.AppResult result = payService.createOrderV3Global( + GlobalTradeTypeEnum.APP, + request +); +``` + +### NATIVE支付示例 +```java +WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest(); +// ... 设置基础信息 ... + +request.setTradeType("NATIVE"); +request.setMerchantCategoryCode("5812"); +request.setPayer(new WxPayUnifiedOrderV3GlobalRequest.Payer()); + +String codeUrl = payService.createOrderV3Global( + GlobalTradeTypeEnum.NATIVE, + request +); +``` + +## 配置说明 + +境外支付使用相同的 `WxPayConfig` 配置,无需特殊设置: + +```java +WxPayConfig config = new WxPayConfig(); +config.setAppId("你的AppId"); +config.setMchId("你的境外商户号"); +config.setMchKey("你的商户密钥"); +config.setNotifyUrl("https://your-domain.com/notify"); + +// V3相关配置 +config.setPrivateKeyPath("你的私钥文件路径"); +config.setCertSerialNo("你的商户证书序列号"); +config.setApiV3Key("你的APIv3密钥"); +``` + +**注意**: 境外支付会自动使用 `https://apihk.mch.weixin.qq.com` 作为基础URL,无需手动设置。 + +## 兼容性 + +- 完全向后兼容,不影响现有的国内支付功能 +- 使用相同的配置类和结果类 +- 遵循现有的代码风格和架构模式 + +## 参考文档 + +- [境外微信支付文档](https://pay.weixin.qq.com/doc/global/v3/zh/4013014223) +- [原始Issue #3618](https://github.com/binarywang/WxJava/issues/3618) \ No newline at end of file diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java index ae86b8c854..8615a2e461 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResult.java @@ -273,7 +273,7 @@ public String toString() { * */ @XStreamAlias("refund_recv_accout") - private String refundRecvAccout; + private String refundRecvAccount; /** *
@@ -324,7 +324,7 @@ public void loadXML(Document d) {
settlementRefundFee = readXmlInteger(d, "settlement_refund_fee");
refundStatus = readXmlString(d, "refund_status");
successTime = readXmlString(d, "success_time");
- refundRecvAccout = readXmlString(d, "refund_recv_accout");
+ refundRecvAccount = readXmlString(d, "refund_recv_accout");
refundAccount = readXmlString(d, "refund_account");
refundRequestSource = readXmlString(d, "refund_request_source");
}
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3GlobalRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3GlobalRequest.java
new file mode 100644
index 0000000000..296d3a8646
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderV3GlobalRequest.java
@@ -0,0 +1,57 @@
+package com.github.binarywang.wxpay.bean.request;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 境外微信支付统一下单请求参数对象.
+ * 参考文档:https://pay.weixin.qq.com/doc/global/v3/zh/4013014223
+ *
+ *
+ * @author Binary Wang
+ */
+@Data
+@NoArgsConstructor
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = true)
+public class WxPayUnifiedOrderV3GlobalRequest extends WxPayUnifiedOrderV3Request implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ *
+ * 字段名:交易类型
+ * 变量名:trade_type
+ * 是否必填:是
+ * 类型:string[1,16]
+ * 描述:
+ * 交易类型,取值如下:
+ * JSAPI--JSAPI支付
+ * NATIVE--Native支付
+ * APP--APP支付
+ * H5--H5支付
+ * 示例值:JSAPI
+ *
+ */
+ @SerializedName(value = "trade_type")
+ private String tradeType;
+
+ /**
+ *
+ * 字段名:商户类目
+ * 变量名:merchant_category_code
+ * 是否必填:是
+ * 类型:string[1,32]
+ * 描述:
+ * 商户类目,境外商户必填
+ * 示例值:5812
+ *
+ */
+ @SerializedName(value = "merchant_category_code")
+ private String merchantCategoryCode;
+}
\ No newline at end of file
diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/enums/GlobalTradeTypeEnum.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/enums/GlobalTradeTypeEnum.java
new file mode 100644
index 0000000000..fd33b240f1
--- /dev/null
+++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/enums/GlobalTradeTypeEnum.java
@@ -0,0 +1,36 @@
+package com.github.binarywang.wxpay.bean.result.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 境外微信支付方式
+ * Overseas WeChat Pay trade types with global endpoints
+ *
+ * @author Binary Wang
+ */
+@Getter
+@AllArgsConstructor
+public enum GlobalTradeTypeEnum {
+ /**
+ * APP
+ */
+ APP("/global/v3/transactions/app"),
+ /**
+ * JSAPI 或 小程序
+ */
+ JSAPI("/global/v3/transactions/jsapi"),
+ /**
+ * NATIVE
+ */
+ NATIVE("/global/v3/transactions/native"),
+ /**
+ * H5
+ */
+ H5("/global/v3/transactions/h5");
+
+ /**
+ * 境外下单url
+ */
+ private final String url;
+}
\ No newline at end of file
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 8ceac2b6ba..c73fb843e8 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.result.enums.GlobalTradeTypeEnum;
import com.github.binarywang.wxpay.bean.transfer.TransferBillsNotifyResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
@@ -640,6 +641,17 @@ public interface WxPayService {
*/
T createPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException;
+ /**
+ * 境外微信支付调用统一下单接口,并组装生成支付所需参数对象.
+ *
+ * @param 请使用{@link WxPayUnifiedOrderV3Result}里的内部类或字段
+ * @param tradeType the global trade type
+ * @param request 境外统一下单请求参数
+ * @return 返回 {@link WxPayUnifiedOrderV3Result}里的内部类或字段
+ * @throws WxPayException the wx pay exception
+ */
+ T createOrderV3Global(GlobalTradeTypeEnum tradeType, WxPayUnifiedOrderV3GlobalRequest request) throws WxPayException;
+
/**
* 在发起微信支付前,需要调用统一下单接口,获取"预支付交易会话标识"
*
@@ -660,6 +672,16 @@ public interface WxPayService {
*/
WxPayUnifiedOrderV3Result unifiedOrderV3(TradeTypeEnum tradeType, WxPayUnifiedOrderV3Request request) throws WxPayException;
+ /**
+ * 境外微信支付在发起支付前,需要调用统一下单接口,获取"预支付交易会话标识"
+ *
+ * @param tradeType the global trade type
+ * @param request 境外请求对象,注意一些参数如appid、mchid等不用设置,方法内会自动从配置对象中获取到(前提是对应配置中已经设置)
+ * @return the wx pay unified order result
+ * @throws WxPayException the wx pay exception
+ */
+ WxPayUnifiedOrderV3Result unifiedOrderV3Global(GlobalTradeTypeEnum tradeType, WxPayUnifiedOrderV3GlobalRequest request) throws WxPayException;
+
/**
*
* 合单支付API(APP支付、JSAPI支付、H5支付、NATIVE支付).
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 5057ef2b6b..4177a54cd5 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,6 +11,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.result.enums.GlobalTradeTypeEnum;
import com.github.binarywang.wxpay.bean.transfer.TransferBillsNotifyResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.config.WxPayConfigHolder;
@@ -746,6 +747,14 @@ public T createPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOr
return result.getPayInfo(tradeType, appId, request.getSubMchId(), this.getConfig().getPrivateKey());
}
+ @Override
+ public T createOrderV3Global(GlobalTradeTypeEnum tradeType, WxPayUnifiedOrderV3GlobalRequest request) throws WxPayException {
+ WxPayUnifiedOrderV3Result result = this.unifiedOrderV3Global(tradeType, request);
+ // Convert GlobalTradeTypeEnum to TradeTypeEnum for getPayInfo method
+ TradeTypeEnum domesticTradeType = TradeTypeEnum.valueOf(tradeType.name());
+ return result.getPayInfo(domesticTradeType, request.getAppid(), request.getMchid(), this.getConfig().getPrivateKey());
+ }
+
@Override
public WxPayUnifiedOrderV3Result unifiedPartnerOrderV3(TradeTypeEnum tradeType, WxPayPartnerUnifiedOrderV3Request request) throws WxPayException {
if (StringUtils.isBlank(request.getSpAppid())) {
@@ -790,6 +799,28 @@ public WxPayUnifiedOrderV3Result unifiedOrderV3(TradeTypeEnum tradeType, WxPayUn
return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class);
}
+ @Override
+ public WxPayUnifiedOrderV3Result unifiedOrderV3Global(GlobalTradeTypeEnum tradeType, WxPayUnifiedOrderV3GlobalRequest request) throws WxPayException {
+ if (StringUtils.isBlank(request.getAppid())) {
+ request.setAppid(this.getConfig().getAppId());
+ }
+ if (StringUtils.isBlank(request.getMchid())) {
+ request.setMchid(this.getConfig().getMchId());
+ }
+ if (StringUtils.isBlank(request.getNotifyUrl())) {
+ request.setNotifyUrl(this.getConfig().getNotifyUrl());
+ }
+ if (StringUtils.isBlank(request.getTradeType())) {
+ request.setTradeType(tradeType.name());
+ }
+
+ // Use global WeChat Pay base URL for overseas payments
+ String globalBaseUrl = "https://apihk.mch.weixin.qq.com";
+ String url = globalBaseUrl + tradeType.getUrl();
+ String response = this.postV3WithWechatpaySerial(url, GSON.toJson(request));
+ return GSON.fromJson(response, WxPayUnifiedOrderV3Result.class);
+ }
+
@Override
public CombineTransactionsResult combine(TradeTypeEnum tradeType, CombineTransactionsRequest request) throws WxPayException {
if (StringUtils.isBlank(request.getCombineAppid())) {
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
index 963afb2618..e7a22ee6cd 100644
--- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/bean/notify/WxPayRefundNotifyResultTest.java
@@ -119,7 +119,7 @@ public void testFromXMLFastMode() throws WxPayException {
refundNotifyResult.loadReqInfo(xmlDecryptedReqInfo);
assertEquals(refundNotifyResult.getReqInfo().getRefundFee().intValue(), 15);
assertEquals(refundNotifyResult.getReqInfo().getRefundStatus(), "SUCCESS");
- assertEquals(refundNotifyResult.getReqInfo().getRefundRecvAccout(), "用户零钱");
+ assertEquals(refundNotifyResult.getReqInfo().getRefundRecvAccount(), "用户零钱");
System.out.println(refundNotifyResult);
} finally {
XmlConfig.fastMode = false;
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceGlobalImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceGlobalImplTest.java
new file mode 100644
index 0000000000..c648c8a171
--- /dev/null
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceGlobalImplTest.java
@@ -0,0 +1,89 @@
+package com.github.binarywang.wxpay.service.impl;
+
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3GlobalRequest;
+import com.github.binarywang.wxpay.bean.result.enums.GlobalTradeTypeEnum;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import me.chanjar.weixin.common.util.RandomUtils;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+/**
+ * 境外微信支付测试类
+ *
+ * @author Binary Wang
+ */
+public class BaseWxPayServiceGlobalImplTest {
+
+ private static final Gson GSON = new GsonBuilder().create();
+
+ @Test
+ public void testWxPayUnifiedOrderV3GlobalRequest() {
+ // Test that the new request class has the required fields
+ WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest();
+
+ // Set basic order information
+ String outTradeNo = RandomUtils.getRandomStr();
+ request.setOutTradeNo(outTradeNo);
+ request.setDescription("Test overseas payment");
+ request.setNotifyUrl("https://api.example.com/notify");
+
+ // Set amount
+ WxPayUnifiedOrderV3GlobalRequest.Amount amount = new WxPayUnifiedOrderV3GlobalRequest.Amount();
+ amount.setCurrency(WxPayConstants.CurrencyType.CNY);
+ amount.setTotal(100); // 1 yuan in cents
+ request.setAmount(amount);
+
+ // Set payer
+ WxPayUnifiedOrderV3GlobalRequest.Payer payer = new WxPayUnifiedOrderV3GlobalRequest.Payer();
+ payer.setOpenid("test_openid");
+ request.setPayer(payer);
+
+ // Set the new required fields for global payments
+ request.setTradeType("JSAPI");
+ request.setMerchantCategoryCode("5812"); // Example category code
+
+ // Assert that all fields are properly set
+ assertNotNull(request.getTradeType());
+ assertNotNull(request.getMerchantCategoryCode());
+ assertEquals("JSAPI", request.getTradeType());
+ assertEquals("5812", request.getMerchantCategoryCode());
+ assertEquals(outTradeNo, request.getOutTradeNo());
+ assertEquals("Test overseas payment", request.getDescription());
+ assertEquals(100, request.getAmount().getTotal());
+ assertEquals("test_openid", request.getPayer().getOpenid());
+
+ // Test JSON serialization contains the new fields
+ String json = GSON.toJson(request);
+ assertTrue(json.contains("trade_type"));
+ assertTrue(json.contains("merchant_category_code"));
+ assertTrue(json.contains("JSAPI"));
+ assertTrue(json.contains("5812"));
+ }
+
+ @Test
+ public void testGlobalTradeTypeEnum() {
+ // Test that all trade types have the correct global endpoints
+ assertEquals("/global/v3/transactions/app", GlobalTradeTypeEnum.APP.getUrl());
+ assertEquals("/global/v3/transactions/jsapi", GlobalTradeTypeEnum.JSAPI.getUrl());
+ assertEquals("/global/v3/transactions/native", GlobalTradeTypeEnum.NATIVE.getUrl());
+ assertEquals("/global/v3/transactions/h5", GlobalTradeTypeEnum.H5.getUrl());
+ }
+
+ @Test
+ public void testGlobalTradeTypeEnumValues() {
+ // Test that we have all the main trade types
+ GlobalTradeTypeEnum[] tradeTypes = GlobalTradeTypeEnum.values();
+ assertEquals(4, tradeTypes.length);
+
+ // Test that we can convert between enum name and TradeTypeEnum
+ for (GlobalTradeTypeEnum globalType : tradeTypes) {
+ // This tests that the enum names match between Global and regular TradeTypeEnum
+ String name = globalType.name();
+ assertNotNull(name);
+ assertTrue(name.equals("APP") || name.equals("JSAPI") || name.equals("NATIVE") || name.equals("H5"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/OverseasWxPayExample.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/OverseasWxPayExample.java
new file mode 100644
index 0000000000..ccccf9c803
--- /dev/null
+++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/OverseasWxPayExample.java
@@ -0,0 +1,153 @@
+package com.github.binarywang.wxpay.service.impl;
+
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3GlobalRequest;
+import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
+import com.github.binarywang.wxpay.bean.result.enums.GlobalTradeTypeEnum;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.constant.WxPayConstants;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import me.chanjar.weixin.common.util.RandomUtils;
+
+/**
+ * 境外微信支付使用示例
+ * Example usage for overseas WeChat Pay
+ *
+ * @author Binary Wang
+ */
+public class OverseasWxPayExample {
+
+ /**
+ * 境外微信支付JSAPI下单示例
+ * Example for overseas WeChat Pay JSAPI order creation
+ */
+ public void createOverseasJsapiOrder(WxPayService payService) throws WxPayException {
+ // 创建境外支付请求对象
+ WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest();
+
+ // 设置基础订单信息
+ request.setOutTradeNo(RandomUtils.getRandomStr()); // 商户订单号
+ request.setDescription("境外商品购买"); // 商品描述
+ request.setNotifyUrl("https://your-domain.com/notify"); // 支付通知地址
+
+ // 设置金额信息
+ WxPayUnifiedOrderV3GlobalRequest.Amount amount = new WxPayUnifiedOrderV3GlobalRequest.Amount();
+ amount.setCurrency(WxPayConstants.CurrencyType.CNY); // 币种
+ amount.setTotal(100); // 金额,单位为分
+ request.setAmount(amount);
+
+ // 设置支付者信息
+ WxPayUnifiedOrderV3GlobalRequest.Payer payer = new WxPayUnifiedOrderV3GlobalRequest.Payer();
+ payer.setOpenid("用户的openid"); // 用户openid
+ request.setPayer(payer);
+
+ // 设置境外支付必需的参数
+ request.setTradeType("JSAPI"); // 交易类型
+ request.setMerchantCategoryCode("5812"); // 商户类目代码,境外商户必填
+
+ // 可选:设置场景信息
+ WxPayUnifiedOrderV3GlobalRequest.SceneInfo sceneInfo = new WxPayUnifiedOrderV3GlobalRequest.SceneInfo();
+ sceneInfo.setPayerClientIp("用户IP地址");
+ request.setSceneInfo(sceneInfo);
+
+ // 调用境外支付接口
+ WxPayUnifiedOrderV3Result.JsapiResult result = payService.createOrderV3Global(
+ GlobalTradeTypeEnum.JSAPI,
+ request
+ );
+
+ // 返回的result包含前端需要的支付参数
+ System.out.println("支付参数:" + result);
+ }
+
+ /**
+ * 境外微信支付APP下单示例
+ * Example for overseas WeChat Pay APP order creation
+ */
+ public void createOverseasAppOrder(WxPayService payService) throws WxPayException {
+ WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest();
+
+ // 设置基础信息
+ request.setOutTradeNo(RandomUtils.getRandomStr());
+ request.setDescription("境外APP商品购买");
+ request.setNotifyUrl("https://your-domain.com/notify");
+
+ // 设置金额
+ WxPayUnifiedOrderV3GlobalRequest.Amount amount = new WxPayUnifiedOrderV3GlobalRequest.Amount();
+ amount.setCurrency(WxPayConstants.CurrencyType.CNY);
+ amount.setTotal(200); // 2元
+ request.setAmount(amount);
+
+ // APP支付不需要设置payer.openid,但需要设置空的payer对象
+ request.setPayer(new WxPayUnifiedOrderV3GlobalRequest.Payer());
+
+ // 境外支付必需参数
+ request.setTradeType("APP");
+ request.setMerchantCategoryCode("5812");
+
+ // 调用境外APP支付接口
+ WxPayUnifiedOrderV3Result.AppResult result = payService.createOrderV3Global(
+ GlobalTradeTypeEnum.APP,
+ request
+ );
+
+ System.out.println("APP支付参数:" + result);
+ }
+
+ /**
+ * 境外微信支付NATIVE下单示例
+ * Example for overseas WeChat Pay NATIVE order creation
+ */
+ public void createOverseasNativeOrder(WxPayService payService) throws WxPayException {
+ WxPayUnifiedOrderV3GlobalRequest request = new WxPayUnifiedOrderV3GlobalRequest();
+
+ request.setOutTradeNo(RandomUtils.getRandomStr());
+ request.setDescription("境外扫码支付");
+ request.setNotifyUrl("https://your-domain.com/notify");
+
+ // 设置金额
+ WxPayUnifiedOrderV3GlobalRequest.Amount amount = new WxPayUnifiedOrderV3GlobalRequest.Amount();
+ amount.setCurrency(WxPayConstants.CurrencyType.CNY);
+ amount.setTotal(300); // 3元
+ request.setAmount(amount);
+
+ // NATIVE支付不需要设置payer.openid
+ request.setPayer(new WxPayUnifiedOrderV3GlobalRequest.Payer());
+
+ // 境外支付必需参数
+ request.setTradeType("NATIVE");
+ request.setMerchantCategoryCode("5812");
+
+ // 调用境外NATIVE支付接口
+ String result = payService.createOrderV3Global(
+ GlobalTradeTypeEnum.NATIVE,
+ request
+ );
+
+ System.out.println("NATIVE支付二维码链接:" + result);
+ }
+
+ /**
+ * 配置示例
+ * Configuration example
+ */
+ public WxPayConfig createOverseasConfig() {
+ WxPayConfig config = new WxPayConfig();
+
+ // 基础配置
+ config.setAppId("你的AppId");
+ config.setMchId("你的境外商户号");
+ config.setMchKey("你的商户密钥");
+ config.setNotifyUrl("https://your-domain.com/notify");
+
+ // 境外支付使用的是全球API,在代码中会自动使用 https://apihk.mch.weixin.qq.com 作为基础URL
+ // 无需额外设置payBaseUrl,方法内部会自动处理
+
+ // V3相关配置(境外支付也使用V3接口)
+ config.setPrivateKeyPath("你的私钥文件路径");
+ config.setCertSerialNo("你的商户证书序列号");
+ config.setApiV3Key("你的APIv3密钥");
+
+ return config;
+ }
+}
\ No newline at end of file