springboot对接小程序微信支付的详细步骤

6 阅读4分钟

在 Spring Boot 中对接微信小程序支付,通常分为两大方案。本指南将重点介绍主流的 V2 版本,并简要说明 V3 版本​ 的差异,助您快速上手。

📝 方案对比:V2 vs. V3

特性V2 版本 (主流)V3 版本 (新趋势)
协议XML + 普通HTTPSJSON + 双向证书 (mTLS)
签名MD5 / HMAC-SHA256RSA / SM 国密
核心依赖wxpay-sdk等第三方库官方 wechatpay-javaSDK
特点上手快,资料多,兼容老项目安全性更高,官方主推,结构清晰

🚀 方案一:V2 版本接入 (推荐)

1. 前期准备

  1. 小程序与商户号

    • 在微信公众平台 (mp.weixin.qq.com) 注册并获取小程序 AppID
    • 在微信支付商户平台 (pay.weixin.qq.com) 注册并获取 商户号 (mch_id)
    • 将小程序与商户号进行绑定。
  2. API 密钥与证书

    • 在商户平台「账户设置」->「API安全」中设置 API 密钥 (key) ,用于 V2 签名。
    • 下载 API 证书​ (apiclient_cert.p12),用于退款等需要证书的接口。
  3. 服务器与域名

    • 准备一台公网可访问的服务器,并配置好 HTTPS​ 域名。
    • 在商户平台设置 支付结果通知 URL (notify_url) ,必须是公网 HTTPS 地址,且不能带参数。

2. Spring Boot 项目搭建

  1. 创建项目

    使用 Spring Initializr 或 IDEA 创建一个标准的 Spring Boot Web 项目。

  2. 添加 Maven 依赖

    推荐使用封装好的第三方 SDK wxpay-sdk

    xml

    org.springframework.boot

    spring-boot-starter-web

    com.github.wxpay

    wxpay-sdk

    0.0.3

    commons-codec

    commons-codec

    org.apache.httpcomponents

    httpclient

    com.thoughtworks.xstream

    xstream

3. 配置参数

  1. application.yml配置

    yaml

    wxpay:

    appId: wx1234567890abcdef

    mchId: 1234567890

    mchKey: your_api_key_here

    notifyUrl: yourdomain.com/api/wxpay/n…

    unifiedOrderUrl: api.mch.weixin.qq.com/pay/unified…

    tradeType: JSAPI

  2. 配置类 WxPayConfig.java

    java

    @Component

    @ConfigurationProperties(prefix = "wxpay")

    @Data

    public class WxPayConfig {

    private String appId;

    private String mchId;

    private String mchKey;

    private String notifyUrl;

    private String unifiedOrderUrl;

    private String tradeType;

    }

4. 核心工具类

  1. 签名工具 WxPayUtil.java

    实现 V2 签名算法:参数按 ASCII 排序,拼接 key=API密钥,再进行 MD5 或 HMAC-SHA256 运算。

  2. XML 工具 XmlUtil.java

    用于 Map 与 XML 格式的相互转换。

5. 统一下单接口

此接口用于向微信申请预支付交易单,获取 prepay_id

java

@RestController

@RequestMapping("/api/wxpay")

@RequiredArgsConstructor

