Skip to content

支付 → Hermes 容器 → 微信绑定 流程文档

目标态(intended design),不是当前 backend 实现。当前代码与本文档存在差异,差异点见底部「现状 gap」章节。前端 v3 重画按本文档目标态写文案。


一、流程总览(4 阶段,目标态)

阶段 1 — 支付回调
  └─ 微信支付 webhook → order.status = PAID

阶段 2 — Spawn Hermes 容器(与客户绑定)
  ├─ 创建 hermesInstance 行,绑定 userId
  ├─ 创建 subscription 行
  ├─ NewAPI 发 LLM token,写入 instance.llmApiKey
  └─ 执行 spawn-profile.sh
     ├─ 起 docker 容器(Hermes 进程 + profile dir)
     ├─ profile 与 instance 一一对应(profileName = instance.id 或 user 标识)
     ├─ 容器暴露 baseUrl + hostPort
     └─ instance.status = READY_TO_BIND

阶段 3 — 微信绑定(QR 来自 Hermes 容器,不是 iLink)
  ├─ 前端 StatusPage 调 backend /services/instance/:id/wechat-qr
  ├─ backend 转发到 Hermes 容器 baseUrl/wechat/qr
  ├─ Hermes 容器返回 QR + token(容器自己生成)
  ├─ 前端展示 QR,轮询 backend /wechat-status
  ├─ backend 转发到 Hermes 容器 baseUrl/wechat/status?qrcode=...
  ├─ 用户微信扫码 → Hermes 确认 → 返回 confirmed
  └─ instance.status = READY,绑定信息保存在容器内

阶段 4 — 服务可用
  └─ 用户在微信里与 Hermes bot 对话

二、状态机(hermesInstance.status)

state含义进入方式用户能看到什么
PROVISIONING支付完,正在 spawn 容器阶段 2 起始"支付成功,正在创建专属 Agent..."
READY_TO_BIND容器已起,等微信扫码spawn-profile.sh 成功"请扫描下方二维码绑定微信" + QR
BINDING用户扫了码,等确认iLink/Hermes 返回 scanned"请在微信中确认绑定"
READY绑定完成,服务可用Hermes 返回 confirmed"Agent 已上线,回到微信对话"
ERRORspawn 失败 or 绑定失败spawn-profile.sh 报错 / 超时报错 + rebind 按钮

旧状态 PENDING_BIND 含义模糊("已建账未绑定" vs "容器已起未绑定"),目标态删除。


三、前端文案对应(v3 Art Deco 重画用)

PurchasePage — 支付成功 banner

✅ 支付成功
账户已创建,正在为你启动专属 Agent...
[前往激活 →]   → navigate('/dashboard/status')

StatusPage — 按 status 分支

status主区文案主区元素CTA
PROVISIONING"Agent 启动中(约 30 秒)"loading spinner
READY_TO_BIND"扫码激活微信"Hermes 出的 QR"刷新二维码"
BINDING"请在微信中确认"模糊化 QR
READY"Agent 已上线"微信链接按钮"回到微信对话"
ERROR"启动失败:{lastError}""重试" → rebind

注意:StatusPage 的 WeixinState enum 现在是 idle/loading/qr/confirmed/provisioning/ready/failed/error,与目标态 status 不完全对齐。重画时按目标态状态机写,源 enum 暂不改。


四、关键设计原则

  1. QR 是容器出的,不是外部服务出的

    • Hermes 容器启动时即生成 WeChat 绑定 QR
    • backend 只做 reverse proxy,不持有 iLink 凭证
  2. 容器先起,绑定后做

    • 付款后立即 spawn,不等绑定
    • 用户付完到看见 QR 之间只隔一个 spawn 耗时(约 30s)
    • 不需要在「拿 QR」这步阻塞用户
  3. profile = instance 一一对应

    • 每个付费用户一个 Hermes profile dir
    • profileName 用 instance.id 作为唯一键
    • profile 包含 LLM key + 用户上下文 + 微信绑定(绑定后)
  4. 绑定信息存在容器内

    • 微信 channel / token 由 Hermes 容器持有,不需要 backend 存
    • rebind 时 backend 停容器 + 重 spawn,让容器重新出 QR

