代码 微信支付 v3 回调收不到?先查 5 秒应答与 24h4m 重试窗口,再谈验签

2026-09-06 09:01:43

微信支付 v3 回调收不到?先查 5 秒应答与 24h4m 重试窗口,再谈验签

用户钱付了,商户订单还停在"未支付"。排查时大部分团队第一反应是"验签失败、解密失败",结果翻半天 RSA、AES-256-GCM。高频原因其实在前面:通知压根没发出来、发了但 5 秒内没答上、答的状态码不规范、被防火墙或反向代理吞了。回调通知只是"尽量送达的提示",不是可靠事件源——官方明确写了不保证通知最终成功,因此查单兜底是必配项。

一、先确认:这笔通知有没有资格发

微信支付只有支付成功的订单才推送支付结果通知,关闭、退款等走其它事件类型,别在支付结果通知里等它们。

两个常被忽略的"不发"前置条件:

  • 下单接口传的 notify_url 没配,或地址不可达,微信不会(无法)发;
  • 没设置 APIv3 密钥,微信不会发送回调通知——通知内容要用 APIv3 密钥做 AES-256-GCM 加密,没有它就没法生成加密报文。

所以查"收不到"第一步不是查代码,是去商户平台/下单参数里确认这两项在不在。

二、应答语义:5 秒、200/204、别带业务逻辑

收到通知后顺序:先验签 → 应答 → 再异步处理业务。

  • 验签通过、接收成功:HTTP 状态码返回 200 或 204,无需应答报文。不要返回 200 的同时还带一段业务 JSON——微信只看状态码。
  • 验签不通过、接收失败:返回 4xx 或 5xx,同时带应答报文 {"code":"FAIL","message":"失败"}

两个关键点:

  1. 5 秒内必须应答。微信收到不符合规范或超时(5 秒)的应答,就判定这次通知失败并重发。业务更新(改订单库、发消息)要放到应答之后异步做,别在应答前同步跑一堆写库逻辑把 5 秒撑爆。
  2. 应答后处理,推荐异步。官方明确建议"先应答再处理后面的业务逻辑(如更新订单状态)",避免处理耗时过长导致应答超时。

三、重试节奏:不是无限重发,是 24 小时 4 分、最多 15 次

通知失败后,微信按固定频次重发(国内普通支付 API v3):

15s / 15s / 30s / 3m / 10m / 20m / 30m / 30m / 30m / 60m / 3h / 3h / 3h / 6h / 6h

总计 24 小时 4 分。超过窗口仍收不到成功应答,微信就停止重发。即便一直答错,微信也只陪一天,之后订单状态只能靠查单拿回来。这就是兜底查单必须存在的原因。

个别文档条目写"4 小时"或"最大 15 次",是跨境/不同版本口径;以接入的 v3 文档页为准。机制一致:有限次数、时间窗内递增、终会停止。

四、重复通知与幂等:同一笔可能收到多次

微信侧"应答成功即不再发"基于它自己收到 200/204。但网络抖动、多台服务器同时投递,同一通知重复到达是常态,几秒内都可能来第二份。

处理原则:

  • 处理前先查该订单对应业务数据的状态,已处理过就直接应答 200,不再重复改库;
  • 在"查状态 + 改状态"之间加数据锁做并发控制,防止函数重入把同一条业务逻辑跑两遍(比如发货动作重复执行、订单状态来回覆盖)。

五、假通知与金额校验

安全上不止验签。官方特别提醒:验签之外,必须校验回调里的金额与商户侧订单金额一致,防止数据泄露导致的"假通知"造成资金损失。

完整链路:验签(确认来自微信)→ 用 APIv3 密钥解密 resource → 核对 out_trade_no 是自己创建的订单 → 核对解密后的 amount.total 等于该订单实际应付金额 → 全部通过才落库为"支付成功"。

一个容易踩的点:微信会在极少数应答/回调里生成错误签名做探测流量,用来测你有没有真验签。特征是签名值带 WECHATPAY/SIGNTEST/ 前缀。正确做法是把它当正常流量一样验签,验签失败就返回失败状态码等微信重发,不要因为认出是探测前缀就"放行"。排查日志时看到这个前缀能快速识别它是探测而非误报。

六、收不到通知的硬链路排查清单

前置条件都满足、验签逻辑也没问题,从链路一层层查:

  • 防火墙/安全组:Linux iptables、Windows 防火墙、云厂商安全组有没有放行微信支付的入站请求 / IP 白名单;
  • 反向代理:Nginx、Cloudflare 等有没有把 POST 正确转发到后端,有没有在代理层就返回了非 200(比如 WAF 拦截、超时);
  • 公网可达:notify_url 是不是内网地址、是不是会被重定向;
  • 网络质量:链路丢包、延迟过高(官方提到超过约 3 秒容易超时)导致通知发不过来或应答发不回去;
  • 应答瞬间:有没有在 5 秒窗口内返回,返回的状态码是不是真的 200/204。

建议直接拿一条真实回调报文在本地起个调试端点(或抓 Nginx access log 看有没有来自微信的 POST 进来),先确认"到没到服务器",再谈验签。

七、查单兜底才是把订单捞回来的正路

官方口径:商户系统不能仅依赖回调通知获取结果,需结合查询接口使用。回调会丢,查询是主动、确定的。

常见落地节奏:

  • 前端:Native 出码后 2 秒轮询一次商户查单接口,轮询约 60 秒;小程序里 requestPayment 返回 success/fail 后,都去调商户查单接口核实,别拿前端回调直接当支付结果。
  • 后端定时任务:每 30 秒扫一次"最近 10 分钟内创建且仍未支付"的订单,逐个调微信查单接口核实。记查询次数,连续 10 次仍不是支付成功就停止,并调关单接口把订单关掉。另一种做法是按 5s/30s/1m/3m/5m/10m/30m 阶梯拉长间隔查。
  • 判成功条件:查单响应验签通过,且 trade_state == SUCCESS,才认为支付成功——和回调走同一套金额核对逻辑。

两个关于重复支付的细节:

  • 用户再次对未支付订单发起支付时,用原单号发起,不要换单号,避免同一订单产生多笔支付、用户重复付款;
  • 查单返回未支付时,引导用户稍后到订单管理页核实,别立刻让他重付。

小结

  1. 通知是"尽量送达",不保证成功——查单兜底是架构一部分;
  2. "收不到"先查 notify_url / APIv3 密钥 / 5 秒应答 / 状态码 / 防火墙反代,再谈验签;
  3. 应答成功只要 200/204 纯状态码,业务处理放应答之后异步做;
  4. 重发窗口只有 24h4m 左右,不是无限次,靠等不靠谱;
  5. 幂等要加数据锁,安全要验签 + 核对金额,别把探测流量当异常放行。

参考:

复制全文 生成海报 支付 微信支付 回调 接口对接 APIv3

推荐文章

程序员茄子在线接单