DEVELOPER REFERENCE · V1

欢の授权站授权接口文档

从查询授权到运行时验证。点击任意接口,展开查看用途、参数、调用示例和返回说明;国内与海外入口使用相同的协议。

国内服务入口https://license.huanware.cn
海外服务入口https://license-out.huanware.cn
CF 容灾入口 · 主节点故障时使用https://license-cf.xkshop.top

接入前先了解这 3 点

  1. 使用可信 HTTPS 入口并固定 Ed25519 公钥;公开查询不是运行时授权验证。
  2. 只查看状态用 lookup;运行产品用 verify;需要保存主动解绑令牌时先调用 activate。
  3. POST 请求使用 Content-Type: application/json,请求体上限 64 KiB;只提交文档列出的字段,多余字段会被拒绝。

以下为 POSIX shell / bash 的 curl 用法;先将 BASE_URL 替换为上方入口(不带结尾斜杠)。Windows PowerShell 可使用 curl.exe 并按 PowerShell 的引号规则传参。所有授权码、令牌、产品和随机数均为示例;当前授权码前缀为 HW-。

BASE_URL='https://license.example.com'

接口列表

9 个公开 / 客户端接口,2 个受保护的管理 / 同步接口。支持键盘 Tab + Enter 展开。

GET/health服务健康检查

检查数据库以及国内、海外入口的连通性,适用于监控探活和接入排障。浏览器直接访问可看到状态页面;程序应发送 Accept: application/json。

权限与副作用:无需授权码或管理密钥。只读,不修改授权。

请求参数

字段类型是否必填说明
Acceptheader建议必填使用 application/json 获取 JSON;接受 text/html 时返回健康检查页面。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request GET "$BASE_URL/health" \
  --header 'Accept: application/json'

返回说明

HTTP 200:数据库与国内、海外入口正常;HTTP 503:数据库或这两个入口之一异常。CF 仅显示备用配置,不参与探测或总体健康判定。JSON 字段位于顶层,不含 data,也不签名。以下为字段节选;另有入口地址和延迟毫秒数。

{
  "status": "ok",
  "service": "huanware-license-server",
  "version": "1.0.3",
  "database": "ok",
  "time": "2026-09-14T12:00:00+08:00",
  "domestic_status": "online",
  "overseas_status": "online",
  "domestic_http_status": 200,
  "overseas_http_status": 200,
  "failover_endpoint": "https://license-cf.xkshop.top",
  "failover_status": "standby_not_probed"
}
字段说明
status / database总体状态 ok / degraded;数据库 ok / error。
domestic_status / overseas_status入口探测状态 online / offline。
failover_endpoint / failover_statusCF 备用入口与状态。standby_not_probed 表示已配置但未主动探测,不等同于在线;避免状态页轮询消耗 CF 免费额度。
time / version检查时间与当前服务版本。
domestic_latency_ms / overseas_latency_ms探测耗时(毫秒);无法获取时为 null。

常见结果与错误

HTTP业务码 / 状态处理方式
503degraded检查数据库、反向代理与两个入口连通性;不要据此直接判定某条授权失效。
返回接口列表 ↑
GET/api/v1/products获取可用授权产品

列出当前启用的产品,供客户端选择 product_code,并了解默认绑定方式与校验策略;不公开客户或授权码。

权限与副作用:无需鉴权。只读。

请求参数

字段类型是否必填说明
请求参数—无不需要查询参数或请求体。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request GET "$BASE_URL/api/v1/products" \
  --header 'Accept: application/json'

返回说明

HTTP 200,返回未签名的 data 数组。停用产品不会列出;没有产品时为 []。示例产品仅用于演示。

{
  "data": [
    {
      "code": "PLUGIN_PRO",
      "slug": "plugin-pro",
      "name": "专业插件",
      "version_label": "1.0",
      "description": "示例产品",
      "client_type": "module",
      "verification_policy": "client_managed",
      "default_binding_mode": "wildcard_domain"
    }
  ]
}
字段说明
code用于授权接口的 product_code。
slug / name / version_label / description产品别名、名称、版本和介绍。
client_typemodule、script 或 general。
verification_policy脚本为 on_execute;其他产品为 client_managed。
default_binding_mode默认绑定模式,含新增 wildcard_domain;已有授权以自身 binding_mode 为准。

常见结果与错误

HTTP业务码 / 状态处理方式
500DATABASE_ERROR稍后重试;检查服务端数据库连接。
返回接口列表 ↑
GET/api/v1/public-key获取 Ed25519 验签公钥

