公开文档 / License API

桌面客户端激活码接入文档

推荐按 License v2 接入:后台准备 projectKey 和激活码,客户端生成 machineId 与 Ed25519 设备密钥,用户输入激活码时 enroll,随后用短期 token 和设备签名调用 status / consume。

代码编辑器中的接口开发工作区

先看这 3 件事

新客户端不用再把 API Secret 放进本地程序。先准备项目和激活码,再让客户端保存稳定 machineId 与设备私钥,最后按 enroll、challenge、renew、status、consume 五个 v2 接口接入。 所有激活码客户端均使用 License v2 的设备签名接口。

推荐接入
License v2

Python 客户端不再内置 API Secret;本机生成 Ed25519 设备密钥,后台项目会提供离线验签公钥。

高强度流程
5 个接口

enroll 绑定设备、公钥和指纹,challenge / renew 续租短期 token,status / consume 每次请求都签名。

客户端需要准备什么

Python 桌面程序通常内置 Base URL 和 projectKey,激活码由用户输入。machineId 和 Ed25519 私钥首次运行生成后保存在本机,以后一直复用。

Base URL
https://你的域名
Content-Type
application/json
projectKey
后台项目管理中复制,用来区分不同软件或产品
machineId
客户端本机生成并持久化,不能每次启动变化
devicePublicKey
Ed25519 公钥,enroll 时提交给服务端
device private key
Ed25519 私钥,只保存在客户端本机安全存储中
licenseToken
服务端 enroll / renew 下发的短期 token
requestId
consume 每次业务动作生成一个,重试时复用同一个

License v2 签名怎么用

v2 不把 API Secret 放进客户端。客户端用 Ed25519 设备私钥签名请求,服务端用 enroll 时登记的设备公钥验证。签名会绑定路径、session、时间戳、nonce、请求体 hash 和 token hash。

签名算法
Ed25519,输出 base64url 字符串
签名内容
LICENSE-V2-PROOF + POST + 路径 + sessionId + 时间戳 + nonce + bodyHash + tokenHash
请求头
Authorization / X-License-Session-Id / X-License-Timestamp / X-License-Nonce / X-License-Signature
注意
签名时用的 body 字符串必须和实际发送的 body 完全一致
重放防护
同一个 nonce 只能使用一次,默认时间窗口前后 5 分钟
Python v2 签名核心代码
import base64
import hashlib
import json
import os
import time

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

private_key = Ed25519PrivateKey.generate()
license_token = "eyJ..."
session_id = "ls_xxx"
path = "/api/license/v2/status"
body = json.dumps({}, separators=(",", ":"), ensure_ascii=False)
timestamp = str(int(time.time()))
nonce = "py_" + base64.urlsafe_b64encode(os.urandom(18)).rstrip(b"=").decode("ascii")
body_hash = hashlib.sha256(body.encode("utf-8")).hexdigest()
token_hash = hashlib.sha256(license_token.encode("utf-8")).hexdigest()

canonical = "\n".join([
    "LICENSE-V2-PROOF",
    "POST",
    path,
    session_id,
    timestamp,
    nonce,
    body_hash,
    token_hash,
])
signature = base64.urlsafe_b64encode(
    private_key.sign(canonical.encode("utf-8")),
).rstrip(b"=").decode("ascii")

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {license_token}",
    "X-License-Session-Id": session_id,
    "X-License-Timestamp": timestamp,
    "X-License-Nonce": nonce,
    "X-License-Signature": signature,
}

完整接入流程

下面是桌面客户端最常见的接入顺序。照这个流程做,用户换设备、网络重试、次数扣减和后台排查都能对应起来。

01

后台创建项目和激活码

在后台项目管理里创建项目,生成 TIME 或 COUNT 激活码,并复制该项目的 License v2 离线公钥。

客户端固定 projectKey 和项目级 offline public key;不再使用 API Secret。

02

客户端生成设备密钥和 machineId

Python 程序首次运行时生成稳定 machineId、设备指纹 hash 和 Ed25519 私钥。私钥保存在系统钥匙串、DPAPI、Keychain 或安全文件存储里。

服务端保存设备公钥和 fingerprintHash;后续请求必须由本机私钥签名并携带匹配指纹。

03

用户输入激活码后调用 enroll

