小程序支付功能完整实现指南:从申请到上线
2026-04-20 01:07:03
分类: 小程序定制开发
tags: 小程序支付,微信支付,支付功能,小程序开发,订单系统,支付安全,商户接入
字数: 约5800字
---
很多想做小程序的人,最怕的就是"支付"这两个字。
觉得复杂、怕出错、怕安全问题、不知道从哪开始。
实际上,微信小程序支付的接入流程是相当成熟和标准化的,如果你懂基本的后端开发,按照文档一步步来,2-3天就能跑通。
今天把完整流程梳理一下,从商户号申请到代码实现,覆盖每个关键步骤。
在接入微信支付之前,你需要:
1. 微信公众号或小程序(已认证)
个人开发者只能申请未认证的小程序,无法使用微信支付。微信支付要求账号必须是企业/个体工商户身份,完成认证(认证费300元/年)。
如果你是个体工商户,需要提供:
- 营业执照
- 法人身份证
- 银行卡(结算账户)
2. 微信支付商户号
去 pay.weixin.qq.com 申请微信支付商户,提交上述材料,一般1-3个工作日审核完成。
审核通过后,你会得到:
- mch_id(商户号)
- 可以在后台生成 api_key(api密钥)
- 可以下载 api证书(apiclient_cert.pem、apiclient_key.pem)
3. 小程序关联商户号
在小程序管理后台→开发管理→支付设置,关联你的商户号。
这步很多人跳过,直接看代码,结果出了问题不知道在哪。
小程序支付的完整流程(5步):
1. 用户点击下单
2. 小程序前端 → 自己的服务端(传递商品信息)
3. 自己的服务端 → 微信服务端(调统一下单api)
4. 微信服务端 → 自己的服务端(返回prepay_id)
5. 自己的服务端 → 小程序前端(返回签名参数)
6. 小程序调用wx.requestpayment(弹出支付框)
7. 用户确认支付
8. 微信服务端 → 自己的服务端(支付结果异步通知)
9. 自己的服务端处理订单状态
关键点:
- 统一下单要在自己的服务端做,不能在小程序前端做(安全原因)
- api密钥不能暴露在前端代码里
- 支付结果要靠"异步通知"确认,不能只靠前端的回调
bash
npm install wxpay-node-v3
推荐用微信官方推荐的sdk,不要自己手写签名算法(容易出错)。
javascript
const wxpay = require(wxpay-node-v3);
const pay = new wxpay({
appid: wx你的小程序appid,
mchid: 你的商户号,
serial_no: 证书序列号,
privatekey: fs.readfilesync(./apiclient_key.pem),
certs: {}
});
// 统一下单
async function createorder(req, res) {
const { openid, amount, orderid, description } = req.body;
try {
const result = await pay.transactions_jsapi({
appid: wx你的小程序appid,
mchid: 你的商户号,
description: description, // 商品描述
out_trade_no: orderid, // 商户订单号(唯一)
notify_url: https://yourdomain.com/pay/notify, // 异步通知地址
amount: {
total: amount, // 金额,单位分
currency: cny
},
payer: {
openid: openid // 用户openid
}
});
// result.prepay_id是下单成功返回的预支付id
// 需要对这个id进行二次签名,返回给小程序
const timestamp = math.floor(date.now() / 1000).tostring();
const noncestr = math.random().tostring(36).substring(2, 15);
const packagestr = prepay_id=${result.prepay_id};
// 签名(这步很重要,不能省略)
const signstr = ${appid}\n${timestamp}\n${noncestr}\n${packagestr}\n;
const sign = createsign(signstr, privatekey); // 用私钥签名
res.json({
timestamp: timestamp,
noncestr: noncestr,
package: packagestr,
signtype: rsa,
paysign: sign
});
} catch (error) {
console.error(下单失败, error);
res.status(500).json({ error: 下单失败 });
}
}
这步非常重要,很多人忽略。
当用户支付成功后,微信会向你的 notify_url 发送一个post请求,告诉你支付结果。你必须处理这个通知,才能更新订单状态。
不能只靠小程序前端的回调!因为用户可能在支付后直接关掉小程序,前端回调不会触发。
javascript
// 处理微信支付通知
app.post(/pay/notify, async (req, res) => {
try {
// 验证通知签名(防止伪造)
const isvalid = await pay.verifysign({
headers: req.headers,
body: req.body
});
if (!isvalid) {
return res.status(401).json({ message: 签名验证失败 });
}
// 解密通知数据
const notifydata = await pay.decipher_gcm(
req.body.resource.ciphertext,
req.body.resource.associated_data,
req.body.resource.nonce,
apikey // v3 api密钥
);
const { trade_state, out_trade_no, transaction_id } = notifydata;
if (trade_state === success) {
// 更新数据库订单状态为已支付
await updateorderstatus(out_trade_no, paid, transaction_id);
// 触发后续业务逻辑(发货、发短信通知等)
await triggerafterpayment(out_trade_no);
}
// 必须回复微信,否则微信会重复通知
res.json({ code: success, message: 成功 });
} catch (error) {
console.error(通知处理失败, error);
res.status(500).json({ code: fail, message: 处理失败 });
}
});
注意:如果你的服务器处理失败,微信会重试通知(最多15次,间隔递增)。你的处理逻辑要做幂等处理,同一个订单号收到多次通知不会重复更新。
javascript
// 小程序前端
page({
async pay() {
try {
// 1. 向自己的服务端提交下单请求
const orderresult = await wx.request({
url: https://yourdomain.com/api/createorder,
method: post,
data: {
orderid: this.data.orderid,
amount: this.data.amount,
description: this.data.description
}
});
if (orderresult.statuscode !== 200) {
wx.showtoast({ title: 下单失败, icon: error });
return;
}
// 2. 调用微信支付
await wx.requestpayment({
...orderresult.data, // timestamp, noncestr, package, signtype, paysign
success: (res) => {
console.log(支付成功, res);
// 注意:这里只是前端确认,不要依赖这个更新业务状态
// 业务状态更新应该在服务端的异步通知里处理
this.handlepaysuccess();
},
fail: (err) => {
if (err.errmsg === requestpayment:fail cancel) {
// 用户取消支付,不需要处理
} else {
wx.showtoast({ title: 支付失败, icon: error });
}
}
});
} catch (error) {
console.error(支付流程出错, error);
}
}
});
问题一:签名错误(sign error)
最常见的原因:
- api密钥和商户号不匹配
- 签名字段顺序错误(微信对字段顺序有要求)
- 金额单位搞错(微信支付金额单位是"分",100分=1元)
问题二:system error / 商户号不存在
通常是appid和mchid没有关联,或者关联还在审核中。
问题三:支付成功但订单没更新
多半是异步通知没有处理,或者 notify_url 不可访问(内网地址)。
开发时可以用 ngrok 等工具把本地端口暴露到公网,让微信能通知到你的开发服务器。
问题四:重复支付
用户在支付流程中途退出,再次支付时用了同一个 out_trade_no,微信可能拒绝第二次下单。
解决方案:检查订单状态,如果已有待支付订单,询问用户是否继续支付同一笔,而不是创建新订单。
支付只是一半,退款功能同样重要。
javascript
// 申请退款
async function refundorder(out_trade_no, refund_amount, total_amount) {
const result = await pay.refunds({
out_trade_no: out_trade_no, // 原订单号
out_refund_no: refund_${date.now()}, // 退款单号(唯一)
reason: 用户申请退款,
amount: {
refund: refund_amount, // 退款金额(分)
total: total_amount, // 原订单金额(分)
currency: cny
}
});
return result;
}
退款结果也有异步通知,同样需要处理。
---
微信小程序支付技术上并不难,难的是理解整个流程,特别是异步通知这个环节。
只要把流程想清楚,代码写起来是很顺的。
---
发布时间:2026-04-21
关键词:小程序支付,微信支付,支付功能,小程序开发,订单系统,支付安全

扫一扫
微信客服在线
24小时服务热线
13807814037