获取服务的公开验签密钥及编码信息。接入时通过可信 HTTPS 通道取得并固定该公钥,后续用于验证签名响应;服务不会返回签名私钥。

权限与副作用:无需鉴权。只读。

请求参数

字段类型是否必填说明
请求参数—无不需要请求体。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request GET "$BASE_URL/api/v1/public-key" \
  --header 'Accept: application/json'

返回说明

HTTP 200,返回未签名的 data 对象。public_key 解码后为 32 字节。切换公钥需要可信的密钥更新流程,不能在验签失败时无条件信任新公钥。

{
  "data": {
    "algorithm": "ed25519",
    "public_key": "<32 字节公钥的标准 Base64>",
    "encoding": "base64",
    "signed_payload_encoding": "base64url-no-padding"
  }
}
字段说明
algorithm固定为 ed25519。
public_key / encoding公开密钥;标准 Base64 编码。
signed_payload_encoding签名载荷使用 Base64URL,不带 = 填充;signature 则使用标准 Base64。

常见结果与错误

HTTP业务码 / 状态处理方式
网络或代理错误—检查 HTTPS 证书、入口地址和反向代理;此接口无业务失败码。
返回接口列表 ↑
POST/api/v1/licenses/public-lookup按域名或 IP 公开查询

无需授权码,查询目标域名 / IP 对应的有效授权产品。域名和 IP 任一匹配即可;同一产品只返回一次。泛域名绑定可以通过主域名或其任意允许的子域名查到。

权限与副作用:无需授权码;不接收 license_key。只读,不激活、不续租。

请求参数

字段类型是否必填说明
domainstring与 IP 至少一项域名或网址,可省略;只按 IP 查询时通过 outbound_ip / outbound_ipv4 / outbound_ipv6 提交目标地址。
product_codestring可选按产品过滤;省略则查询所有匹配产品。
outbound_ipstring可选兼容字段,可传 IPv4 或 IPv6;与同协议族独立字段同时填写时必须一致。
outbound_ipv4 / outbound_ipv6string可选分别填写存在的出口地址,不能把 IPv6 放入 IPv4 字段。IP 类授权会核对当前请求协议族的上报值和实际来源。
installation_idstring可选用于辅助匹配实例;不能代替至少提供域名或 IP 的要求。
client_noncestring建议必填每次请求生成新的 16–128 位随机串,仅含字母、数字或 _ - . :;响应原样回传。省略或空字符串时服务端生成。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request POST "$BASE_URL/api/v1/licenses/public-lookup" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "domain": "shop.example.com",
  "client_nonce": "demo-client-nonce-0001"
}'

返回说明

HTTP 200,签名封装;以下展示 data 的关键字段。应检查 matches 数组,而不是只看 HTTP 状态。未找到时 code 为 LOOKUP_NOT_FOUND 且 matches 为 [],不是 404。

{
  "purpose": "license_status_lookup_v1",
  "license_active": true,
  "query_match": true,
  "code": "LOOKUP_ACTIVE_MATCH",
  "matches": [
    {
      "product_code": "PLUGIN_PRO",
      "product_name": "专业插件",
      "license_active": true,
      "query_match": true,
      "binding_mode": "wildcard_domain",
      "matched_by": [
        "domain"
      ],
      "license_id": null,
      "licensee": null,
      "custom_info": {}
    }
  ],
  "client_nonce": "demo-client-nonce-0001",
  "runtime_verification_required": true
}
字段说明
matches全部匹配产品。摘要字段兼容旧客户端,代表首个匹配结果。
matched_by匹配维度:domain、ipv4、ipv6、instance。
license_id / licensee / custom_info公开查询中分别为 null、null、{};不泄露客户、授权 UUID、安装信息或未查询的另一端地址。
runtime_verification_required固定为 true;公开查询不能替代运行时 verify。

常见结果与错误

HTTP业务码 / 状态处理方式
400PUBLIC_LOOKUP_INPUT_REQUIRED必须提供目标域名或目标 IP,不会用访问者来源 IP 自动替代。
200LOOKUP_NOT_FOUND检查目标地址、产品筛选、授权有效期和实际绑定。
429RATE_LIMITED相关失败请求过多,请十分钟后再试。
返回接口列表 ↑
POST/api/v1/licenses/lookup持授权码查询绑定状态

查看授权状态并严格核对查询信息,适合控制台展示与诊断。不会创建激活记录,也不会发放运行租约;domain_ip 模式必须同时匹配域名与出口 IP。

权限与副作用:请求体携带 license_key;不要暴露给公共前端。只读。

请求参数