public class WxPayController {

private final WxPayConfig wxPayConfig;
private final WxPayUtil wxPayUtil;
private final XmlUtil xmlUtil;

@PostMapping("/create")
public ResponseEntity<?> createOrder(@RequestBody CreateOrderRequest req) throws Exception {
    // 1. 构建请求参数
    Map<String, String> params = new HashMap<>();
    params.put("appid", wxPayConfig.getAppId());
    params.put("mch_id", wxPayConfig.getMchId());
    params.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
    params.put("body", req.getBody());
    params.put("out_trade_no", req.getOutTradeNo());
    params.put("total_fee", String.valueOf(req.getTotalFee())); // 单位:分
    params.put("spbill_create_ip", req.getSpbillCreateIp());
    params.put("notify_url", wxPayConfig.getNotifyUrl());
    params.put("trade_type", wxPayConfig.getTradeType());
    params.put("openid", req.getOpenid()); // JSAPI 支付必填

    // 2. 生成签名并放入参数
    String sign = wxPayUtil.generateSign(params, wxPayConfig.getMchKey());
    params.put("sign", sign);

    // 3. 发送请求到微信
    String xml = xmlUtil.mapToXml(params);
    String responseXml = HttpUtil.post(wxPayConfig.getUnifiedOrderUrl(), xml);
    Map<String, String> respMap = xmlUtil.xmlToMap(responseXml);

    // 4. 处理响应
    if ("SUCCESS".equals(respMap.get("return_code")) && "SUCCESS".equals(respMap.get("result_code"))) {
        String prepayId = respMap.get("prepay_id");
        // 5. 二次签名,返回给小程序
        Map<String, String> paySignMap = buildPaySignParams(prepayId);
        return ResponseEntity.ok(paySignMap);
    } else {
        // 处理错误
        return ResponseEntity.badRequest().body(respMap.get("return_msg"));
    }
}

private Map<String, String> buildPaySignParams(String prepayId) {
    Map<String, String> paySignMap = new HashMap<>();
    paySignMap.put("appId", wxPayConfig.getAppId());
    paySignMap.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
    paySignMap.put("nonceStr", UUID.randomUUID().toString().replace("-", ""));
    paySignMap.put("package", "prepay_id=" + prepayId);
    paySignMap.put("signType", "MD5");

    String paySign = wxPayUtil.generateSign(paySignMap, wxPayConfig.getMchKey());
    paySignMap.put("paySign", paySign);
    return paySignMap;
}

}

6. 支付结果回调通知

微信支付成功后,会向 notify_url发送 POST 请求,通知支付结果。

java

@PostMapping(value = "/notify", consumes = "application/xml")

public void payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {

// 1. 读取回调的 XML 数据

String xmlData = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);

Map<String, String> notifyMap = xmlUtil.xmlToMap(xmlData);

// 2. 验签
String sign = notifyMap.get("sign");
String localSign = wxPayUtil.generateSign(notifyMap, wxPayConfig.getMchKey());
if (!sign.equals(localSign)) {
    // 签名失败,直接返回
    response.getWriter().write("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
    return;
}

// 3. 处理业务逻辑 (务必保证幂等性)
if ("SUCCESS".equals(notifyMap.get("return_code")) && "SUCCESS".equals(notifyMap.get("result_code"))) {
    String outTradeNo = notifyMap.get("out_trade_no");
    String transactionId = notifyMap.get("transaction_id");
    // TODO: 更新本地订单状态为“已支付”,处理发货、记账等
    // orderService.updateOrderStatus(outTradeNo, "PAID", transactionId);
}

// 4. 返回成功响应,告知微信已收到通知
response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");

}

7. 小程序端调用

  1. 获取 openid

    小程序通过 wx.login()获取 code,然后请求你自己的后端接口,由后端调用微信接口换取 openid

  2. 发起支付

    调用你自己的 /api/wxpay/create接口获取支付参数,然后使用 wx.requestPayment调起支付。

    javascript

    // 小程序端代码示例

    wx.request({

    url: 'yourdomain.com/api/wxpay/c…',

    method: 'POST',

    data: {

    outTradeNo: 'ORDER_' + Date.now(),

    body: '测试商品',

    totalFee: 1, // 单位:分

    spbillCreateIp: '127.0.0.1',

    openid: 'USER_OPENID_FROM_BACKEND'

    },

    success(res) {

    const payData = res.data;

    wx.requestPayment({

    timeStamp: payData.timeStamp,

    nonceStr: payData.nonceStr,

    package: payData.package,

    signType: payData.signType,

    paySign: payData.paySign,

    success() {

    wx.showToast({ title: '支付成功' });

    },

    fail() {

    wx.showToast({ title: '支付失败' });

    }

    });

    }

    });


💡 方案二:V3 版本简要说明

V3 版本流程与 V2 类似,但技术细节有显著不同:

  1. 核心差异

    • 数据格式:JSON 替代 XML。
    • 安全机制:采用双向证书 (mTLS) 和 RSA 签名,安全性更高。
    • API 地址:为 RESTful 风格,如 https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
  2. 接入步骤

    • 获取 V3 密钥:在商户平台设置 APIv3 密钥,并下载商户私钥 apiclient_key.pem

    • 引入官方 SDK:使用 Maven 引入 wechatpay-javaSDK。

      xml

      com.github.wechatpay-apiv3

      wechatpay-java

      0.2.11

    • 初始化配置:使用商户号、APIv3密钥、证书序列号和私钥初始化 RSAAutoCertificateConfig

    • 调用 SDK:使用 SDK 提供的 JsapiServiceExtension等服务类,调用 prepay接口获取支付参数,流程与 V2 类似。