把 code、machineId、projectKey、fingerprintHash、devicePublicKey 和 deviceSignature 发到 /api/license/v2/enroll。

服务端验证设备私钥持有证明,绑定设备并下发短期 licenseToken。

04

续租短期 token

token 快过期时先调用 /api/license/v2/challenge,再用设备私钥签名 challenge 调用 /api/license/v2/renew。

短期 token 和设备签名同时存在,复制 token 不能长期绕过授权。

05

启动检查和扣次都走签名请求

程序启动调用 /api/license/v2/status,COUNT 次数卡真实使用成功后调用 /api/license/v2/consume。

每次请求都签名方法、路径、session、时间戳、nonce、请求体 hash 和 token hash,服务端防重放。

06

检测设备指纹漂移

status、consume、challenge 和 renew 会复核已绑定 fingerprintHash;缺失或变化会拒绝请求并记录安全事件。

复制 session、token 或设备私钥到另一台机器时,还会触发 HostID / fingerprint 漂移防护。

07

在后台日志里排查问题

遇到客户反馈时,用激活码、machineId、sessionId 或安全事件在后台查询。

可以确认设备是否被吊销、客户端版本是否被封禁、是否发生签名失败、nonce 重放或指纹漂移。

08

可选启用离线签名授权快照

新建项目会自动生成项目级 Ed25519 离线签名密钥;老项目启动时会自动补齐。环境变量级密钥仅作为兼容兜底。

客户端用项目级公钥在短暂断网时验证授权快照,适合弱网宽限和只读状态校验。

客户端调用明细

下面按真实客户端生命周期展开:哪些动作在本机完成、哪些接口要调用、服务端会校验什么、成功后客户端要保存什么。

01 / 后台准备

后台:/api/admin/projects + /api/admin/codes/generate

License v2 flow
客户端:客户端暂不接入,先由运营或管理员在后台创建 projectKey,并生成 TIME 或 COUNT 激活码。
服务端:服务端保存项目、授权模式、有效期或次数,并把激活码归属到指定项目。
结果:拿到 Base URL、projectKey 和要发给用户的激活码。
projectKey 可以内置在客户端;API Secret 不再放进 Python 程序。
不同产品、客户或渠道建议使用不同 projectKey 隔离。
02 / 本机初始化

本地动作,无需请求服务端

License v2 flow
客户端:首次运行时生成稳定 machineId 和 Ed25519 设备密钥;以后必须复用同一份 machineId 和私钥。
服务端:此阶段服务端没有参与,真正的公钥登记会在 enroll 完成。
结果:客户端本地保存 machineId、设备私钥,并准备好 base64url Ed25519 公钥。
正式客户端优先用 DPAPI、Keychain、Secret Service 或 keyring 保存私钥。
machineId 不能每次启动重新生成,否则会被识别成新设备。
03 / 首次激活

POST /api/license/v2/enroll

License v2 flow
客户端:用户输入激活码后,客户端用设备私钥签名 enroll canonical message,并提交激活码、machineId、fingerprintHash、公钥和签名。
服务端:验证 Ed25519 签名、项目和激活码规则,绑定 machineId、设备公钥与 fingerprintHash,创建短期 session。
结果:返回 licenseToken、sessionId、deviceId、expiresAt、licenseMode 和 remainingCount。
enroll 成功后应保存 sessionId、licenseToken 和 expiresAt。
同一设备重新 enroll 会吊销旧 session,避免多份 token 长期并存。
04 / 启动检查

POST /api/license/v2/status

License v2 flow
客户端:程序启动或打开授权页时,带 Bearer token、fingerprintHash 和 X-License-* 签名头调用状态查询。
服务端:校验 token、session、设备状态、客户端版本、时间戳、nonce、防重放、设备签名和指纹漂移。
结果:返回 valid、licenseMode、expiresAt、remainingCount、sessionId 和 tokenExpiresAt。
状态查询不会扣减 COUNT 次数。
签名 bodyHash 必须对应实际发送的 JSON body 字节。
05 / 功能扣次

POST /api/license/v2/consume

