小程序微信支付 V3 接入实战手册:从商户配置到前后端落地

如果你已经申请好了微信支付商户号,要给小程序接入支付能力,这篇文章将按官方V3接口标准,从账号绑定、平台配置到前后端代码实现、联调上线,一步步讲透。全程基于Node.js技术栈,代码可直接复用,最后附高频踩坑清单。

一、开工前准备

1.1 核心参数清单

先把下面这些参数整理好,后面全程要用,省得来回切换平台找。

参数名称 参数含义 获取位置
appid 小程序唯一标识 微信公众平台 → 开发管理 → 开发者ID
mchid 微信支付商户号 微信支付商户平台首页顶部
apiV3Key APIv3对称加密密钥 商户平台 → 账户中心 → API安全 → APIv3密钥
商户证书序列号 API证书编号 商户平台 → 账户中心 → API安全 → 证书管理
商户私钥 apiclient_key.pem 证书文件 商户平台下载的API证书压缩包内
notify_url 支付结果异步回调地址 自行部署的后端HTTPS接口

1.2 前置条件校验

先确认这几点,避免做无用功:

  • 小程序是非个人主体,且已完成微信认证;个人主体不支持支付能力
  • 商户号已完成开户验证、签署支付协议,账户状态正常
  • 后端服务支持HTTPS协议,对应域名已完成备案
  • 小程序支付底层复用JSAPI支付,无需单独配置支付授权目录

二、商户号与小程序AppID绑定

这步是核心前提,没绑定的话小程序没有权限调用这个商户号的支付能力。

2.1 商户平台发起授权

  1. 用商户号超级管理员账号登录微信支付商户平台
  2. 左侧导航进「产品中心 → 账号关联(AppID绑定)」,点右侧「新增授权AppID」
  3. 准确填写小程序的AppID,阅读并签署授权协议后提交
    • 商户号与小程序主体一致:提交后等待小程序侧确认即可
    • 主体不一致:需额外填写小程序认证主体名称,签署《联合营运承诺函》

2.2 小程序后台确认授权

  1. 登录微信公众平台(小程序账号)
  2. 左侧进「功能 → 微信支付 → 商户号管理」,在「待关联商户号」列表找到对应申请
  3. 点击「确认授权」完成绑定

也可以直接在小程序后台「微信支付」页面选「已有商户号,快速绑定」,流程和效果都一样。

2.3 绑定结果校验

回到商户平台的「账号关联」页面,对应AppID状态显示「已授权」就完成了。

三、商户平台API安全配置

API安全页面一共四个配置项,做小程序V3支付,只需要配置其中两个,剩下两个不用动。

3.1 商户API证书(必须配置)

后端调用所有V3接口时,都要用证书里的商户私钥对请求签名,用来证明商户身份,强制要求。

操作步骤:

  1. 进入商户平台「账户中心 → API安全 → 商户API证书」,点击「申请证书」
  2. 按页面指引生成证书请求文件,提交后下载证书压缩包
  3. 解压后得到两个核心文件:
    • apiclient_key.pem:商户私钥,后端签名核心凭证,严禁泄露
    • apiclient_cert.pem:商户公钥证书

证书详情页可以查到对应的证书序列号,记下来后面要用。

3.2 APIv3密钥(必须配置)

也就是常说的apiV3Key,32位对称加密密钥,用来解密微信支付回调通知、下载平台证书。

生成规则

  • 长度必须正好32位字符
  • 支持数字、大写字母、小写字母组合
  • 支持随机生成,且无规律的随机字符串安全性最高

三种快速生成方式

挑顺手的用就行:

方式一:浏览器控制台(零依赖,最快) 随便打开一个网页,按F12打开开发者工具,切到Console控制台,执行以下代码直接出结果:

javascript 复制代码
btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(24))))
  .replace(/[^a-zA-Z0-9]/g, '')
  .slice(0, 32)

方式二:Node.js命令行

Windows的CMD/PowerShell对嵌套引号解析有坑,用这个版本:

js 复制代码
node -e "const c=require('crypto');console.log(c.randomBytes(24).toString('base64').replace(/[^a-zA-Z0-9]/g,'').slice(0,32))"

方式三:本地脚本文件(全系统兼容,最稳) 新建genkey.js文件,写入以下代码,终端执行node genkey.js即可:

js 复制代码
const crypto = require('crypto');
const apiV3Key = crypto.randomBytes(24)
  .toString('base64')
  .replace(/[^a-zA-Z0-9]/g, '')
  .slice(0, 32);
console.log('生成的APIv3密钥:', apiV3Key);
console.log('长度校验:', apiV3Key.length);