字段类型是否必填说明
license_keystring必填完整授权码,12–64 位;仅在服务端保存,不要放进前端源码或 URL。
product_codestring可选省略时由授权码识别;正式客户端建议始终提交,防止产品混用。
domainstring必填字段实际使用域名或网址。域名 / 泛域名 / 域名 + IP 模式不可为空;其他模式可传空字符串。
installation_idstring按绑定模式实例模式必填,其他模式可省略;最多 96 位字母、数字或 _ - . :。
outbound_ipstring可选兼容字段,可传 IPv4 或 IPv6;与同协议族独立字段同时填写时必须一致。
outbound_ipv4 / outbound_ipv6string可选分别填写存在的出口地址,不能把 IPv6 放入 IPv4 字段。IP 类授权会核对当前请求协议族的上报值和实际来源。
client_noncestring建议必填每次请求生成新的 16–128 位随机串,仅含字母、数字或 _ - . :;响应原样回传。省略或空字符串时服务端生成。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request POST "$BASE_URL/api/v1/licenses/lookup" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "license_key": "HW-PLUG-ABCD-EFGH-JKLM-NPQR",
  "product_code": "PLUGIN_PRO",
  "domain": "shop.example.com",
  "client_nonce": "demo-client-nonce-0001"
}'

返回说明

HTTP 200,签名封装,示例为 data 节选。license_active 表示授权本身有效,query_match 才表示查询匹配;二者不可混用。

{
  "purpose": "license_status_lookup_v1",
  "license_active": true,
  "query_match": true,
  "code": "LOOKUP_ACTIVE_MATCH",
  "product_code": "PLUGIN_PRO",
  "binding_mode": "wildcard_domain",
  "binding_state": "matched",
  "active_bindings": 1,
  "bound_domain": "example.com",
  "queried_domain": "shop.example.com",
  "runtime_verification_required": true,
  "client_nonce": "demo-client-nonce-0001"
}
字段说明
binding_statematched 已匹配、unbound 尚未绑定、not_required 无需绑定、input_required 缺少匹配信息或 mismatch 不匹配。
active_bindings / bound_domain绑定记录数量和匹配域名;泛域名的 bound_domain 为可注册主域名。
runtime_verification_required为 true;查询成功仍须由目标服务器调用 verify。

常见结果与错误

HTTP业务码 / 状态处理方式
200LOOKUP_ACTIVE_UNBOUND授权有效但尚未绑定,可由目标服务器后续 activate / verify。
200LOOKUP_BINDING_MISMATCH / LOOKUP_INPUT_REQUIRED补齐并核对域名、IP 或实例 ID;查询不会修改绑定。
200LOOKUP_INVALID_KEY / LOOKUP_EXPIRED / LOOKUP_INACTIVE检查授权码、有效期和启用状态。
400INVALID_WILDCARD_DOMAIN泛域名模式必须提交有效的可注册域名或子域名。
429RATE_LIMITED失败请求过多,十分钟后重试。
返回接口列表 ↑
POST/api/v1/licenses/verify运行时验证并获取租约

由实际运行产品的服务器调用。校验产品、授权状态、期限与绑定;有剩余名额时首次验证会自动绑定,并生成新的短期租约。泛域名下同一主域名的子域名复用一个绑定名额,出口 IP 变化不会使泛域名绑定失效。

权限与副作用:请求体携带 license_key;这是会写入绑定、租约及检查记录的接口。

请求参数

字段类型是否必填说明
license_keystring必填完整授权码,12–64 位;仅在服务端保存,不要放进前端源码或 URL。
product_codestring可选省略时由授权码识别;正式客户端建议始终提交,防止产品混用。
domainstring必填字段实际使用域名或网址。域名 / 泛域名 / 域名 + IP 模式不可为空;其他模式可传空字符串。
installation_idstring按绑定模式实例模式必填,其他模式可省略;最多 96 位字母、数字或 _ - . :。
outbound_ipstring可选兼容字段,可传 IPv4 或 IPv6;与同协议族独立字段同时填写时必须一致。
outbound_ipv4 / outbound_ipv6string可选分别填写存在的出口地址,不能把 IPv6 放入 IPv4 字段。IP 类授权会核对当前请求协议族的上报值和实际来源。
custom_infoobject可选非敏感安装信息;序列化后不超过 4096 字节。不可用数组、字符串代替对象;不要上报密码或密钥。
client_noncestring建议必填每次请求生成新的 16–128 位随机串,仅含字母、数字或 _ - . :;响应原样回传。省略或空字符串时服务端生成。
previous_lease_tokenstring可选传入上一次租约令牌时,会撤销该授权下匹配的旧租约,再发放新租约。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request POST "$BASE_URL/api/v1/licenses/verify" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "license_key": "HW-PLUG-ABCD-EFGH-JKLM-NPQR",
  "product_code": "PLUGIN_PRO",
  "domain": "shop.example.com",
  "custom_info": {
    "client_version": "1.0.0"
  },
  "client_nonce": "demo-client-nonce-0001"
}'

