跳到主要内容

面向 LLM / Vibe Coding 的接入指南

这份文档不是后台使用说明,而是给“其他项目接入柚卡”时使用的工程指南,目标是让 LLM、vibe coding 工具和工程师都能直接据此生成接入代码。

1. 项目背景与定位

柚卡(yuzuca)是一个面向第三方应用接入的授权与订阅管理系统,核心作用是把原本散落在业务系统内的这些能力统一收口:

  • 项目级隔离
  • 客户管理
  • 套餐与订阅管理
  • 功能权限判断
  • 额度计量与扣量
  • 卡密兑换
  • API Key 鉴权
  • 客户端登录态管理

从接入方视角,可以把柚卡理解为“授权中台”或“订阅与额度中心”。业务系统负责自己的用户和业务数据,柚卡负责授权、套餐、额度、卡密和登录令牌。

2. 柚卡负责什么,不负责什么

柚卡负责:

  • 判断某个客户是否有某个功能
  • 判断某个客户剩余额度是否足够
  • 扣减、返还或直接设置某个计量功能的用量
  • 为客户开通/延长套餐
  • 处理卡密兑换
  • 提供客户端登录和令牌刷新能力

接入方通常仍负责:

  • 自己的业务用户体系
  • 业务用户与柚卡客户的映射关系
  • 什么时候触发权限校验
  • 什么时候触发额度扣减
  • 密钥保管、轮换与审计
  • 业务失败后的补偿逻辑

3. 核心模型

建议先记住这几个对象:

  • Project:最外层隔离单元。通常对应一个应用、一个环境或一个租户。
  • Customer:授权主体,内部主记录保留 username 作为显示名和默认登录名。
  • CustomerIdentifier:客户标识。用于承载外部系统用户 ID、邮箱、卡号、用户名等多个可定位标识。
  • Feature:功能标识,调用时使用稳定 slug,例如 chat-basicimage-generate
  • Plan:套餐,表示一组功能与额度组合。
  • PlanQuota:套餐内某个功能的额度配置。
  • Subscription:某客户在某段时间内拥有某套餐。
  • SubscriptionUsage:某订阅下某功能的当前用量快照。
  • LicenseCode:卡密,兑换后为客户创建或延长订阅。
  • ProjectAPIKey:项目 API Key,分 PRIVATEPUBLIC 两类。

几个关键事实:

  • 项目之间数据隔离。
  • 同项目内同一个 (type,value) 只能绑定到一个客户。
  • Customer.username 仍然存在,但外部接入应优先使用 { type, value } 标识对象,而不是把用户名当资源主键。
  • 第三方调用时,项目通常由 API Key 自动确定,不需要额外传 projectId
  • 功能调用应始终使用 feature.slug,套餐调用应始终使用 plan.slug

4. 接入方式怎么选

4.1 服务端接入

调用代码运行在你自己的服务端,并且可以安全保存密钥时,使用 Private API 或 YuzucaPrivateClient

适合:

  • 管理任意客户
  • 创建/更新/删除客户
  • 为任意客户开通套餐
  • 扣减任意客户额度
  • 后端业务网关统一判断权限

4.2 客户端接入

调用代码运行在浏览器、移动端、桌面客户端等公开环境时,使用 Public Client API 或 YuzucaPublicClient

适合:

  • 客户登录后查询自己的资料、订阅、额度、卡密
  • 客户自己兑换卡密
  • 客户端判断“当前登录用户自己是否有权限”

