Telegram 付费群门禁前端对接文档
本文档基于 feature/tgbot 对应的“增加 chat gate”改动(当前 test 分支提交 483351f8)整理,面向 App/H5 前端。
本次新增 Telegram 付费群入口:符合资格的用户可在前端按需获取个人邀请链接,跳转 Telegram 提交入群申请;后端机器人校验 Splay 与 Telegram 账号绑定关系后自动审批。
1. 前端改动总览
| 接口 | 用途 | 调用时机 |
|---|---|---|
POST /v1/user/telegram/group/access/status/get |
查询入口、申请和入群状态,不创建邀请链接 | 进入相关页面、App 回到前台、等待审批时 |
POST /v1/user/telegram/group/join-link/create |
创建或复用当前用户的邀请链接 | 用户主动点击“加入 Telegram 群”时 |
前端只需要接入以上两个接口。机器人审批、账号绑定、邀请链接失效和未授权成员移除均由后端处理。
2. 通用约定
- 两个接口均为登录态
POST请求,请继续携带现有的登录凭证、签名头和设备头。 - 请求体均为空对象
{}。 - JSON 字段使用
snake_case。 status按当前网关配置返回枚举字符串。expires_at是 Unix 秒。它是 Protobufint64,JSON 中通常为字符串;前端应兼容string | number。- 响应启用了零值输出,无链接时仍可能返回
"link": ""、"expires_at": "0"。 - 资格不足、功能关闭、等待审批和已经入群都不是接口异常,均通过 HTTP 200 响应中的
status表达。
邀请链接与用户一一对应且有效期较短,不要写入埋点、日志或分享渠道。前端不需要、也不应持有 Telegram Bot Token 和目标群 chat_id。
3. 用户资格
入口资格由后端判断,前端不要根据订单列表自行计算。当前条件为同时满足:
- Splay 用户状态正常;
- 至少存在一笔金额大于 0、支付已完成的外部支付订单。
当前计入的支付渠道包括 Apple、Google、Stripe、In The Bag、Moneta Pay、EZ、HaiPay、FuturePay、Jaya、UnisPay 和 Bank。业务规则以后端返回状态为准,前端无需固化这份渠道列表。
4. 状态枚举与 UI 行为
status |
数字值 | show_entry |
can_request_link |
含义 | 前端建议 |
|---|---|---|---|---|---|
TELEGRAM_GROUP_ACCESS_STATUS_UNKNOWN |
0 | false |
false |
未知状态,正常不应出现 | 隐藏入口并记录异常 |
TELEGRAM_GROUP_ACCESS_STATUS_DISABLED |
1 | false |
false |
功能未启用或配置不完整 | 隐藏入口 |
TELEGRAM_GROUP_ACCESS_STATUS_NOT_ELIGIBLE |
2 | false |
false |
当前用户不符合入群资格 | 隐藏入口 |
TELEGRAM_GROUP_ACCESS_STATUS_AVAILABLE |
3 | true |
true |
可以申请邀请链接 | 展示可点击的入群入口 |
TELEGRAM_GROUP_ACCESS_STATUS_LINK_READY |
4 | true |
true |
已有未过期链接可复用 | 展示可点击的入群入口;点击后仍调用创建接口取链接 |
TELEGRAM_GROUP_ACCESS_STATUS_PENDING |
5 | true |
false |
已在 Telegram 提交入群申请,等待机器人完成审批 | 展示“处理中”,禁用重复申请 |
TELEGRAM_GROUP_ACCESS_STATUS_JOINED |
6 | false |
false |
已经在目标群内 | 按返回字段隐藏入口 |
前端应优先使用后端返回的 show_entry 和 can_request_link 控制 UI,不要重复推导:
type TelegramGroupAccess = {
status: string;
show_entry: boolean;
can_request_link: boolean;
};
function telegramEntryState(data: TelegramGroupAccess) {
return {
visible: data.show_entry,
disabled: !data.can_request_link,
pending:
data.status === "TELEGRAM_GROUP_ACCESS_STATUS_PENDING",
};
}
PENDING 可能持续时间很短,机器人审批较快时,前端可能直接从 LINK_READY 看到 JOINED,不要依赖一定观察到中间状态。
5. 查询 Telegram 群入口状态
POST /v1/user/telegram/group/access/status/get
请求体:
{}
可申请时的响应示例:
{
"status": "TELEGRAM_GROUP_ACCESS_STATUS_AVAILABLE",
"show_entry": true,
"can_request_link": true
}
等待审批时的响应示例:
{
"status": "TELEGRAM_GROUP_ACCESS_STATUS_PENDING",
"show_entry": true,
"can_request_link": false
}
注意:本接口只查询状态,不创建邀请链接。即使返回 LINK_READY,响应中也不包含链接;用户点击入口后仍需调用下一节的创建接口。
建议在以下时机重新查询:
- 进入包含 Telegram 群入口的页面;
- 从 Telegram 返回 App/H5、页面重新获得焦点时;
- 创建链接并跳转 Telegram 后,短时间等待审批时;
- 用户手动点击“刷新状态”时。
如果需要自动刷新,建议前台每 3 秒查询一次,最多持续 30 秒;页面退到后台后停止轮询,回到前台立即查询一次。
6. 创建或复用邀请链接
POST /v1/user/telegram/group/join-link/create
只在用户主动点击入口且 can_request_link = true 时调用。请求期间应禁用按钮,避免重复点击。
请求体:
{}
成功取得链接:
{
"status": "TELEGRAM_GROUP_ACCESS_STATUS_LINK_READY",
"link": "https://t.me/+exampleInviteCode",
"expires_at": "1786781400"
}
| 字段 | 类型 | 说明 |
|---|---|---|
status |
enum string | 创建后的最新业务状态,必须重新判断,不能假定一定是 LINK_READY |
link |
string | 当前用户的 Telegram 邀请链接;仅 LINK_READY 时使用 |
expires_at |
string \| number |
链接过期时间,Unix 秒;仅 LINK_READY 时有效 |
处理逻辑:
- 判断响应
status; - 仅当状态为
LINK_READY且link非空时,使用系统浏览器或 Telegram Deep Link 能力打开link; - 用户需要在 Telegram 页面点击“申请加入”;打开链接本身不代表已经入群;
- 返回 App/H5 后重新查询入口状态;
- 如果链接已过期,再次调用本接口获取新链接,不要继续使用前端缓存。
示例:
const LINK_READY = "TELEGRAM_GROUP_ACCESS_STATUS_LINK_READY";
async function joinTelegramGroup() {
const result = await api.post("/v1/user/telegram/group/join-link/create", {});
if (result.status === LINK_READY && result.link) {
openExternalUrl(result.link);
return;
}
// 资格、审批或成员状态可能已发生变化,按最新状态刷新页面。
applyTelegramGroupStatus(result.status);
}
本接口具备复用语义:已有未过期邀请链接时返回原链接;并发创建时后端只保留一个有效链接。但前端仍应在请求期间禁用按钮,避免无意义的重复请求。
7. 创建接口的非链接响应
用户点击按钮到请求到达后端之间,状态可能已经变化。因此,HTTP 200 不代表一定能获得链接:
{
"status": "TELEGRAM_GROUP_ACCESS_STATUS_PENDING",
"link": "",
"expires_at": "0"
}
| 返回状态 | 前端处理 |
|---|---|
DISABLED / NOT_ELIGIBLE / JOINED |
关闭加载态,按状态响应中的最新语义隐藏入口 |
PENDING |
展示“入群申请处理中”,开始有限轮询或提供刷新按钮 |
LINK_READY |
打开非空 link |
AVAILABLE / UNKNOWN |
不打开空链接,重新查询状态并提供重试 |
8. 完整交互流程
页面加载
→ 查询 access/status/get
→ show_entry = false:隐藏入口
→ show_entry = true:展示入口
→ can_request_link = false:展示“处理中”,禁止点击
→ can_request_link = true:允许点击
用户点击入口
→ 调用 join-link/create
→ LINK_READY + link:跳转 Telegram
→ 其他状态:不跳转,按最新状态刷新 UI
Telegram 内
→ 用户点击“申请加入”
→ 机器人校验资格及账号绑定
→ 校验通过:自动批准并私聊成功消息
→ 校验失败:拒绝申请并私聊失败原因
用户回到前端
→ 再次查询 access/status/get
→ PENDING:短时间轮询
→ JOINED:隐藏入口
→ LINK_READY / AVAILABLE:仍可重新申请
9. Telegram 账号绑定限制
系统维护 Splay 账号与 Telegram 账号的一对一绑定:
- 一个 Splay 账号不能绑定多个 Telegram 账号;
- 一个 Telegram 账号不能绑定多个 Splay 账号。
绑定冲突、链接过期或资格失效发生在 Telegram 申请阶段,前端接口不会返回对应的专用错误码。机器人会在 Telegram 中拒绝申请并私聊具体原因。前端可统一提示用户:
如果入群申请被拒绝,请查看 Telegram 中机器人发送的消息,并确认当前 Telegram 账号是否已经绑定其他 Splay 账号。
10. 接口错误处理
统一错误响应示例:
{
"code": 11003,
"message": "服务器内部错误"
}
| HTTP 状态 | code |
场景 | 前端处理建议 |
|---|---|---|---|
401 |
11004 CODE_UNAUTHORIZED |
登录失效 | 走现有重新登录流程 |
500 |
11009 CODE_DATABASE |
查询或保存状态失败 | Toast“加载失败,请稍后重试”,保留重试入口 |
503 |
11003 CODE_INTERNAL_SERVER |
Telegram 服务、机器人客户端或邀请链接创建暂不可用 | Toast“Telegram 服务暂不可用,请稍后重试” |
前端应以 code 为准做分支,message 只用于兜底展示。通用签名错误、限流和网络异常继续使用现有全局处理逻辑。
11. 联调验收清单
- 功能关闭时返回
DISABLED,入口不展示。 - 无符合条件订单的用户返回
NOT_ELIGIBLE,入口不展示。 - 符合资格且未入群的用户返回
AVAILABLE,入口可点击。 - 点击后返回
LINK_READY、非空https://t.me/+...链接和有效expires_at。 - 有效期内重复点击返回可复用链接,不产生多个前端入口状态。
- 链接过期后可以重新获取新链接。
- 在 Telegram 点击申请后能够被机器人自动批准。
- 回到前端重新查询最终返回
JOINED,入口隐藏。 - 用户主动退群后重新查询返回
AVAILABLE,可再次获取链接。 - 使用已绑定其他 Splay 账号的 Telegram 账号申请时,Telegram 收到拒绝和说明消息。
- App/H5 使用系统能力打开 Telegram 链接,不在内部 WebView 中拦截
t.me跳转。