返回说明

HTTP 200,签名封装;必须验签后读取 valid,不能把 200 当作授权通过。以下为成功 data 节选。自动绑定不会返回可用于 deactivate 的 activation_token。

{
  "valid": true,
  "code": "VALID",
  "product_code": "PLUGIN_PRO",
  "domain": "shop.example.com",
  "binding_mode": "wildcard_domain",
  "bound_domain": "example.com",
  "activation_required": true,
  "activation_id": "<activation_uuid>",
  "entitlements": [
    "updates"
  ],
  "checked_at": "2026-09-14T12:00:00+08:00",
  "next_check_at": null,
  "lease_token": "HWL-<secret>",
  "lease_expires_at": "2026-09-15T18:00:00+08:00",
  "verification_policy": "client_managed",
  "client_nonce": "demo-client-nonce-0001"
}
字段说明
valid / code业务校验结果;失败也可能是 HTTP 200 + valid:false。
lease_token / lease_expires_at安全保存租约;当前模块 / 通用产品租约为 30 小时,脚本为 15 分钟。
verification_policy / next_check_at脚本 on_execute 需每次执行验证;其他 client_managed 由客户端安排。当前 verify 的 next_check_at 为 null,不表示可永久离线。
domain / bound_domaindomain 为本次实际域名;泛域名 bound_domain 为主域名。
activation_required表示此授权使用绑定机制;即使本次已自动完成绑定,也可能为 true。

常见结果与错误

HTTP业务码 / 状态处理方式
200INVALID_KEY / EXPIRED / INACTIVE / PRODUCT_DISABLED读取签名载荷 valid:false,并停止使用受保护功能。
200LICENSE_BOUND绑定名额已用完;管理员扩容或释放旧绑定后再试。
200DOMAIN_REQUIRED / DOMAIN_MISMATCH / IP_REPORT_MISMATCH检查域名字段、白名单或当前协议族的实际出口 IP。泛域名不核对 IP。
400INVALID_WILDCARD_DOMAIN / INVALID_CUSTOM_INFO修正域名或安装信息的格式。
429RATE_LIMITED十分钟后重试,不要不断自动重复失败请求。
返回接口列表 ↑
POST/api/v1/licenses/activate显式激活并取得解绑令牌

在安装或部署阶段主动创建绑定。首次成功会返回 activation_token,供未来主动解绑使用;同一绑定重复激活不会多占名额,也不会再次返回原令牌。

权限与副作用:license_key 与 product_code 必填;会写入绑定记录。

请求参数

字段类型是否必填说明
license_keystring必填完整授权码,12–64 位;仅在服务端保存,不要放进前端源码或 URL。
product_codestring必填产品代码,最多 64 位;自动转大写,应与授权所属产品一致。
domainstring必填字段实际使用域名或网址。域名 / 泛域名 / 域名 + IP 模式不可为空;其他模式可传空字符串。
installation_idstring按绑定模式实例模式必填,其他模式可省略;最多 96 位字母、数字或 _ - . :。
outbound_ipstring可选兼容字段,可传 IPv4 或 IPv6;与同协议族独立字段同时填写时必须一致。
outbound_ipv4 / outbound_ipv6string可选分别填写存在的出口地址,不能把 IPv6 放入 IPv4 字段。IP 类授权会核对当前请求协议族的上报值和实际来源。
custom_infoobject可选非敏感安装信息;序列化后不超过 4096 字节。不可用数组、字符串代替对象;不要上报密码或密钥。
client_noncestring建议必填每次请求生成新的 16–128 位随机串,仅含字母、数字或 _ - . :;响应原样回传。省略或空字符串时服务端生成。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request POST "$BASE_URL/api/v1/licenses/activate" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "license_key": "HW-PLUG-ABCD-EFGH-JKLM-NPQR",
  "product_code": "PLUGIN_PRO",
  "domain": "shop.example.com",
  "custom_info": {
    "client_version": "1.0.0"
  },
  "client_nonce": "demo-client-nonce-0001"
}'

返回说明

HTTP 200,签名封装,示例为 data 节选。请安全保存首次返回的令牌;next_check_at 由服务端缓存配置决定,示例时间不代表固定值。激活不能替代后续运行时 verify。

