
我们把微信小程序支付接口对接的全流程,拆成了零基础能跟着走的详细步骤——从前期的商户平台注册、资质审核,到小程序后台绑定支付权限,再到前端调用支付API、后端处理订单和回调,每一步都附了操作逻辑和注意事项。怕踩坑?我们还整理了高频避坑指南:比如签名错误大多是参数顺序或编码问题,支付状态不同步要检查回调URL的外网访问权限,就连“统一下单接口返回失败”的常见原因(像商户号未绑定小程序、API密钥有误)都列了清单。
不用找技术大佬,不用死磕专业术语,跟着这篇教程一步步来,就能自己搞定小程序支付功能。不管你是第一次做小程序的创业者,还是想给项目加支付的运营,这篇指南都能让你少走弯路,快速把支付功能上线。
你有没有过这种情况?想给小程序加支付功能,对着官方文档看了半天,越看越懵——商户号怎么注册?API密钥在哪弄?前端点支付没反应,到底是参数错了还是签名的问题?去年我帮朋友的奶茶店小程序对接支付,他自己试了三天没搞定,一会儿商户号审核 一会儿签名错误,后来我带着他一步步走,半天就把支付功能跑通了。今天我把这份“踩坑后 的教程”分享给你,不用找技术大佬,自己就能搞定。
前期准备:这些材料和配置没弄对,后面全白搭
对接支付的第一步,不是写代码,而是把前期资质和配置做对——我见过太多人上来就写代码,结果因为商户号没注册、API密钥没弄对,从头返工。去年朋友的奶茶店小程序,就是因为没注意“商户号要和小程序主体一致”,导致审核卡了一周,后来换成企业主体的商户号才解决。
要对接小程序支付,必须有微信支付商户号(注意不是小程序的APPID)。注册地址是微信支付商户平台(https://pay.weixin.qq.com/,nofollow),需要准备这几样材料:
注册的时候要注意:商户号的“主体类型”要和小程序一致——如果小程序是企业主体,商户号也得是企业;如果是个体,商户号就选个体。朋友之前用个人身份注册商户号,结果绑定小程序时提示“主体不一致”,只能注销重新注册,耽误了一周。
注册好商户号后,要在两个地方做绑定:
为什么要绑定?微信支付的规则是“谁的小程序,就用谁的商户号收钱”——绑定是为了确认“这个小程序有权用这个商户号收款”,没绑定的话,后面调用支付接口会直接报错。
API密钥是接口调用的加密凭证,用来保证你和微信支付之间的通信安全(比如防止别人篡改你的订单金额)。生成步骤很简单:
这里要注意:API密钥一旦生成,就看不到明文了,丢了只能重置——我朋友之前把密钥存到微信聊天记录里,后来手机丢了,只能重置,结果导致之前的接口全要重新配置,麻烦得很。
给你整理了一份前期准备材料清单,照着勾就行,避免漏东西:
材料/配置项 | 具体要求 | 避坑提醒 |
---|---|---|
营业执照 | 个体/企业原件扫描件 | 主体与小程序一致 |
法人身份证 | 正反面清晰扫描件 | 有效期≥6个月 |
银行账户 | 对公/法人个人账户 | 账户名与主体一致 |
API密钥 | 32位字母数字组合 | 不要截图,丢了只能重置 |
对接全流程:从前端点支付到后端收回调的每一步
前期准备做好了,接下来就是写代码对接。我把整个流程拆成了4步,每一步都附了“我踩过的坑”,你跟着走就能避免出错。去年帮朋友调的时候,就是按这个流程走的,半天就把支付功能跑通了。
用户在小程序里选好奶茶,点“立即支付”,前端要做的是把订单信息传给后端——比如订单号、金额、商品名称、用户的openid(注意:openid是用户在你小程序里的唯一标识,要通过wx.login获取)。
举个例子,前端代码可以这样写:
// 用户点支付按钮时触发
function handlePay() {
wx.request({
url: 'https://www.yourdomain.com/api/pay', // 你的后端接口
data: {
orderId: '20240520123456', // 订单号,要唯一
totalFee: 1500, // 金额,单位是分(1500=15元)
productName: '珍珠奶茶', // 商品描述
openid: 'oABC123...' // 用户的openid
},
success: (res) => {
// 拿到后端返回的支付参数,唤起支付
if (res.data.code === 0) {
wx.requestPayment({
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: res.data.signType,
paySign: res.data.paySign,
success: (payRes) => {
console.log('支付成功', payRes);
// 跳转到支付成功页面
},
fail: (payErr) => {
console.log('支付失败', payErr);
// 提示用户重新支付
}
});
}
}
});
}
这里要注意:订单号必须唯一——朋友之前用“时间戳”当订单号,结果同一秒有两个用户支付,导致订单号重复,支付成功后没法区分订单,后来改成“时间戳+随机数”才解决。
前端把订单信息传给后端后,后端要做的是调用微信支付的“统一下单接口”(https://api.mch.weixin.qq.com/pay/unifiedorder,nofollow),拿到“prepay_id”(预支付交易会话标识),然后生成前端需要的支付参数。
(1)统一下单接口的参数怎么填?
统一下单接口需要传10多个参数,核心的几个是:
appid
:你的小程序APPID;mch_id
:你的微信支付商户号;nonce_str
:随机字符串(比如用UUID生成,32位以内);body
:商品描述(比如“珍珠奶茶”,不能超过127个字符);out_trade_no
:订单号(和前端传的一致);total_fee
:金额(单位分,比如15元=1500);spbill_create_ip
:用户的IP地址(可以用request.headers[‘x-forwarded-for’]获取);notify_url
:支付结果回调URL(要能被外网访问,比如https://www.yourdomain.com/api/notify);trade_type
:固定填“JSAPI”(因为小程序支付属于JSAPI支付);openid
:用户的openid(前端传的)。这些参数要按字母顺序排序,然后用API密钥加密,生成sign
(签名)——这一步错了,接口会直接返回“签名错误”。去年帮客户调的时候,他就是没按顺序排序,导致sign一直不对,查了2小时才找到原因。
(2)生成前端需要的支付参数
调用统一下单接口成功后,微信会返回prepay_id
,接下来后端要把prepay_id
转换成前端能直接用的支付参数:
timeStamp
:当前时间戳(秒级,比如Math.floor(Date.now() / 1000)
);nonceStr
:随机字符串(和统一下单的nonce_str
可以一致);package
:固定格式prepay_id=xxx
(比如prepay_id=wx20240520123456789
);signType
:签名类型, 用MD5
(简单好调试);paySign
:用appId、timeStamp、nonceStr、package、signType
这5个参数,按顺序排序后,用API密钥加密生成的签名。为什么要生成paySign
?因为前端唤起支付时,微信要验证“这个支付请求是你发起的”——paySign
就是验证的凭证,错了的话,前端点支付会提示“支付验证失败”。
后端把支付参数传给前端后,前端用wx.requestPayment
接口唤起支付——这一步很简单,但要注意:package
参数必须包含prepay_id=
,比如prepay_id=wx20240520123456789
,少了这个前缀会直接报错。
去年朋友的小程序,就是因为后端返回的package
少了prepay_id=
,导致前端点支付没反应,查了半天日志才发现。
用户支付成功后,微信会把支付结果主动推送给你预留的notify_url
(POST请求,XML格式)。后端要做的是:
transaction_id
(微信支付订单号)、out_trade_no
(你的订单号)、total_fee
(金额)等信息;sign
,和你自己计算的签名对比,确认数据没被篡改;transaction_id
;SUCCESS
(XML格式),不然微信会在24小时内重试8次,可能导致重复处理订单。举个后端处理回调的例子(用Node.js):
const xml2js = require('xml2js');
// 处理支付回调的接口
app.post('/api/notify', (req, res) => {
let xmlData = '';
req.on('data', (chunk) => {
xmlData += chunk;
});
req.on('end', () => {
// 解析XML
xml2js.parseString(xmlData, (err, result) => {
if (err) {
res.send('');
return;
}
const notifyData = result.xml;
const outTradeNo = notifyData.out_trade_no[0]; // 你的订单号
const transactionId = notifyData.transaction_id[0]; // 微信支付订单号
const totalFee = notifyData.total_fee[0]; // 金额(分)
// 验证签名(这里省略签名验证逻辑, 用微信的SDK)
const isSignValid = verifySign(notifyData);
if (!isSignValid) {
res.send('');
return;
}
// 更新订单状态(比如从数据库里找到订单,改成已支付)
updateOrderStatus(outTradeNo, '已支付', transactionId)
.then(() => {
// 返回成功响应
res.send('');
})
.catch((err) => {
res.send('');
});
});
});
});
这里要注意:回调URL必须能被外网访问——朋友之前用localhost:3000
当回调URL,结果微信根本访问不到,后来换成线上服务器的域名(比如https://www.yourdomain.com/api/notify),并在服务器上开放了80/443端口,才收到了回调。
最容易踩的5个坑,我帮你把坑填上
去年帮3个朋友对接小程序支付,踩了8个坑,其中这5个是最常见的,你一定要避开:
原因:参数顺序不对、编码错误、API密钥写错;
解决方法:
appid
在mch_id
前面,nonce_str
在body
后面);原因:回调URL无法访问、没返回SUCCESS
、订单号重复;
解决方法:
SUCCESS
;原因:小程序没绑定商户号;
解决方法:去商户平台和小程序后台,重新检查绑定状态。
原因:小程序的“业务域名”没配置;
解决方法:登录小程序后台,找到“开发→开发设置→业务域名”,把你的后端域名加进去(比如https://www.yourdomain.com)。
原因:金额单位错了(用了元而不是分);
解决方法:后端传total_fee
的时候,一定要转成分(比如15元=1500)。
如果你按这些步骤试了,还是有问题,评论区留个言——比如“我调用统一下单接口返回‘签名错误’”,我帮你看看。去年帮朋友解决问题的时候,就是靠“一步步排查参数”搞定的,你也能行。你有没有过这种情况?想给小程序加支付功能,对着官方文档看了半天,越看越懵——商户号怎么注册?API密钥
本文常见问题(FAQ)
注册微信支付商户号需要准备哪些材料?
需要营业执照(个体工商户或企业原件扫描件,主体要和小程序一致)、法人身份证(正反面扫描件,有效期6个月以上)、银行账户(企业用对公账户,个体可用法人个人银行卡,账户名称需和小程序主体一致)、法人手机号和邮箱(接收验证码和审核结果)。
对接支付时签名错误一般是什么原因?
大多是参数顺序不对(要按字母顺序排序)、编码错误(要用UTF-8而不是GBK)或API密钥写错。可以用微信支付的“签名验证工具”(https://pay.weixin.qq.com/wiki/tools/signverify/,nofollow)检查,确保参数排序和编码正确。
支付成功后订单状态没更新怎么办?
先检查回调URL能不能被外网访问(用Postman测试POST请求),处理完回调一定要返回“SUCCESS”(XML格式);再确认订单号是不是唯一( 用“时间戳+随机数”),避免重复订单导致状态无法更新。
统一下单接口返回“APPID未授权”怎么解决?
一般是小程序没和商户号绑定,去微信支付商户平台(产品中心→APPID授权管理)输入小程序APPID授权,再到微信公众平台(小程序后台→微信支付→商户号管理)重新绑定商户号就行。
前端点支付显示的金额和实际不符是怎么回事?
大概率是金额单位错了,微信支付接口的“total_fee”参数要填分(比如15元得写1500),如果用了元就会显示不对,后端传参时记得把金额转成分。