企业微信 Webhook 接收不到消息?网络连通性、URL 签名验签与 API 返回错误码排查指南

回调不通,八成不是代码问题。这篇按"连通性、验签、错误码"三层走,每层给可执行的验证命令。

第一层:先确认请求到没到你这里

别急着看代码。先看有没有请求进来。

从外网 curl。找一台不在你内网的机器执行:

python 复制代码
curl -v -X POST https://your-domain.com/wx-api/webhook/ \
  -H "Content-Type: application/json" \
  -d '{"data":{"content":"test"}}'

看到 HTTP/1.1 200 说明链路通。看到 Connection refused 就是端口不通,看到 SSL certificate problem 就是证书有问题。

在服务器上抓包。如果 curl 通但回调不来,抓包看有没有请求到达:

复制代码
sudo tcpdump -i any -n port 443 -A | grep -i "POST /wx-api"

没抓到,问题在平台侧或中间网络。抓到了,问题在你的应用。

常见的拦路虎:

现象 原因
外网 curl 超时 安全组/防火墙没放行
证书报错 证书链不全,或用了自签名
8080 能通、443 不通 只开了 HTTP,回调要求 HTTPS
偶发 502 反向代理超时,应用处理太慢

回调必须公网可达、HTTPS、证书有效。自签名证书和 IP 直连都不行。

第二层:URL 验证过没过

回调地址配置时,平台会发一次验证请求,带 echostr。这一步不过,后面所有消息都不会推。

验证请求的硬性要求:

原样返回 echostr。不能包 JSON,不能加引号,不能带换行。

python 复制代码
@app.route("/wx-api/webhook/", methods=["GET", "POST"])
def webhook():
    echostr = request.args.get("echostr")
    if echostr is None and request.is_json:
        echostr = request.json.get("echostr")

    if echostr:
        return echostr, 200, {"Content-Type": "text/plain"}   # 纯文本
    ...

三个容易踩的点:

  • 返回 jsonify({"echostr": ...}) → 失败

  • 返回时加了 \n → 失败

  • 只支持 POST,验证请求发的是 GET → 405

手动模拟验证请求:

复制代码
curl -i "https://your-domain.com/wx-api/webhook/?echostr=abc123"

返回体应该只有 abc123,不能有别的字符。

第三层:验签对不对

验证过了、消息也来了,但返回 403,说明验签失败。

python 复制代码
curl -i -X POST https://your-domain.com/wx-api/webhook/ \
  -H "Content-Type: application/json" \
  -H "X-Eyun-Signature: wrong-signature" \
  -d '{"data":{}}'

预期返回 403。如果返回 200,说明你的验签根本没生效------比验签失败更危险。

验签三原则:原始字节、UTF-8、compare_digest。框架提前读过 body 是最常见的坑。

python 复制代码
raw = request.get_data(cache=True)     # 不是 request.json
expected = hmac.new(SECRET.encode(), raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get("X-Eyun-Signature", "")):
    return "forbidden", 403

第四层:API 返回的错误码怎么读

消息收到但发不出去,看响应码。企微的 code 有两种类型,处理方式完全不同。

code 类型 含义 处理
0 数字 成功 继续
-1 数字 业务失败,具体码在 message 读 message,不重试
-301002 数字 限流 指数退避重试
字符串(如 "401") 字符串 网关/鉴权错误 检查 Token 和路径

判断要按类型分:

python 复制代码
code = resp.get("code")
if code == 0:
    return resp["data"]
if isinstance(code, str):
    raise AuthError(resp)          # 别重试,先查凭证
if code == -301002:
    backoff_and_retry()

把字符串码当业务码重试,会一直撞墙。这是排查时最常见的误判。

最后一种情况:其实收到了,只是你没看见

有两个"看起来没回调"的经典场景:

接口自己发的消息不回推 。你用 message/sendText 发出去的消息,平台不会推正文给你,只推一条 2001 已读回执。想看自己发的消息,得查历史消息接口。

只推了回执事件 。事件类型是已读回执,正文在另一条事件里。按 X-Eyun-Event 分流时,把回执事件单独处理,别丢。

一套可复用的排查顺序

  1. 外网 curl,确认 200

  2. tcpdump,确认请求到达

  3. 手动请求 echostr,确认纯文本返回

  4. 故意发错签名,确认返回 403

  5. 发一条真实消息,看日志里 code 的类型

  6. 确认不是"自己发的消息"或"只收到回执"

六步走完,问题基本能定位到具体一层。

回调配置和错误码说明在开发文档里能查到,实例的密钥和回调地址在 Eyun 控制台的实例配置里改。

相关推荐
SimonKing1 小时前
Workbuddy太占C盘空间了,写个迁移工具迁走10G的数据
java·后端·程序员
在所不辞兄1 小时前
为什么在电池供电场景下,不能采用固定电压频率策略,而需要动态调整以最大化 Race-to-Sleep 收益?
人工智能·深度学习·神经网络·机器学习
jianqiang.xue1 小时前
3倍向量算力:Arm给最小MCU装上本地AI
arm开发·人工智能·单片机
北京迅为1 小时前
案例分享 | RK182X算力卡:多路视频VLM监控,四路画面AI同时看懂,一句人话说清现场
linux·人工智能·嵌入式·rk3588·算力卡·rk182x
Hvitur1 小时前
Java - Lambda
java·开发语言·python
墨林陌1 小时前
AI 热点日报(2026-11-13):黄仁勋辞任英伟达CEO,百度发智能组织,SpaceX斥740亿建算力
人工智能
喵了几个咪2 小时前
GoWind Flow 拆解:一个 Go 工作流引擎与它的双模式设计器
开发语言·人工智能·后端·golang
aneasystone本尊2 小时前
学习大模型训练的 DPO 与 GRPO:偏好对齐的简化与进化
人工智能
BYSJMG2 小时前
计算机毕业设计选题推荐|【基于深度学习的电梯电动车违规进入检测研究】YOLO26m+可视化大屏
人工智能·深度学习·神经网络·目标检测·机器学习·计算机视觉·课程设计