{
  "activated": true,
  "already_active": false,
  "code": "ACTIVATED",
  "license_id": "<license_uuid>",
  "activation_id": "<activation_uuid>",
  "activation_token": "HWA-<secret>",
  "product_code": "PLUGIN_PRO",
  "domain": "shop.example.com",
  "installation_id": "",
  "binding_mode": "wildcard_domain",
  "client_nonce": "demo-client-nonce-0001",
  "activated_at": "2026-09-14T12:00:00+08:00",
  "next_check_at": "2026-09-14T12:05:00+08:00"
}
字段说明
activated / already_active新绑定成功:true / false;已经存在:true / true。
activation_token仅新建绑定时返回;ALREADY_ACTIVE 时为 null,不能借重复激活取回令牌。
activation_id同一泛域名下的主域名、兄弟子域名、多级子域名返回同一 ID。
installation_id域名和泛域名模式返回空字符串;解绑时可以不提交该字段。

常见结果与错误

HTTP业务码 / 状态处理方式
404LICENSE_NOT_FOUND授权码与产品不匹配或不存在。
400ACTIVATION_LIMIT_REACHED不同主域名需要新的名额;同根子域名不增加名额。
400ACTIVATION_NOT_REQUIREDnone 模式无需激活,请直接 verify。
400DOMAIN_MISMATCH / INVALID_WILDCARD_DOMAIN / IP_REPORT_MISMATCH检查域名白名单、主域名合法性或 IP 模式的出口地址。
200ALREADY_ACTIVE正常幂等结果,保留之前保存的激活令牌。
返回接口列表 ↑
POST/api/v1/instances/report上报非敏感安装信息

在激活前后记录客户端版本、运行环境等安装信息。相同 product_code + domain + installation_id 会更新同一记录;上报成功不代表拥有授权,也不会占用绑定名额或发放租约。

权限与副作用:无需授权码。必须填写 installation_id;仅用于非敏感安装信息记录。

请求参数

字段类型是否必填说明
product_codestring必填产品代码,最多 64 位;自动转大写,应与授权所属产品一致。
domainstring必填字段实际使用域名或网址;没有域名的实例可传空字符串。
installation_idstring必填不可为空,最多 96 位字母、数字或 _ - . :。
custom_infoobject可选非敏感安装信息;序列化后不超过 4096 字节。不可用数组、字符串代替对象;不要上报密码或密钥。
client_noncestring建议必填每次请求生成新的 16–128 位随机串,仅含字母、数字或 _ - . :;响应原样回传。省略或空字符串时服务端生成。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request POST "$BASE_URL/api/v1/instances/report" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "product_code": "PLUGIN_PRO",
  "domain": "shop.example.com",
  "installation_id": "site-01",
  "custom_info": {
    "client_version": "1.0.0",
    "runtime": "php-8.2"
  },
  "client_nonce": "demo-client-nonce-0001"
}'

返回说明

HTTP 200,签名封装。以下为 data;重复上报保留 report_id 并更新 custom_info、来源 IP 和上报时间。省略 custom_info 时记录 {}。

{
  "reported": true,
  "code": "REPORTED",
  "message": "安装实例信息已记录。",
  "report_id": "<report_uuid>",
  "product_code": "PLUGIN_PRO",
  "domain": "shop.example.com",
  "installation_id": "site-01",
  "custom_info": {
    "client_version": "1.0.0",
    "runtime": "php-8.2"
  },
  "reported_ip": "203.0.113.10",
  "reported_at": "2026-09-14T12:00:00+08:00",
  "client_nonce": "demo-client-nonce-0001"
}
字段说明
reported / codetrue / REPORTED 仅代表信息记录成功。
report_id此安装记录的标识;不是 license_id 或 activation_id。
custom_info本次保存的 JSON 对象,更新会替换原内容。
reported_ip / reported_at服务端观察到的来源 IP 与记录时间。

常见结果与错误

HTTP业务码 / 状态处理方式
400INSTALLATION_ID_REQUIRED补充非空且稳定的安装标识。
400INVALID_CUSTOM_INFO / CUSTOM_INFO_TOO_LARGE改为 JSON 对象,并控制在 4096 字节以内。
429RATE_LIMITED相关失败请求过多,十分钟后重试。
返回接口列表 ↑
POST/api/v1/licenses/deactivate解绑一条激活记录

使用首次显式激活获得的 activation_token 释放对应绑定,并撤销该绑定的全部租约。只释放这一条,不影响同一授权的其他绑定;泛域名释放后,其主域名和全部子域名共用的绑定都会失效。

