首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring Boot 集成微信支付 V3 版,关键配置与下单代码详解

Spring Boot 集成微信支付 V3 版,关键配置与下单代码详解

原创
作者头像
悟空码字
发布于 2026-11-04 22:33:47
发布于 2026-11-04 22:33:47
780
举报
文章被收录于专栏:编程技术编程技术

为什么选择 API V3 与官方 SDK

在早期的微信支付接入中,开发者往往需要手动处理复杂的签名算法(如 MD5 或 SHA1)、拼接参数字符串以及应对频繁变动的安全策略。旧版接口(V2)不仅文档分散,而且代码耦合度高,一旦密钥轮换或算法升级,维护成本极高。

微信支付 API V3 的推出彻底改变了这一局面。它采用了更安全的 HTTP 原生 JSON 交互方式,强制使用 AES-256-GCM 加密回调数据,并引入了自动证书下载机制。对于 Spring Boot 开发者而言,最核心的变化在于官方推出了成熟的 Java SDK(wechatpay-java)。不再需要去 GitHub 寻找第三方封装库或自己造轮子,官方 SDK 已经内置了签名生成、证书验证、自动重试等底层逻辑。直接使用官方 SDK 不仅能大幅减少样板代码,还能确保在微信侧安全策略调整时,只需升级依赖版本即可平滑过渡,极大提升了系统的稳定性和可维护性。

核心配置:安全加载密钥与证书

接入 V3 版本的第一步是正确配置商户信息。与传统硬编码字符串不同,V3 版本强烈建议将敏感信息(如私钥、API V3 密钥)通过配置文件或环境变量注入,避免泄露风险。

首先,在 pom.xml 中引入官方依赖:

代码语言:javascript
复制
<dependency>
    <groupId>com.wechat.pay.java</groupId>
    <artifactId>service</artifactId>
    <version>0.2.15</version>
</dependency>

接下来是构建配置类。我们需要准备四个核心要素:商户号(mchid)、商户私钥文件路径、API V3 密钥(在商户平台设置)以及商户证书序列号。特别注意,V3 版本不再需要手动下载微信公钥证书文件,SDK 支持自动从微信服务器拉取并缓存,这大大简化了初始化流程。

下面是一个典型的 Spring Boot 配置 Bean 写法:

代码语言:javascript
复制
@Configuration
public class WeChatPayConfig {

    @Value("${wechat.mch-id}")
    private String mchId;

    @Value("${wechat.mch-serial-no}")
    private String merchantSerialNumber;

    @Value("${wechat.api-v3-key}")
    private String apiV3Key;

    @Value("${wechat.private-key-path}")
    private String privateKeyPath;

    @Bean
    public Config weChatPayConfig() throws IOException {
        // 读取本地私钥文件
        File privateKeyFile = new File(privateKeyPath);
        String privateKeyContent = Files.toString(privateKeyFile, StandardCharsets.UTF_8);

        // 构建 RSA 公钥模式配置(推荐新商户使用)
        return new RSAPublicKeyConfig.Builder()
                .merchantId(mchId)
                .privateKey(privateKeyContent)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
    }
}

这段代码利用 RSAPublicKeyConfig.Builder 链式调用,清晰地组装了所有必要参数。其中 apiV3Key 用于解密回调通知和请求敏感接口,而私钥则用于生成请求签名。务必确保 privateKeyPath 指向的文件权限安全,且不在版本控制系统中提交。

构建服务实例与预支付下单

配置完成后,就可以注入 JsapiServiceExtension 来发起支付了。这个扩展服务类专门针对小程序、公众号等 JSAPI 场景优化,它能直接返回前端调起支付所需的所有参数,省去了二次签名的麻烦。

在 Service 层编写下单逻辑时,有几个关键细节需要注意:

  1. 金额单位:微信支付所有金额字段必须以“分”为单位整数传输,切勿传入小数。
  2. 订单号唯一性:outTradeNo 必须保证全局唯一,建议使用“时间戳 + 随机数”或数据库自增 ID 组合。
  3. OpenID 获取:必须使用当前小程序 AppID 对应的用户 OpenID,不能混用公众号或其他应用的标识。

以下是核心的下单方法实现:

代码语言:javascript
复制
@Service
public class PaymentService {

    @Autowired
    private Config config;