License v2 flow
客户端:COUNT 次数卡在付费动作真正完成后调用 consume,并传入稳定 requestId 和 fingerprintHash;TIME 授权可用 consume 做权益校验。
服务端:先完成 v2 签名验证,再进入原有 TIME / COUNT 授权逻辑;COUNT 成功后扣减 remainingCount。
结果:返回扣次后的 remainingCount、valid 和 idempotent。
网络重试必须复用同一个 requestId,避免重复扣次。
建议业务成功后再扣次;失败或取消时不要调用 consume。
06 / 续租 token

POST /api/license/v2/challenge -> POST /api/license/v2/renew

License v2 flow
客户端:token 快过期时先携带 fingerprintHash 请求 challenge,再用设备私钥签名服务端返回的 signInput 调用 renew。
服务端:校验一次性 challenge、设备签名、session 状态和 fingerprintHash,通过后递增 tokenVersion 并签发新 token。
结果:返回新的 licenseToken 和 expiresAt;旧 token 立即失效。
challenge 默认只能使用一次,过期或重放都会被拒绝。
建议在 expiresAt 前几分钟主动续租,失败时再提示用户重新联网验证。
07 / 离线宽限

响应字段:offlineLicense / offlineLicenseExpiresAt / offlineLicensePublicKey

License v2 flow
客户端:客户端保存服务端返回的 offlineLicense,并用内置 Ed25519 公钥验证签名、有效期、设备指纹和授权信息。
服务端:服务端优先用项目级私钥签发短期授权快照;客户端只需要后台项目里的离线公钥。
结果:短暂断网时可以读取可信授权状态;联网后仍应尽快恢复 status / consume 在线校验。
离线 license 适合状态宽限,不适合离线可信扣减 COUNT 次数。
正式客户端建议内置或配置项目级固定公钥,不要完全依赖接口返回的 publicKey。

公共请求参数

License v2 接口共用以下字段。projectKey 用于项目隔离,code 是激活码正文,machineId 表示设备,devicePublicKey / deviceSignature 用于注册设备,requestId 用于 consume 幂等扣次。

参数名类型必填说明
projectKey / project_keystring否项目标识。建议每个桌面软件或产品使用独立 projectKey;不传时走 default 项目。
codestring是后台生成并发放给用户的激活码正文。
machineId / machine_idstring是设备唯一标识。必须稳定保存,不能每次启动重新生成。
fingerprintHash / fingerprint_hashstringv2 推荐客户端本地计算的设备指纹 hash。enroll 后服务端绑定该值,status、consume、challenge、renew 缺失或漂移都会被拒绝并记录安全事件。
requestId / request_idstring仅 consume 推荐每次真实业务动作的唯一 ID。网络重试时复用同一个值,不会重复扣次。
devicePublicKeystringv2 enroll 是base64url 编码的 32 字节 Ed25519 公钥。服务端保存公钥,客户端保管私钥。
deviceSignaturestringv2 enroll 是客户端用设备私钥签名 enroll canonical message,证明持有对应私钥。
licenseTokenstringv2 challenge / signed request 是服务端下发的短期授权 token。正式请求建议放在 Authorization: Bearer 头里。
sessionId / challengeId / nonce / signaturestringv2 renew 是challenge / renew 续租流程字段。signature 是设备私钥对服务端 signInput 的 Ed25519 签名。

响应数据结构

响应同时返回 camelCase 和 snake_case 字段,便于新旧客户端共存。前端展示授权状态时优先使用 licenseMode、expiresAt、remainingCount、valid 和 message。

参数名类型必填说明
successboolean总是返回业务是否通过。客户端一定要判断这个字段,不要只看 HTTP 状态码。
messagestring总是返回失败原因或成功提示,可直接用于日志,也可整理后展示给用户。
licenseMode / license_modeTIME | COUNT | null按场景返回授权模型;TIME 表示有效期授权,COUNT 表示按次数消费。
expiresAt / expires_atstring | null时间型常用TIME 模式的过期时间;COUNT 模式通常为空。
remainingCount / remaining_countnumber | null次数型常用COUNT 模式的剩余次数;TIME 模式通常为空。
isActivated / is_activatedboolean | null按场景返回当前激活码是否已经绑定到设备。
validboolean | null按场景返回当前授权是否仍可继续使用。启动检查时重点看 success 和 valid。
idempotentboolean | nullconsume 常用consume 是否命中 requestId 幂等重放;true 表示没有再次扣次。
licenseTokenstringv2 enroll / renew 返回License v2 短期 token。客户端保存后,status / consume 请求必须同时带 token 和设备签名。
sessionId / deviceId / tokenExpiresAtstring | numberv2 常用服务端会话、设备记录和 token 过期时间,用于续租和后台排查。
signInputstringv2 challenge 返回服务端生成的一次性 challenge canonical message,客户端直接用设备私钥签名后提交 renew。
offlineLicense / offlineLicenseExpiresAt / offlineLicensePublicKeystringv2 可选返回项目级私钥签发的短期授权快照。正式客户端应固定后台项目里的 offline public key,并校验 projectKey、machineId、fingerprintHash、有效期和授权状态。
RECOMMENDED