权限与副作用:同时携带 license_key、product_code 和 activation_token;会消耗当前周期 1 次重置次数。

请求参数

字段类型是否必填说明
license_keystring必填完整授权码,12–64 位;仅在服务端保存,不要放进前端源码或 URL。
product_codestring必填产品代码,最多 64 位;自动转大写,应与授权所属产品一致。
activation_tokenstring必填首次 activate 返回的激活令牌,16–96 字符;lease_token 不能代替。
domainstring必填字段普通域名应与激活一致;泛域名可提交该绑定下任意实际子域名或主域名。其他模式使用激活时的 domain,可为空。
installation_idstring可选填写时必须与激活返回值一致;域名 / 泛域名模式建议省略,实例模式使用原 ID。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request POST "$BASE_URL/api/v1/licenses/deactivate" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "license_key": "HW-PLUG-ABCD-EFGH-JKLM-NPQR",
  "product_code": "PLUGIN_PRO",
  "activation_token": "HWA-<replace-with-activation-token>",
  "domain": "shop.example.com"
}'

返回说明

HTTP 200,签名封装。解绑后可按授权名额重新激活;重复请求返回 ACTIVATION_NOT_FOUND,不会再次消耗重置次数。令牌丢失或只有 verify 自动绑定时,使用登录后的授权控制台解绑 / 重置。

{
  "deactivated": true,
  "code": "DEACTIVATED",
  "message": "该实例已解绑,可按授权额度重新激活。",
  "product_code": "PLUGIN_PRO",
  "domain": "shop.example.com",
  "installation_id": "",
  "deactivated_at": "2026-09-14T12:00:00+08:00"
}
字段说明
deactivated / codetrue / DEACTIVATED 表示该绑定已撤销。
domain / installation_id本次解绑目标及实际绑定的实例标识。
deactivated_at解绑完成时间。

常见结果与错误

HTTP业务码 / 状态处理方式
404ACTIVATION_NOT_FOUND授权、产品、令牌、域名或实例不匹配,或已解绑。
400INVALID_ACTIVATION_TOKEN请使用 activate 的令牌,不是授权码或租约令牌。
400RESET_DISABLED / RESET_LIMIT_REACHED当前周期禁止重置或次数用尽,等待新周期或联系管理员。
400RESET_PERIOD_UNAVAILABLE当前没有有效授权周期。
返回接口列表 ↑

管理接口 · 仅可信服务端使用

POST/internal/v1/licenses管理端签发授权

仅供可信后台或管理脚本签发新授权。不会自动激活;绑定方式由 activation_mode 指定,支持 wildcard_domain。管理密钥只能保存在可信服务端,不能嵌入网页或客户端。

权限与副作用:请求头 X-Admin-Key 必填。会创建授权及当前权益周期;应限制此路径的网络访问。

请求参数

字段类型是否必填说明
X-Admin-Keyheader必填服务配置中的 ADMIN_API_KEY。
user_idinteger必填有效归属用户 ID,用户须启用并有有效邮箱。
product_codestring必填产品代码,最多 64 位;自动转大写,应与授权所属产品一致。
planstring必填授权版本标识,最多 32 位;例如 basic / professional。
activation_modestring必填none / domain / wildcard_domain / ip / domain_ip / instance。
max_activationsinteger必填绑定上限;绑定类模式至少为 1。泛域名按可注册主域名计数。
starts_at / expires_atRFC3339 string可选开始时间默认当前时间;到期时间省略为永久,提供时必须晚于开始时间;应包含时区。
reset_limit_per_periodinteger可选默认 3;-1 表示无限次,0 禁止重置,其余为 1–10000。
allowed_domainsstring[]可选域名白名单,默认 [];可同时填 example.com 与 *.example.com 来允许主域名及子域名。
entitlementsstring[]可选权益标识,单项最多 64 位字母、数字或 _ - .。
metadataJSON可选后台附加数据;不要包含需要返回给客户端的秘密。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request POST "$BASE_URL/internal/v1/licenses" \
  --header 'Accept: application/json' \
  --header "X-Admin-Key: $ADMIN_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "user_id": 2,
  "product_code": "PLUGIN_PRO",
  "plan": "professional",
  "activation_mode": "wildcard_domain",
  "max_activations": 1,
  "reset_limit_per_period": 3,
  "allowed_domains": [
    "example.com",
    "*.example.com"
  ],
  "entitlements": [
    "updates"
  ]
}'

返回说明

HTTP 201 Created,返回未签名的 data 对象。完整授权码只在成功签发响应中返回,调用方必须安全保存;不要将此接口暴露给公共客户端。