注意:生成密钥后立刻永久保存。微信后台设置完成后不支持查看明文,丢失了只能重置,会影响线上回调解密。

3.3 无需配置的两项说明

剩下两个配置项不用管,别浪费时间:

  • 商户APIv2密钥:仅用于旧版V2接口签名,全程使用V3接口的话完全不需要,和APIv3密钥相互独立、互不影响
  • 微信支付公钥:新版V3开发可以通过APIv3密钥自动下载平台证书完成验签,主流SDK都内置了这个能力,不需要手动申请

四、小程序后台基础配置

两件事,做完就可以开始写代码了。

  1. 配置服务器域名 进入小程序后台「开发管理 → 开发设置 → 服务器域名」,在「request合法域名」中添加后端接口对应的域名。

    注意:小程序支付不需要配置支付授权目录,这是和公众号JSAPI支付的核心区别,不用瞎找这个选项。

  2. 支付能力校验 在小程序后台「微信支付」页面,能看到已绑定的商户号且状态为「正常」,就说明支付权限已经生效。

五、后端服务开发(Node.js + V3接口)

5.1 完整支付流程

先理清楚整个交互逻辑,别写反了顺序:

  1. 用户在小程序点击支付,前端获取登录code传给后端
  2. 后端通过code换取用户openid
  3. 后端生成唯一商户订单号,调用微信JSAPI统一下单接口,获取prepay_id
  4. 后端基于prepay_id生成前端调起支付所需的签名参数,返回给小程序
  5. 小程序调用wx.requestPayment唤起支付收银台
  6. 用户支付完成后,微信异步回调后端notify_url,后端更新订单状态
  7. 前端支付回调中,请求后端查询订单真实状态,再展示最终结果

别嫌流程啰嗦,很多人跳步骤,最后出问题查半天。

5.2 依赖安装与初始化

推荐用wechatpay-node-v3这个SDK,不用自己手写签名逻辑,能少踩90%的坑。

安装依赖:

bash 复制代码
npm install wechatpay-node-v3 axios

初始化支付配置:

js 复制代码
const WxPay = require('wechatpay-node-v3');
const fs = require('fs');
const path = require('path');

// 读取商户私钥文件
const privateKey = fs.readFileSync(
  path.join(__dirname, './cert/apiclient_key.pem'),
  'utf8'
);

const wxPay = new WxPay({
  appid: '你的小程序appid',
  mchid: '你的商户号mchid',
  privateKey: privateKey,
  serial_no: '你的商户证书序列号',
  apiv3_private_key: '你的APIv3密钥',
});

5.3 JSAPI统一下单接口

js 复制代码
/**
 * JSAPI统一下单
 * @param {string} openid 用户openid
 * @param {string} outTradeNo 商户订单号(全局唯一)
 * @param {number} total 支付金额,单位:分
 * @param {string} description 商品描述
 */
async function createOrder(openid, outTradeNo, total, description) {
  const params = {
    description: description,
    out_trade_no: outTradeNo,
    notify_url: 'https://你的域名/api/pay/notify',
    amount: {
      total: total,
      currency: 'CNY',
    },
    payer: {
      openid: openid,
    },
  };

  const result = await wxPay.transactions_jsapi(params);
  
  // 生成前端调起支付需要的完整签名参数
  const payParams = await wxPay.getSignParams(result.prepay_id);
  return payParams;
}

5.4 支付结果异步回调处理

回调接口要求:公网可访问的HTTPS地址,支持POST请求,不能带端口号和参数。

核心逻辑:先验签解密,再处理订单,最后必须给微信返回成功应答,否则微信会持续重试回调。

js 复制代码
// 支付回调接口
async function payNotify(req, res) {
  try {
    // 验证签名并解密回调报文
    const result = wxPay.verifySignAndDecrypt(req.headers, req.body);
    
    if (result.trade_state === 'SUCCESS') {
      // 支付成功,更新数据库订单状态,处理业务逻辑
      const outTradeNo = result.out_trade_no;
      const transactionId = result.transaction_id;
      
      // 必须返回成功应答,否则微信会持续重试
      res.status(200).json({ code: 'SUCCESS', message: '成功' });
    } else {
      res.status(200).json({ code: 'FAIL', message: '支付未成功' });
    }
  } catch (err) {
    console.error('支付回调处理失败', err);
    res.status(500).json({ code: 'FAIL', message: '处理失败' });
  }
}

5.5 订单查询接口

划重点:绝对不能只依赖微信异步回调。前端支付完成后必须主动查询订单状态,回调可能延迟、丢失,也可能被伪造。

js 复制代码
async function queryOrder(outTradeNo) {
  const result = await wxPay.query({ out_trade_no: outTradeNo });
  return result;
}

