如果你已经申请好了微信支付商户号,要给小程序接入支付能力,这篇文章将按官方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 商户平台发起授权
- 用商户号超级管理员账号登录微信支付商户平台
- 左侧导航进「产品中心 → 账号关联(AppID绑定)」,点右侧「新增授权AppID」
- 准确填写小程序的
AppID,阅读并签署授权协议后提交- 商户号与小程序主体一致:提交后等待小程序侧确认即可
- 主体不一致:需额外填写小程序认证主体名称,签署《联合营运承诺函》
2.2 小程序后台确认授权
- 登录微信公众平台(小程序账号)
- 左侧进「功能 → 微信支付 → 商户号管理」,在「待关联商户号」列表找到对应申请
- 点击「确认授权」完成绑定
也可以直接在小程序后台「微信支付」页面选「已有商户号,快速绑定」,流程和效果都一样。
2.3 绑定结果校验
回到商户平台的「账号关联」页面,对应AppID状态显示「已授权」就完成了。
三、商户平台API安全配置
API安全页面一共四个配置项,做小程序V3支付,只需要配置其中两个,剩下两个不用动。
3.1 商户API证书(必须配置)
后端调用所有V3接口时,都要用证书里的商户私钥对请求签名,用来证明商户身份,强制要求。
操作步骤:
- 进入商户平台「账户中心 → API安全 → 商户API证书」,点击「申请证书」
- 按页面指引生成证书请求文件,提交后下载证书压缩包
- 解压后得到两个核心文件:
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都内置了这个能力,不需要手动申请
四、小程序后台基础配置
两件事,做完就可以开始写代码了。
-
配置服务器域名 进入小程序后台「开发管理 → 开发设置 → 服务器域名」,在「request合法域名」中添加后端接口对应的域名。
注意:小程序支付不需要配置支付授权目录,这是和公众号JSAPI支付的核心区别,不用瞎找这个选项。
-
支付能力校验 在小程序后台「微信支付」页面,能看到已绑定的商户号且状态为「正常」,就说明支付权限已经生效。
五、后端服务开发(Node.js + V3接口)
5.1 完整支付流程
先理清楚整个交互逻辑,别写反了顺序:
- 用户在小程序点击支付,前端获取登录
code传给后端 - 后端通过
code换取用户openid - 后端生成唯一商户订单号,调用微信JSAPI统一下单接口,获取
prepay_id - 后端基于
prepay_id生成前端调起支付所需的签名参数,返回给小程序 - 小程序调用
wx.requestPayment唤起支付收银台 - 用户支付完成后,微信异步回调后端
notify_url,后端更新订单状态 - 前端支付回调中,请求后端查询订单真实状态,再展示最终结果
别嫌流程啰嗦,很多人跳步骤,最后出问题查半天。
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 前端支付流程
- 调用
wx.login()获取临时登录凭证code - 将
code传递给后端,发起下单请求 - 接收后端返回的支付参数,调用
wx.requestPayment唤起支付 - 支付操作完成后,请求后端查询订单真实状态,展示结果
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密钥配置正确,无泄露风险
- 支付回调已做签名验证,防止伪造回调请求
- 订单状态以查询接口结果为准,不依赖前端回调状态
- 商户订单号全局唯一,避免重复下单
- 支付金额由后端校验计算,不直接使用前端传入的参数
八、高频踩坑避坑指南
都是实际项目里踩过的坑,列出来省得你再踩:
- 金额单位错误:微信支付所有接口的金额单位都是分,不是元,传错会差100倍
- 签名验证失败:90%的情况是参数大小写错误、证书序列号不匹配、APIv3密钥不一致
- 回调不触发:检查接口是不是POST、支不支持HTTPS、有没有被防火墙拦截,必要时放行微信官方回调IP段
- 重复回调处理:微信支付回调可能会多次推送,后端必须做幂等处理,避免重复执行业务逻辑
- 异主体绑定限制:商户号和小程序主体不一致需要额外审核,且部分支付功能受限,优先用同主体的商户号