Driver 开发生命周期 API v1
状态:2026-09-22 双方确认冻结 v1.0;尚未部署。类型定义与响应样例随服务一起维护。基础接口见开发者平台 API。
所有路径前缀 /oauth/developer-platform。下表 T=/teams/:teamId,P=T/products/:productId,C=T/clients/:managementClientId,R=P/releases/:releaseId。ID 使用管理 UUID,不把公开 OAuth clientId 当管理 ID。 成功 JSON 均含 success:true;失败 {error:string}。所有响应 no-store。分页 page 从1起,pageSize默认20、最大50,created_at DESC,id DESC;返回 {items,page,pageSize,hasMore},不返回未经计算的total。多 Client 列表可用 managementClientId 过滤。
| 方法/路径 | scope | 响应字段(除success) |
|---|---|---|
| GET /legacy-products | developer:platform | products:Page<LegacyProduct>(仅token subject自己的未归属plugin) |
| GET P/capabilities?managementClientId=UUID | developer:platform | capabilities:Capabilities |
| GET C/draft | developer:draft | draft:Draft |
| PATCH C/draft | developer:draft | draft:Draft |
| GET P/materials | developer:draft | materials:Page<Material> 或 operation:UploadOperation |
| POST P/materials | developer:draft | operation:UploadOperation(完成201,重放200) |
| GET C/review/precheck | developer:review | precheck:ReviewPrecheck |
| POST C/review | developer:review | submission: |
| POST C/reviews/:submissionId/withdraw | developer:review | review:Review |
| GET P/reviews | developer:review | reviews:Page<Review> 或 operation:ReviewOperation |
| GET T/review-history | developer:review | history:ReviewHistory(query:month=YYYY-MM,page=1,settlementPage=1,各page<=10000,固定pageSize沿用ledger并回传) |
| GET P/releases | developer:release | releases:Page<Release>,clients:[{managementClientId,currentPublicRevisionId}] |
| GET R/publish-precheck | developer:release | precheck:ReleasePrecheck |
| POST R/publish | developer:release | release:Release,currentPublicRevisionId:string |
| POST R/delist | developer:release | release:Release,currentPublicRevisionId:string |
体验者继续使用现有三个接口和 developer:manage,不改变其现有 DTO。基础六接口保留。
权限与范围
所有 scope 指定官方启用的 standard Host App 且在独立 allowlist,用户 consent 后 token 才可拥有新 scope,旧token/refresh不扩权。普通或其他官方App均不能绕过。Host另验证官方系统插件可信来源。 产品仅 assigned + plugin + Team/Project绑定正确;所有新产品读操作要求当前用户Team成员且Project至少read;草稿写/上传要求Project write,审核/费用/发布操作额外要求Team owner/admin。capabilities只能作提示,每次写入重验;materialsDelete固定not_supported。 legacy不接收teamId,不给未归属记录虚构team;只返回当前用户自己拥有的未归属plugin摘要。迁移URL由Host使用已知同区域网站基址和网站开放平台路由构造(已核实网站路由 /o/:teamSlug/open-platform,teamSlug来自context;选定目标Team后跳转,不采纳服务端任意URL);本期不实施迁移。多Client须显式选择,materials上传/恢复和review恢复均绑定选定managementClientId。 本期不做web、多Client增删、移动Project、删除Product、listing-kind转换,也不授予审核员权限。不新增plugin development-ticket。
草稿/材料与恢复
DraftUpdate仅允许类型文件的字段;expectedUpdatedAt取最近GET的draft.updatedAt;409 oauth_app_draft_conflict需刷新后让用户处理,不覆盖其他编辑。OAuth配置服务端权威;审核表单本机持久化,明确不跨端,按subject/Team/Product/Client隔离。 POST materials为multipart:唯一file,kind=package|icon|screenshot、version(三种均必填SemVer)、managementClientId均为字符串;其余字段拒绝。Idempotency-Key为Host持久化UUID,同时就是requestId。包<=64MiB,媒体<=10MiB;还需满足当前团队套餐的单文件与总存储额度(可能更低);完整请求上限64MiB+64KiB;使用Host选择文件的opaque handle,不收本地路径。包上传即核验appId/publisherId/oauthAppId/version/Manifest及压缩炸弹限制,提交复验;媒体按文件字节检测图片类型。每次先鉴权和校验绑定/内容指纹;已完成重放直接恢复原结果,不再占配额;只有新写入或续传写入才检查新增配额。 GET materials?requestId=UUID&managementClientId=UUID返回当前actor该操作的not_found/in_progress/completed/failed;不暴露其他人的key。列表返回该Product可见已完成材料,managementClientId可过滤。completed材料若源文件已删除返回unavailable,不能复用。重复key不同kind/version/Client/字节摘要返回409 developer_upload_idempotency_conflict。in_progress返回202并用相同key重试/查询;租约超时后允许同key恢复。failed保留稳定错误码,同key同内容允许重试。重启不生成新key。 材料永久计入已有项目存储配额,没有本期自动过期删除或客户端删除入口;用户可从网站项目文件管理处理未使用的源文件,审核冻结副本按原有保留规则保护。列表不返回存储bucket/path/内部URL。
审核
precheck无版本/材料query输入,只检查实时额度/费用与开发草稿资格,validationScope固定quota_and_fee_only;不表示包、表单或权限声明已经审核通过。收费指纹由既有RPC绑定Team/付款人/Client/Product/草稿ID及更新时间/余额快照/费率,返回不透明paidQuoteFingerprint。 提交强制UUID Idempotency-Key,body严格遵循SubmitReview;1 universal包、1图标、1–9截图。材料ID必须属于当前产品/选定Client;冻结catalogMetadata输出的是artifactId而非源fileId。confirmPaid必须用户明确选择,不默认勾选;价格单位为整数milliCredits。 GET reviews?requestId=原提交Idempotency-Key&managementClientId=UUID只按当前actor精确恢复。completed含完整Review;not_found意味着尚无已提交记录(也可能仍在执行),不代表失败;使用同key相同负载查询/重试,服务端既有提交事务幂等避免重复扣费。Review.billing给出费用/退款状态;withdraw同样强制操作UUID key,响应丢失后读取review最终status和billing。withdraw只接受空JSON对象;后台决定可撤回条件和是否退款。 审核期间可继续改草稿但不改变冻结快照;单活动审核单约束遵循现有RPC;拒绝/撤回后能再提审,已生成release的重复版本号不允许。新版本审核不影响线上版本。
发布
GET releases同时返回每个Client当前publicRevision,可按releaseId或managementClientId过滤;以目标release.status=published且release.revisionId等于对应Client当前revision恢复发布成功;仅revision相等不能确认成功,因为下架保留该指针。publish-precheck成功返回canPublish:true,失败以稳定HTTP错误返回,不返回伪造可发布结果。 publish携带expectedCurrentPublicRevisionId,null明确指首次/当前无线上版本;冲突409 oauth_product_release_publish_precheck_stale后刷新并重新确认。若该插件是官方 Host 依赖登记里的 blocking 插件(如 Voice),候选包 manifest 必须对每个 active Driver 登记通过兼容谓词(hostApi.range 覆盖登记 hostApi,range 只接受 >= > < <= = 加完整 x.y.z;targets 覆盖登记 platforms),否则 409 plugin_release_host_incompatible(附 message 与 hostCompatibility.reasons),不提供普通放行;依赖读取失败 503 plugin_release_host_check_unavailable。publish-precheck 不含此检查。成功但响应丢失先查询release状态及对应Client的currentPublicRevisionId,已匹配即完成。 delist必须带非空expectedCurrentPublicRevisionId,并在数据库事务中检查目标release是当前线上版本;否则409 oauth_product_release_delist_conflict。下架保持网站语义,不清除Client currentPublicRevisionId。下架响应丢失通过目标release.status=delisted确认该目标已下架,同时读取当前Client revision及其release状态;若新版已上线,应显示目标已下架而当前线上为新版,不自动下架新版。重新上架丢响应同样必须目标status=published且currentPublicRevisionId匹配;被新版替代时按真实状态展示,不盲目重放。
错误与验收
401 invalid_token;403 unauthorized_client/insufficient_scope/oauth_team_member_required/oauth_project_read_required/oauth_project_write_required/oauth_team_admin_required;404 oauth_product_not_found/oauth_app_not_found/oauth_review_submission_not_found;400 developer_request_invalid/developer_idempotency_key_required;409草稿、报价、上传幂等、版本和发布冲突;413 developer_upload_too_large/STORAGE_QUOTA_EXCEEDED;429 developer_upload_busy;503 developer_platform_unavailable。其余沿用现有网站稳定RPC业务码,文档和fixture不得用伪成功代替错误。 缺新scope时capabilities仍可由旧platform授权读取,返回动作missingScopes/reauthorization_required;状态不允许与角色不够分别返回state_conflict/permission_denied。服务未部署由Host识别合同端点缺失;服务已部署但DB依赖缺失返回503,不报告已可用。 后端负责真实HTTP+数据库/存储的端到端验证(含拒绝、并发和重试),签名Driver另做真实consent及上传→提审→发布→另一客户端安装联调。PR合并/健康检查不替代业务验收。
v1 补充约束
capabilities强制managementClientId query,并校验Client属于Product、响应回显;动作状态按该Client计算。releasePublish仅表示该Client存在可进入发布预检的候选,具体能否发布以目标release的precheck为准。 Draft.name去首尾空白后1–120 UTF-8字节,description<=4000 UTF-8字节且可null;homepageUrl/logoUrl接受null清除,非null只允许https且沿用现有URI验证;空字符串不用于URL清除。scopeCodes最多32个非空、不重复code,实时资格复验。 CatalogMetadata name/developerName必填各<=200字符、description必填<=10000字符、tagline必填<=240字符;caption可null且<=200字符;类别与网站五枚举一致。所有提交材料必须匹配本次version和managementClientId,不跨版本直接引用;复用图片时用新版本重新上传(存储CAS可去重,请求key仍新建)。 已知审核/费用状态详见类型;未来未知状态必须只读显示,不自行推断操作成功或解锁按钮,刷新capabilities/详情。ReviewHistory campaign和settlements中历史沿用的refundMilli/rewardMilli同样是milliCredits整数,非元/浮点数。 预览增加受限二进制GET:P/materials/:fileId/content?managementClientId=UUID(developer:draft)和P/reviews/:submissionId/artifacts/:artifactId/content?managementClientId=UUID(developer:review)。仅icon/screenshot;每次权限/产品/Client校验,固定图片MIME、<=10MiB、核验摘要,nosniff/no-store,不返回可重定向任意URL。Host只从固定API获取字节,转换为本地受控预览资源交给插件;包不从这些接口下载。已删除/过期材料返回404,展示不可预览而非伪造图片。Material包含originalFileName(仅展示,不作为Host文件路径)。