PG官网

400-920-5594
173-6014-8050
首页 > 资讯中心 > 技术分享
客户付了钱,订单还是「待支付」?微信支付回调这几处最容易写错
2026-11-05 1 技术分享

  去年有个做社区团购的客户找过来,说用户付完钱订单一直挂着「待支付」,客服手动改单改了三十多笔。翻日志才发现,回调地址填的还是上一版测试域名,微信的通知全打在旧服务器上,正式环境一次都没收到。钱是真扣了,单是真没变。这种事在定制项目里不算少见,支付对接看着就是调个接口,真正容易翻车的都在回调这一环。

一、同步跳转成功,不代表你收到了钱

PG官网

  很多人第一次接支付,会把用户付完款跳回来的那个页面当成成功标志,页面一回来就把订单改成已支付。这个判断是错的。那个跳转地址(return_url、前端 redirect)只是给用户看的展示页,微信把结果推给你服务器用的是另一个地址,叫 notify_url,走的是后台 POST,跟用户有没有跳回来、有没有关掉 APP 没有关系。

  真实的场景是这样的,用户在收银台付完款直接把 APP 切后台,同步跳转压根不会发生,但钱已经进账了。你要是只靠前端跳转改状态,这笔单就永远停在待支付。反过来也一样,用户在网络不好的情况下反复点返回,前端可能跳回来好几次,你的订单被改来改去。所以订单状态的唯一驱动来源只能是异步通知,或者你主动去查单,前端那一跳只能用来做页面提示。

  还有一点容易忽略,notify_url 必须是公网能访问的 HTTPS 地址,不能带参数、不能有登录态、不能走内网。测试阶段用内网穿透凑合可以,上线前一定要换成正式域名,并且在小程序、公众号、APP 三套配置里分别登记,别只改了一处。

二、验签和金额校验,别谁调你都信

PG官网

  notify_url 是个公开接口,理论上任何人都能往这个地址 POST 一段报文过来。你要是不验签,等于把改单权限挂在公网上,别人构造一条「支付成功」的通知就能白拿货。微信支付 v2 用的是 MD5 或 HMAC-SHA256 签名,v3 用的是平台证书验签,不管哪个版本,第一步都得把签名验过去,验不过直接拒掉,连业务逻辑都不要进。

  验签过了还不算完,报文里的关键字段要跟自己库里的订单逐项对上。商户号、APPID 是不是自己的,out_trade_no 是不是本系统发出的单号,金额跟订单应付金额是否一致,币种对不对。金额这一项特别要盯,有些实现图省事,直接拿回调里的金额去更新订单,结果遇到一分钱测试或者被人改过金额的单,账面直接对不上。正确做法是拿回调金额跟本地订单应付金额比对,不一致就记异常、报警、人工介入,绝不能默默改单。

  签发时间和随机串也可以校验一下,超过五分钟的报文直接丢弃,减少重放的空间。这些校验加起来不过十几行代码,但它是整条支付链路里唯一一道能挡住伪造通知的关口。

三、幂等没做,一笔订单能被处理好几次

PG官网

  微信的通知不是「发一次就完事」。你的服务器如果没有及时返回成功响应,或者网络抖了一下,微信会按自己的节奏重试,间隔从十几秒一路拉长到几小时,前后加起来十几次。同一笔订单收到多条通知是常态,不是异常。

  这就要求处理逻辑必须幂等。最直接的做法是,进业务之前先查这笔订单的当前状态,如果已经是已支付,直接返回成功,后面的发货、加积分、发消息全部跳过。只靠这一查还不够,两个通知同时到达时仍可能并发进去,所以要么在订单表上做状态更新的条件更新(带上 where status = 待支付),要么用分布式锁按订单号串行化,两者选一个,别都不做。

  订单状态本身也要有个明确的流转规则。待支付、支付中、已支付、已发货、已完成,退款是另一条分支,每个状态只允许往规定的下一个状态走,不允许从已支付退回待支付。状态机写清楚,后面查问题时一眼就能看出是哪一步跳错了,而不是对着一堆 if 猜。

  返回 success 的时机也有讲究。要等业务真正处理完再返回,不能在收到通知的第一时间先回 success、然后异步慢慢处理。因为一旦回了 success,微信就不再重试,你后面处理失败也没人再来通知你。反过来说,处理过程中如果抛了异常,就应该返回失败或者不返回,让微信继续重试,给自己留一次补救机会。

四、回调真丢了,兜底还得靠查单和对账

PG官网

  上面这些做齐了,也不能保证百分之百不漏单。服务器重启、发布过程中短暂的不可用、证书过期,都可能让某几条通知彻底丢掉,所以还得有两层兜底。

  • 第一层是主动查单。起一个定时任务,把创建超过一定时间还停在待支付的订单捞出来,逐笔调微信的查单接口,查到已支付就补一笔状态变更,查到未支付就按超时关单处理。这个任务的频率别太高,按订单量来定,几分钟跑一次就够,注意控制并发,别把查单接口打到限流。

  • 第二层是每日对账。把微信给的账单文件下载下来,跟本地订单表按单号核对,金额、手续费、退款逐项比。对不上的拉到差异表里单独处理,别直接在线上改数。对账这件事很多项目上线时都没做,等到财务月底发现账不平,再回头查两三个月的流水,成本高得多。

  • 最后是日志。回调的原始报文要落库,敏感字段脱敏,至少保留订单号、金额、交易状态、返回结果这几项,出问题能按单号把整条链路还原出来。验签失败、金额不一致、处理异常这几种情况要接告警,发到群里或者短信,别等客户打电话才知道。支付这条链路,出问题的成本从来不是改一行代码,是客户的信任。

推荐文章查看更多》

网站地图