package com.sky.Utils;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.*;
import com.wechat.pay.java.core.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ThreadLocalRandom;
@Slf4j
@Component
public class QuickStartUtil {
// 配置参数(需替换为实际值)
private static final String MERCHANT_ID = "";
private static final String PRIVATE_KEY_PATH = "";
private static final String MERCHANT_SERIAL_NUMBER = "";
private static final String API_V3_KEY = ""; // 必须32字符
private static final String APP_ID = "";
private static final String NOTIFY_URL = "";
private static final String CURRENCY = "USD";
private static final int MAX_OUT_TRADE_NO_LENGTH = 32;
// 跨境支付专用参数
private static final String TRADE_TYPE = "NATIVE";
private static final String MERCHANT_CATEGORY_CODE = "5311"; // 一般商品零售
private PrivateKey loadPrivateKey() throws IOException {
try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("apiclient_key.pem")) {
if (inputStream != null) {
return PemUtil.loadPrivateKeyFromString(new String(inputStream.readAllBytes(), StandardCharsets.UTF_8));
}
return PemUtil.loadPrivateKeyFromPath(PRIVATE_KEY_PATH);
}
}
private Config createWechatPayConfig() {
try {
return new RSAAutoCertificateConfig.Builder()
.merchantId(MERCHANT_ID)
.privateKey(loadPrivateKey())
.merchantSerialNumber(MERCHANT_SERIAL_NUMBER)
.apiV3Key(API_V3_KEY)
.build();
} catch (IOException e) {
throw new RuntimeException("加载证书失败", e);
}
}
private String generateOutTradeNo(String prefix) {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String randomStr = String.valueOf(ThreadLocalRandom.current().nextInt(10000));
String baseNo = prefix + "_" + timestamp + "_" + randomStr;
return baseNo.length() <= MAX_OUT_TRADE_NO_LENGTH
? baseNo
: baseNo.substring(0, MAX_OUT_TRADE_NO_LENGTH);
}
/**
* 创建跨境支付订单(100%兼容最新SDK版本)
*/
public String createCrossBorderPayment(Integer total, String title, String clientIp) {
try {
NativePayService service = new NativePayService.Builder()
.config(createWechatPayConfig())
.build();
String outTradeNo = generateOutTradeNo("GLOBAL");
log.info("跨境支付请求 - 订单号: {}, 金额: {} {}", outTradeNo, total, CURRENCY);
// 1. 构建基础请求
PrepayRequest request = new PrepayRequest();
// 2. 设置金额
Amount amount = new Amount();
amount.setTotal(total);
amount.setCurrency(CURRENCY);
request.setAmount(amount);
// 3. 设置基础参数
request.setAppid(APP_ID);
request.setMchid(MERCHANT_ID);
request.setDescription(title.length() > 128 ? title.substring(0, 128) : title);
request.setNotifyUrl(NOTIFY_URL);
request.setOutTradeNo(outTradeNo);
// 4. 设置支付场景
SceneInfo sceneInfo = new SceneInfo();
sceneInfo.setPayerClientIp(clientIp);
sceneInfo.setDeviceId("WEB");
request.setSceneInfo(sceneInfo);
// 5. 通过反射设置跨境专用参数(关键修改点)
try {
// 设置交易类型
java.lang.reflect.Field tradeTypeField = request.getClass().getDeclaredField("tradeType");
tradeTypeField.setAccessible(true);
tradeTypeField.set(request, TRADE_TYPE);
// 设置商户分类代码
java.lang.reflect.Field mccField = request.getClass().getDeclaredField("merchantCategoryCode");
mccField.setAccessible(true);
mccField.set(request, MERCHANT_CATEGORY_CODE);
} catch (Exception e) {
log.warn("设置跨境支付扩展参数失败(不影响主要功能)", e);
}
// 6. 调用支付接口
PrepayResponse response = service.prepay(request);
return response.getCodeUrl();
} catch (Exception e) {
log.error("跨境支付失败 - 请检查:1.商户跨境权限 2.参数完整性 3.证书有效性", e);
throw new RuntimeException("跨境支付创建失败: " + e.getMessage());
}
}
}
2025-06-19 21:16:06.375 ERROR 180488 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: 跨境支付创建失败: Wrong HttpStatusCode[400]
httpResponseBody[{"code":"INVALID_REQUEST","message":"暂不支持境外支付"}] HttpRequest[{"http_method":"POST","url":"https://api.mch.weixin.qq.com/v3/pay/transactions/native","uri":"https://api.mch.weixin.qq.com/v3/pay/transactions/native","headers":{"headers":{"Authorization":"WECHATPAY2-SHA256-RSA2048 mchid=\"777463044\",nonce_str=\"Ew7UiBZYweOEY5fJMT1bF19XJlbHmUFx\",timestamp=\"1750338966\",serial_no=\"7B90DEE3ABB5B5BCF3048BC24F72C0A767BCE451\",signature=\"LRuUdl2umnyp+iA4zI7CHBWMQ6XMe5ubBzHTMTHnSrKfrtwSIoF51BSmJy2sTFfFs4xiRsnj+tQlIydvyvzMpMXgJ8dM4NcbOe4Rp4Zwz30+n1gyvBXWtsJJp3/7qyRM0CkqOYgHfc1pbe0YXsX3ajHZ75nkBIHTRYN/cdSIOI0nVN0Egjf+kvBg4VrcYHHPiLJsb2NZmy4B6dMaSjYZbsn9prp+yL7ssyv0MS4puvXEwcoPmeLADsiFVvxj86Nptk2yM+kAFrPH9WLqgAWgAvOzoyDgLXSEnUghGEVwlbgNzqAfJhg21DWo8wxQLFMIrIAazKFEkrW9Bwk6LrSQQw==\"","Accept":"application/json","User-Agent":"WechatPay-Java/0.2.16 (Windows 11/10.0) Java/17.0.13 Credential/WechatPay2Credential Validator/WechatPay2Validator okhttp3/null","Content-Type":"application/json","Wechatpay-Serial":"6A00C69D09C0777622E10C03555AE96B54F63CD6"}},"body":{"body":"{\"appid\":\"wx2bd197bcb4b986a0\",\"mchid\":\"777463044\",\"description\":\"购买商品: 123\",\"out_trade_no\":\"GLOBAL_20250619211606_5738\",\"notify_url\":\"https://your-domain.com/notify\",\"amount\":{\"total\":3000,\"currency\":\"USD\"},\"scene_info\":{\"payer_client_ip\":\"8.218.65.151\",\"device_id\":\"WEB\"}}"}}]] with root cause
java.lang.RuntimeException: 跨境支付创建失败: Wrong HttpStatusCode[400]
httpResponseBody[{"code":"INVALID_REQUEST","message":"暂不支持境外支付"}] HttpRequest[{"http_method":"POST","url":"https://api.mch.weixin.qq.com/v3/pay/transactions/native","uri":"https://api.mch.weixin.qq.com/v3/pay/transactions/native","headers":{"headers":{"Authorization":"WECHATPAY2-SHA256-RSA2048 mchid=\"777463044\",nonce_str=\"Ew7UiBZYweOEY5fJMT1bF19XJlbHmUFx\",timestamp=\"1750338966\",serial_no=\"7B90DEE3ABB5B5BCF3048BC24F72C0A767BCE451\",signature=\"LRuUdl2umnyp+iA4zI7CHBWMQ6XMe5ubBzHTMTHnSrKfrtwSIoF51BSmJy2sTFfFs4xiRsnj+tQlIydvyvzMpMXgJ8dM4NcbOe4Rp4Zwz30+n1gyvBXWtsJJp3/7qyRM0CkqOYgHfc1pbe0YXsX3ajHZ75nkBIHTRYN/cdSIOI0nVN0Egjf+kvBg4VrcYHHPiLJsb2NZmy4B6dMaSjYZbsn9prp+yL7ssyv0MS4puvXEwcoPmeLADsiFVvxj86Nptk2yM+kAFrPH9WLqgAWgAvOzoyDgLXSEnUghGEVwlbgNzqAfJhg21DWo8wxQLFMIrIAazKFEkrW9Bwk6LrSQQw==\"","Accept":"application/json","User-Agent":"WechatPay-Java/0.2.16 (Windows 11/10.0) Java/17.0.13 Credential/WechatPay2Credential Validator/WechatPay2Validator okhttp3/null","Content-Type":"application/json","Wechatpay-Serial":"6A00C69D09C0777622E10C03555AE96B54F63CD6"}},"body":{"body":"{\"appid\":\"wx2bd197bcb4b986a0\",\"mchid\":\"777463044\",\"description\":\"购买商品: 123\",\"out_trade_no\":\"GLOBAL_20250619211606_5738\",\"notify_url\":\"https://your-domain.com/notify\",\"amount\":{\"total\":3000,\"currency\":\"USD\"},\"scene_info\":{\"payer_client_ip\":\"8.218.65.151\",\"device_id\":\"WEB\"}}"}}]
at com.sky.Utils.QuickStartUtil.createCrossBorderPayment(QuickStartUtil.java:123) ~[classes/:na]
at com.sky.controller.pay.WechatPayController.WechatPay(WechatPayController.java:27) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1070) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.22.jar:5.3.22]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.22.jar:5.3.22]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:665) ~[javax.servlet-api-4.0.1.jar:4.0.1]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.22.jar:5.3.22]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:750) ~[javax.servlet-api-4.0.1.jar:4.0.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at com.sky.config.CorsFilter.doFilter(CorsFilter.java:27) ~[classes/:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.22.jar:5.3.22]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.22.jar:5.3.22]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at java.base/java.lang.Thread.run(Thread.java:842) ~[na:na]