微信回调防重发需卡死签名验证、状态检查、响应格式和并发控制四点:验签必须前置且分模式校验;每次回调均查订单状态再处理;响应须严格XML格式并立即终止;高并发下用Redis锁+数据库唯一索引保幂等。

微信回调防重发不是加个判断就能解决的事,关键在签名验证、状态检查、响应格式和并发控制四点卡死。漏掉任一环,微信就会反复推送,最多重试9次,间隔从15秒拉长到1小时。
验签必须前置且严格
所有回调入口第一件事是校验签名,明文/兼容/安全模式对应不同逻辑:
- 明文模式:用 $_GET['signature'] 对比本地按 token+timestamp+nonce 字典序拼接后 sha1 的结果
- 兼容或安全模式:必须额外校验 X-WX-MSG-SIGNATURE 请求头,解密前先用 EncodingAESKey + AppID + timestamp + nonce + 密文生成 msg_signature 比对
- 任意一步失败,直接返回失败响应(如 XML 中 return_code=FAIL),不进业务逻辑
订单状态查完再处理
微信通知可能重复到达,不能依赖“第一次来才处理”。每次收到回调都得走完整查询流程:
- 用 out_trade_no 或 msgid 查数据库当前状态
- 若已是“已支付”“已发送”“已失败”,直接跳过业务更新,准备响应
- 只在状态为“待处理”“待支付”时才执行更新订单、发通知、扣库存等操作
- 避免用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE 替代状态判断——它们无法拦截非法参数或伪造请求
响应必须干净、标准、立即终止
微信只认特定格式的响应,多一个空格、少一个 CDATA、没设 header 都会导致重试:
立即学习“PHP免费学习笔记(深入)”;
- 明文/兼容模式:输出 <xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>,不要 JSON,不要 echo 其他内容
- 安全模式:响应体也需 AES 加密并带上 msg_signature,否则微信视为无效应答
- 务必在 echo 后加 exit 或 die,防止框架自动追加 HTML 或日志污染响应流
- 不要用
ob_clean()或header('Content-type: text/xml')—— 微信不校验 header,只解析 body
高并发下加锁保幂等
同一订单可能被多个请求同时命中,尤其在云环境或负载均衡下:
- 用 Redis 实现分布式锁:SET order_lock_{out_trade_no} 1 EX 30 NX,获取成功才继续,失败则直接返回 SUCCESS
- 数据库层加唯一索引(如 trade_no + status 组合),让重复插入直接报错,而非静默忽略
- 避免用文件锁或进程锁——PHP-FPM 场景下进程不共享,无效



















