开发者文档

用脚本或 AI 管理店里的卡密:新建卡种、批量补货、查库存、读售出记录、回收和撤回。卡密在你电脑上加密后才上传,服务器看不到内容。

交给 AI 来做

下载技能包,装进 Claude Code、Codex、Cursor 等工具,直接说「把 cards.txt 补到豆包月卡」就行。

  1. 在我的账户生成令牌(插件「账户 → 开发者」也可以)
  2. 设置环境变量 JL_TOKEN=jlk_...
  3. 把技能包解压到工具的 skills 目录(如 ~/.claude/skills/)
下载 AI 技能包

锦鲤掌柜 · 卡密开放接口(给 AI 写脚本用)

把这份文档整段交给 AI,说明要做什么(补货、读售出记录、回收),它就能写出脚本。

一览:6 个接口(增删改查)

用途 接口
增 新建卡种(普通卡 / 循环卡) POST /v1/stock/kinds
增 补货:推送卡密 POST /v1/stock/push
删 撤回未售的卡密 POST /v1/stock/revoke
改 标记售出记录为已回收 POST /v1/stock/sales/mark
查 库存、卡种、加密公钥、撤回结果 GET /v1/stock/status
查 售出记录 GET /v1/stock/sales

卡密加密后才上传,服务器看不到内容,所以没有「改内容」:推错了先撤回再补货。只补货用「查」和「补货」两个就够;还没有卡种时先「新建卡种」。

基本

项 说明
地址 https://yzg-relay.aiyuan.dpdns.org
鉴权 请求头 Authorization: Bearer jlk_...。令牌在官网「我的账户 → 开发者令牌」或插件「账户 → 开发者」生成,只显示一次
店铺 令牌绑定一家店铺,所有接口只作用于这家店;多家店各用各的令牌
格式 JSON,UTF-8;时间均为毫秒时间戳
限流 每个令牌每分钟 60 次;超出返回 429 rate_limited,带 retry_after(秒)
出错 { "ok": false, "error": "代码", "msg": "中文说明" }

加密必须

卡密在本机加密后才上传,服务器只存密文,明文会被拒收(bad_ct)。不想自己实现,直接用 push.py 或 push.mjs。

  1. 取公钥:GET /v1/stock/status 返回的 pub(P-256 未压缩公钥,65 字节,base64url)
  2. 临时密钥:每条卡密生成一对新的 P-256 临时密钥
  3. 共享密钥:ECDH(临时私钥,pub)
  4. 派生密钥:HKDF-SHA256,salt = 临时公钥 65 字节,info = jinli-stock-v1,输出 32 字节
  5. 加密:AES-256-GCM,随机 12 字节 iv,明文为卡密 UTF-8
  6. 拼接:v1.<临时公钥>.<iv>.<密文含 tag>,三段均为 base64url

查库存与卡种

GET/v1/stock/status

(可选 ?batch=<补货返回的 batch>)

{ "ok": true, "online": true, "device": "Windows · Chrome",
  "pub": "BCpW...",
  "kinds": [
    { "id": "k1", "name": "腾讯视频·年卡", "group": "视频会员", "type_name": "普通卡", "unsold": 3, "sold": 10, "warn": 5, "pending": 0,
      "format": { "kind": "normal", "content": "text", "cols": ["账号", "密码"], "multi": true, "sep": "dash", "join": "----", "example": "值1----值2", "dup": false } }
  ],
  "revokes": [ { "label": "13800002222", "at": 1760850000000, "done": true, "result": "removed" } ],
  "batch": { "batch": "b_x", "pushed": 3, "received": 3, "done": true } }
  • online:店铺在岗的插件是否在线(在线才会取货)
  • pub:加密公钥(见下方「加密」);账号没开云同步时为 null
  • kinds:卡种目录。补货用 id 作 kind_id;没有合适的卡种用下方「新建卡种」
  • unsold / sold:插件里的未售 / 已售数(插件在线时上报);pending:云端还没被插件取走的条数
  • type_name:普通卡(一张卖一次)/ 固定卡(一份内容每单都发)/ 循环卡(一批卡轮着发)
  • format:每条卡密该怎么写。multi=false 一条 = 一个值;multi=true 一条 = 一张卡,按 cols 顺序用 join 连起来(join 为空时写 列名:值 列名:值,见 example);pushable=false 的卡种(固定卡、图片卡)不走推送
  • revokes:最近 50 条撤回的结果:removed 已移除 / sold 已卖出未动 / missing 插件里没有;done=false 表示等插件上线处理
  • batch:传了 ?batch= 才有,done=true 表示这批已被插件取走入库