新版 License v2(推荐)

给新的 Python 桌面程序和需要高强度防逆向的客户端使用。客户端不内置 API Secret,所有正式请求都绑定短期 token 和 Ed25519 设备签名。

调用顺序:enroll -> status / consume,token 快过期时 challenge -> renew
POST推荐接口

License v2:设备注册和激活

/api/license/v2/enroll

用户第一次输入激活码时调用。客户端提交设备公钥和私钥签名,服务端绑定设备并下发短期 token。

新 Python 客户端、桌面程序、需要防逆向强化的正式接入都优先使用这个入口。

请求说明

客户端不需要也不应该内置项目 API Secret。
deviceSignature 会证明客户端持有 devicePublicKey 对应私钥。
fingerprintHash 会绑定设备硬件/系统指纹,后续请求缺失或漂移会被拒绝。
成功后返回 licenseToken 和 sessionId,后续 status / consume 走签名请求。
项目级离线私钥会签发 offlineLicense,客户端用项目离线公钥验签。
请求示例
{
  "projectKey": "browser-plugin",
  "code": "A1B2C3D4E5F6G7H8",
  "machineId": "machine-001",
  "appVersion": "1.0.0",
  "fingerprintHash": "sha256-of-device-fingerprint",
  "devicePublicKey": "base64url-ed25519-public-key",
  "deviceSignature": "base64url-ed25519-signature"
}
响应示例
{
  "success": true,
  "message": "License v2 设备注册成功",
  "tokenType": "LicenseV2",
  "licenseToken": "eyJ...",
  "sessionId": "ls_xxx",
  "expiresAt": "2026-04-27T13:30:00.000Z",
  "deviceId": 12,
  "licenseMode": "COUNT",
  "remainingCount": 2,
  "offlineLicense": "amlic2.payload.signature",
  "offlineLicenseExpiresAt": "2026-04-28T13:30:00.000Z",
  "offlineLicensePublicKey": "base64url-ed25519-public-key"
}
POST推荐接口

License v2:创建续租 challenge

/api/license/v2/challenge

token 快过期时调用,服务端返回一次性 challenge 和 signInput。

客户端定时续租或启动后发现 token 即将过期时调用。

请求说明

请求需要 sessionId 和短期 licenseToken。
licenseToken 可放在 Authorization: Bearer 头,也可放请求体。
如果设备已绑定 fingerprintHash,challenge 也必须携带匹配指纹。
返回的 signInput 不要自己重组,直接用设备私钥签名。
请求示例
{
  "sessionId": "ls_xxx",
  "licenseToken": "eyJ...",
  "fingerprintHash": "sha256-of-device-fingerprint"
}
响应示例
{
  "success": true,
  "message": "License v2 challenge 创建成功",
  "sessionId": "ls_xxx",
  "challengeId": "lc_xxx",
  "nonce": "nonce_xxx",
  "expiresAt": "2026-04-27T12:35:00.000Z",
  "signInput": "LICENSE-V2-CHALLENGE\nPOST\n/api/license/v2/renew\n..."
}
POST推荐接口

License v2:续租短期 token

/api/license/v2/renew

客户端用设备私钥签名 challenge 后调用,服务端验证通过后刷新 session 和 token。

token 过期前续租;推荐在客户端本地缓存 expiresAt,提前几分钟续租。

请求说明