五、API 路由(目标态)

MethodPath行为
POST/orders创建订单
POST/payments/wechat/qrcode出微信支付 QR
GET/orders/:id/status查支付状态
POST/payments/wechat/dev-mock-paidDEV-only 模拟回调
GET/services/status查当前 subscription + instance.status
GET/services/instance/:id/wechat-qr代理到 Hermes 容器${baseUrl}/wechat/qr
GET/services/instance/:id/wechat-status代理到 Hermes 容器${baseUrl}/wechat/status?qrcode=...
POST/services/instance/:id/rebind停容器 + 重 spawn → 新 QR

六、现状 gap(当前 backend 与目标态差异)

仅记录,本次前端重画不动 backend。等前端跑通后再单独开 PR 改 backend。

  • 现状:service-runtime.service.ts:54-60 getWeixinQr() 直接 axios.get('https://ilinkai.weixin.qq.com/ilink/bot/get_bot_qrcode')
  • 目标:proxy 到 ${instance.baseUrl}/wechat/qr
  • 改动:删 ILINK_BASE 常量 + iLink 调用,改为读 instance + 转发到容器
  • 依赖:Hermes 端必须暴露 /wechat/qr 路由

gap 2: spawn 时机错位

  • 现状:spawn 发生在 BIND_CONFIRMED 之后(provisioning.service.ts:119 spawnAfterBind
  • 目标:spawn 发生在 PAID 之后(provisionPaidOrder 内联 spawn)
  • 改动:
    • provisionPaidOrder 末尾直接调 spawnHermesProfilePublic
    • spawnAfterBind 或保留作为重启路径(rebind 复用)
    • onBindConfirmed 不再调 spawnAfterBind,只更新 status = READY

gap 3: 状态机不一致

  • 现状:PENDING_BIND → BIND_CONFIRMED → PROVISIONING → READY
  • 目标:PROVISIONING → READY_TO_BIND → BINDING → READY
  • 改动:Prisma schema 改 enum;写 migration;service 代码改状态转移;前端 StatusPage 状态分支同步

gap 4: spawn-profile.sh 入参依赖绑定信息

  • 现状:spawnHermesProfilePublic 接受 weixinChannel / weixinToken 作为 env 注入
  • 目标:spawn 时还没绑定,这两个参数应为空;容器自己处理绑定
  • 改动:
    • provision-worker.service.ts:147-148 删掉这两个入参的传递
    • spawn-profile.sh 内不再依赖 WEIXIN_HOME_CHANNEL / WEIXIN_TOKEN
    • Hermes 启动时自己生成 QR 流程

gap 5: NewAPI token 创建在 spawn 前 OK

  • 现状:provisionPaidOrder 内创建 NewAPI token → 存 instance.llmApiKey
  • 目标:相同,spawn 时把 llmApiKey 注入到容器 env
  • 无 gap

七、迁移顺序(backend 改造 PR 拆分建议)

  1. PR-A:Hermes 端加 /wechat/qr + /wechat/status 路由(其它仓库)
  2. PR-B:backend service-runtime 改 proxy(删 iLink 调用)
  3. PR-C:backend provisioning 改 spawn 时机(PAID → spawn 一步到位)
  4. PR-D:Prisma schema status enum 改造 + 状态机 service 改写
  5. PR-E:前端 StatusPage 状态分支按新 enum 改

本次前端 v3 重画 = PR-E 的视觉部分。不动 backend,文案按目标态写,行为暂用现有 enum 兜底(差异点用注释标 // TODO: align with target state machine)。


最后更新:2026-05-18 作者:v3 Art Deco redesign session 关联文档docs/product-doc-v3.md(marketing 文案)、backend/src/modules/provisioning/(现状代码)

团队内部文档