{
  "data": {
    "license_id": "<license_uuid>",
    "license_key": "HW-PLUG-ABCD-EFGH-JKLM-NPQR",
    "product_code": "PLUGIN_PRO",
    "customer_name": "示例用户",
    "plan": "professional",
    "activation_mode": "wildcard_domain",
    "max_activations": 1,
    "reset_limit_per_period": 3,
    "starts_at": "2026-09-14T12:00:00+08:00",
    "expires_at": null,
    "allowed_domains": [
      "example.com",
      "*.example.com"
    ],
    "entitlements": [
      "updates"
    ]
  }
}
字段说明
license_key / license_id新授权码与授权 UUID,不是激活令牌。
activation_mode / max_activations实际绑定规则及名额,不会因为 products 默认值后续变化而自动改动。
starts_at / expires_at带 +08:00 的时间;expires_at:null 表示永久。

常见结果与错误

HTTP业务码 / 状态处理方式
401UNAUTHORIZED管理密钥缺失或错误。
404USER_NOT_FOUND / PRODUCT_NOT_FOUND检查用户、邮箱及产品是否有效启用。
400INVALID_ACTIVATION_MODE / INVALID_ACTIVATION_LIMIT检查绑定方式与数量。
400INVALID_RESET_LIMIT / INVALID_EXPIRY检查重置限制与时间范围。
返回接口列表 ↑
GET/internal/v1/failover-snapshot备用节点同步签名快照

供独立子域名上的 Cloudflare Worker 自动同步使用。导出当前有效授权及已有绑定的完整签名快照,不包含明文授权码、客户身份、安装元数据或任何私钥。默认关闭,配置 FAILOVER_SYNC_KEY 后启用。

权限与副作用:请求头 X-Failover-Key 使用独立同步密钥,不能用普通授权码代替。仅允许可信 Worker 调用;响应禁止缓存且不得公开。

请求参数

字段类型是否必填说明
X-Failover-Keyheader必填源站 FAILOVER_SYNC_KEY,至少 32 位高强度随机串;与 Worker secret 保持一致。
noncequery string必填每次同步生成新的 16–128 位随机串。Worker 验签并核对回传 client_nonce,以阻止重放。

请求示例

先在终端设置 BASE_URL;示例中的授权码、随机数和其他占位值需要替换。

curl --request GET "$BASE_URL/internal/v1/failover-snapshot?nonce=demo-client-nonce-0001" \
  --header 'Accept: application/json' \
  --header "X-Failover-Key: $FAILOVER_SYNC_KEY"

返回说明

HTTP 200,Ed25519 签名封装;以下为 data 结构概览。快照载荷最多 4 MiB,超过限制拒绝同步,绝不静默截断。Worker 原子替换整个快照,未包含的撤销 / 过期授权和解绑记录随成功同步移除。

{
  "purpose": "license_failover_snapshot_v1",
  "schema_version": 1,
  "snapshot_id": "<snapshot_uuid>",
  "client_nonce": "demo-client-nonce-0001",
  "generated_at": "2026-09-14T12:00:00+08:00",
  "valid_until": "2026-09-14T18:00:00+08:00",
  "products": "<当前启用产品数组>",
  "licenses": "<授权码哈希、状态范围、白名单、权益和有效绑定数组>"
}
字段说明
generated_at / valid_until快照生成与最晚失效时间。源站默认最长 6 小时;FAILOVER_SNAPSHOT_MAX_AGE_SECONDS 可配置 300–86400 秒。
licenses[].key_hash规范化授权码的 SHA-256;不是明文授权码。
licenses[].activations仅现有有效绑定。Worker 离线时不新增绑定或 IP 协议族,不签发授权、不解绑。
client_nonce / signature核对本次请求 nonce,并用预先固定的源站公钥验签;不能只信任外层 data。

常见结果与错误

HTTP业务码 / 状态处理方式
404FAILOVER_DISABLED源站未配置 FAILOVER_SYNC_KEY。
401UNAUTHORIZED独立同步密钥错误。
400INVALID_CLIENT_NONCE / SNAPSHOT_TOO_LARGE修正 nonce,或升级大规模复制方案。
500DATABASE_ERROR保留旧快照,但不得延长其原有效期。
返回接口列表 ↑

绑定方式与泛域名规则

domain · 精确域名

不同子域名分别绑定和计数。保持原有域名规范化规则,www 前缀会被去除;不校验出口 IP。

wildcard_domain · 泛域名

example.com、shop.example.com、x.shop.example.com 共用一条 example.com 绑定,合计占 1 个名额;不锁定出口 IP。客户端仍上报实际域名,不提交 *.example.com。

