# 腾讯超级应用小游戏登录配置

## 官方依据

来源为项目已保存的钉钉文档第 5 章和第 13 章引用的腾讯资料。核对日期：2026-09-10。

- [登录实践教程](https://www.tencentcloud.com/zh/document/product/1219/68263)，页面更新时间 2026-05-20，明确方案适用于小程序或小游戏，并写明“调用地址请从控制台获取”。示例 `/sns/jscode2session`。
- [OpenServer 登录接口](https://www.tencentcloud.com/zh/document/product/1219/67646)，页面更新时间 2025-10-28，接口路径 `/openserver/sns/jscode2session`，GET 请求。该页注明仅公有云支持。
- [OpenServer 配置](https://www.tencentcloud.com/document/product/1219/60624#5e07695c-5f7a-44f3-b002-4b8d89c04386)。
- 用户提供的[控制台](https://t10620is7361772zddj.console.tcmpp.com/tcsas/login)是管理入口，不能用作换码接口，不能由其域名猜测 OpenServer 域名。

教程和接口页示例路径有所不同，因此本系统保存控制台提供的完整 URL，保留实际路径。TCMPP 不会默认请求 api.weixin.qq.com。

2026-09-11 在局域网服务器 Docker 内实测：`https://openapi-fk.tcmpp.com/sns/jscode2session` 返回 HTTP 404；`https://openapi-fk.tcmpp.com/openserver/sns/jscode2session` 返回 HTTP 200 JSON。Number Colour 已修正为后一个地址。无效探测 code 返回平台错误码 `11152102`（code 缓存不存在），这只能证明已访问换码接口，不能证明真实玩家登录成功。其他租户仍应以各自控制台的完整地址为准。

## 管理员如何填写

登录后台 → 新建或选择项目 → 登录与项目密钥。

| 字段 | 填写内容 |
|---|---|
| AppID | 当前小游戏标识，创建后不可修改，避免破坏数据归属 |
| 登录平台 | 腾讯超级应用 TCMPP |
| AppSecret | 在小游戏开发管理 → 密钥管理生成的密钥；只在服务器保存 |
| OpenServer 完整地址 | 从控制台复制的完整兑换 URL，例如 `https://实际服务域名/openserver/sns/jscode2session` |
| 兑换超时 | 200～10000 毫秒，默认 5000 |
| 其他平台独立凭据 | 当同一项目不同渠道使用不同 AppID/Secret 时添加；请求 platform 与该标识匹配 |

服务运维人员通过服务器命令 `sh deploy/ops.sh allow-host 域名` 将 OpenServer 主机名加入数据库允许列表（无需重建后台），避免后台配置被用于任意地址请求。只允许 HTTPS 443、公网 IPv4 解析和精确主机匹配；拒绝 URL 用户名密码、查询参数、片段及重定向。私有云部署如需内网 OpenServer，应单独审查配置和网络边界后扩展策略。

保存后密钥输入框清空，仅显示“已配置”；空白或脱敏值表示保留原密钥。勾选“清除”才删除根密钥；移除独立平台会删除其凭据。项目保存采用 version 并发保护，409 时刷新后重新修改。

## 请求流程

小游戏调用 `wx.login` 获取一次性 code，提交 `POST /api/projects/:appId/login`。TCMPP 仍可能使用 wx 作为 SDK 平台标识；默认 wx/tcmpp 使用项目凭据，其他平台必须显式配置。

后台使用服务端凭据发送：

```http
GET /控制台提供的路径?appid=项目凭据AppID&secret=服务端密钥&js_code=一次性code&grant_type=authorization_code
```

只接受包含有效 openid 和 session_key 的成功响应。返回业务 token、过期时间和项目隔离的 userId，不返回平台 openid、session_key 或 AppSecret；客户端提交的 userId 不参与身份生成。再次使用相同 code 返回 409，即使首次兑换超时，也需要客户端重新调用 wx.login 获取新 code。

| 错误 | 含义与处理 |
|---|---|
| app_secret_not_configured / code_exchange_not_configured | 管理员补齐密钥或兑换地址后重试；配置错误不消耗 code |
| provider_host_not_allowed | 运维核对 OpenServer 主机名并加入允许列表 |
| invalid_login_code（401，providerCode 40029） | 重新获取 code，检查小游戏与项目凭据是否对应 |
| code_consumed（409） | code 已用；重新调用 wx.login |
| code_exchange_timeout（504） | 上游超时；重新获取 code 后重试 |
| code_exchange_failed / code_exchange_http_error（502） | 核对租户配置及平台服务；仅返回数字平台错误码，不回显原始响应 |

服务器排障使用 `docker logs --since 10m zymix-backend`，查找 `code_exchange_error`。日志包含时间、项目 AppID、平台、上游主机/路径、错误类型、耗时，以及存在时的 `upstreamStatus` / `providerCode`；不记录查询参数、code、密钥、token 或原始响应。登录失败后的存档 401 表示客户端没有有效玩家 token，应暂停云存档写入、保留本地数据，并重新获取 code 后发起登录。

AppSecret 以独立 CREDENTIAL_ENCRYPTION_KEY 进行 AES-256-GCM 加密后写入 MySQL。该密钥不进入 Git，丢失后无法解密凭据，必须单独备份。原始 session_key 当前仅用于计算摘要，系统未提供手机号或邮箱解密接口。

## token 的区分

后台 Cookie 自动轮换属于管理登录会话。小游戏业务 token 由本服务签发，到期需重新 wx.login。腾讯 getAccessToken 是另一种平台服务端凭据，用于订阅消息等接口；当前后台不调用这些接口，也不会把它作为业务登录 token。本次交付不声称已实现平台订阅消息发送或 getAccessToken 中控服务。

## 验证范围

Docker 协议测试核对完整路径、GET、四个参数、编码字符、多平台凭据和密钥保留、加密落库、超时、错误码、重定向拒绝。真实租户端到端登录仍需项目实际 AppID、密钥、OpenServer 地址和小游戏新获取的 code；这些不能用协议测试数据替代。
