微信支付接口调半天调不通,到底是哪里出了问题?
做微信小程序,卡在最关键的一步——调通支付接口。网上教程千篇一律,都是复制官方文档,告诉你“先调统一下单,再调发起支付”,但实际跑起来全是坑。我今天用讲课的方式,把整个流程拆开揉碎,结合我本地跑过的真实案例,把你可能遇到的每个问题都提前堵死。
先讲一个最常见的误区:你以为小程序支付就是前端调一下wx.requestPayment就完事了?错。支付的核心逻辑全在后端。前端只是最后收尾的“表演者”。真正的“导演”是你的服务器,它要去跟微信支付服务器通信,拿到预支付订单号,再交给前端去唤起支付界面。这个顺序搞反了,你永远调不通。
第一步,你得先搞定微信支付的商户号和API密钥。在这里就卡住。商户号要单独申请,跟你小程序的AppID不是一回事。你登录微信支付商户平台,在“账户中心”里找到“API安全”,设置一个32位的密钥(叫APIv3密钥)。注意,这个密钥不能随便编,得是大小写字母加数字的混合体,而且一定要保存好,丢了就得重置,重置后所有旧订单的退款都查不了。
第二步,后端统一下单。我拿Node.js举例(其他语言逻辑一样)。你写一个接口,接收前端传过来的商品ID、用户openid、支付金额。然后你用这些参数拼一个XML(微信支付老接口用XML,新接口用JSON,但大多数老商户还在用XML),里面必须包含:appid、mch_id(商户号)、nonce_str(随机字符串,自己生成)、body(商品描述)、out_trade_no(你自己的订单号,不能用重复的)、total_fee(单位是分,注意不是元)、spbill_create_ip(用户终端IP,小程序里可以传客户端的IP)、notify_url(支付成功后的回调地址,这个必须公网能访问,不能用localhost)、trade_type(固定填JSAPI,因为是小程序)。
这里面最容易出错的几个点:total_fee如果你传了“1”,那就是1分钱,测试时用这个。但正式上线,如果金额是10元,你要传“1000”。另外out_trade_no要保证唯一,我见过有人用时间戳加随机数,结果并发时重复了,导致订单支付失败。我的做法是用“年月日+用户ID+商品ID+4位随机数”,基本不会撞车。还有notify_url,用百度云或者阿里云的服务器,记得在商户平台里配置好白名单,否则微信支付的回调请求会被拦截。
第三步,签名。这是最折磨人的环节。你要把上面所有参数按照字典序排序,拼接成key=value&key=value的格式,最后加上&key=你的API密钥,然后做MD5加密(32位小写)。微信支付对签名验证极其严格,多一个空格、少一个换行都不行。我建议你写一个签名工具函数,每次生成签名后,把原始字符串打印出来,跟微信支付官方提供的签名校验工具对比一下。很多新手死活调不通,90%是签名错了。
第四步,发送请求到微信支付统一下单接口(https://api.mch.weixin.qq.com/pay/unifiedorder)。返回的结果里,如果return_code是SUCCESS,result_code也是SUCCESS,那就拿到了prepay_id。这个prepay_id就是前面说的“预支付订单号”,有效期只有2小时,你要及时传给前端。
第五步,前端接收prepay_id后,组装参数调起支付。你需要生成一个“小程序调起支付”的签名。参数包括:appId、timeStamp(当前时间戳,单位秒)、nonceStr(随机字符串)、package(固定格式:prepay_id=你拿到的那个ID)、signType(一般是MD5)。把这些参数按字典序排序,拼接后加key做MD5,得到paySign。然后调用wx.requestPayment,把上面这些参数传进去。
这里有个本地化的问题:如果你在微信开发者工具里测试,支付是调不通的,因为工具模拟器不支持唤起微信支付。你必须用真机调试。而且真机调试时,小程序的AppID必须是你自己的,不能用测试号。我见过有人用测试号调了一天,最后发现测试号没有支付权限。
第六步,也是最容易被忽略的——支付结果回调。用户支付成功后,微信支付服务器会异步通知你的notify_url。你必须在那个接口里做两件事:第一,验证签名(跟前面一样的逻辑);第二,更新订单状态。只做了第一件事,结果用户支付成功了,你的数据库里订单还是“未支付”,用户来找你退款,你就得人工对账。正确做法是:收到回调后,先判断return_code和result_code是不是SUCCESS,然后根据out_trade_no更新订单状态为“已支付”,再给微信返回一个XML(内容是
对比一下市面上常见的错误做法:有人为了省事,直接把前端传过来的支付结果当成最终结果,这是极其危险的。前端可以被伪造,用户可以用抓包工具修改支付成功的返回数据。所以一切以服务器接收到的微信回调为准。
再举一个本地案例:我帮一个做本地烘焙店的小程序调支付,他们卖的是生日蛋糕,用户下单后需要选择配送时间。他们的需求是:支付成功后,给用户发一条短信通知,同时给店里打印机自动打印小票。这就要在回调里加逻辑——支付成功后,调用短信接口和打印接口。但注意,回调接口必须幂等,因为微信可能重复通知。我的做法是:在更新订单状态时,先查一下这个订单是否已经处理过,如果已经“已支付”,就直接返回SUCCESS,不再重复发短信和打印。
还有一个细节:退款。很多小程序做完支付就不管退款了。但实际运营中,退款是高频场景。退款接口需要证书,你要在微信支付商户平台下载一个p12或pem格式的证书文件,放在服务器上。退款时要用这个证书发起HTTPS请求。证书文件不要放在公开目录,最好放在只有服务器能读到的路径。退款金额也要传分,而且退款订单号不能跟支付订单号重复,通常我会在支付订单号后面加个“_refund”。
最后说一个实战技巧:调试时,把微信支付商户平台的“支付日志”打开。那里会记录每一次请求的详细参数和错误原因。如果后端返回了错误码,比如“PARAM_ERROR”,你去日志里看具体是哪个参数传错了,比你自己瞎猜快十倍。另外,微信支付有沙箱环境(叫“沙箱测试”),你可以用虚拟金额测试,不用真金白银。但沙箱环境的密钥跟正式环境不同,别搞混了。
总结一下,整个流程就是:前端传参数→后端统一下单→拿到prepay_id→前端调起支付→用户输入密码→微信回调服务器→更新订单。每一步都有坑,但只要你把签名算对、回调处理好、真机调试,就能跑通。如果你现在还在纠结为什么调不起支付,建议你从头检查一遍:商户号有没有绑定AppID?API密钥有没有复制对?notify_url能不能从外网访问?签名用的key是不是商户平台的那个?这些排查完了,90%的问题都能解决。