challenge 只能使用一次,过期或重复提交会被拒绝。
续租成功后 tokenVersion 会递增,旧 token 立即失效。
已绑定 fingerprintHash 的设备续租时必须提交同一指纹。
被吊销设备或被封禁版本无法续租。
续租成功会用项目级私钥刷新 offlineLicense,让弱网宽限状态保持最新。
请求示例
{
  "sessionId": "ls_xxx",
  "challengeId": "lc_xxx",
  "nonce": "nonce_xxx",
  "signature": "base64url-ed25519-signature",
  "fingerprintHash": "sha256-of-device-fingerprint"
}
响应示例
{
  "success": true,
  "message": "License v2 session 续租成功",
  "tokenType": "LicenseV2",
  "licenseToken": "eyJ...",
  "sessionId": "ls_xxx",
  "expiresAt": "2026-04-27T14:30:00.000Z",
  "deviceId": 12,
  "licenseMode": "COUNT",
  "remainingCount": 2,
  "offlineLicense": "amlic2.payload.signature",
  "offlineLicenseExpiresAt": "2026-04-28T14:30:00.000Z"
}
POST推荐接口

License v2:签名查询授权状态

/api/license/v2/status

程序启动或刷新授权页时调用。请求必须带短期 token 和设备私钥签名。

启动检查、授权信息展示、用户点击刷新授权时调用。

请求说明

请求头必须带 X-License-Timestamp、X-License-Nonce、X-License-Signature。
签名会绑定方法、路径、session、请求体 hash 和 token hash。
nonce 只能使用一次,服务端会拒绝重放请求。
请求体可携带 fingerprintHash;设备已绑定指纹时该字段必需且必须匹配。
在线 status 成功后可更新本地 offlineLicense。
请求示例
POST /api/license/v2/status
Authorization: Bearer eyJ...
X-License-Session-Id: ls_xxx
X-License-Timestamp: 1777280000
X-License-Nonce: py_xxx
X-License-Signature: base64url-ed25519-signature

{
  "fingerprintHash": "sha256-of-device-fingerprint"
}
响应示例
{
  "success": true,
  "message": "获取激活码状态成功",
  "licenseMode": "COUNT",
  "remainingCount": 2,
  "isActivated": true,
  "valid": true,
  "sessionId": "ls_xxx",
  "deviceId": 12,
  "tokenExpiresAt": "2026-04-27T14:30:00.000Z",
  "offlineLicense": "amlic2.payload.signature",
  "offlineLicenseExpiresAt": "2026-04-28T14:30:00.000Z"
}
POST推荐接口

License v2:签名扣次 / 校验权益

/api/license/v2/consume

COUNT 授权每次真实使用成功后调用一次;TIME 授权调用它只做有效性校验。

导出、生成、分析、识别、下载等付费功能真正完成后调用。

请求说明

请求签名规则与 v2 status 相同。
fingerprintHash 会参与请求体 hash,防止中间人改写设备指纹字段。
COUNT 每次成功 consume 扣 1 次,requestId 支持幂等。
同一个 requestId 重试不会重复扣次。
离线 license 可缓存最新 remainingCount,但离线状态下不要做可信扣次。
请求示例
POST /api/license/v2/consume
Authorization: Bearer eyJ...
X-License-Session-Id: ls_xxx
X-License-Timestamp: 1777280000
X-License-Nonce: py_xxx
X-License-Signature: base64url-ed25519-signature

{
  "requestId": "req-001",
  "fingerprintHash": "sha256-of-device-fingerprint"
}
响应示例
{
  "success": true,
  "message": "激活码验证成功",
  "licenseMode": "COUNT",
  "remainingCount": 1,
  "isActivated": true,
  "valid": true,
  "idempotent": false,
  "sessionId": "ls_xxx",
  "deviceId": 12,
  "tokenExpiresAt": "2026-04-27T14:30:00.000Z",
  "offlineLicense": "amlic2.payload.signature",
  "offlineLicenseExpiresAt": "2026-04-28T14:30:00.000Z"
}

可直接参考的代码

Python 示例是桌面客户端最小可用接入方式,包含设备密钥、machineId 持久化、token 续租和签名请求。cURL 只用于看请求结构。

Python License v2 客户端

适合 PyQt、Tkinter、Flet、命令行工具等 Python 客户端。示例不保存 API Secret,而是本机生成 Ed25519 设备私钥。

# 安装示例依赖
python -m pip install cryptography keyring

# 项目里已提供完整示例:
# examples/python/license_v2_client.py

