不同子域名分别绑定和计数。保持原有域名规范化规则,www 前缀会被去除;不校验出口 IP。
DEVELOPER REFERENCE · V1
欢の授权站授权接口文档
从查询授权到运行时验证。点击任意接口,展开查看用途、参数、调用示例和返回说明;国内与海外入口使用相同的协议。
https://license.huanware.cnhttps://license-out.huanware.cnhttps://license-cf.xkshop.top接入前先了解这 3 点
- 使用可信 HTTPS 入口并固定 Ed25519 公钥;公开查询不是运行时授权验证。
- 只查看状态用 lookup;运行产品用 verify;需要保存主动解绑令牌时先调用 activate。
- 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。
权限与副作用:无需授权码或管理密钥。只读,不修改授权。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| Accept | header | 建议必填 | 使用 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_status | CF 备用入口与状态。standby_not_probed 表示已配置但未主动探测,不等同于在线;避免状态页轮询消耗 CF 免费额度。 |
| time / version | 检查时间与当前服务版本。 |
| domestic_latency_ms / overseas_latency_ms | 探测耗时(毫秒);无法获取时为 null。 |
常见结果与错误
| HTTP | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 503 | degraded | 检查数据库、反向代理与两个入口连通性;不要据此直接判定某条授权失效。 |
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_type | module、script 或 general。 |
| verification_policy | 脚本为 on_execute;其他产品为 client_managed。 |
| default_binding_mode | 默认绑定模式,含新增 wildcard_domain;已有授权以自身 binding_mode 为准。 |
常见结果与错误
| HTTP | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 500 | DATABASE_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。只读,不激活、不续租。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| domain | string | 与 IP 至少一项 | 域名或网址,可省略;只按 IP 查询时通过 outbound_ip / outbound_ipv4 / outbound_ipv6 提交目标地址。 |
| product_code | string | 可选 | 按产品过滤;省略则查询所有匹配产品。 |
| outbound_ip | string | 可选 | 兼容字段,可传 IPv4 或 IPv6;与同协议族独立字段同时填写时必须一致。 |
| outbound_ipv4 / outbound_ipv6 | string | 可选 | 分别填写存在的出口地址,不能把 IPv6 放入 IPv4 字段。IP 类授权会核对当前请求协议族的上报值和实际来源。 |
| installation_id | string | 可选 | 用于辅助匹配实例;不能代替至少提供域名或 IP 的要求。 |
| client_nonce | string | 建议必填 | 每次请求生成新的 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 | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 400 | PUBLIC_LOOKUP_INPUT_REQUIRED | 必须提供目标域名或目标 IP,不会用访问者来源 IP 自动替代。 |
| 200 | LOOKUP_NOT_FOUND | 检查目标地址、产品筛选、授权有效期和实际绑定。 |
| 429 | RATE_LIMITED | 相关失败请求过多,请十分钟后再试。 |
POST/api/v1/licenses/lookup持授权码查询绑定状态
查看授权状态并严格核对查询信息,适合控制台展示与诊断。不会创建激活记录,也不会发放运行租约;domain_ip 模式必须同时匹配域名与出口 IP。
权限与副作用:请求体携带 license_key;不要暴露给公共前端。只读。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| license_key | string | 必填 | 完整授权码,12–64 位;仅在服务端保存,不要放进前端源码或 URL。 |
| product_code | string | 可选 | 省略时由授权码识别;正式客户端建议始终提交,防止产品混用。 |
| domain | string | 必填字段 | 实际使用域名或网址。域名 / 泛域名 / 域名 + IP 模式不可为空;其他模式可传空字符串。 |
| installation_id | string | 按绑定模式 | 实例模式必填,其他模式可省略;最多 96 位字母、数字或 _ - . :。 |
| outbound_ip | string | 可选 | 兼容字段,可传 IPv4 或 IPv6;与同协议族独立字段同时填写时必须一致。 |
| outbound_ipv4 / outbound_ipv6 | string | 可选 | 分别填写存在的出口地址,不能把 IPv6 放入 IPv4 字段。IP 类授权会核对当前请求协议族的上报值和实际来源。 |
| client_nonce | string | 建议必填 | 每次请求生成新的 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_state | matched 已匹配、unbound 尚未绑定、not_required 无需绑定、input_required 缺少匹配信息或 mismatch 不匹配。 |
| active_bindings / bound_domain | 绑定记录数量和匹配域名;泛域名的 bound_domain 为可注册主域名。 |
| runtime_verification_required | 为 true;查询成功仍须由目标服务器调用 verify。 |
常见结果与错误
| HTTP | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 200 | LOOKUP_ACTIVE_UNBOUND | 授权有效但尚未绑定,可由目标服务器后续 activate / verify。 |
| 200 | LOOKUP_BINDING_MISMATCH / LOOKUP_INPUT_REQUIRED | 补齐并核对域名、IP 或实例 ID;查询不会修改绑定。 |
| 200 | LOOKUP_INVALID_KEY / LOOKUP_EXPIRED / LOOKUP_INACTIVE | 检查授权码、有效期和启用状态。 |
| 400 | INVALID_WILDCARD_DOMAIN | 泛域名模式必须提交有效的可注册域名或子域名。 |
| 429 | RATE_LIMITED | 失败请求过多,十分钟后重试。 |
POST/api/v1/licenses/verify运行时验证并获取租约
由实际运行产品的服务器调用。校验产品、授权状态、期限与绑定;有剩余名额时首次验证会自动绑定,并生成新的短期租约。泛域名下同一主域名的子域名复用一个绑定名额,出口 IP 变化不会使泛域名绑定失效。
权限与副作用:请求体携带 license_key;这是会写入绑定、租约及检查记录的接口。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| license_key | string | 必填 | 完整授权码,12–64 位;仅在服务端保存,不要放进前端源码或 URL。 |
| product_code | string | 可选 | 省略时由授权码识别;正式客户端建议始终提交,防止产品混用。 |
| domain | string | 必填字段 | 实际使用域名或网址。域名 / 泛域名 / 域名 + IP 模式不可为空;其他模式可传空字符串。 |
| installation_id | string | 按绑定模式 | 实例模式必填,其他模式可省略;最多 96 位字母、数字或 _ - . :。 |
| outbound_ip | string | 可选 | 兼容字段,可传 IPv4 或 IPv6;与同协议族独立字段同时填写时必须一致。 |
| outbound_ipv4 / outbound_ipv6 | string | 可选 | 分别填写存在的出口地址,不能把 IPv6 放入 IPv4 字段。IP 类授权会核对当前请求协议族的上报值和实际来源。 |
| custom_info | object | 可选 | 非敏感安装信息;序列化后不超过 4096 字节。不可用数组、字符串代替对象;不要上报密码或密钥。 |
| client_nonce | string | 建议必填 | 每次请求生成新的 16–128 位随机串,仅含字母、数字或 _ - . :;响应原样回传。省略或空字符串时服务端生成。 |
| previous_lease_token | string | 可选 | 传入上一次租约令牌时,会撤销该授权下匹配的旧租约,再发放新租约。 |
请求示例
先在终端设置 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_domain | domain 为本次实际域名;泛域名 bound_domain 为主域名。 |
| activation_required | 表示此授权使用绑定机制;即使本次已自动完成绑定,也可能为 true。 |
常见结果与错误
| HTTP | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 200 | INVALID_KEY / EXPIRED / INACTIVE / PRODUCT_DISABLED | 读取签名载荷 valid:false,并停止使用受保护功能。 |
| 200 | LICENSE_BOUND | 绑定名额已用完;管理员扩容或释放旧绑定后再试。 |
| 200 | DOMAIN_REQUIRED / DOMAIN_MISMATCH / IP_REPORT_MISMATCH | 检查域名字段、白名单或当前协议族的实际出口 IP。泛域名不核对 IP。 |
| 400 | INVALID_WILDCARD_DOMAIN / INVALID_CUSTOM_INFO | 修正域名或安装信息的格式。 |
| 429 | RATE_LIMITED | 十分钟后重试,不要不断自动重复失败请求。 |
POST/api/v1/licenses/activate显式激活并取得解绑令牌
在安装或部署阶段主动创建绑定。首次成功会返回 activation_token,供未来主动解绑使用;同一绑定重复激活不会多占名额,也不会再次返回原令牌。
权限与副作用:license_key 与 product_code 必填;会写入绑定记录。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| license_key | string | 必填 | 完整授权码,12–64 位;仅在服务端保存,不要放进前端源码或 URL。 |
| product_code | string | 必填 | 产品代码,最多 64 位;自动转大写,应与授权所属产品一致。 |
| domain | string | 必填字段 | 实际使用域名或网址。域名 / 泛域名 / 域名 + IP 模式不可为空;其他模式可传空字符串。 |
| installation_id | string | 按绑定模式 | 实例模式必填,其他模式可省略;最多 96 位字母、数字或 _ - . :。 |
| outbound_ip | string | 可选 | 兼容字段,可传 IPv4 或 IPv6;与同协议族独立字段同时填写时必须一致。 |
| outbound_ipv4 / outbound_ipv6 | string | 可选 | 分别填写存在的出口地址,不能把 IPv6 放入 IPv4 字段。IP 类授权会核对当前请求协议族的上报值和实际来源。 |
| custom_info | object | 可选 | 非敏感安装信息;序列化后不超过 4096 字节。不可用数组、字符串代替对象;不要上报密码或密钥。 |
| client_nonce | string | 建议必填 | 每次请求生成新的 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 | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 404 | LICENSE_NOT_FOUND | 授权码与产品不匹配或不存在。 |
| 400 | ACTIVATION_LIMIT_REACHED | 不同主域名需要新的名额;同根子域名不增加名额。 |
| 400 | ACTIVATION_NOT_REQUIRED | none 模式无需激活,请直接 verify。 |
| 400 | DOMAIN_MISMATCH / INVALID_WILDCARD_DOMAIN / IP_REPORT_MISMATCH | 检查域名白名单、主域名合法性或 IP 模式的出口地址。 |
| 200 | ALREADY_ACTIVE | 正常幂等结果,保留之前保存的激活令牌。 |
POST/api/v1/instances/report上报非敏感安装信息
在激活前后记录客户端版本、运行环境等安装信息。相同 product_code + domain + installation_id 会更新同一记录;上报成功不代表拥有授权,也不会占用绑定名额或发放租约。
权限与副作用:无需授权码。必须填写 installation_id;仅用于非敏感安装信息记录。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| product_code | string | 必填 | 产品代码,最多 64 位;自动转大写,应与授权所属产品一致。 |
| domain | string | 必填字段 | 实际使用域名或网址;没有域名的实例可传空字符串。 |
| installation_id | string | 必填 | 不可为空,最多 96 位字母、数字或 _ - . :。 |
| custom_info | object | 可选 | 非敏感安装信息;序列化后不超过 4096 字节。不可用数组、字符串代替对象;不要上报密码或密钥。 |
| client_nonce | string | 建议必填 | 每次请求生成新的 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 / code | true / REPORTED 仅代表信息记录成功。 |
| report_id | 此安装记录的标识;不是 license_id 或 activation_id。 |
| custom_info | 本次保存的 JSON 对象,更新会替换原内容。 |
| reported_ip / reported_at | 服务端观察到的来源 IP 与记录时间。 |
常见结果与错误
| HTTP | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 400 | INSTALLATION_ID_REQUIRED | 补充非空且稳定的安装标识。 |
| 400 | INVALID_CUSTOM_INFO / CUSTOM_INFO_TOO_LARGE | 改为 JSON 对象,并控制在 4096 字节以内。 |
| 429 | RATE_LIMITED | 相关失败请求过多,十分钟后重试。 |
POST/api/v1/licenses/deactivate解绑一条激活记录
使用首次显式激活获得的 activation_token 释放对应绑定,并撤销该绑定的全部租约。只释放这一条,不影响同一授权的其他绑定;泛域名释放后,其主域名和全部子域名共用的绑定都会失效。
权限与副作用:同时携带 license_key、product_code 和 activation_token;会消耗当前周期 1 次重置次数。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| license_key | string | 必填 | 完整授权码,12–64 位;仅在服务端保存,不要放进前端源码或 URL。 |
| product_code | string | 必填 | 产品代码,最多 64 位;自动转大写,应与授权所属产品一致。 |
| activation_token | string | 必填 | 首次 activate 返回的激活令牌,16–96 字符;lease_token 不能代替。 |
| domain | string | 必填字段 | 普通域名应与激活一致;泛域名可提交该绑定下任意实际子域名或主域名。其他模式使用激活时的 domain,可为空。 |
| installation_id | string | 可选 | 填写时必须与激活返回值一致;域名 / 泛域名模式建议省略,实例模式使用原 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 / code | true / DEACTIVATED 表示该绑定已撤销。 |
| domain / installation_id | 本次解绑目标及实际绑定的实例标识。 |
| deactivated_at | 解绑完成时间。 |
常见结果与错误
| HTTP | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 404 | ACTIVATION_NOT_FOUND | 授权、产品、令牌、域名或实例不匹配,或已解绑。 |
| 400 | INVALID_ACTIVATION_TOKEN | 请使用 activate 的令牌,不是授权码或租约令牌。 |
| 400 | RESET_DISABLED / RESET_LIMIT_REACHED | 当前周期禁止重置或次数用尽,等待新周期或联系管理员。 |
| 400 | RESET_PERIOD_UNAVAILABLE | 当前没有有效授权周期。 |
管理接口 · 仅可信服务端使用
POST/internal/v1/licenses管理端签发授权
仅供可信后台或管理脚本签发新授权。不会自动激活;绑定方式由 activation_mode 指定,支持 wildcard_domain。管理密钥只能保存在可信服务端,不能嵌入网页或客户端。
权限与副作用:请求头 X-Admin-Key 必填。会创建授权及当前权益周期;应限制此路径的网络访问。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| X-Admin-Key | header | 必填 | 服务配置中的 ADMIN_API_KEY。 |
| user_id | integer | 必填 | 有效归属用户 ID,用户须启用并有有效邮箱。 |
| product_code | string | 必填 | 产品代码,最多 64 位;自动转大写,应与授权所属产品一致。 |
| plan | string | 必填 | 授权版本标识,最多 32 位;例如 basic / professional。 |
| activation_mode | string | 必填 | none / domain / wildcard_domain / ip / domain_ip / instance。 |
| max_activations | integer | 必填 | 绑定上限;绑定类模式至少为 1。泛域名按可注册主域名计数。 |
| starts_at / expires_at | RFC3339 string | 可选 | 开始时间默认当前时间;到期时间省略为永久,提供时必须晚于开始时间;应包含时区。 |
| reset_limit_per_period | integer | 可选 | 默认 3;-1 表示无限次,0 禁止重置,其余为 1–10000。 |
| allowed_domains | string[] | 可选 | 域名白名单,默认 [];可同时填 example.com 与 *.example.com 来允许主域名及子域名。 |
| entitlements | string[] | 可选 | 权益标识,单项最多 64 位字母、数字或 _ - .。 |
| metadata | JSON | 可选 | 后台附加数据;不要包含需要返回给客户端的秘密。 |
请求示例
先在终端设置 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 | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 401 | UNAUTHORIZED | 管理密钥缺失或错误。 |
| 404 | USER_NOT_FOUND / PRODUCT_NOT_FOUND | 检查用户、邮箱及产品是否有效启用。 |
| 400 | INVALID_ACTIVATION_MODE / INVALID_ACTIVATION_LIMIT | 检查绑定方式与数量。 |
| 400 | INVALID_RESET_LIMIT / INVALID_EXPIRY | 检查重置限制与时间范围。 |
GET/internal/v1/failover-snapshot备用节点同步签名快照
供独立子域名上的 Cloudflare Worker 自动同步使用。导出当前有效授权及已有绑定的完整签名快照,不包含明文授权码、客户身份、安装元数据或任何私钥。默认关闭,配置 FAILOVER_SYNC_KEY 后启用。
权限与副作用:请求头 X-Failover-Key 使用独立同步密钥,不能用普通授权码代替。仅允许可信 Worker 调用;响应禁止缓存且不得公开。
请求参数
| 字段 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| X-Failover-Key | header | 必填 | 源站 FAILOVER_SYNC_KEY,至少 32 位高强度随机串;与 Worker secret 保持一致。 |
| nonce | query 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 | 业务码 / 状态 | 处理方式 |
|---|---|---|
| 404 | FAILOVER_DISABLED | 源站未配置 FAILOVER_SYNC_KEY。 |
| 401 | UNAUTHORIZED | 独立同步密钥错误。 |
| 400 | INVALID_CLIENT_NONCE / SNAPSHOT_TOO_LARGE | 修正 nonce,或升级大规模复制方案。 |
| 500 | DATABASE_ERROR | 保留旧快照,但不得延长其原有效期。 |
绑定方式与泛域名规则
example.com、shop.example.com、x.shop.example.com 共用一条 example.com 绑定,合计占 1 个名额;不锁定出口 IP。客户端仍上报实际域名,不提交 *.example.com。
IP 按 IPv4、IPv6 分开保存;当前连接使用哪个协议族,就核对对应来源。domain_ip 运行时要求域名和 IP 同时符合。
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"
}- 先检查 HTTP 状态;业务错误可能位于 HTTP 200 的签名 data 中,例如
valid:false,必须继续检查。 - 对 signed_payload 做 Base64URL 无填充解码;对 signature 和固定的公钥做标准 Base64 解码,分别应为 64 和 32 字节。
- 使用 Ed25519 对解码后的原始载荷字节验签,不能把外层 data 重新 JSON 序列化后验签。
- 验签成功后再解析载荷,核对产品、请求域名、支持随机数的接口的 client_nonce,以及 checked_at / lease_expires_at 等时间;以载荷为准,不单独信任外层 data。
- 请求 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 位于顶层。不要把这些只读元数据响应当作授权凭据。