钉钉回调验证失败主因是签名验签逻辑未手写实现:get请求需用token、timestamp、nonce排序后sha1比对signature,并原样返回echostr;post审批回调为aes加密xml,须用encodingaeskey解密,且userid必须为钉钉内部id而非手机号。

回调地址验证失败:签名验签逻辑必须手写
ThinkPHP 默认不内置钉钉签名验证逻辑,直接用 input() 或 $_POST 拿数据会跳过 timestamp/nonce/signature 校验,导致钉钉判定为非法回调而拒绝推送。你得自己实现 SHA1 签名比对,不能依赖框架自动解析。
- 钉钉回调验证是 GET 请求,带
signature、timestamp、nonce、echostr四个参数,必须原样拼接$token(你在开放平台设置的 Token)、$timestamp、$nonce后排序 + SHA1 -
$token必须和钉钉后台「事件订阅」里填的一致,大小写敏感,不能多空格 - 验签通过后,**必须原样返回
$_GET['echostr']字符串**,不能加换行、JSON 包裹或 echo 以外的输出 - ThinkPHP 的中间件或控制器里,要先判断请求 method 是 GET 且含
signature,再执行验签,否则后续 POST 消息会被误判
审批流回调接收:POST 数据不是 JSON,是加密 XML
钉钉审批事件(如审批通过、驳回)走的是 POST 回调,但 body 不是 JSON,而是 AES 加密的 XML;直接 file_get_contents('php://input') 拿到的是密文,不解密就无法解析字段。
- 解密前需从钉钉后台拿到
encodingAesKey(43位 Base64 字符串),和token、appSecret一起参与 AES-256-CBC 解密 - XML 解密后结构固定,根节点是
<xml></xml>,关键字段包括<eventtype>bpms_instance_change</eventtype>、<processinstanceid></processinstanceid>、<result>agree</result> - ThinkPHP 中建议封装一个
DingTalkCrypto类,复用官方 PHP SDK 的解密逻辑(注意:SDK 的decrypt方法要求 IV 为 16字节全 0,别用随机 IV) - 解密失败常见原因是
encodingAesKey填错、未 base64_decode、或 POST body 被框架自动 trim 或转义
审批实例详情接口调用:user_id 不能直接传手机号
拿到 processInstanceId 后,想查审批单具体内容,得调用 /topapi/processinstance/get。但该接口的 userid 参数必须是钉钉内部 userid(如 zhangsan123),不是手机号或邮箱 —— 直接传手机号会返回 errcode: 50005, errmsg: "userid is invalid"。
- 如果只知道员工手机号,得先调
/topapi/user/get_by_mobile换成userid,注意该接口需要contacts:read权限且管理员授权 -
/topapi/processinstance/get返回的审批表单值在form_component_values数组里,每个元素含name(控件名)和value(填写值),不是扁平 key-value - 调用时
access_token必须用企业自建应用的 token(由appKey/appSecret换取),不能用个人扫码登录的 token - 频繁调用可能触发限流(默认 6000 次/天),建议缓存审批实例结果,尤其当同一单被多次回调(如加签、转审)
ThinkPHP 路由与 HTTPS 强制要求
钉钉只接受 HTTPS 回调地址,且域名必须备案、SSL 证书有效;用 http:// 或本地 127.0.0.1 地址调试必然失败,连验证步骤都进不去。
- ThinkPHP 的路由必须显式绑定域名,比如
Route::domain('api.example.com', function () { ... });,不能靠 Nginx 反代隐式转发,否则$_SERVER['HTTP_HOST']可能为空导致验签失败 - 确保 Nginx/Apache 配置了正确的 SSL 证书,并关闭 HTTP 重定向(钉钉不跟随 301/302)
- 回调 URL 在钉钉后台填的是完整路径,例如
https://api.example.com/dingtalk/callback,ThinkPHP 对应路由必须精确匹配,尾部斜杠不能多也不能少 - 服务器时间偏差超过 1 小时,会导致 timestamp 校验失败,务必用
ntpdate -u ntp.aliyun.com同步系统时间
encodingAesKey 的 base64_decode 和 IV 处理,错一个字节整个回调就静默失败,日志里还看不出原因。大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