增新建卡种

POST/v1/stock/kinds
{ "name": "Tims 咖啡券", "type": "normal", "cols": ["券码", "密码"], "join": "----", "group": "卡券", "warn": 5 }
  • name 必填,≤30 字;本店已有同名卡种时不新建,直接返回那个(created=false)
  • type:normal 普通卡(一张卖一次,默认)/ cycle 循环卡(一批卡轮着发)。固定卡、图片卡只能在插件里建
  • cols:列名,1–5 个,默认 ["卡密"];多列时每列都要有名字
  • join:多列时一条卡密怎么连,"----"(默认)/ "," / " " / "\n" / ""(写成 列名:值 列名:值)
  • group(分组,可选,≤20 字)、warn(库存预警线,默认 5)
  • 返回 { created, kind },kind 同「查」里 kinds[] 的一项。拿到 kind.id 立刻就能补货;插件在线时约 5 分钟内建好,卡密随后入库
  • 改名、改格式、删除在插件里做

增补货

POST/v1/stock/push
{ "kind_id": "k1", "batch_note": "注册批次-20261019",
  "items": [ { "ct": "v1....", "label": "13800001111" } ] }
  • items 可以是密文字符串,也可以是 { ct, label }
  • label(标识,可选):这张卡来自哪个账号,比如注册用的手机号。售出记录会带上它,回收、撤回都靠它。明文存储,别放密码或 key;只能字母数字和 . : @ # / - _,≤64 位
  • 单批 ≤500 条;云端待取 ≤20000 条
  • 返回 { batch, accepted };插件在线时约 5 分钟内取走、解密入库,云端随即删除

删撤回未售

POST/v1/stock/revoke

{ "labels": ["13800002222"] } - 云端还没取走的直接删除;已在插件里的,插件下次在线时移除未售的,已卖出的不动 - 结果在 GET /v1/stock/status 的 revokes 里

查售出记录

GET/v1/stock/sales?status=sold&kind_id=&since=0&limit=200
{ "ok": true, "sales": [
  { "label": "13800001111", "n": 1, "kind_id": "k1", "sold_at": 1760850000000, "expires_at": 1760936400000, "status": "sold", "note": "", "updated_at": 1760850001000 }
], "next_since": 1760850001000, "more": false }
  • status:sold 已售(默认,不含退款)/ refunded 已退款 / recycled 已回收 / all
  • n:第几次卖出(固定卡一张会卖多次)
  • expires_at:按插件里卡种的有效期算,没设为 null
  • 增量读取:把上次的 next_since 作为 since 传回;more=true 继续翻。退款会让记录从 sold 变 refunded,增量能读到
  • 不含买家信息和订单号

改标记已回收

POST/v1/stock/sales/mark
{ "labels": ["13800001111"], "note": "已删 key" }
  • 也可写 "items": [ { "label": "13800001111", "n": 1 } ] 只标某一次卖出
  • 标记后插件显示「已回收」,不会再发这张卡;之后的退款不会覆盖回收状态

典型回收脚本

  1. 定时 GET /v1/stock/sales?status=all&since=<上次>
  2. status=refunded,或 status=sold 且 expires_at 已过 → 登录账号删除 key
  3. POST /v1/stock/sales/mark 标记已回收
  4. 保存 next_since