API Reference

License API 文档

快速开始

所有授权接口统一走 /api/v1/。客户端先拿到用户购买的卡密和本机 Device ID,再按 激活 → 每次启动验证 → 换设备换绑 → 到期后续期 接入。

Base URL
http://localhost:3000
演示凭证
App ID: quant_pro
API Key: demo_api_key_quant_pro
Secret: demo_api_secret_quant_pro

API 认证

每个请求必须带齐签名头。服务端用 API Secret 对原始请求体做 HMAC-SHA256,并校验 5 分钟内时间戳、Nonce 防重放、IP / API Key 限流。

Header说明
X-App-Id应用 ID,如 quant_pro
X-Api-Key应用 API Key
X-Timestamp毫秒时间戳,与服务器相差不超过 5 分钟
X-Nonce随机字符串,同一 Key 10 分钟内不可重复
X-SignatureHMAC-SHA256(secret, timestamp + "\\n" + nonce + "\\n" + rawBody) 的 hex

限流:每个 IP 每分钟 120 次,每个 API Key 每分钟 60 次。黑名单 IP / Device / License / API Key 会被拦截。

POST/api/v1/license/activate

激活授权

使用卡密与 Device ID 创建 License 并绑定设备。同一设备重复激活会返回已有授权。

请求体

Request JSON
{
  "app_id": "quant_pro",
  "license_key": "K8F2-X7QM-92LA-P6TZ",
  "device_id": "DEVICE-001",
  "version": "1.0.0"
}
curl · /api/v1/license/activate
TIMESTAMP=$(node -e "console.log(Date.now())")
NONCE=$(node -e "console.log(require('crypto').randomBytes(8).toString('hex'))")
BODY='{"app_id":"quant_pro","license_key":"K8F2-X7QM-92LA-P6TZ","device_id":"DEVICE-001","version":"1.0.0"}'
SIGN=$(node -e "const c=require('crypto');const t=process.argv[1],n=process.argv[2],b=process.argv[3];console.log(c.createHmac('sha256','demo_api_secret_quant_pro').update(t+'\\n'+n+'\\n'+b).digest('hex'))" "$TIMESTAMP" "$NONCE" "$BODY")

curl -X POST 'http://localhost:3000/api/v1/license/activate' \
  -H 'Content-Type: application/json' \
  -H 'X-App-Id: quant_pro' \
  -H 'X-Api-Key: demo_api_key_quant_pro' \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGN" \
  -d "$BODY"
Response JSON
{
  "success": true,
  "code": 0,
  "message": "授权成功",
  "data": {
    "license_id": "LIC_123456",
    "status": "active",
    "expires_at": "2027-08-24T00:00:00.000Z",
    "device_id": "DEVICE-001"
  }
}
POST/api/v1/license/verify

验证授权

软件每次启动时应调用。校验卡密、设备绑定和有效期。

请求体

Request JSON
{
  "app_id": "quant_pro",
  "license_key": "K8F2-X7QM-92LA-P6TZ",
  "device_id": "DEVICE-001",
  "version": "1.0.0"
}
curl · /api/v1/license/verify
TIMESTAMP=$(node -e "console.log(Date.now())")
NONCE=$(node -e "console.log(require('crypto').randomBytes(8).toString('hex'))")
BODY='{"app_id":"quant_pro","license_key":"K8F2-X7QM-92LA-P6TZ","device_id":"DEVICE-001","version":"1.0.0"}'
SIGN=$(node -e "const c=require('crypto');const t=process.argv[1],n=process.argv[2],b=process.argv[3];console.log(c.createHmac('sha256','demo_api_secret_quant_pro').update(t+'\\n'+n+'\\n'+b).digest('hex'))" "$TIMESTAMP" "$NONCE" "$BODY")

curl -X POST 'http://localhost:3000/api/v1/license/verify' \
  -H 'Content-Type: application/json' \
  -H 'X-App-Id: quant_pro' \
  -H 'X-Api-Key: demo_api_key_quant_pro' \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGN" \
  -d "$BODY"
Response JSON
{
  "success": true,
  "code": 0,
  "message": "授权有效",
  "data": {
    "status": "active",
    "expires_at": "2027-08-24T00:00:00.000Z",
    "days_remaining": 365,
    "device_bound": true
  }
}
POST/api/v1/license/rebind

换绑设备

将当前 License 从旧设备换到新设备,并消耗一次换绑次数。接口使用数据库事务,避免并发超扣。

请求体

Request JSON
{
  "license_key": "K8F2-X7QM-92LA-P6TZ",
  "device_id": "DEVICE-002"
}
curl · /api/v1/license/rebind
TIMESTAMP=$(node -e "console.log(Date.now())")
NONCE=$(node -e "console.log(require('crypto').randomBytes(8).toString('hex'))")
BODY='{"license_key":"K8F2-X7QM-92LA-P6TZ","device_id":"DEVICE-002"}'
SIGN=$(node -e "const c=require('crypto');const t=process.argv[1],n=process.argv[2],b=process.argv[3];console.log(c.createHmac('sha256','demo_api_secret_quant_pro').update(t+'\\n'+n+'\\n'+b).digest('hex'))" "$TIMESTAMP" "$NONCE" "$BODY")