ip / domain_ip · 出口 IP / 双绑定

IP 按 IPv4、IPv6 分开保存;当前连接使用哪个协议族,就核对对应来源。domain_ip 运行时要求域名和 IP 同时符合。

instance / none · 实例 / 无绑定

instance 使用稳定的 installation_id;none 无需 activate,但仍需校验授权状态和有效期。

泛域名按内置 Public Suffix List(含私有后缀)确定可注册主域名:example.co.uk 不会被当成 co.uk,alice.github.io 与 bob.github.io 不会共享授权。IP、localhost、仅公共后缀和无法识别的后缀不能用于泛域名模式。域名白名单仍按实际请求域名检查;*.example.com 仅允许子域名,若也要允许主域名,请同时填写 example.com。修改产品默认绑定方式只影响以后签发的授权,不自动转换已有授权。

独立子域名备用入口

Cloudflare Worker 使用新的子域名(https://license-cf.xkshop.top),不替换现有国内 / 海外域名。默认每 15 分钟同步,快照最多使用 6 小时;离线只验证已有绑定或 none 模式授权,发放最多 15 分钟且不超过授权 / 快照到期时间的备用租约。

源站正常时转发请求;仅网络故障或 5xx 才允许快照降级,明确的业务拒绝和 HTTP 4xx 不会被快照覆盖。冷启动无快照或快照过期时返回 503。撤销与解绑在最近一次成功同步后才会被备用节点获知,因此离线容灾不等于强一致实时授权;降低快照有效期可以缩短此窗口。

容灾入口 https://license-cf.xkshop.top 已加入授权站的节点列表;客户端接入时同样应保持国内、海外节点优先,仅网络失败、超时或 HTTP 5xx 时尝试 CF。客户端需更新节点配置后才能自动切换,不必把正常请求全部发给 Worker。免费模式默认最多同步 100 条授权 / 500 条绑定 / 256 KiB 快照,离线请求预算每日 20000 次,只写入发生变化的数据;匿名公开查询离线时关闭,持授权码验证继续可用。超出保护阈值不会静默截断或放行授权。Cloudflare 账户共享额度和入口请求仍需在控制台监控;完整部署步骤见独立 Worker 发布包中的 DEPLOY.txt。

响应结构与安全验签

lookup、public-lookup、verify、activate、instances/report、deactivate 的成功 HTTP 响应使用下面的签名封装。签名保护的是 signed_payload 解码后的原始字节(即 data 的 JSON),不包括外层 request_id。

{
  "request_id": "<request_uuid>",
  "data": {
    "code": "VALID",
    "client_nonce": "demo-client-nonce-0001"
  },
  "signed_payload": "<data JSON 的 Base64URL,无填充>",
  "signature": "<Ed25519 签名的标准 Base64>",
  "signature_algorithm": "ed25519"
}
  1. 先检查 HTTP 状态;业务错误可能位于 HTTP 200 的签名 data 中,例如 valid:false,必须继续检查。
  2. 对 signed_payload 做 Base64URL 无填充解码;对 signature 和固定的公钥做标准 Base64 解码,分别应为 64 和 32 字节。
  3. 使用 Ed25519 对解码后的原始载荷字节验签,不能把外层 data 重新 JSON 序列化后验签。
  4. 验签成功后再解析载荷,核对产品、请求域名、支持随机数的接口的 client_nonce,以及 checked_at / lease_expires_at 等时间;以载荷为准,不单独信任外层 data。
  5. 请求 nonce 每次重新生成并保留到响应校验完毕;deactivate 不接收 nonce,需核对目标字段和解绑时间。公钥应预先可信固定,不要在验签失败时无条件换成网络返回的新公钥。

时间字段以带 +08:00 的北京时间 RFC3339 输出。授权码、activation_token、lease_token 和 X-Admin-Key 都是秘密;上报 custom_info 不得包含这些值。

非成功响应

业务请求错误通常返回 HTTP 400 / 401 / 404 / 429 / 500 和未签名的 error 对象;JSON 解析失败、缺少必填字段、未知字段、错误 Content-Type 或超大请求体也可能由框架直接返回 400 / 415 / 422 / 413 文本错误,客户端不应假定每个响应都能解析为同一 JSON 结构。

{
  "error": {
    "code": "ACTIVATION_LIMIT_REACHED",
    "message": "该授权的激活数量已达到上限。"
  }
}

GET /api/v1/products、GET /api/v1/public-key 和管理端签发成功使用未签名的 data 封装;/health 的 JSON 位于顶层。不要把这些只读元数据响应当作授权凭据。