第三方 API Key 集成文档
baozi 第三方接入:你(第三方)通过 ohoooho 统一认证(OAuth2 client_credentials 标准协议 + ES384 JWT)调 baozi 接口,一步完成「下单 + 创建 API Key」,5 分钟接入。
baozi 是 ohoooho 统一认证的 Resource Server:第三方在 ohooho 统一认证后台创建 M2M App,用 OAuth2 client_credentials 拿 access_token,调 baozi 接口时带 Authorization: Bearer <access_token>。baozi 只验签,不签 token。access_token 有效期 2 小时(7200 秒),客户端需自管理刷新。
1. 准备工作(4 步)
1.1 创建 API Resource
| 步骤 | 操作 |
|---|---|
| 后台 → API Resources → + Create API resource | 创 API Resource |
| API name | baozi Third-Party API |
| API identifier | https://baozi.ohoooho.com |
1.2 创建 M2M Role
| UI 路径 | 操作 |
|---|---|
后台 Roles → + Create role → 命名 baozi Third-Party |
创 Role |
1.3 创建 M2M App + 授权 Role
| UI 路径 | 操作 |
|---|---|
| 后台 Applications → + Create application → Machine-to-machine | 创 M2M App |
| M2M App 配置页 → Roles tab | 勾 baozi Third-Party Role |
1.4 确认 end_user(无需绑定)
OAuth2 client_credentials 只验证 M2M App 本身的身份,不绑定任何 baozi 用户。API Key 归属于请求中 external_user_id 所指定的 end_user。
1.4.1 external_user_id 的语义
external_user_id 是 end_user 在 ohooho 统一认证体系中的稳定唯一标识,对应 ohooho 统一认证 user.id(字符串,Mgmt API 返 12 字符 base64-like,非 UUID)。
baozi 用 M2M token 调 GET https://login.ohoooho.com/api/users/{external_user_id} 反查 ohooho 统一认证,拿到完整 profile(含 sub / primaryEmail / primaryPhone / identities),再用 sub 在本地 users 表 upsert 拿到 newapi_uid。
| user id 类型 | baozi 处理方式 |
|---|---|
ohoooho 统一认证 user.id(字符串) |
✅ 三方传 external_user_id=<ohoooho 统一认证 user.id>,baozi 用 M2M token 调 GET /api/users/{id} 反查 |
sub(OIDC subject claim) |
❌ ohooho 统一认证内部主键,三方系统不持有,不可作为 external_user_id |
email / phone / username |
❌ 不参与身份识别,不要传 |
| 第三方登录 ID(微信 openid / GitHub id / 企业微信 userid) | ❌ 三方系统自有,baozi 通过 identities API 反查,不要传 |
external_user_info(身份兜底 + 自由文本) |
✅ 必传(邮箱格式时)/ ⚪ 可选(追溯字段,详 §1.4.2) |
baozi users.id(响应中的 user_id 自增整数) |
⚠️ baozi 内部主键,仅供 baozi 内部关联使用 |
1.4.2 字段说明
external_user_id:end_user 在 ohooho 统一认证体系中的稳定唯一标识,对应user.id(字符串,Mgmt API 返 12 字符 base64-like,非 UUID)。external_user_info:身份兜底字段(K-312-28)。当 ohooho 统一认证里查不到external_user_id、邮箱格式时 baozi 会用它调外部认证 Mgmt API search/create user。非邮箱格式时作为展示/追溯任意字符串,max 512 字符(UTF-8),baozi 透传到baozi_orders.external_user_info列。示例:"user@example.com"/"team-A 用户"/"13800001111"/"备注"。client_id:M2M App 的 OAuth2 客户端标识。OAuth2client_credentials是机器对机器认证,不与任何 baozi 用户绑定。
1.4.3 external_user_id 获取流程
- end_user 使用任意 OIDC connector(邮箱 / 手机 / 微信 / GitHub)在 https://baozi.ohoooho.com 完成一次登录。
- baozi 完成 OIDC 回调,通过 webhook 或
GET /api/v1/end_users?email=反查接口告知该 end_user 的user.id(字符串,Mgmt API 返 12 字符 base64-like)。 - 三方系统记录该
user.id,用于后续下单。 - 三方下单时直接传
external_user_id=<ohoooho 统一认证 user.id>,baozi 通过 M2M token 反查 ohooho 统一认证,不依赖 end_user 使用的具体 connector。
补充说明:
- 同一个 M2M App 可为不同
external_user_id创建不同 key(每个订单独立 key)。 - 同一个 end_user 可从多个 M2M App 各拿到独立 key(都在该用户的 baozi 账号下,登录 baozi 后台可见)。
1.4.4 admin checkout 例外
baozi 内部运营对账 / 补单时,可能传整数 baozi users.id 作为 external_user_id(admin checkout 流程)。该路径仅供 baozi 内部使用:服务端先按 sub 查询;若 external_user_id 为纯数字且按 sub 查不到,则 WHERE id = ? 查询 baozi users.id,并打 [partner/orders] admin checkout warn 日志。
对于使用本地 users.id 整数或 sub 字符串的旧集成,服务端按 ohooho 统一认证 user.id 字符串匹配失败后,会按上述 admin checkout 逻辑兜底查询。
2. 接入流程(3 步)
2.1 拿 access_token(OAuth2 client_credentials)
curl -X POST https://login.ohoooho.com/oidc/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=api"
返回:
{
"access_token": "eyJhbGciOiJFUzM4NCIs...",
"token_type": "Bearer",
"expires_in": 7200,
"scope": "api"
}
access_token 默认 2 小时过期,建议在调用前 5 分钟刷新。
2.2 查 baozi 可购买套餐(推荐先调)
curl https://baozi.ohoooho.com/api/v1/packages \
-H "Authorization: Bearer ***"
响应 200(精简版示例):
{
"success": true,
"api_version": "v1",
"data": [
{
"id": "bz-taster",
"name": "体验套餐",
"scene": "taster",
"tiers": [
{ "id": "lite", "name": "Lite", "price_yuan": 0, "quota": 1000, "period": "月" }
]
}
],
"total": 1
}
2.3 一步下单 + 创 API Key(核心)
curl -X POST https://baozi.ohoooho.com/api/v1/partner/orders \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
-d '{
"package_id": "bz-taster",
"period": "月",
"external_order_id": "my-order-2026-09-18-001",
"external_user_id": "abc12345-1234-5678-9abc-def012345678",
"external_user_info": "user@example.com",
"settlement_mode": "baozi_paid"
}'
每个字段详细参数使用和限制说明(K-312-12 完整化):
| 字段 | 类型 | 必填 | 长度限制 | 格式校验 | 示例值 | 含义 |
|---|---|---|---|---|---|---|
package_id |
string | ✅ | 1-64 chars | baozi 套餐 ID(来自 GET /api/v1/packages) |
"bz-taster" |
baozi 套餐标识 |
period |
string | ✅ | enum | month / year / 月 / 年 |
"月" |
计费周期 |
external_order_id |
string | ✅ | 1-128 chars | 唯一,三方自生成(推荐 UUID v4 或带日期前缀) | "my-order-2026-09-18-001" |
三方订单号 = 幂等键(baozi 去重用) |
external_user_id |
string | ✅ | 1-128 chars | ohoooho 统一认证 user.id 字符串(Mgmt API 返 12 字符 base64-like) |
"qbtk3tka66u8" |
end_user 在 ohooho 统一认证体系中的稳定唯一标识;baozi 用 M2M token 调 GET /api/users/{id} 反查 |
external_user_info |
string | ⚪ 可选 / ✅ 必传(兜底创号时) | 0-512 chars | 邮箱格式(兜底)或自由文本(追溯) | "user@example.com" / "13800001111" / "wxid_abc" / "备注" |
身份兜底字段(邮箱格式时调外部认证 Mgmt search/create);非邮箱格式作为展示/追溯任意字符串,baozi 透传到订单表 |
settlement_mode |
string | ✅ | enum | baozi_paid(你付 baozi) / external_paid(已收款,仅记账) |
"baozi_paid" |
结算模式 |
settlement_amount_yuan |
number | ⚠️ external_paid 时必填 |
0 < x ≤ 1000000 | 正数(元,2 位小数) | 199.00 |
已结算金额(external_paid 模式必填,baozi_paid 模式按套餐价) |
token_name |
string | ⚪ 可选 | 1-64 chars | 自由文本(new-api 限 50 chars,建议 ≤50) | "my-team-key" |
API Key 名称(默认 co-${pkg.id}-${ts}) |
metadata |
object | ⚪ 可选 | JSON < 4KB | baozi 透传(不解析) | {"campaign":"promo-2026"} |
三方自定义元数据,透传到订单 metadata 字段 |
HTTP Header:
| Header | 必填 | 长度限制 | 格式 | 含义 |
|---|---|---|---|---|
Authorization: Bearer <token> |
✅ | - | ohooho 统一认证 JWT(ES384) | M2M access_token |
Content-Type |
✅ | - | application/json |
- |
X-Idempotency-Key |
⚪ 可选 | 1-128 chars | 推荐 UUID v4 | 幂等键(与 external_order_id 二选一或同时带) |
字段名变更说明:
external_user_id:固定为 ohoooho 统一认证user.id字符串。baozi 用 M2M token 反查 ohooho 统一认证,不依赖 baozi users 表 sub 列。external_user_info:身份兜底字段 + 展示/追溯任意字符串,max 512 字符长度限制(K-312-28)。邮箱格式时 baozi 调外部认证 Mgmt API search/create user;非邮箱格式直接透传到baozi_orders.external_user_info列。
响应 201(订单 + API Key 一次给齐):
{
"success": true,
"api_version": "v1",
"order_id": "co-1789379842859-k4hy49",
"transaction_id": "txn-co-1789379842859-k4hy49",
"amount_yuan": 0,
"status": "fulfilled",
"user_token_id": 19,
"full_key": "9hIJ**********JGEA",
"masked_key": "9hIJ***...JGEA",
"quota_added": 1000,
"expires_at": 1791971842000,
"_one_time": true,
"_hint": "full_key 仅 partner/orders 一次性返, 后续不返. 请立即保存."
}
⚠️ full_key 仅创时一次性返回,请立即安全保存!后续 list / detail 都不再返。
2.4 用 API Key 调 baozi 模型
curl -X POST https://llm.ohoooho.com/v1/chat/completions \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "Hello"}]
}'
3. 对账(1 步)
第三方跟 baozi 对账时,调下面端点查自己创建的全部订单:
curl https://baozi.ohoooho.com/api/v1/partner/orders \
-H "Authorization: Bearer ***"
响应 200(实测 2026-09-14):
{
"success": true,
"api_version": "v1",
"total": 1,
"limit": 20,
"offset": 0,
"count": 1,
"orders": [
{
"order_id": "co-1789379842859-k4hy49",
"user_id": 1,
"package_id": "bz-taster",
"scene": "taster",
"tier": "lite",
"period": "月",
"amount_yuan": 0,
"status": "fulfilled",
"payment_provider": "mock",
"external_order_id": "my-order-2026-09-14-001",
"external_user_id": "abc12345-1234-5678-9abc-def012345678",
"external_user_info": "team-A 用户",
"settlement_mode": "baozi_paid",
"quota_added": 1000,
"created_at": 1789379842431,
"paid_at": 1789379842431,
"fulfilled_at": 1789379842431
}
],
"_hint": "支持查询参数: limit (1-100, 默认 20), offset (默认 0), status, external_user_id (ohoooho 统一认证 user.id 字符串), external_user_info (模糊匹配), start_date, end_date (ms timestamp)"
}
对账要点:
| 字段 | 对账逻辑 |
|---|---|
orders[].amount_yuan |
你实际应付 baozi 多少钱 |
orders[].quota_added |
baozi 实际给多少配额 |
orders[].status |
fulfilled = 已履约;refunded = 已退款 |
orders[].external_order_id |
你的幂等键 |
支持 query 参数:
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
limit |
int | 20 | 1-100 |
offset |
int | 0 | 分页偏移 |
status |
string | - | 过滤: fulfilled / refunded / cancelled |
external_user_id |
string | - | 过滤: ohooho 统一认证 user.id 字符串(baozi 用 M2M token 反查) |
external_user_info |
string | - | 过滤: 模糊匹配订单表 external_user_info 列(任意字符串子串) |
start_date |
int (ms) | - | 起始时间戳 |
end_date |
int (ms) | - | 结束时间戳 |
防越权:强制 WHERE client_id = req.m2m.client_id,看不到其他 partner 的订单。
4. 错误码(精简版)
| code | 含义 | 触发场景 |
|---|---|---|
unauthorized |
缺 Authorization 头 | Authorization 头缺失 |
invalid_token |
access_token 无效 | token 错或过期 |
invalid_request |
参数缺失或格式错 | 必填字段缺失 |
package_not_found |
套餐不存在 / 已下架 | package_id 错 |
end_user_not_found |
end_user 不存在 | baozi 用 external_user_id 调 ohooho 统一认证 API 查不到该用户(让用户先用任意 OIDC connector 在 baozi 登录一次) |
end_user_not_linked |
end_user 未关联 new-api | 用户登录过 baozi 但还没完成 new-api 映射(baozi 会自动建,可能需要等几秒重试) |
self_create_failed |
后端创 token 失败 | new-api 不可用,重试 |
5. 完整版
需要其他接口(POST /api/v1/orders 分步下单 / GET /api/v1/orders/:id 单查 / POST /api/v1/orders/:id/refund 退款 / GET /api/v1/tokens 列 keys / GET /api/v1/tokens/:id 单查 / DELETE /api/v1/tokens/:id 撤销)请看 完整版第三方文档。无 basicauth 凭据要求,公开可读。
6. 联系 baozi
- 邮箱:support@ohoooho.com(仅当 ohooho 统一认证 / baozi 服务故障或合规需求时联系;流程问题请先翻文档)
- client_secret 泄露 / 撤销:去你自己的 ohooho 统一认证控制台后台处理(baozi 不参与 secret 生命周期)