下面给你一份 微信小程序支付(含 uni-app)最全、最详细、从零到成品的讲解,涵盖 小程序端、云函数/服务端、uni-app 两种写法、注意事项、常见报错、回调通知、退款 等完整流程。
✅ 微信小程序支付(含 uni-app)超详细教程
核心流程:
小程序端下单 → 后端统一下单(生成预支付单)→ 小程序拉起支付 → 支付成功 → 服务端支付回调验证 → 更新订单状态
目录
- 必备前置配置
- 小程序支付流程图
- 小程序端 JS(原生写法)
- uni-app 端代码(APP + 小程序同写法)
- Node.js 服务端统一下单代码
- 支付结果回调(服务端)
- 常见坑与错误
- 退款示例
1. 📌 必备前置配置
你需要:
✔ 已开通微信支付商户号
- 商户号 MchID
- APIv3 密钥
- API 证书(私钥 + cert)
✔ 小程序 AppID
- 配置到商户平台:
路径:商户平台 → 产品中心 → APPID 绑定
✔ 后端服务(Node.js / Java / PHP)
这里示例 Node.js。
2. 📌 小程序支付流程图
用户点击支付
↓
小程序请求后端下单
↓
后端调用微信支付统一下单 API
↓
微信返回 prepay_id
↓
小程序 wx.requestPayment 调起支付
↓
支付成功
↓
微信服务器发送支付回调 → 后端
↓
后端校验签名并修改订单状态
3. 📌 小程序端代码(原生 WeChat Mini-program)
① 前端发起订单请求
wx.request({
url: 'https://your-server.com/pay/unifiedorder',
method: 'POST',
data: { orderId: '12345' },
success(res) {
const pay = res.data;
wx.requestPayment({
timeStamp: pay.timeStamp,
nonceStr: pay.nonceStr,
package: pay.package,
signType: pay.signType,
paySign: pay.paySign,
success(res) {
console.log('支付成功', res);
},
fail(err) {
console.log('支付失败', err);
}
});
}
});
返回的 pay 信息来自后端签名。
4. 📌 uni-app 端完整支付写法(小程序 + APP 通用)
uni.request + uni.requestPayment
payOrder(orderId) {
uni.request({
url: 'https://your-server.com/pay/unifiedorder',
method: 'POST',
data: { orderId },
success: (res) => {
const pay = res.data;
uni.requestPayment({
provider: 'wxpay',
timeStamp: pay.timeStamp,
nonceStr: pay.nonceStr,
package: pay.package,
signType: 'RSA',
paySign: pay.paySign,
success: res => {
console.log('支付成功', res);
},
fail: err => {
console.log('支付取消/失败', err);
}
});
}
});
}
在 App 端 支付时 provider 必须写
"wxpay"。
5. 📌 Node.js 后端统一下单(最全实现)
使用官方 微信支付 v3 API:
安装库:
npm i axios crypto
统一下单接口代码(Node.js Koa/Express 都可用)
const axios = require('axios');
const crypto = require('crypto');
const fs = require('fs');
const appId = "你的appid";
const mchId = "你的商户号";
const apiKey = "你的APIv3密钥";
// 私钥文件(apiclient_key.pem)
const privateKey = fs.readFileSync('./cert/apiclient_key.pem');
// 生成签名函数
function sign(method, url, timestamp, nonceStr, body = '') {
const message = `${method}\n${url}\n${timestamp}\n${nonceStr}\n${body}\n`;
const signer = crypto.createSign('RSA-SHA256');
signer.update(message);
return signer.sign(privateKey, 'base64');
}
exports.unifiedorder = async (req, res) => {
const { orderId } = req.body;
const url = '/v3/pay/transactions/jsapi';
const fullUrl = 'https://api.mch.weixin.qq.com' + url;
const data = {
appid: appId,
mchid: mchId,
description: "商品描述",
out_trade_no: orderId,
notify_url: "https://your-server.com/pay/notify",
amount: {
total: 100, // 单位:分
},
payer: {
openid: req.body.openid,
}
};
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonceStr = crypto.randomBytes(16).toString('hex');
const bodyStr = JSON.stringify(data);
const signature = sign('POST', url, timestamp, nonceStr, bodyStr);
const auth = `WECHATPAY2-SHA256-RSA2048 mchid="${mchId}",nonce_str="${nonceStr}",signature="${signature}",timestamp="${timestamp}",serial_no="你的证书序列号"`;
const result = await axios.post(fullUrl, data, {
headers: {
'Authorization': auth,
'Content-Type': 'application/json'
}
});
const prepayId = result.data.prepay_id;
// 生成前端需要的签名
const payTimestamp = timestamp;
const payNonceStr = nonceStr;
const payPackage = `prepay_id=${prepayId}`;
const paySignString = `${appId}\n${payTimestamp}\n${payNonceStr}\n${payPackage}\n`;
const paySign = crypto
.createSign('RSA-SHA256')
.update(paySignString)
.sign(privateKey, 'base64');
res.json({
timeStamp: payTimestamp,
nonceStr: payNonceStr,
package: payPackage,
signType: 'RSA',
paySign,
});
};
6. 📌 微信支付回调通知(必须处理)
微信支付成功后会 POST 到你的:
https://your-server.com/pay/notify
Node.js 代码:
exports.notify = async (req, res) => {
const { resource, summary } = req.body;
console.log("支付回调内容:", req.body);
// 解密 resource.ciphertext
const decipher = crypto.createDecipheriv(
'aes-256-gcm',
apiKey,
Buffer.from(resource.nonce, 'hex')
);
decipher.setAuthTag(Buffer.from(resource.tag, 'hex'));
const decrypted = JSON.parse(
decipher.update(resource.ciphertext, 'base64', 'utf8') + decipher.final('utf8')
);
console.log("已解密的支付信息:", decrypted);
// 修改订单状态…
// updateOrder(decrypted.out_trade_no)
res.status(200).json({ code: 'SUCCESS' });
};
7. 📌 常见报错与解决方法
| 错误 | 原因 | 解决 |
|---|---|---|
INVALID_REQUEST | 参数不完整 | 检查 appid, mchid, notify_url |
appid 和 openid 不匹配 | openid 非当前小程序 | 必须用 wx.login 获取 |
| 支付调起后无反应 | 前端签名错误 | 重点检查 paySign |
prepay_id 已失效 | 5 分钟未使用 | 重签名并重新下单 |
| 小程序显示 “商户号未开通小程序支付” | 商户号未绑定 appid | 去商户平台绑定 |
8. 📌 退款示例(Node.js)
exports.refund = async (req, res) => {
const url = '/v3/refund/domestic/refunds';
const data = {
out_trade_no: req.body.orderId,
out_refund_no: 'refund_' + Date.now(),
amount: {
refund: 100,
total: 100,
currency: 'CNY'
}
};
// 与统一下单一样:生成签名 → 调用微信 API
};