支付回调超时处理,重试队列,回调失败存入队列定时重试

支付回调超时是支付对接中很常见的故障:用户已经付款,但平台的回调请求因为网络波动、应用卡顿或接口异常没有及时到达你的服务器。
如果直接丢弃这次回调,订单就会一直处于“未支付”状态,等到对账时才发现问题。
正确的处理思路是:先把回调数据持久化到一个重试队列,再通过定时任务轮询发送,直到对方返回成功或超过最大重试次数。
下面按实际落地步骤展开。

设计重试队列前需要准备什么

实现这套方案不需要额外购买中间件,用 Redis 或数据库就能完成。
建议提前准备好:

  • 一个可用的 Redis 服务(版本 3.0 以上),用于存放待重试的回调数据;
  • 自己业务环境的队列客户端,比如 Python 的 redis-py、PHP 的 predis,或 Laravel 自带的队列驱动;
  • 一个用来模拟回调的测试接口,方便验证重试效果;
  • 考虑好回调超时上限,通常支付平台允许的同步响应时间为 10~30 秒,具体以平台文档为准。

如果项目还没有接入消息队列,直接用 Redis 的 List 结构最省事,既可以当队列,也能配合定时任务实现延迟重试。

第一步:把回调失败的数据写入队列

在支付回调接口中,先捕获所有异常和超时情况,不能只依赖框架的异常处理。
伪代码如下:

import redis
import json

r = redis.Redis.from_url("redis://localhost:6379/0")

def pay_callback(request):
    try:
        # 原有业务处理:验签、更新订单状态、通知发货等
        process_payment(request)
        return {"code": "SUCCESS"}
    except Exception as e:
        # 回调失败,原始数据保存到队列,等待重试
        payload = {
            "order_no": request.get("order_no"),
            "raw_data": request,
            "retry_count": 0,
            "last_error": str(e)
        }
        r.rpush("pay:callback:retry", json.dumps(payload))
        return {"code": "FAIL"}

这里的关键点是:即使接口返回失败,支付平台短时间内可能会重推,但你不能依赖平台的推送次数。
把数据写进自己的 Redis 队列后,就掌握了重试的主动权。

第二步:定时任务扫描并重新发送回调

需要一个独立定时任务,建议每分钟跑一次,从队列左侧取出数据,重新请求自己的业务处理逻辑,而不是再次调用回调接口本身。
以 Python 的 schedule 库为例:

import schedule
import time
import json

def process_retry():
    # 从队列取出待重试数据
    item_json = r.lpop("pay:callback:retry")
    if not item_json:
        return
    item = json.loads(item_json)
    if item["retry_count"] >= 5:
        # 超过最大次数,转入死信队列或记录告警
        r.rpush("pay:callback:dead", item_json)
        return
    try:
        # 再次执行业务处理
        process_payment(item["raw_data"])
    except Exception as e:
        item["retry_count"] += 1
        item["last_error"] = str(e)
        # 重试次数没到,重新放回队列,也可以使用延迟队列控制频率
        r.rpush("pay:callback:retry", json.dumps(item))

schedule.every(1).minutes.do(process_retry)

while True:
    schedule.run_pending()
    time.sleep(1)

定时任务需要保证只有一个实例在运行,否则多个进程同时 lpop 可能产生重复处理。
生产环境建议用 blpop 阻塞读取,并配合分布式锁或单实例部署避免并发消费。

第三步:设置重试次数上限和降级告警

重试不能无限进行。
上面代码中设置了 retry_count >= 5 后转入 pay:callback:dead 队列,这个队列要额外监控。
当死信队列有数据时,说明存在无法自动修复的订单,需要人工介入。
建议在死信写入时发送告警通知(企业微信/钉钉机器人),并记录完整日志,方便排查。

关于重试间隔,如果同一订单连续失败,最好使用指数退避,比如第一次等 1 分钟,第二次等 5 分钟,而不是每单都固定间隔。
Redis 的 ZSET 或消息队列的延迟消息可以实现,这里用 List 实现的是定时间隔,适合初期快速落地。

验证重试是否生效

写一个简单的测试脚本:

redis-cli lpush pay:callback:retry '{"order_no":"123","raw_data":{},"retry_count":0}'

然后手动停止业务处理服务,观察定时任务日志,确认数据被取出、重试次数递增,服务恢复后再次处理成功。
还可以故意把 retry_count 设为 4,触发最后一次重试,检查是否转入死信队列。

避坑时请记住:回调处理逻辑必须支持幂等,同一个订单状态重复更新多次不能出错;
队列消费失败要用 try/catch 包裹,避免异常导致进程退出;
Redis 数据要定期持久化,防止重启丢失。
如果对账后发现漏单,优先查死信队列和业务日志。

回调超时重试的核心就三件事:失败先入库、定时再发送、超限转人工。
按这个流程实现后,支付状态不一致的问题基本能被自动修复,剩下的只在死信队列里等待人工处理。
如果你在集成时遇到重试后依然失败,先检查验签函数是否依赖时间戳,以及回调地址是否被防火墙拦截。

分享到:
上一篇
独立站图片WebP自动转换,Nginx实现图片格式自适应输出
下一篇
WordPress迁移服务器完整流程
1
系统公告

泽御云中秋国庆双节活动上线:新购8折,拼团3.99元起

尊敬的用户:
泽御云“月满中秋·礼贺国庆”双节活动现已开启,活动时间为2026年9月23日至10月10日。 活动期间可享以下福利:
1. 常规云服务器新购使用优惠码“泽御中秋国庆同乐”,符合条件的订单享8折优惠。
2. 香港精品云服务器5人拼团低至3.99元,部分4核4G套餐3人拼团年付388元,续费同价。
3. 新用户购买年付云服务器,符合活动规则可赠送2个月使用时长。
4. 老用户续费季度赠15天,续费年度赠2个月;活动期间升级配置免收配置迁移手续费。
5. 推荐好友成功下单,符合条件的推荐人可获赠7天服务器使用时长。
6. 活动期间享宕机补偿标准翻倍、简单网站迁移协助及技术工单优先处理权益。
温馨提示:优惠码不适用于拼团套餐、活动轻量产品、年付订单及续费订单;拼团套餐为独立特价活动,不与赠时类福利叠加。赠送时长不可折现、退款或跨账户转移,具体规则以活动页面说明为准。
服务中心
客服
在线客服
24小时为您服务
咨询
联系我们
联系我们,为您的业务提供专属服务。
24/7 技术支持
如果您遇到寻求进一步的帮助,请过工单与我们进行联系。
24/7 即时支持
泽御云
售前客服
泽御云
泽御云
售后客服
泽御云
技术支持
评价
您对当前页面的整体感受是否满意?
😞
非常不满意
😕
不满意
😐
一般
🙂
满意
😊
非常满意