企业微信代开发应用组织架构同步踩坑实录

企业微信代开发应用组织架构同步踩坑实录:从 48002 到 32 个部门 443 人

本文记录了一次真实的企业微信代开发应用授权与组织架构同步调试过程。核心涉及:代开发应用授权事件、suite_ticketsuite_access_token 时序、permanent_code 作为 corpsecret/gettoken/service/get_corp_token 的区别、以及 open_corpid 与真实企业 ID 的差异。


一、背景与目标

我们要做一个企业微信代开发应用,部署到客户服务器后:

  1. 接收企业微信的回调事件(suite_ticketcreate_authchange_auth
  2. 完成授权,拿到永久授权码
  3. 拉取客户企业的组织架构(部门 + 成员)
  4. 输出为 data/organization.json

技术栈:Node.js + Express(Webhook)+ axios(调用企业微信 API)。


二、前置知识:应用类型、管理后台与 IP 白名单

在深入授权链路前,有必要先理清三种常见的企业微信应用形态,以及各自对应的管理后台和 IP 白名单配置位置。

2.1 第三方应用、代开发应用、自建应用对比

维度 自建应用 第三方应用 代开发应用
开发者 企业自己的 IT 团队 服务商 服务商
部署位置 企业自己的服务器 服务商服务器 服务商服务器(也可私有化部署到企业)
管理员入口 企业微信管理后台 → 应用管理 → 自建应用 服务商后台 → 应用管理 → 第三方应用 服务商后台 → 应用管理 → 应用代开发
授权方式 企业内直接用 客户企业授权安装 客户企业授权安装
获取 access_token GET /gettoken corpid + corpsecret POST /service/get_corp_token auth_corpid + permanent_code GET /gettoken corpid + corpsecret (其中 corpsecret 就是 permanent_code
corpid 来源 企业后台「我的企业」 get_permanent_code 返回的 auth_corp_info.corpid(open_corpid) get_permanent_code 返回的是 open_corpid,gettoken 必须用真实企业 ID
典型使用场景 企业内部系统 服务商 SaaS 产品 服务商为客户定制开发应用

#mermaid-svg-ExIDo6mGX8DG6jHA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ExIDo6mGX8DG6jHA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ExIDo6mGX8DG6jHA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ExIDo6mGX8DG6jHA .error-icon{fill:#552222;}#mermaid-svg-ExIDo6mGX8DG6jHA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ExIDo6mGX8DG6jHA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ExIDo6mGX8DG6jHA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ExIDo6mGX8DG6jHA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ExIDo6mGX8DG6jHA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ExIDo6mGX8DG6jHA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ExIDo6mGX8DG6jHA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ExIDo6mGX8DG6jHA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ExIDo6mGX8DG6jHA .marker.cross{stroke:#333333;}#mermaid-svg-ExIDo6mGX8DG6jHA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ExIDo6mGX8DG6jHA p{margin:0;}#mermaid-svg-ExIDo6mGX8DG6jHA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ExIDo6mGX8DG6jHA .cluster-label text{fill:#333;}#mermaid-svg-ExIDo6mGX8DG6jHA .cluster-label span{color:#333;}#mermaid-svg-ExIDo6mGX8DG6jHA .cluster-label span p{background-color:transparent;}#mermaid-svg-ExIDo6mGX8DG6jHA .label text,#mermaid-svg-ExIDo6mGX8DG6jHA span{fill:#333;color:#333;}#mermaid-svg-ExIDo6mGX8DG6jHA .node rect,#mermaid-svg-ExIDo6mGX8DG6jHA .node circle,#mermaid-svg-ExIDo6mGX8DG6jHA .node ellipse,#mermaid-svg-ExIDo6mGX8DG6jHA .node polygon,#mermaid-svg-ExIDo6mGX8DG6jHA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ExIDo6mGX8DG6jHA .rough-node .label text,#mermaid-svg-ExIDo6mGX8DG6jHA .node .label text,#mermaid-svg-ExIDo6mGX8DG6jHA .image-shape .label,#mermaid-svg-ExIDo6mGX8DG6jHA .icon-shape .label{text-anchor:middle;}#mermaid-svg-ExIDo6mGX8DG6jHA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ExIDo6mGX8DG6jHA .rough-node .label,#mermaid-svg-ExIDo6mGX8DG6jHA .node .label,#mermaid-svg-ExIDo6mGX8DG6jHA .image-shape .label,#mermaid-svg-ExIDo6mGX8DG6jHA .icon-shape .label{text-align:center;}#mermaid-svg-ExIDo6mGX8DG6jHA .node.clickable{cursor:pointer;}#mermaid-svg-ExIDo6mGX8DG6jHA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ExIDo6mGX8DG6jHA .arrowheadPath{fill:#333333;}#mermaid-svg-ExIDo6mGX8DG6jHA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ExIDo6mGX8DG6jHA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ExIDo6mGX8DG6jHA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ExIDo6mGX8DG6jHA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ExIDo6mGX8DG6jHA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ExIDo6mGX8DG6jHA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ExIDo6mGX8DG6jHA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ExIDo6mGX8DG6jHA .cluster text{fill:#333;}#mermaid-svg-ExIDo6mGX8DG6jHA .cluster span{color:#333;}#mermaid-svg-ExIDo6mGX8DG6jHA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ExIDo6mGX8DG6jHA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ExIDo6mGX8DG6jHA rect.text{fill:none;stroke-width:0;}#mermaid-svg-ExIDo6mGX8DG6jHA .icon-shape,#mermaid-svg-ExIDo6mGX8DG6jHA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ExIDo6mGX8DG6jHA .icon-shape p,#mermaid-svg-ExIDo6mGX8DG6jHA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ExIDo6mGX8DG6jHA .icon-shape .label rect,#mermaid-svg-ExIDo6mGX8DG6jHA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ExIDo6mGX8DG6jHA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ExIDo6mGX8DG6jHA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ExIDo6mGX8DG6jHA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 代开发应用
corpsecret
suite_id/secret/ticket
/get_suite_token
suite_access_token
/get_permanent_code
permanent_code 即 secret
真实企业ID
/gettoken
access_token
第三方应用
suite_id/secret/ticket
/get_suite_token
suite_access_token
/get_permanent_code
permanent_code
/service/get_corp_token
access_token
自建应用
corpid + corpsecret
/gettoken
access_token

2.2 两个管理后台

企业微信生态里至少会涉及两个后台,容易混淆:

后台 网址 使用方 主要用途
服务商后台 https://open.work.weixin.qq.com/wwopen/login 服务商(我们) 创建/管理第三方应用或代开发应用模板、配置回调 URL、查看 suite_id/suite_secret、配置 IP 白名单
企业微信管理后台 https://open.work.weixin.qq.com/wwopen/developers/home 客户企业管理员 管理企业通讯录、审批应用授权、设置应用可见范围、查看真实企业 ID

2.3 IP 白名单配置位置

调用企业微信 API 的服务器 IP 必须加入白名单,否则会被直接拒绝(如 60020 not allow to access from your ip)。

对于代开发应用,关键配置位置在服务商后台:

  1. 登录服务商后台
  2. 应用管理 → 应用代开发 → 选择对应模板
  3. 找到「服务信息」TAB 页
  4. 在「IP 白名单」处填入调用 API 的服务器公网 IP

特别注意:IP 白名单通常要求是大陆 IP。 海外 IP 即使填入了白名单,企业微信仍可能拒绝访问。本次调试就是把服务部署到阿里云国内服务器(例如 8.149.297.268)后才正常的。如果你本地是海外网络,直接用本地 IP 调用接口会报 60020,不要误以为是白名单没配好。
#mermaid-svg-QKdpMntzqGdoTCKB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QKdpMntzqGdoTCKB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QKdpMntzqGdoTCKB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QKdpMntzqGdoTCKB .error-icon{fill:#552222;}#mermaid-svg-QKdpMntzqGdoTCKB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QKdpMntzqGdoTCKB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QKdpMntzqGdoTCKB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QKdpMntzqGdoTCKB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QKdpMntzqGdoTCKB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QKdpMntzqGdoTCKB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QKdpMntzqGdoTCKB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QKdpMntzqGdoTCKB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QKdpMntzqGdoTCKB .marker.cross{stroke:#333333;}#mermaid-svg-QKdpMntzqGdoTCKB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QKdpMntzqGdoTCKB p{margin:0;}#mermaid-svg-QKdpMntzqGdoTCKB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QKdpMntzqGdoTCKB .cluster-label text{fill:#333;}#mermaid-svg-QKdpMntzqGdoTCKB .cluster-label span{color:#333;}#mermaid-svg-QKdpMntzqGdoTCKB .cluster-label span p{background-color:transparent;}#mermaid-svg-QKdpMntzqGdoTCKB .label text,#mermaid-svg-QKdpMntzqGdoTCKB span{fill:#333;color:#333;}#mermaid-svg-QKdpMntzqGdoTCKB .node rect,#mermaid-svg-QKdpMntzqGdoTCKB .node circle,#mermaid-svg-QKdpMntzqGdoTCKB .node ellipse,#mermaid-svg-QKdpMntzqGdoTCKB .node polygon,#mermaid-svg-QKdpMntzqGdoTCKB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QKdpMntzqGdoTCKB .rough-node .label text,#mermaid-svg-QKdpMntzqGdoTCKB .node .label text,#mermaid-svg-QKdpMntzqGdoTCKB .image-shape .label,#mermaid-svg-QKdpMntzqGdoTCKB .icon-shape .label{text-anchor:middle;}#mermaid-svg-QKdpMntzqGdoTCKB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QKdpMntzqGdoTCKB .rough-node .label,#mermaid-svg-QKdpMntzqGdoTCKB .node .label,#mermaid-svg-QKdpMntzqGdoTCKB .image-shape .label,#mermaid-svg-QKdpMntzqGdoTCKB .icon-shape .label{text-align:center;}#mermaid-svg-QKdpMntzqGdoTCKB .node.clickable{cursor:pointer;}#mermaid-svg-QKdpMntzqGdoTCKB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QKdpMntzqGdoTCKB .arrowheadPath{fill:#333333;}#mermaid-svg-QKdpMntzqGdoTCKB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QKdpMntzqGdoTCKB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QKdpMntzqGdoTCKB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QKdpMntzqGdoTCKB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QKdpMntzqGdoTCKB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QKdpMntzqGdoTCKB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QKdpMntzqGdoTCKB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QKdpMntzqGdoTCKB .cluster text{fill:#333;}#mermaid-svg-QKdpMntzqGdoTCKB .cluster span{color:#333;}#mermaid-svg-QKdpMntzqGdoTCKB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QKdpMntzqGdoTCKB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QKdpMntzqGdoTCKB rect.text{fill:none;stroke-width:0;}#mermaid-svg-QKdpMntzqGdoTCKB .icon-shape,#mermaid-svg-QKdpMntzqGdoTCKB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QKdpMntzqGdoTCKB .icon-shape p,#mermaid-svg-QKdpMntzqGdoTCKB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QKdpMntzqGdoTCKB .icon-shape .label rect,#mermaid-svg-QKdpMntzqGdoTCKB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QKdpMntzqGdoTCKB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QKdpMntzqGdoTCKB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QKdpMntzqGdoTCKB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 发起 API 请求
配置
校验
可能被拒绝
部署服务器
企业微信 API
服务商后台 应用代开发 → 服务信息
IP 白名单
海外 IP


三、企业微信代开发应用授权链路

代开发应用不是普通的第三方应用,它的授权和 token 获取流程有自己的特点:
#mermaid-svg-8kJjj6mXwE83N6PC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8kJjj6mXwE83N6PC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8kJjj6mXwE83N6PC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8kJjj6mXwE83N6PC .error-icon{fill:#552222;}#mermaid-svg-8kJjj6mXwE83N6PC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8kJjj6mXwE83N6PC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8kJjj6mXwE83N6PC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8kJjj6mXwE83N6PC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8kJjj6mXwE83N6PC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8kJjj6mXwE83N6PC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8kJjj6mXwE83N6PC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8kJjj6mXwE83N6PC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8kJjj6mXwE83N6PC .marker.cross{stroke:#333333;}#mermaid-svg-8kJjj6mXwE83N6PC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8kJjj6mXwE83N6PC p{margin:0;}#mermaid-svg-8kJjj6mXwE83N6PC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8kJjj6mXwE83N6PC .cluster-label text{fill:#333;}#mermaid-svg-8kJjj6mXwE83N6PC .cluster-label span{color:#333;}#mermaid-svg-8kJjj6mXwE83N6PC .cluster-label span p{background-color:transparent;}#mermaid-svg-8kJjj6mXwE83N6PC .label text,#mermaid-svg-8kJjj6mXwE83N6PC span{fill:#333;color:#333;}#mermaid-svg-8kJjj6mXwE83N6PC .node rect,#mermaid-svg-8kJjj6mXwE83N6PC .node circle,#mermaid-svg-8kJjj6mXwE83N6PC .node ellipse,#mermaid-svg-8kJjj6mXwE83N6PC .node polygon,#mermaid-svg-8kJjj6mXwE83N6PC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8kJjj6mXwE83N6PC .rough-node .label text,#mermaid-svg-8kJjj6mXwE83N6PC .node .label text,#mermaid-svg-8kJjj6mXwE83N6PC .image-shape .label,#mermaid-svg-8kJjj6mXwE83N6PC .icon-shape .label{text-anchor:middle;}#mermaid-svg-8kJjj6mXwE83N6PC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8kJjj6mXwE83N6PC .rough-node .label,#mermaid-svg-8kJjj6mXwE83N6PC .node .label,#mermaid-svg-8kJjj6mXwE83N6PC .image-shape .label,#mermaid-svg-8kJjj6mXwE83N6PC .icon-shape .label{text-align:center;}#mermaid-svg-8kJjj6mXwE83N6PC .node.clickable{cursor:pointer;}#mermaid-svg-8kJjj6mXwE83N6PC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8kJjj6mXwE83N6PC .arrowheadPath{fill:#333333;}#mermaid-svg-8kJjj6mXwE83N6PC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8kJjj6mXwE83N6PC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8kJjj6mXwE83N6PC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8kJjj6mXwE83N6PC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8kJjj6mXwE83N6PC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8kJjj6mXwE83N6PC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8kJjj6mXwE83N6PC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8kJjj6mXwE83N6PC .cluster text{fill:#333;}#mermaid-svg-8kJjj6mXwE83N6PC .cluster span{color:#333;}#mermaid-svg-8kJjj6mXwE83N6PC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8kJjj6mXwE83N6PC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8kJjj6mXwE83N6PC rect.text{fill:none;stroke-width:0;}#mermaid-svg-8kJjj6mXwE83N6PC .icon-shape,#mermaid-svg-8kJjj6mXwE83N6PC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8kJjj6mXwE83N6PC .icon-shape p,#mermaid-svg-8kJjj6mXwE83N6PC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8kJjj6mXwE83N6PC .icon-shape .label rect,#mermaid-svg-8kJjj6mXwE83N6PC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8kJjj6mXwE83N6PC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8kJjj6mXwE83N6PC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8kJjj6mXwE83N6PC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 推送 suite_ticket
suite_id + suite_secret + suite_ticket
推送 create_auth + AuthCode
suite_access_token + AuthCode
corpid + corpsecret
企业微信服务器
服务商回调服务
/service/get_suite_token
suite_access_token
客户管理员扫码授权
/service/get_permanent_code
permanent_code 即代开发应用的 secret
/gettoken
access_token
调用通讯录接口

关键区别:

应用类型 取企业 token 的接口 参数
第三方应用 POST /service/get_corp_token auth_corpid + permanent_code
代开发应用 GET /cgi-bin/gettoken corpid + corpsecret(即 permanent_code

官方文档明确说明:代开发应用获取 permanent_code 后,该值即为应用 secret,后续用自建应用 的方式换取 access_token


四、踩坑实录

坑 1:get_suite_token 成功响应没有 errcode

企业微信的 get_suite_token 成功时返回:

json 复制代码
{
  "suite_access_token": "xxxxx",
  "expires_in": 7200
}

没有 errcode 字段。 如果代码写成 if (data.errcode === 0) 判断成功,就会把它当失败处理,错误信息还是 undefined

修复: 改为判断 data.suite_access_token 是否存在。

javascript 复制代码
if (data.suite_access_token) {
  return data.suite_access_token;
}

坑 2:create_auth 比 suite_ticket 先到

授权完成后,企业微信会推送 create_auth 事件。但回调服务刚启动时,suite_ticket 可能还没推过来,此时直接调用 get_permanent_code 会失败。
回调服务 企业微信 回调服务 企业微信 #mermaid-svg-rTNwWDx0CaS46VQA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rTNwWDx0CaS46VQA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rTNwWDx0CaS46VQA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rTNwWDx0CaS46VQA .error-icon{fill:#552222;}#mermaid-svg-rTNwWDx0CaS46VQA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rTNwWDx0CaS46VQA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rTNwWDx0CaS46VQA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rTNwWDx0CaS46VQA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rTNwWDx0CaS46VQA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rTNwWDx0CaS46VQA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rTNwWDx0CaS46VQA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rTNwWDx0CaS46VQA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rTNwWDx0CaS46VQA .marker.cross{stroke:#333333;}#mermaid-svg-rTNwWDx0CaS46VQA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rTNwWDx0CaS46VQA p{margin:0;}#mermaid-svg-rTNwWDx0CaS46VQA .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rTNwWDx0CaS46VQA text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-rTNwWDx0CaS46VQA .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-rTNwWDx0CaS46VQA .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-rTNwWDx0CaS46VQA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-rTNwWDx0CaS46VQA .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-rTNwWDx0CaS46VQA #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-rTNwWDx0CaS46VQA .sequenceNumber{fill:white;}#mermaid-svg-rTNwWDx0CaS46VQA #sequencenumber{fill:#333;}#mermaid-svg-rTNwWDx0CaS46VQA #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-rTNwWDx0CaS46VQA .messageText{fill:#333;stroke:none;}#mermaid-svg-rTNwWDx0CaS46VQA .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rTNwWDx0CaS46VQA .labelText,#mermaid-svg-rTNwWDx0CaS46VQA .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-rTNwWDx0CaS46VQA .loopText,#mermaid-svg-rTNwWDx0CaS46VQA .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-rTNwWDx0CaS46VQA .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-rTNwWDx0CaS46VQA .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-rTNwWDx0CaS46VQA .noteText,#mermaid-svg-rTNwWDx0CaS46VQA .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-rTNwWDx0CaS46VQA .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rTNwWDx0CaS46VQA .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rTNwWDx0CaS46VQA .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rTNwWDx0CaS46VQA .actorPopupMenu{position:absolute;}#mermaid-svg-rTNwWDx0CaS46VQA .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-rTNwWDx0CaS46VQA .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rTNwWDx0CaS46VQA .actor-man circle,#mermaid-svg-rTNwWDx0CaS46VQA line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-rTNwWDx0CaS46VQA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} suite_ticket 尚未就绪 create_auth (AuthCode)等待 suite_ticket 超时重复推送 create_auth

更麻烦的是,如果 create_auth 处理逻辑里先 await waitForSuiteTicket(60000)res.send('success'),响应会被阻塞几十秒,企业微信认为回调失败,会反复重推 create_auth,授权页面也会一直转圈。

修复:

  1. 收到 create_auth 立即回 success
  2. 在后台异步等待 suite_ticket
  3. Set 对同一个 AuthCode 去重,避免重复兑换
javascript 复制代码
if (infoType === 'create_auth') {
  const authCode = eventXml.AuthCode;
  res.send('success'); // 立即响应

  if (!processedAuthCodes.has(authCode)) {
    processedAuthCodes.add(authCode);
    (async () => {
      await waitForSuiteTicket(600000);
      await exchangeAuthCodeForPermanentCode(suiteId, suiteSecret, authCode);
    })();
  }
}

坑 3:open_corpid 与真实企业 ID

get_permanent_code 返回的 auth_corp_info.corpidopen_corpid (以 wp 开头),用于隐私隔离。但代开发应用调用 /gettoken 时,必须填客户企业在管理后台看到的真实企业 ID (以 ww 开头)。
#mermaid-svg-ejpQVOyYhCdSxheJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ejpQVOyYhCdSxheJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ejpQVOyYhCdSxheJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ejpQVOyYhCdSxheJ .error-icon{fill:#552222;}#mermaid-svg-ejpQVOyYhCdSxheJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ejpQVOyYhCdSxheJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ejpQVOyYhCdSxheJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ejpQVOyYhCdSxheJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ejpQVOyYhCdSxheJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ejpQVOyYhCdSxheJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ejpQVOyYhCdSxheJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ejpQVOyYhCdSxheJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ejpQVOyYhCdSxheJ .marker.cross{stroke:#333333;}#mermaid-svg-ejpQVOyYhCdSxheJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ejpQVOyYhCdSxheJ p{margin:0;}#mermaid-svg-ejpQVOyYhCdSxheJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ejpQVOyYhCdSxheJ .cluster-label text{fill:#333;}#mermaid-svg-ejpQVOyYhCdSxheJ .cluster-label span{color:#333;}#mermaid-svg-ejpQVOyYhCdSxheJ .cluster-label span p{background-color:transparent;}#mermaid-svg-ejpQVOyYhCdSxheJ .label text,#mermaid-svg-ejpQVOyYhCdSxheJ span{fill:#333;color:#333;}#mermaid-svg-ejpQVOyYhCdSxheJ .node rect,#mermaid-svg-ejpQVOyYhCdSxheJ .node circle,#mermaid-svg-ejpQVOyYhCdSxheJ .node ellipse,#mermaid-svg-ejpQVOyYhCdSxheJ .node polygon,#mermaid-svg-ejpQVOyYhCdSxheJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ejpQVOyYhCdSxheJ .rough-node .label text,#mermaid-svg-ejpQVOyYhCdSxheJ .node .label text,#mermaid-svg-ejpQVOyYhCdSxheJ .image-shape .label,#mermaid-svg-ejpQVOyYhCdSxheJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-ejpQVOyYhCdSxheJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ejpQVOyYhCdSxheJ .rough-node .label,#mermaid-svg-ejpQVOyYhCdSxheJ .node .label,#mermaid-svg-ejpQVOyYhCdSxheJ .image-shape .label,#mermaid-svg-ejpQVOyYhCdSxheJ .icon-shape .label{text-align:center;}#mermaid-svg-ejpQVOyYhCdSxheJ .node.clickable{cursor:pointer;}#mermaid-svg-ejpQVOyYhCdSxheJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ejpQVOyYhCdSxheJ .arrowheadPath{fill:#333333;}#mermaid-svg-ejpQVOyYhCdSxheJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ejpQVOyYhCdSxheJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ejpQVOyYhCdSxheJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ejpQVOyYhCdSxheJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ejpQVOyYhCdSxheJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ejpQVOyYhCdSxheJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ejpQVOyYhCdSxheJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ejpQVOyYhCdSxheJ .cluster text{fill:#333;}#mermaid-svg-ejpQVOyYhCdSxheJ .cluster span{color:#333;}#mermaid-svg-ejpQVOyYhCdSxheJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ejpQVOyYhCdSxheJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ejpQVOyYhCdSxheJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-ejpQVOyYhCdSxheJ .icon-shape,#mermaid-svg-ejpQVOyYhCdSxheJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ejpQVOyYhCdSxheJ .icon-shape p,#mermaid-svg-ejpQVOyYhCdSxheJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ejpQVOyYhCdSxheJ .icon-shape .label rect,#mermaid-svg-ejpQVOyYhCdSxheJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ejpQVOyYhCdSxheJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ejpQVOyYhCdSxheJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ejpQVOyYhCdSxheJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} auth_corp_info.corpid
不能直接用
企业ID
corpid
get_permanent_code 响应
open_corpid 如 wptbxIbwAA...
/gettoken
企业微信管理后台 我的企业
真实 corp_id 如 ww983d39d9...
access_token

验证:

bash 复制代码
# 错误:用 open_corpid + /service/get_corp_token
{"errcode":48002,"errmsg":"api forbidden"}

# 正确:用真实 corp_id + /gettoken,corpsecret 即 permanent_code
curl "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=YOUR_REAL_CORPID&corpsecret=YOUR_PERMANENT_CODE"
{"errcode":0,"access_token":"xxxxx"}

坑 4:48002 api forbidden

代开发应用如果调用第三方应用的 /service/get_corp_token,就会报 48002 api forbidden

根因: 接口选错了。

修复:api.js 中把 getCorpAccessToken 从:

javascript 复制代码
POST /service/get_corp_token
{ auth_corpid, permanent_code }

改为:

javascript 复制代码
GET /gettoken
{ corpid, corpsecret }

其中 corpsecret 就是 .env 里的 PERMANENT_CODE


坑 5:60011 可见范围不足

拿到 access_token 后,调用 /department/list 又报错:

复制代码
API 错误 (60011): no privilege to access/modify contact/party/agent

原因是应用可见范围只设置了某个成员,没有包含要读取的部门。企业微信里「可见范围」同时控制:

  1. 谁能在工作台看到应用
  2. 应用 API 能访问哪些部门/成员

修复: 在客户企业后台把应用可见范围设为根部门或目标部门。


五、最终同步成功

修复完以上问题后,服务成功跑通:

复制代码
[OK] Webhook 服务已启动,监听端口 3301
[OK] suite_ticket 已就绪
[START] 开始同步组织架构...
[OK] Corp access token obtained
[INFO] 共发现 32 个部门
[OK] 部门详情获取完成
[OK] 成员信息获取完成
[OK] 树结构构建完成
[OK] 组织架构已保存到 data/organization.json
[STATS] 部门总数: 32, 成员总数: 443

六、关键配置示例

env 复制代码
# 代开发应用模板信息
SUITE_ID=YOUR_SUITE_ID
SUITE_SECRET=YOUR_SUITE_SECRET

# AUTH_CORPID 必须填客户企业的真实企业 ID
# 不是 get_permanent_code 返回的 open_corpid
AUTH_CORPID=YOUR_REAL_CORPID

# PERMANENT_CODE 即代开发应用的 secret
PERMANENT_CODE=YOUR_PERMANENT_CODE

# Webhook 回调配置
WEBHOOK_PORT=3301
WEBHOOK_TOKEN=YOUR_WEBHOOK_TOKEN
WEBHOOK_ENCODING_AES_KEY=YOUR_AES_KEY

七、完整事件与数据流

#mermaid-svg-6bqDhxoQbql8htgk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6bqDhxoQbql8htgk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6bqDhxoQbql8htgk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6bqDhxoQbql8htgk .error-icon{fill:#552222;}#mermaid-svg-6bqDhxoQbql8htgk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6bqDhxoQbql8htgk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6bqDhxoQbql8htgk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6bqDhxoQbql8htgk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6bqDhxoQbql8htgk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6bqDhxoQbql8htgk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6bqDhxoQbql8htgk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6bqDhxoQbql8htgk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6bqDhxoQbql8htgk .marker.cross{stroke:#333333;}#mermaid-svg-6bqDhxoQbql8htgk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6bqDhxoQbql8htgk p{margin:0;}#mermaid-svg-6bqDhxoQbql8htgk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6bqDhxoQbql8htgk .cluster-label text{fill:#333;}#mermaid-svg-6bqDhxoQbql8htgk .cluster-label span{color:#333;}#mermaid-svg-6bqDhxoQbql8htgk .cluster-label span p{background-color:transparent;}#mermaid-svg-6bqDhxoQbql8htgk .label text,#mermaid-svg-6bqDhxoQbql8htgk span{fill:#333;color:#333;}#mermaid-svg-6bqDhxoQbql8htgk .node rect,#mermaid-svg-6bqDhxoQbql8htgk .node circle,#mermaid-svg-6bqDhxoQbql8htgk .node ellipse,#mermaid-svg-6bqDhxoQbql8htgk .node polygon,#mermaid-svg-6bqDhxoQbql8htgk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6bqDhxoQbql8htgk .rough-node .label text,#mermaid-svg-6bqDhxoQbql8htgk .node .label text,#mermaid-svg-6bqDhxoQbql8htgk .image-shape .label,#mermaid-svg-6bqDhxoQbql8htgk .icon-shape .label{text-anchor:middle;}#mermaid-svg-6bqDhxoQbql8htgk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6bqDhxoQbql8htgk .rough-node .label,#mermaid-svg-6bqDhxoQbql8htgk .node .label,#mermaid-svg-6bqDhxoQbql8htgk .image-shape .label,#mermaid-svg-6bqDhxoQbql8htgk .icon-shape .label{text-align:center;}#mermaid-svg-6bqDhxoQbql8htgk .node.clickable{cursor:pointer;}#mermaid-svg-6bqDhxoQbql8htgk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6bqDhxoQbql8htgk .arrowheadPath{fill:#333333;}#mermaid-svg-6bqDhxoQbql8htgk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6bqDhxoQbql8htgk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6bqDhxoQbql8htgk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6bqDhxoQbql8htgk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6bqDhxoQbql8htgk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6bqDhxoQbql8htgk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6bqDhxoQbql8htgk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6bqDhxoQbql8htgk .cluster text{fill:#333;}#mermaid-svg-6bqDhxoQbql8htgk .cluster span{color:#333;}#mermaid-svg-6bqDhxoQbql8htgk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6bqDhxoQbql8htgk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6bqDhxoQbql8htgk rect.text{fill:none;stroke-width:0;}#mermaid-svg-6bqDhxoQbql8htgk .icon-shape,#mermaid-svg-6bqDhxoQbql8htgk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6bqDhxoQbql8htgk .icon-shape p,#mermaid-svg-6bqDhxoQbql8htgk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6bqDhxoQbql8htgk .icon-shape .label rect,#mermaid-svg-6bqDhxoQbql8htgk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6bqDhxoQbql8htgk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6bqDhxoQbql8htgk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6bqDhxoQbql8htgk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 同步阶段
Token 阶段
授权阶段
create_auth
suite_ticket
suite_id/secret/ticket
AuthCode + suite_access_token
corpsecret
管理员扫码授权
回调服务
企业微信定时推送
get_suite_token
suite_access_token
get_permanent_code
permanent_code / secret
.env AUTH_CORPID=真实企业ID
/gettoken
access_token
/department/list
/department/get
/user/list
data/organization.json


八、经验总结

  1. 先确认应用类型:第三方应用和代开发应用的 token 接口完全不同,官方文档的「代开发授权应用 access_token 的获取」一节一定要看。
  2. 响应优先 :Webhook 回调里不要做长耗时操作,尤其 create_auth 要立即回 success,后台异步处理。
  3. 区分两种 corp_idget_permanent_code 返回的是 open_corpid,代开发应用调 gettoken 要用真实企业 ID。
  4. 可见范围即权限范围:不只是 UI 可见性,也直接决定 API 能读哪些部门。
  5. 打印原始响应:对接第三方 API 时,把原始响应打印出来,很多错误一眼就能定位。

转载请注明出处。

相关推荐
胡萝卜术2 小时前
V040:RAG 检索增强生成的工程链路解构与生产级系统设计
面试·架构·github
艾德克斯8 小时前
AI服务器电源POWER SHELF测试破局:HVDC高压架构挑战与ITECH全栈测试方案
人工智能·架构
山东云弈创峰科技12 小时前
山东云弈创峰:跨境电商AI自动化中的环境隔离与风控架构
人工智能·架构·自动化
俞凡16 小时前
从战略到战术:真正能落地的软件设计方法论
架构
物联网软硬件开发-轨物科技16 小时前
【轨物方案】从传感器到AI诊断:箱变智能化技术栈四层架构精讲
人工智能·架构
像我这样帅的人丶你还19 小时前
🚀大文件上传的那些事
前端·javascript·架构
CaffeinePro19 小时前
四⼤极简架构原则KISS/DRY/YAGNI/LOD
设计模式·架构
Dreamland工坊19 小时前
从AI 视频到可用资产:聊浏览器端图像编辑器的架构设计
架构
传感器与混合集成电路19 小时前
基于FPGA与ADC协同架构的高密度数据采集模块设计原理与应用场景分析
fpga开发·架构