六、小程序前端支付实现

6.1 前端支付流程

  1. 调用wx.login()获取临时登录凭证code
  2. code传递给后端,发起下单请求
  3. 接收后端返回的支付参数,调用wx.requestPayment唤起支付
  4. 支付操作完成后,请求后端查询订单真实状态,展示结果

6.2 完整代码示例

js 复制代码
Page({
  // 支付按钮点击事件
  async handlePay() {
    wx.showLoading({ title: '支付中...', mask: true });
    
    try {
      // 1. 获取登录凭证code
      const { code } = await wx.login();
      
      // 2. 请求后端下单,获取支付参数
      const payRes = await wx.request({
        url: 'https://你的域名/api/pay/createOrder',
        method: 'POST',
        data: {
          code: code,
          goodsId: '商品ID',
        },
      });
      
      const payParams = payRes.data.data;
      
      // 3. 唤起微信支付收银台
      const paymentResult = await wx.requestPayment({
        timeStamp: payParams.timeStamp,
        nonceStr: payParams.nonceStr,
        package: payParams.package,
        signType: payParams.signType,
        paySign: payParams.paySign,
      });
      
      // 4. 支付操作完成,主动查询订单状态
      if (paymentResult.errMsg === 'requestPayment:ok') {
        const orderRes = await wx.request({
          url: 'https://你的域名/api/pay/queryOrder',
          data: { outTradeNo: '当前商户订单号' },
        });
        
        if (orderRes.data.data.tradeState === 'SUCCESS') {
          wx.showToast({ title: '支付成功', icon: 'success' });
          // 支付成功后的业务跳转
        }
      }
    } catch (err) {
      if (err.errMsg === 'requestPayment:fail cancel') {
        wx.showToast({ title: '已取消支付', icon: 'none' });
      } else {
        wx.showToast({ title: '支付失败', icon: 'error' });
        console.error('支付异常', err);
      }
    } finally {
      wx.hideLoading();
    }
  },
});

注意:支付金额不要由前端传递,后端根据商品ID计算,避免被篡改。

七、联调测试与上线

7.1 联调注意事项

  • 必须真机测试:微信开发者工具里无法唤起真实支付收银台,必须扫码预览用真机测
  • 小额测试:建议用0.01元的测试金额,验证完整支付与回调流程
  • 本地调试回调:本地开发没有公网地址的,用内网穿透工具(cpolar、ngrok都可以)临时映射
  • 打日志:后端把完整的请求、响应、回调日志都打印出来,出问题能快速定位

7.2 上线前检查清单

对着勾一遍,别漏项:

  • 后端接口域名已添加至小程序request合法域名
  • 商户证书、APIv3密钥配置正确,无泄露风险
  • 支付回调已做签名验证,防止伪造回调请求
  • 订单状态以查询接口结果为准,不依赖前端回调状态
  • 商户订单号全局唯一,避免重复下单
  • 支付金额由后端校验计算,不直接使用前端传入的参数

八、高频踩坑避坑指南

都是实际项目里踩过的坑,列出来省得你再踩:

  1. 金额单位错误:微信支付所有接口的金额单位都是分,不是元,传错会差100倍
  2. 签名验证失败:90%的情况是参数大小写错误、证书序列号不匹配、APIv3密钥不一致
  3. 回调不触发:检查接口是不是POST、支不支持HTTPS、有没有被防火墙拦截,必要时放行微信官方回调IP段
  4. 重复回调处理:微信支付回调可能会多次推送,后端必须做幂等处理,避免重复执行业务逻辑
  5. 异主体绑定限制:商户号和小程序主体不一致需要额外审核,且部分支付功能受限,优先用同主体的商户号
相关推荐
马可家的菠萝1 小时前
自动保存已经有了,为什么笔记软件还需要“历史版本”?
前端·后端·架构
leavesleo1 小时前
AI Agent 开发实战:从零搭一个能用的 Agent
后端
行百里er2 小时前
加个依赖就生效?一行搞定 Spring Boot Starter 自动装配
java·后端·监控
程序员鱼皮2 小时前
3 大 DeepSeek Harness 进阶玩法,招多个大肥鱼帮我干活!
前端·后端·ai编程
苍何2 小时前
DeepSeek 终于支持多模态了(附实测及接入教程)
后端
KoPa2 小时前
HeySmart:大模型开源网关基座-请求生命周期与钩子引擎
前端·后端
苍何2 小时前
做AI视频还在拆盲盒?手把手教你导演级运镜(附教程)
后端
爱学习的小邓同学2 小时前
Golang语言入门
开发语言·后端·golang