图奕统一认证服务 API
本文面向网云穿、远程控制和异地组网的服务端。产品客户端、浏览器和桌面 Native Client 不得持有 Service Client Secret,也不得直接调用本 API。用户登录请看 OIDC,桌面不跳浏览器的边界见 Native。
外部短信是“模板发送服务”,不是“平台登录认证服务”。发送产品提供的六位数字不会创建平台登录挑战,也不能换取用户 Token。不要用该接口替代统一登录。
认证模型
用户登录与服务调用使用两套独立客户端:
- OIDC Web/Native Client:只用于用户登录,Scope 为
openid profile phone offline_access。 - Confidential Service Client:只部署在产品服务端,Scope 为
sms:send sms:read payments:create payments:read refunds:create的授权子集。
获取 5 分钟服务令牌:
POST /oauth2/token HTTP/1.1
Host: platform.too1.net
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&scope=sms%3Asend%20payments%3Aread
成功响应为标准 OAuth JSON。Service Token 是不透明字符串,不包含用户身份、不签发 Refresh Token。产品应在到期前重新获取并只保存在服务端内存中。
Service Client 支持多个并行密钥。轮换后旧密钥默认保留 24 小时;调用方确认新密钥生效后,可在后台立即吊销旧密钥。可为客户端配置来源 CIDR 白名单。
通用约定
- 基础地址:
https://platform.too1.net - 所有请求与响应使用 UTF-8 JSON。
- 金额使用整数分,首期币种固定为
CNY。 - 所有写接口必须携带
Idempotency-Key,长度 1–64 字符,并在同一 Service Client 下唯一。 - 推荐 Key 只用 ASCII,添加操作与资源前缀:
sms:订单或操作编号、pay:订单号、close:平台支付ID、refund:退款号。Key 是该 Client 下跨接口共享的,不是每条路由独立;关闭不同订单不能重复使用同一个 Key。 - 当前摘要按原始 JSON 请求字节计算,空格、字段顺序变化也可能冲突。重试必须复用同一份序列化结果,不能只保证 JSON 语义相同。
- 幂等记录当前保存至少约 24 小时,过期由维护任务清理;不能依赖它永久防重,产品仍需唯一业务单号和可靠记录。
- 同一幂等键和相同请求体会重放首次结果,并返回
Idempotent-Replayed: true。 - 同一幂等键和不同请求体返回 HTTP
409 idempotency_conflict。 - 成功响应:
{"data": ...}。 - 失败响应:
{"error":{"code":"...","message":"...","request_id":"..."}}。 X-Request-ID可用于后台和结构化日志定位,不要在日志中记录 Bearer Token 或请求中的短信参数。- JSON 请求体上限 64 KiB,仅接受单个 JSON 对象,不接受未定义字段。OIDC Token 端点仍是表单与标准 OAuth JSON,不使用平台
data包装。
Scope 与 Token 缓存
| Scope | 可调用方法 |
|---|---|
sms:send |
POST 短信发送 |
sms:read |
GET 短信记录 |
payments:create |
POST 支付会话、关闭支付 |
payments:read |
GET 支付、GET 退款 |
refunds:create |
POST 退款申请 |
仅有 refunds:create 不代表可以查询退款,读取仍需 payments:read。省略 scope 时平台按 Client 授权集合发令牌;建议显式请求最小权限。
产品服务端按 Client 和 Scope 集合缓存 Token,依据 expires_in 提前约 30 秒更新;并发请求合并取 Token,避免每条业务请求重新获取。Client Credentials 没有 Refresh Token,到期重新申请。网络请求必须有超时,401 时先检查类型/禁用状态;不要盲目刷新并自动重放具有副作用的业务请求。
无副作用的取令牌示例(curl 会提示输入 Secret;避免把密码写进 shell 历史或 -v 日志;输出包含 Token,不要截图外发):
curl --fail-with-body --max-time 10 \
--user wyc-service \
--data-urlencode grant_type=client_credentials \
--data-urlencode 'scope=sms:send sms:read' \
https://platform.too1.net/oauth2/token
业务程序应使用 HTTP 客户端和内存缓存,不通过 shell 调用带 Token 的命令。Basic 认证按 OAuth 库编码,不把 Secret 放查询参数。CIDR 限制使用产品服务端真实出口地址,不使用用户电脑 IP。
短信
发送单号码事务短信:
POST /api/v1/sms/messages
Authorization: Bearer SERVICE_TOKEN
Idempotency-Key: wyc-login-20260902-000001
Content-Type: application/json
{
"template_code": "login_code",
"phone_e164": "+8613812345678",
"params": {"code": "123456"}
}
平台模板代码会在后台映射到联麓 Template ID;产品看不到渠道模板 ID。参数名称、顺序、长度和正则由管理员登记的 Schema 决定。参数值只在本次渠道调用中使用,不落库、不写日志。
请求字段
| 字段 | 类型 | 规则 |
|---|---|---|
| template_code | string | 后台已登记并授权给产品的模板代码,不是联麓模板 ID |
| phone_e164 | string | 当前仅中国大陆 +86 手机号,不接受数组 |
| params | object<string,string> | 字段集合与模板 Schema 完全相同,不缺字段、不额外添加字段 |
例如 Schema 规定 code 为六位数字,必须传 {"code":"012345"},不能传数字 12345 丢掉前导零。参数顺序由后台 Schema 决定;前端页面不能任意选择模板或群发名单。
默认每产品 10 RPS、突发 20、每天 10,000 条;同手机号同模板 60 秒一次、每天 10 次,跨模板每天 20 次。管理员可调整授权额度;以实际限制及返回值为准。没有模板授权默认拒绝。
返回规则:
201 accepted:渠道明确受理。202 unknown:连接超时或结果未知,平台不会自动重发;产品应查询消息状态并避免立即换幂等键重复发送。502 provider_rejected:渠道明确拒绝。403 template_not_granted:产品没有模板授权。429 quota_exceeded/rate_limited:额度或频率限制。
受理返回示例:
{
"data": {
"message_id": "00000000-0000-4000-8000-000000000030",
"template_code": "login_code",
"phone": "+86****0000",
"parameter_names": ["code"],
"status": "accepted",
"provider": "lianlu",
"provider_reference": "PROVIDER_TASK_REFERENCE",
"latency_ms": 180,
"created_at": "2026-09-03T00:00:00Z",
"updated_at": "2026-09-03T00:00:00Z"
}
}
示例脱敏格式不应作为解析协议;原样显示即可。可选字段可能省略。accepted 仅代表渠道受理,不代表手机已经收到;unknown 查询也可能仍是 unknown,当前没有向产品承诺最终送达回执。
失败与未知结果
同键重试只用于领取已经完成的原请求结果,不代表再次发送。收到明确拒绝需要修正原因后,若用户重新发起一次新的发送,使用新的操作 Key。遇到连接中断、202 unknown 或结果保存异常,应先按已知 message_id 查询;没有 ID 则根据请求编号联系运维,不能自动换 Key 重发。
发送按钮倒计时由产品界面负责,平台不会给产品页面自动加倒计时。建议默认 60 秒,处理 429/503/未知结果并禁用重复点击;不要在失败回调中立即再次发短信。需要产品自行验证的业务验证码应独立保存 HMAC/哈希、用途、过期、次数、单次消费,不能与平台登录验证码混用。
查询发送记录:
GET /api/v1/sms/messages/{message_id}
Authorization: Bearer SERVICE_TOKEN
记录仅返回脱敏手机号、模板代码、状态、渠道任务号和耗时,不返回模板参数值。
支付与退款
支付 API、收银台、状态机、回调核对和退款规则见 PAYMENTS.md。
Webhook
平台以至少一次语义投递产品事件。请求头:
X-Tuyi-Event-ID:产品幂等键。X-Tuyi-Timestamp:Unix 秒时间戳。X-Tuyi-Signature:sha256=HEX(HMAC-SHA256(secret, timestamp + "." + raw_body))。
支付事件包括 payment.succeeded、payment.closed、payment.duplicate_suspected、refund.pending_approval、refund.succeeded、refund.failed。账号事件包括 user.disabled、user.enabled、user.phone_changed、user.sessions_revoked 和 mapping.created;产品成员事件包括 product_membership.activated、product_membership.suspended、product_membership.reactivated。成员邀请与产品/代理职责边界见产品成员文档。
产品必须先验证时间窗口与签名,再按事件 ID 幂等入库,最后交付权益。平台返回支付成功不等于产品权益已交付;Webhook 失败会指数退避并在后台显示积压。
完整消息结构、Go 验签函数、数据库 inbox 幂等及乱序处理见 Webhook 详细文档。常见 HTTP 与业务错误见排障文档。
最小接入顺序
- 管理员创建产品并登记精确的 HTTPS 收银台返回地址。
- 创建 Service Client,仅授予产品需要的 Scope,并保存一次性显示的 Secret。
- 为产品授权短信模板、绑定支付账户并保持事故开关关闭。
- 产品服务端实现 Token 缓存、幂等键、Webhook 验签与事件幂等。
- 在 staging 使用模拟支付和假渠道完成闭环。
- 分别完成真实短信、微信/支付宝 0.01 元支付、查询、回调和退款后,才开启相应生产开关。