把兑换能力接入你自己的网页
使用已授权的卡密,你的网站后端即可调用本 API。凭证仅用于服务端鉴权,不会在兑换页面或接口响应中展示。
Base URL
https://你的域名/api/v101
安全原则
接口只面向客户的服务端,不允许浏览器直接携带卡密访问。
不要在网页 JavaScript、HTML、浏览器 LocalStorage、URL 参数或截图中放入卡密。卡密等同于兑换凭证。前端只请求你的业务后端;你的业务后端再请求本 API。
服务端鉴权仅接受
Authorization: Bearer,不接受查询参数中的卡密。会话隔离每个请求检查卡密与会话归属,不能读取其他卡密的会话。
最小暴露响应不返回原始卡密、资源库路径、供应商 Key、后台数据或内部 SMS Token。
无跨域直连API 不开放 CORS;浏览器跨域无法直接带卡密调用。
02
鉴权方式
已授权卡密即为这套 API 的 Bearer 凭证。
每个 API 请求均带同一张卡密。卡密只应保存在你的服务端环境变量、加密数据库字段或用户会话服务中;不要将其返回到浏览器。
HTTP Header
Authorization: Bearer KAMI-XXXX-XXXX-XXXX-XXXX
Content-Type: application/json会话隔离:同一张卡密只能访问它创建的兑换会话与对应领取结果。
03
5 分钟接入
以 Node.js 18+ 的业务后端为例。
- 1保存凭证
后端保存
KAMI_API_BASE和用户的KAMI_CARD_CODE。 - 2创建或恢复会话
调用
POST /sessions;重复调用同一卡密会恢复未完成会话。 - 3按步骤推进
以
current_step为准:review/info 继续,confirm 明确确认,输入步骤传用户内容。 - 4领取结果
JSON 走带鉴权下载;SMS 使用同一会话下的取码与换号接口。
server.mjs
const baseUrl = process.env.KAMI_API_BASE; // https://api.example.com/api/v1
const cardCode = process.env.KAMI_CARD_CODE; // 仅服务端保存
async function kami(path, options = {}) {
const response = await fetch(`${baseUrl}${path}`, {
...options,
headers: { authorization: `Bearer ${cardCode}`, 'content-type': 'application/json', ...(options.headers || {}) }
});
const body = await response.json();
if (!response.ok || !body.ok) throw new Error(body.error?.message || 'API 请求失败');
return body.data;
}
const { session } = await kami('/sessions', { method: 'POST' });
console.log(session.id, session.current_step);04
兑换流程
不要假设所有卡密的流程都一样,始终读取接口返回的 current_step。
POST/sessions创建 / 恢复
→POST/steps推进当前步骤
→GET/sessions/:id读取状态
→GET / POST结果接口下载或取码
推进步骤
// review / info / delivery:payload 可为空
await kami(`/sessions/${sessionId}/steps`, { method: 'POST', body: JSON.stringify({ payload: {} }) });
// confirm:必须由你的业务逻辑明确确认
await kami(`/sessions/${sessionId}/steps`, { method: 'POST', body: JSON.stringify({ payload: { confirmed: true } }) });
// text / textarea:key 由 current_step.key 返回
await kami(`/sessions/${sessionId}/steps`, { method: 'POST', body: JSON.stringify({ payload: { value: userInput } }) });05
接口列表
所有路径都相对 Base URL,并且每个请求都需要 Bearer 卡密。
方法路径说明
POST
/sessions创建或恢复兑换会话GET
/sessions/:sessionId查询会话、步骤和领取状态POST
/sessions/:sessionId/steps推进 review / confirm / input / deliveryGET
/sessions/:sessionId/download下载 JSON ZIP(二进制响应)GET
/sessions/:sessionId/sms读取号码与验证码状态POST
/sessions/:sessionId/sms/fetch向上游查询一次验证码POST
/sessions/:sessionId/sms/swap未收码前申请换号创建会话
cURL
curl --request POST 'https://你的域名/api/v1/sessions' \\
--header 'Authorization: Bearer KAMI-XXXX-XXXX-XXXX-XXXX' \\
--header 'Content-Type: application/json'下载 JSON
下载接口直接返回 ZIP 文件流,不返回 JSON。服务端下载时必须继续附带 Authorization Header;不要把卡密写到下载链接。
Node.js 下载
const response = await fetch(`${baseUrl}/sessions/${sessionId}/download`, {
headers: { authorization: `Bearer ${cardCode}` }
});
if (!response.ok) throw new Error('下载失败');
const zipBuffer = Buffer.from(await response.arrayBuffer());
// 按业务需要保存或转发 zipBuffer;不要把卡密写入下载链接。06
SMS 取码与换号
SMS 也必须在服务端轮询;每个会话只操作自己的号码。
轮询间隔至少 4 秒。接口保护上游号码服务;过快请求可能返回
throttled: true 或 429。收到验证码后立即停止轮询。轮询验证码
async function waitForSms(sessionId) {
for (let attempt = 0; attempt < 30; attempt += 1) {
const { sms, new_code } = await kami(`/sessions/${sessionId}/sms/fetch`, { method: 'POST', body: JSON.stringify({}) });
if (new_code && sms.code) return sms.code;
await new Promise((resolve) => setTimeout(resolve, 4000));
}
throw new Error('等待验证码超时,请展示重试或换号入口');
}只有 sms.can_swap === true 时才允许换号。已收码、平台未开放取消窗口或号码池无可用号码时会拒绝;不要无条件重试换号。
07
请求限制
限制按 60 秒窗口计算。遇到 429 时,等待 Retry-After 或错误中的秒数后重试。
鉴权失败30 / IP / 分钟防止猜测卡密
创建会话20 / 卡密 / 分钟创建或恢复
状态查询120 / 卡密 / 分钟会话与号码状态
流程推进60 / 卡密 / 分钟步骤确认与领取
SMS 操作30 / 卡密 / 分钟取码与换号共用
文件下载30 / 卡密 / 分钟ZIP 下载
08
返回与错误格式
除下载接口外,所有响应都为 JSON。
成功响应
{
"ok": true,
"data": {
"resumed": false,
"session": {
"id": "session_xxx",
"status": "active",
"current_step": { "type": "confirm", "title": "确认兑换" },
"delivery": null
}
}
}错误响应
{
"ok": false,
"error": {
"code": "rate_limited",
"message": "SMS 请求过于频繁,请稍后再试",
"details": { "retry_after_seconds": 12 }
}
}invalid_card_credential卡密缺失、格式错误或不存在session_not_found会话不属于当前卡密,或不存在card_redeemed卡密已兑换且不能重复领取inventory_unavailable当前库存不足delivery_not_ready资源尚未领取完成operation_failed流程、上游号码或换号失败;查看 message 决定下一步09
上线检查
以下项目完成后再向客户开放 API。
- 使用 HTTPS 域名;反向代理正确设置
X-Forwarded-Proto,并开启TRUST_PROXY=true。 - 后端日志、监控和异常上报必须脱敏
Authorization。 - 客户前端只能调用客户自己的后端,绝不直连本 API。
- 网络超时后先
GET /sessions/:id查询状态,不要盲目重复推进步骤或换号。 - 正确处理
429、409、502,为用户提供可重试提示。 - 发布前用测试卡完成 JSON 或 SMS 全流程联调。