    public PrepayWithRequestPaymentResponse createPrepayOrder(String openId, String orderNo, int totalAmountCents, String description) {
        // 构建服务实例
        JsapiServiceExtension service = new JsapiServiceExtension.Builder()
                .config(config)
                .build();

        // 组装请求参数
        PrepayRequest request = new PrepayRequest();
        request.setAppid("你的小程序 AppID");
        request.setMchid(config.getMerchantId());
        request.setDescription(description);
        request.setOutTradeNo(orderNo);
        request.setNotifyUrl("https://your-domain.com/api/pay/notify");

        // 设置金额(单位:分)
        Amount amount = new Amount();
        amount.setTotal(totalAmountCents);
        request.setAmount(amount);

        // 设置支付者信息
        Payer payer = new Payer();
        payer.setOpenid(openId);
        request.setPayer(payer);

        try {
            // 调用下单并获取前端所需参数
            return service.prepayWithRequestPayment(request);
        } catch (ServiceException e) {
            // 处理业务异常,如重复下单、余额不足等
            throw new RuntimeException("微信支付下单失败:" + e.getMessage(), e);
        }
    }
}

调用 service.prepayWithRequestPayment(request) 后,返回的 PrepayWithRequestPaymentResponse 对象中包含了 timeStamp、nonceStr、package、signType 和 paySign。后端只需将此对象序列化返回给前端,前端即可直接调用 wx.requestPayment 唤起收银台,无需任何额外处理。

支付结果通知的解密与验签

支付完成后的回调处理是保障业务一致性的最后一道防线。微信服务器会以 POST 请求发送加密的 JSON 数据到 notifyUrl。由于数据经过 AES-256-GCM 加密且带有签名,直接读取 Body 是乱码,必须先解密再验签。

官方 SDK 提供了便捷的工具类 NotificationParser 来处理这一过程。你需要创建一个 Controller 接收请求,并使用解析器还原出真实的交易对象:

代码语言:javascript
复制
@RestController
@RequestMapping("/api/pay")
public class PayNotifyController {

    @Autowired
    private Config config;

    @PostMapping("/notify")
    public String handleNotify(HttpServletRequest request) throws IOException {
        String body = IoUtil.readUtf8(request.getInputStream());
        String serialNumber = request.getHeader("Wechatpay-Serial");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String signature = request.getHeader("Wechatpay-Signature");

        // 构建解析器
        NotificationParser parser = new NotificationParser(
                new RSAPublicKeyConfig.Builder()
                        .merchantId(config.getMerchantId())
                        .privateKey(config.getPrivateKey()) // 此处需传入私钥用于验签
                        .merchantSerialNumber(config.getMerchantSerialNumber())
                        .apiV3Key(config.getApiV3Key())
                        .build()
        );

        try {
            // 解析并解密数据,泛型指定为 Transaction
            Transaction transaction = parser.parse(
                    new HttpHeaders(serialNumber, timestamp, nonce, signature),
                    body,
                    Transaction.class
            );

            // 业务逻辑处理
            if ("SUCCESS".equals(transaction.getTradeState())) {
                String outTradeNo = transaction.getOutTradeNo();
                // TODO: 根据 outTradeNo 更新本地订单状态为“已支付”
                // 注意:此处需做幂等性检查,防止重复处理
                updateOrderStatus(outTradeNo);
            }

            // 返回成功应答,格式必须严格符合微信要求
            WxPayNotifyResponse response = WxPayNotifyResponse.success("处理成功");
            return JsonUtil.toJson(response);

        } catch (ParseException e) {
            // 验签或解密失败,返回失败应答
            WxPayNotifyResponse response = WxPayNotifyResponse.fail("解析失败");
            return JsonUtil.toJson(response);
        }
    }
    
    private void updateOrderStatus(String orderNo) {
        // 模拟数据库更新逻辑
        System.out.println("订单 " + orderNo + " 支付成功,状态已更新");
    }
}

在处理回调时,务必注意幂等性。网络波动可能导致微信多次发送相同的通知,因此在更新订单状态前,应先查询该订单是否已处理过。只有当验签通过、解密成功且业务逻辑执行无误后,才返回标准的成功响应,否则微信会认为投递失败并在后续继续重试。

通过以上步骤,一个基于 Spring Boot 和官方 SDK 的微信支付 V3 接入流程就完成了。从配置加载到下单,再到安全的回调处理,全程利用 SDK 屏蔽了底层密码学细节,让开发者能更专注于业务逻辑的实现。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 为什么选择 API V3 与官方 SDK
  • 核心配置:安全加载密钥与证书
  • 构建服务实例与预支付下单
  • 支付结果通知的解密与验签
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档