4.3 绝对不要混用

  • 不要把 PRIVATE API Key 放到前端、客户端或公开环境。
  • 不要用 Public API 管理其他客户。
  • 不要让 LLM 生成依赖 /api/admin/*/api/portal/* 的外部接入代码,这些不是面向第三方项目的标准接入面。

5. 最小接入流程

5.1 服务端最小流程

  1. 在柚卡后台创建项目、功能、套餐、API Key。
  2. 在业务系统内保存 PRIVATE API Key。
  3. 业务用户首次接入时,建立“业务用户 -> yuzuca customer identifier”映射。
  4. 需要授权判断时调用 POST /api/private/v1/entitlements
  5. 业务成功后调用 POST /api/private/v1/usage 扣量。
  6. 需要直接开通套餐时调用 POST /api/private/v1/subscriptions/activate
  7. 需要卡密激活时调用 POST /api/private/v1/codes/redeem

5.2 客户端最小流程

  1. 在柚卡后台创建 PUBLIC API Key。
  2. 如果该 Key 启用了 requireSignature,客户端必须附带签名头。
  3. 客户使用标识对象和密码调用 POST /api/public/v1/auth/login 登录。
  4. 保存 accessTokenrefreshToken
  5. 客户端读取本人信息用 GET /api/public/v1/me
  6. 权限检查用 POST /api/public/v1/me/entitlements/check
  7. 卡密兑换用 POST /api/public/v1/me/redemptions
  8. 访问令牌过期后调用 POST /api/public/v1/auth/refresh

6. 通用协议约定

所有接口都返回统一结构:

{ "ok": true, "requestId": "req_xxx", "data": {} }

错误结构:

{ "ok": false, "requestId": "req_xxx", "error": { "code": "INVALID_REQUEST", "message": "...", "details": [] } }

通用约定:

  • 响应头里会返回 X-Request-Id,排错时应记录下来。
  • 写接口建议始终传 Idempotency-Key,尤其是扣量、兑换、激活这类会重复重试的操作。
  • Public API 始终需要 X-Yuzuca-Key
  • Public API 的登录态接口还需要 Authorization: Bearer <ACCESS_TOKEN>

7. HTTP 接口清单

7.1 Private API

基址:/api/private/v1

鉴权:

Authorization: Bearer <PRIVATE_API_KEY>
方法路径作用
GET/api/private/v1/info读取当前项目信息与端点清单
POST/api/private/v1/entitlements批量校验任意客户的功能权限与额度
POST/api/private/v1/usage扣减、返还、设置用量
POST/api/private/v1/redemptions为任意客户兑换卡密
GET/api/private/v1/customers分页查询客户
POST/api/private/v1/customers创建客户
POST/api/private/v1/customers/resolve按标识解析客户并拿到 customerId
GET/api/private/v1/customers/:customerId查询客户详情
PATCH/api/private/v1/customers/:customerId更新客户
DELETE/api/private/v1/customers/:customerId删除客户
GET/api/private/v1/plans分页查询套餐
GET/api/private/v1/subscriptions分页查询订阅
GET/api/private/v1/subscriptions/:subscriptionId查询订阅详情
DELETE/api/private/v1/subscriptions/:subscriptionId删除订阅
POST/api/private/v1/subscriptions/activate直接开通或延长套餐
POST/api/private/v1/codes/redeem兑换卡密

最常用请求体:

{ "customer": { "type": "external_user_id", "value": "u_1001" }, "features": [{ "feature": "image-generate", "required": 2 }] }
{ "customer": { "type": "external_user_id", "value": "u_1001" }, "feature": "image-generate", "action": "deduct", "amount": 1 }
{ "customer": { "type": "email", "value": "user@example.com" }, "plan": "pro", "duration": "P30D" }
{ "code": "ABC-DEF-GHI", "customer": { "type": "external_user_id", "value": "u_1001" }, "autoCreate": false }

7.2 Public Client API

基址:/api/public/v1

公共鉴权:

X-Yuzuca-Key: <PUBLIC_API_KEY>

登录态接口额外要求:

Authorization: Bearer <ACCESS_TOKEN>

PUBLIC API Key 开启强制签名时,还必须携带:

X-Yuzuca-Timestamp: <unix-seconds>
X-Yuzuca-Nonce: <random>
X-Yuzuca-Signature: <hmac-signature>
方法路径作用
GET/api/public/v1/info读取项目信息与公开端点
POST/api/public/v1/auth/login客户登录
POST/api/public/v1/auth/refresh刷新令牌
POST/api/public/v1/auth/logout登出
GET/api/public/v1/me读取当前客户资料
GET/api/public/v1/me/subscriptions查询自己的订阅列表
GET/api/public/v1/me/subscriptions/:subscriptionId查询自己的订阅详情
GET/api/public/v1/me/subscriptions/:subscriptionId/usages查询订阅下各功能用量
GET/api/public/v1/me/subscriptions/:subscriptionId/usages/:featureId/history查询某功能历史用量快照
GET/api/public/v1/me/license-codes查询自己的卡密记录
POST/api/public/v1/me/redemptions为自己兑换卡密
POST/api/public/v1/me/entitlements/check检查自己是否有功能权限

最常用请求体:

{ "identifier": { "type": "email", "value": "user@example.com" }, "password": "secret" }
{ "features": [{ "feature": "chat-basic" }, { "feature": "image-generate", "required": 2 }] }
{ "code": "ABC-DEF-GHI" }

8. SDK 接口清单

安装:

pnpm add @yuzuca/sdk

导出项:

  • YuzucaPrivateClient
  • YuzucaPublicClient
  • createHmacSignatureProvider
  • createIdempotencyKey
  • YuzucaError
  • 各类请求/响应 TypeScript 类型

8.1 YuzucaPrivateClient

import { YuzucaPrivateClient } from "@yuzuca/sdk";

const client = new YuzucaPrivateClient({
apiKey: process.env.YUZUCA_PRIVATE_KEY!,
baseUrl: "https://your-yuzuca.example.com",
});

方法清单:

分组方法对应 HTTP
infoget()GET /info
entitlementscheck(input)POST /entitlements
entitlementscheckOne({ customer, feature, required })POST /entitlements
usagemutate(input)POST /usage
usagededuct(input)POST /usage
usageadd(input)POST /usage
usagesetUsed(input)POST /usage
usagesetRemaining(input)POST /usage
redemptionsredeem(input)POST /redemptions
customerslist(query?)GET /customers
customerscreate(input)POST /customers
customersresolve(identifier)POST /customers/resolve
customersget(customerId)GET /customers/:customerId
customersupdate(customerId,input)PATCH /customers/:customerId
customersdelete(customerId)DELETE /customers/:customerId
planslist(query?)GET /plans
subscriptionslist(query?)GET /subscriptions
subscriptionsget(subscriptionId)GET /subscriptions/:subscriptionId
subscriptionsdelete(subscriptionId)DELETE /subscriptions/:subscriptionId
subscriptionsactivate(input)POST /subscriptions/activate
codesredeem(input)POST /codes/redeem
实例方法withApiKey(apiKey)切换服务端密钥

8.2 YuzucaPublicClient

import { YuzucaPublicClient, createHmacSignatureProvider } from "@yuzuca/sdk";

const client = new YuzucaPublicClient({
apiKey: process.env.YUZUCA_PUBLIC_KEY!,
baseUrl: "https://your-yuzuca.example.com",
signatureProvider: createHmacSignatureProvider(process.env.YUZUCA_SIGNING_SECRET!),
});

方法清单:

分组方法对应 HTTP
infoget()GET /info
meget()GET /me
subscriptionslist()GET /me/subscriptions
subscriptionsget(subscriptionId)GET /me/subscriptions/:subscriptionId
subscriptionsgetUsages(subscriptionId)GET /me/subscriptions/:subscriptionId/usages
subscriptionsgetUsageHistory(subscriptionId,featureId)GET /me/subscriptions/:subscriptionId/usages/:featureId/history
licenseCodeslist()GET /me/license-codes
redemptionsredeem(input)POST /me/redemptions
entitlementscheck(input)POST /me/entitlements/check
entitlementscheckOne({ feature, required })POST /me/entitlements/check
实例方法login(identifier,password)POST /auth/login
实例方法refresh(refreshToken?)POST /auth/refresh
实例方法logout(refreshToken?)POST /auth/logout
实例方法getTokens()读取本地 token
实例方法setTokens(tokens)写入本地 token
实例方法clearTokens()清空本地 token
实例方法setSignatureProvider(provider)动态设置签名器

9. 推荐给 LLM 的接入规则

如果你要让 LLM 为其他项目生成接入代码,建议把下面这些规则直接放进上下文:

  • 这是一个授权/订阅/额度中心,不是业务用户中心。
  • 外部项目接入时优先使用 @yuzuca/sdk,只有在非 TypeScript 或需要极致控制时才直接调 HTTP。
  • 服务端环境只能使用 PRIVATE API Key,对应 /api/private/v1/*
  • 浏览器、App、桌面客户端只能使用 PUBLIC API Key,对应 /api/public/v1/*
  • 不要为外部接入生成 /api/admin/*/api/portal/* 的调用代码。
  • customer 一律使用稳定标识对象 { type, value },不要把昵称或展示名当标识。
  • 客户详情管理接口使用 customerId 路径参数,不再使用 username 路由。
  • featureplan 一律使用 slug,不要用展示名称。
  • 对任何写请求都优先生成 Idempotency-Key
  • 扣量应在业务成功后触发,权限检查应在业务执行前触发。
  • 如果 Public API Key 启用了签名,必须附加 X-Yuzuca-TimestampX-Yuzuca-NonceX-Yuzuca-Signature
  • Public API 登录后要保存 accessTokenrefreshToken,过期后用 refresh 换新。

10. 最小示例

10.1 服务端校验权限并扣量

import { YuzucaPrivateClient, createIdempotencyKey } from "@yuzuca/sdk";

const client = new YuzucaPrivateClient({
apiKey: process.env.YUZUCA_PRIVATE_KEY!,
baseUrl: process.env.YUZUCA_BASE_URL!,
});

const customer = { type: "external_user_id", value: "user_1001" };

const entitlement = await client.entitlements.checkOne({
customer,
feature: "image-generate",
required: 1,
});

if (!entitlement.allowed) throw new Error("Quota not enough");

await client.usage.deduct(
{ customer, feature: "image-generate", amount: 1 },
{ idempotencyKey: createIdempotencyKey("img_gen") },
);

10.2 客户端登录并查询自己的订阅

import { YuzucaPublicClient } from "@yuzuca/sdk";

const client = new YuzucaPublicClient({
apiKey: process.env.NEXT_PUBLIC_YUZUCA_KEY!,
baseUrl: process.env.NEXT_PUBLIC_YUZUCA_BASE_URL!,
});

await client.login({ type: "email", value: "user@example.com" }, "secret");
const me = await client.me.get();
const subscriptions = await client.subscriptions.list();

11. 接入建议总结

最推荐的接入姿势是:

  • 业务服务端统一走 YuzucaPrivateClient
  • 公开客户端统一走 YuzucaPublicClient
  • 把客户标识对象、功能 slug、套餐 slug 当成稳定契约
  • 先做“鉴权检查 + 扣量”两条链路,再补“卡密兑换”和“客户自助中心”

如果只是要让另一个项目快速接入,最先实现的通常只有 4 个动作:

  • 识别客户标识
  • 检查功能权限
  • 扣减额度
  • 查询或兑换订阅/卡密