python examples/python/license_v2_client.py demo \
  --base-url https://mi.gxslgg.cn \
  --project-key browser-plugin \
  --code A1B2C3D4E5F6G7H8 \
  --machine-id machine-001 \
  --app-version 1.0.0 \
  --fingerprint-hash sha256-of-device-fingerprint

# 真实流程:
# 1. enroll: 生成 Ed25519 设备密钥,提交公钥、fingerprintHash 和私钥签名
# 2. challenge + renew: 用设备私钥续租短期 licenseToken,并复核 fingerprintHash
# 3. status / consume: 每次请求都签名 timestamp、nonce、bodyHash、tokenHash 和请求体指纹
# 4. offline-status: 用后台项目里的离线公钥验证本地 offlineLicense 快照和 fingerprintHash
cURL 快速看 v2 结构

适合临时查看请求体结构。v2 的 signature 必须由客户端 Ed25519 私钥生成,正式调用请用 Python 示例或你的客户端代码签名。

# enroll: 用户输入激活码后调用
curl -X POST "https://mi.gxslgg.cn/api/license/v2/enroll" \
  -H "Content-Type: application/json" \
  -d '{
    "projectKey": "browser-plugin",
    "code": "A1B2C3D4E5F6G7H8",
    "machineId": "machine-001",
    "appVersion": "1.0.0",
    "fingerprintHash": "sha256-of-device-fingerprint",
    "devicePublicKey": "base64url-ed25519-public-key",
    "deviceSignature": "base64url-ed25519-signature"
  }'

# challenge: 用 sessionId 和 token 换一次性 signInput
curl -X POST "https://mi.gxslgg.cn/api/license/v2/challenge" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJ..." \
  -d '{
    "sessionId": "ls_xxx",
    "fingerprintHash": "sha256-of-device-fingerprint"
  }'

# status / consume: 必须带 token 和设备签名头
curl -X POST "https://mi.gxslgg.cn/api/license/v2/status" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJ..." \
  -H "X-License-Session-Id: ls_xxx" \
  -H "X-License-Timestamp: 1777280000" \
  -H "X-License-Nonce: py_xxx" \
  -H "X-License-Signature: base64url-ed25519-signature" \
  -d '{"fingerprintHash":"sha256-of-device-fingerprint"}'

# 响应可选包含 offlineLicense,客户端用项目级 Ed25519 公钥验签后可做短期断网宽限。

后台需要做什么

客户端不要调用后台接口。后台只负责创建项目、生成激活码、查看 v2 设备和 session、吊销异常设备、封禁客户端版本和查看安全事件。

项目与发码

用于准备接入环境:先创建 projectKey,再为对应项目生成 TIME 或 COUNT 激活码。

POST/api/admin/projects

创建项目,为不同产品、插件或客户生成独立 projectKey。

PATCH/api/admin/projects/{id}

维护项目名称、描述、启停状态和项目级策略。

POST/api/admin/codes/generate

为指定项目批量生成 TIME 或 COUNT 激活码。

GET/api/admin/codes/list

查看已发放激活码,反查绑定设备、剩余次数和有效期。

日志与统计

用于排查客户反馈、核对扣次结果和导出运营数据。

GET/api/admin/consumptions

按 projectKey、关键词和时间范围查询消费日志。

GET/api/admin/consumptions/export

按当前筛选条件导出消费日志 CSV,便于对账和留档。

GET/api/admin/consumptions/trend

查看 1-90 天消费趋势,支持按日、周、月聚合。

GET/api/admin/codes/stats

查看全局和项目级授权统计。

License v2 安全管理

用于管理设备密钥、短期会话、安全事件和客户端版本封禁。

GET/api/admin/license-v2/devices

分页查看 v2 设备,支持按 projectKey、状态、版本和关键词筛选。

POST/api/admin/license-v2/devices/{id}/revoke

吊销异常设备,并同步吊销该设备下的活跃 session。

GET/api/admin/license-v2/sessions

分页查看 v2 短期会话,排查 token 续租和过期问题。

POST/api/admin/license-v2/client-versions/block

按项目封禁指定客户端版本,用于阻断已破解或存在高风险的版本。

GET/api/admin/license-v2/security-events

查看签名失败、nonce 重放、版本封禁、设备吊销等安全事件。