curl -X POST 'http://localhost:3000/api/v1/license/rebind' \
  -H 'Content-Type: application/json' \
  -H 'X-App-Id: quant_pro' \
  -H 'X-Api-Key: demo_api_key_quant_pro' \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGN" \
  -d "$BODY"
Response JSON
{
  "success": true,
  "code": 0,
  "message": "换绑成功",
  "data": {
    "device_id": "DEVICE-002",
    "remaining_rebinds": 2
  }
}
POST/api/v1/license/renew

续期授权

用一张未激活的同商品卡密延长当前 License。未过期则从到期日叠加;已过期则从当前时间起算。永久套餐会将到期时间清空。

请求体

Request JSON
{
  "app_id": "quant_pro",
  "license_key": "K8F2-X7QM-92LA-P6TZ",
  "renew_key": "B7K3-N4WP-8C5D-H9RF",
  "device_id": "DEVICE-001"
}
curl · /api/v1/license/renew
TIMESTAMP=$(node -e "console.log(Date.now())")
NONCE=$(node -e "console.log(require('crypto').randomBytes(8).toString('hex'))")
BODY='{"app_id":"quant_pro","license_key":"K8F2-X7QM-92LA-P6TZ","renew_key":"B7K3-N4WP-8C5D-H9RF","device_id":"DEVICE-001"}'
SIGN=$(node -e "const c=require('crypto');const t=process.argv[1],n=process.argv[2],b=process.argv[3];console.log(c.createHmac('sha256','demo_api_secret_quant_pro').update(t+'\\n'+n+'\\n'+b).digest('hex'))" "$TIMESTAMP" "$NONCE" "$BODY")

curl -X POST 'http://localhost:3000/api/v1/license/renew' \
  -H 'Content-Type: application/json' \
  -H 'X-App-Id: quant_pro' \
  -H 'X-Api-Key: demo_api_key_quant_pro' \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGN" \
  -d "$BODY"
Response JSON
{
  "success": true,
  "code": 0,
  "message": "续期成功",
  "data": {
    "license_id": "LIC_123456",
    "status": "active",
    "expires_at": "2027-09-23T00:00:00.000Z"
  }
}
POST/api/v1/security/report

异常上报 / 自动封禁

客户端检测到反编译、调试器、完整性失败或破解补丁时调用。服务端会按上报的 Device ID 和卡密写入黑名单,并封禁对应授权。已封禁设备仍可上报。reason:decompile / debugger / integrity / crack / tamper。

请求体

Request JSON
{
  "app_id": "quant_pro",
  "license_key": "K8F2-X7QM-92LA-P6TZ",
  "device_id": "DEVICE-001",
  "reason": "decompile",
  "detail": "detected unpacker"
}
curl · /api/v1/security/report
TIMESTAMP=$(node -e "console.log(Date.now())")
NONCE=$(node -e "console.log(require('crypto').randomBytes(8).toString('hex'))")
BODY='{"app_id":"quant_pro","license_key":"K8F2-X7QM-92LA-P6TZ","device_id":"DEVICE-001","reason":"decompile","detail":"detected unpacker"}'
SIGN=$(node -e "const c=require('crypto');const t=process.argv[1],n=process.argv[2],b=process.argv[3];console.log(c.createHmac('sha256','demo_api_secret_quant_pro').update(t+'\\n'+n+'\\n'+b).digest('hex'))" "$TIMESTAMP" "$NONCE" "$BODY")

curl -X POST 'http://localhost:3000/api/v1/security/report' \
  -H 'Content-Type: application/json' \
  -H 'X-App-Id: quant_pro' \
  -H 'X-Api-Key: demo_api_key_quant_pro' \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGN" \
  -d "$BODY"
Response JSON
{
  "success": true,
  "code": 0,
  "message": "已记录并封禁",
  "data": {
    "report_id": "clx_report_1",
    "reason": "decompile",
    "banned": [
      "device",
      "license",
      "license_key"
    ],
    "license_id": "LIC_123456",
    "device_id": "DEVICE-001"
  }
}

解绑

POST /api/v1/license/unbind 已列入规划,当前 MVP 未开放。需要清空设备时,请在后台「授权管理」使用「重置设备」,或走换绑接口绑定新设备。

错误码

统一响应:{ success, code, message, data }

Code含义
0成功
1001无效卡密
1002授权已过期
1003授权已封禁
1004设备不匹配
1005超过设备数量
1006App 不存在
1007App 已停用
1008API Key 无效 / 签名错误
1009请求频率过高
1010请求参数错误
1011换绑次数已用完

SDK 示例

上方语言切换会同步所有接口示例。完整可运行脚本见仓库 scripts/license-demo.tsdocs/API.md。 OpenAPI 描述文件:/openapi.json