把授权接进你的软件
用约 15 分钟完成一个受授权保护的导出功能:取得配置、运行示例、接入自己的应用,再验证权限拒绝。前提是已安装对应语言的开发环境。
SDK 负责请求、设备识别、验签、凭证保存、缓存和心跳。你提供客户输入的授权码和业务函数,无需自己组装 HTTP 请求或保存 activation_id。
- 准备接入包控制台生成产品设置
- 生成导出文件检查授权与业务结果
- 接到软件启动、业务入口、退出
1. 在控制台准备接入包
打开 快速接入,填写软件名称,选择绑定设备或浮动席位,点击“创建产品,下一步”。系统会准备产品、策略、测试许可证、公钥和 1.0.0 版本。选择语言,下载并解压已配置的接入包。
包里有两个配置:sdk-demo.json 含测试授权码,留给你自己测试;product.json 只含产品设置和公钥,可以随软件分发。
首次接入建议选择绑定设备。默认示例有 export 功能,先不配置次数限制;已有产品需要在策略和许可证中允许同名功能。
2. 检查环境并导出一个文件
在解压根目录打开终端,保留 product.json、sdk-demo.json、start.ps1、start.sh 和 sdk 目录。选择语言和系统后执行下列命令;环境检查不发送激活请求。
成功标志:退出码为 0,出现下列输出,并生成 licensed-report.txt。去掉测试参数后再运行,SDK 会恢复本机凭证,绑定设备不会重复占用名额。
License OK: export is available
Export completed: licensed-report.txt缺少工具时先安装对应开发环境;C/C++ 还需 libcurl 开发文件。Windows MinGW 可传入 -CurlRoot。浏览器接受证书警告不会让 SDK 信任证书,测试服务器的证书也必须被运行环境信任。
3. 接到客户的软件里
在现有项目目录运行官网安装器。九种语言共用版本 0.10.0,安装器先校验 SHA-256,再安装依赖或解压构建包。仅将 product.json 加入应用资源。
使用已下载的接入包离线安装
将 SDK_ROOT 换成解压目录的绝对路径。
当前语言的可运行文件:。复制其业务入口到自己的项目,查看完整代码 →
| 应用中的位置 | 要做的事 |
|---|---|
| 启动或激活窗口 | 传入 product.json 和客户授权码。激活成功后再次启动传空字符串;界面应用在后台任务中完成首次联网激活。 |
| 导出按钮、快捷键、菜单或命令 | 把自己的导出函数传给 RunFeature。SDK 先检查权限,允许时才调用业务函数;保持同一个 client 存活。 |
| 应用退出 | 关闭 client,停止心跳;浮动席位归还,绑定设备登记保留。 |
4. 验证没有权限时无法导出
确认策略未包含 licentivo_denied_probe,然后运行以下命令。应返回非零退出码,且不创建 denied-report.txt;请使用尚不存在的输出文件检查。
5. 需要按次数收费时
将业务函数传给 RunMeteredFeature,并提供稳定的任务编号。SDK 自动预留次数、成功后确认、失败时取消,并记录待确认任务。查看计次接入和重试 →
正式安装包只分发所需 SDK 和 product.json。每位客户使用自己的授权码;不分发 sdk-demo.json、授权缓存、设备凭证或厂商管理 API Key。cache_path 留空时,SDK 自动选择当前用户的私有目录。
先分清这三个名字
| 名称 | 谁使用 | 做什么 |
|---|---|---|
许可证密钥 lv_lic_… | 购买软件的客户 | 在自己的软件里激活。换机临时码 lv_tmp_… 也填在同一位置。 |
| 产品 ID | SDK / 软件开发者 | 告诉服务器正在验证哪个产品,可随软件分发。 |
| 管理 API Key | 厂商自己的服务器 | 自动签发、续期和管理许可证。客户软件的激活不需要它。 |
心跳间隔与允许离线时长在 授权策略分别设置;许可证有效期从首次激活开始。功能次数、浮动席位和离线文件激活可以在基础接入完成后添加。
暂时不注册也可以打开 在线体验查看请求与返回。需要自动签发许可证时,继续读 管理 API Key。
客户端 SDK
将业务函数交给 SDK:普通功能用 RunFeature,计次功能用 RunMeteredFeature。请求、验签、缓存和心跳由 SDK 处理。
运行前准备
在控制台 快速接入下载已配置的接入包。演示使用私有的 sdk-demo.json;放进实际软件时使用 product.json,客户授权码作为启动参数传入。下方“下载 SDK”是通用源码包,尚未填写你的产品设置。
配置文件的每一项是什么?
{
"base_url": "https://www.licentivo.com",
"product_id": "PRODUCT_UUID",
"license_key": "",
"device_name": "Customer app",
"app_version": "1.0.0",
"trusted_keys": {
"SIGNING_KEY_UUID": "BASE64URL_PUBLIC_KEY"
},
"timeout_seconds": 10,
"allow_http": false
}base_url- 授权服务器地址,只填域名和端口,不加
/api/v1。本地测试可用https://127.0.0.1:8080。 product_id- 这个软件对应的产品 ID,必须与许可证所属产品一致。
license_key- product.json 中留空。用 OpenWithLicense 的参数传入客户自己的
lv_lic_…或换机的lv_tmp_…;不会修改共用配置文件。 app_version- 当前软件版本,格式为主版本.次版本.补丁,例如 1.0.0。开启版本范围或维护期后必须填写;改版本后要重新联网验证。
device_name- 控制台显示的设备名称,方便你识别设备;不用于决定设备身份。
trusted_keys- 产品的签名密钥 ID 和公钥。正式发布时随你的应用或经过认证的更新分发,不能直接信任陌生响应提供的公钥。
cache_path- 可省略或留空,自动使用当前用户的私有目录;也可指定应用自己的私有文件位置。
timeout_seconds- 单次请求超时秒数,默认 10,可设 1–120;临时网络错误最多尝试两次。
proxy_url- 可选代理地址。Java 使用 HTTP 代理;Node.js 设置代理时需要安装 undici。证书校验仍保持开启。
allow_http- 正式环境保持
false,使用 HTTPS。本地测试可设为true,但只允许 localhost 或本机回环地址。
不需要手填 device_id,SDK 会读取本机身份。product.json 中的产品公钥可以分发;客户授权码与 sdk-demo.json 要保存在私有位置。
选择你的语言
以下代码与下载包中的文件一致,会实际生成导出文件。示例以环境变量代替激活输入框;接入时换成自己的界面输入,并保留应用生命周期内的 client。
统一安装与版本发布
选择语言和系统,在自己的项目目录运行。SDK 由 Licentivo 官网发布,无需 GitHub 账号。GitHub 与公共包仓库发布仍暂缓。
版本: · 版本安装包 · SHA-256 校验文件 · 发布清单
Windows x64 和 Linux x64 已验收。macOS 与 ARM64 尚待实机验证;C/C++ 预编译包按平台和编译器区分。
安装之后如何引用?
| 语言 | 项目接入 |
|---|---|
| Go | import licentivo "github.com/spf86/licentivo-sdk" |
| Java | implementation(files('.licentivo-sdk/java/v0.10.0/licentivo-sdk-0.10.0.jar')) |
| C / C++ | find_package(Licentivo 0.10 REQUIRED) · target_link_libraries(MyApp PRIVATE Licentivo::C) / Licentivo::CPP |
| C# | using Licentivo; |
| Python | from licentivo import Client |
| Node.js | import {Client} from '@licentivo/sdk' |
| Rust | use licentivo::Client; |
| Ruby | require 'licentivo' |
Go、Rust 和 C/C++ 使用的 .licentivo-sdk 目录应保留在项目中。更新时指定新版本,不会覆盖 product.json 或客户授权缓存。
用 SDK 执行受保护的业务,退出时关闭 client。
正在加载代码…使用其他客户授权码,或运行完整协议演示
在终端中设置环境变量,再运行快速开始的脚本,不加测试参数。授权码不是管理 API Key。
运行成功会输出 License OK: export is available。将它放到正式应用时,不必让客户设置环境变量;由激活窗口取得输入;SDK 自动保存本机凭证。不要把客户授权码放进日志。
运行下载包里的完整演示
解压 ZIP,保留其中的目录结构,把 sdk-demo.json 放到解压后的根目录。选择运行系统,在这个目录打开终端,执行:
此处的完整协议演示用于进阶排查。首次接入先运行快速开始里的 start.ps1 或 start.sh;这些接入示例在日常退出时保留绑定设备登记。
这段管理代码如何运行?
先在你的服务器环境中设置 LICENTIVO_URL 和 LICENTIVO_API_KEY,分别填授权服务器地址和完整的管理 Key。示例会读取产品列表,并输出 HTTP 状态和返回内容。
返回 HTTP 200 且包含 data.items,说明请求成功。返回 401 时检查 Key 是否完整、过期或已被吊销。Key 的创建方法和权限见 管理 API Key。
运行一个真正的应用示例
示例包含激活输入、授权状态、导出文件和主动解绑。Java、Python、C# 有窗口,其余语言使用终端菜单。首次输入授权码,以后留空恢复。
导出成功会生成 licensed-report.txt。按次数导出前,请在策略里配置 export 的次数规则。
窗口和菜单演示展示了底层预留接口。新接入建议用下方源码中的 RunMeteredFeature,九种语言均会保存待确认记录;业务执行中途崩溃时,仍需从自己的业务结果核对是否完成。
如何显示授权状态?
Status 和 FeatureStatus 只读本地状态,不发送请求,也不等待后台网络验证。状态变化通知可以用来刷新界面。
| 状态 | 含义 |
|---|---|
active | 最近一次联网验证成功,本机授权有效。 |
offline_valid | 本机签名仍有效,启动后的联网确认尚未成功。 |
verification_required | 没有可用签名,需要激活或联网刷新。 |
| expired / revoked / released | 服务器明确拒绝授权,应停止受保护操作。 |
quota_exhausted | 这次功能计次超出额度,其他已授权功能仍可用。 |
allowed 表示本机授权是否有效;按次数的功能仍需请求额度。lease_valid_until 是签名缓存截止时间,不是许可证最终到期时间。code、request_id 和 retryable 分别说明错误、日志编号和能否重试。
各语言的方法名、通知回调和安装包用法见下载包的 sdk/README.md。官网提供版本安装包与统一安装器,公共包仓库发布仍暂缓。
接到实际应用时,放在哪些位置?
| 应用中的位置 | 要做的事 |
|---|---|
| 启动或用户输入授权码后 | 调用 OpenWithLicense(C++ 使用构造函数和 Start),SDK 完成激活并按策略开启心跳 |
| 使用付费功能前 | 调用 RunFeature,把业务函数交给 SDK;计次功能使用 RunMeteredFeature |
| 应用运行期间 | 保持 client 存活,SDK 按策略中的间隔发送心跳 |
| 应用退出时 | Close / Dispose / Destroy,停止心跳;浮动授权归还席位,绑定设备授权保留设备登记 |
| 用户主动解绑时 | 调用 Deactivate,再关闭 client |
表中的名称表示对应操作,不同语言的实际方法名以示例为准。心跳、离线缓存和联网刷新的详细行为见 心跳与离线设置。
管理 API Key
当你希望自己的订单系统自动签发许可证,或从服务器读取授权数据时,使用管理 API Key。它代表你的厂商工作空间。
客户端需要的是哪一种密钥?
| 凭证 | 交给谁 | 用来做什么 |
|---|---|---|
lv_api_…管理 API Key | 你自己的服务器 | 创建产品、策略和许可证,查看设备、用量和审计记录 |
lv_lic_…许可证密钥 | 客户的软件 | 激活、验证、发送心跳和释放当前设备 |
| 产品公钥 | 随客户端发布 | 检查服务器返回的授权签名是否有效 |
如果你只接入客户软件的授权验证,使用许可证密钥就够了。管理 API Key 可以操作整个工作空间,不能放进客户软件或公开网页。
创建一个 Key
- 用工作空间 Owner(所有者)账号登录,打开 API Key,点击“创建 API Key”。
- 名称填它的用途,例如“订单系统”。只查询时选“只读”;要创建或修改许可证时选“读写”。有效期可设为 1–365 天。
- 保存后立即复制完整 Key,它只显示一次。把它放在你服务器的私有配置或环境变量中。
发送第一个管理请求
示例读取产品列表。将 LICENTIVO_URL 设置为授权服务器地址,将 LICENTIVO_API_KEY 设置为刚创建的完整 Key,再执行:
curl "$LICENTIVO_URL/api/v1/products?limit=25" \
-H "Authorization: Bearer $LICENTIVO_API_KEY"这是 Bash / macOS / Linux 的写法。Windows PowerShell 请使用 curl.exe,环境变量用 $env:LICENTIVO_URL 和 $env:LICENTIVO_API_KEY。九种语言的完整请求代码也放在 SDK 页的“服务端管理 API Key”选项中。
成功时返回 HTTP 200,正文包含 data.items。使用 Bearer Key 的管理请求不需要登录 Cookie 或 CSRF Token。
权限和失效处理
只读 Key 能查看资源;读写 Key 还能创建和修改产品、策略、许可证。管理 Key 不用于账号、团队、系统设置或收款操作,这些操作需要相应账号登录。
Key 泄漏或不再使用时,在控制台吊销;需要更换时点击“轮换”。旧 Key 会立即失效,随后更新你服务器上的配置。
套餐中的 API 调用额度统计激活、验证、心跳和释放。这里的管理请求不计入这项额度。
产品与许可证 API
把下单后的签发工作接到你自己的服务器:先建立产品和策略,再为每位客户创建许可证。前两项通常只需配置一次。
先在控制台创建有写权限的 管理 API Key。以下示例使用 Authorization: Bearer 你的管理Key;这个 Key 保留在厂商服务器中。
产品响应的 data.id 用作 product_id;策略响应的 data.id 用作 policy_id。签发许可证后保存 data.id 和 data.key。完整许可证密钥只在签发时返回一次,列表里的 key_prefix 不能用来激活。
尚未激活的限期许可证,expires_at 和 first_activated_at 都是 null。首次成功激活后才开始计时;换机不会重新获得有效天数。
后续管理
| 方法与路径(省略 /api/v1) | 用途与请求正文 |
|---|---|
GET /products | 查产品列表。响应 data.items 是记录数组,data.total 是总数。 |
GET /licenses/{id} | 查询许可证详情和首次激活、到期时间。 |
POST /licenses/{id}/renew | 续期,例如 {"days":30}。成功后沿用原许可证。 |
POST /licenses/{id}/revoke | 吊销,请求正文为 {}。 |
GET /products/{id}/public-keys | 查看产品公钥,公开可读。SDK 分发时固定受信公钥。 |
GET /activations | 查看设备绑定及运行会话。 |
列表分页使用 limit=25&offset=0,limit 最大为 100。其他接口的参数和结构见 OpenAPI 文件;不同授权方式的接入步骤见左侧对应主题。
已有浏览器登录会话时,如何调用?
使用浏览器 Cookie 的写请求需要 X-CSRF-Token。先 GET /api/v1/auth/csrf,再把 data.csrf_token 放进这个请求头。Bearer Key 请求不需要 CSRF。
SDK 授权调用
选择你的语言和操作,直接调用 SDK。激活、设备识别、验签、缓存、心跳,以及计次的预留、确认和取消都由 SDK 处理。
业务示例中的 client 来自启动授权,path 是你的导出路径,exportReport / export_report 是你自己的业务函数。计次的 jobID 是由应用保存的稳定任务编号。
查看完整代码 → · 快速开始 · 查看计次接入和重试 →
权限允许时 SDK 才执行业务。普通 RunFeature 不扣次数;RunMeteredFeature 自动预留、成功后确认、失败时取消。确认失败后重试原任务,只补确认,不重复已完成的业务。
展开 HTTP 请求与响应(自定义客户端或排错时使用)
下面是 SDK 使用的协议,不需要在使用这九种 SDK 的应用中再次实现。
这八个接口用许可证密钥和设备身份认证。请求不需要登录 Cookie、CSRF 或厂商管理 Key。浮动授权退出时还要释放会话;绑定设备授权日常退出保留绑定。
激活、验证或心跳成功后,怎么判断能用?
- 确认 HTTP 200,读取
data.activation_id。后续验证、心跳、计次和解绑都要带上这个 ID。 - 用预先信任的产品公钥验证
data.lease的签名,再核对产品、设备、有效时间和功能。只把 payload 解码成 JSON,不代表授权有效。 - 使用 SDK 时前两步由 SDK 完成。RunFeature 检查权限后执行业务;计次功能由 RunMeteredFeature 管理预留、确认和取消。
SDK 会保存签名缓存并按策略发送心跳。调用失败时,不要仅因网络超时就抹掉有效缓存;如果收到吊销、释放、到期等明确拒绝,则按 SDK 的结果阻止受保护功能。
签名 payload 解码后,每个字段是什么意思?
下面只是为了读懂数据。自行实现验签时,使用收到的 payload 原始字节,不能把 JSON 重新排序或序列化后验签。
重试、心跳与计费
激活、解绑和功能计次必须填写 Idempotency-Key,最长 80 字符,不含空白。新操作生成新值;同一次网络重试保留原值和原正文。验证与心跳可不填,SDK 仍会自动处理自己的请求编号。
成功的激活、验证、心跳和解绑请求,每次消耗 1 次平台 API 调用额度。失败和相同幂等请求的重放不重复计数,本地 CheckFeature 不请求服务器。当前 Consume 只扣客户软件的功能次数,不增加这四类平台 API 计费统计;quantity 表示本次功能消耗次数。
心跳间隔、离线时长和永久离线分别按 授权策略控制。payload.expires_at 是当前签名缓存的截止时间;许可证的最终到期时间在许可证详情中查看,两者不能混用。
心跳与离线设置
这两个设置都在“授权策略”中,但解决的是两件事:有网时多久联系服务器,以及联系不上时还能使用多久。
心跳间隔:多久验证一次
在“心跳请求间隔(秒)”中填写 600,SDK 就会在运行期间大约每 10 分钟发送一次心跳。每次成功都会取得最新的运行规则,并刷新本机授权缓存。
可填写 600–86400 秒。填写 0 会关闭定时心跳,应用仍可以主动调用验证或刷新。定时请求会增加 0–10% 的随机延迟,实际间隔不会低于 600 秒。
绑定设备且缓存有效时,启动会先恢复授权,再在后台联网刷新,关闭定时心跳时也会刷新。不允许离线或使用浮动席位时,启动必须等服务器确认。
离线时间:服务器联系不上时能用多久
“允许离线方式”有三种选择。它不会替你修改心跳间隔。
- 允许指定时长
- 例如填写 24 小时:从最近一次成功联网验证开始,签名缓存最多有效 24 小时。期间断网或服务器临时故障,软件可以继续用;超过这个时间后,需要成功联网验证。
- 不允许离线回退
- 启动必须成功联系服务器,请求失败时不会靠磁盘缓存放行。运行中取得的短期签名最多有效
max(10, 心跳间隔)秒;应用需要持续验证并检查功能权限。 - 允许永久离线
- 已经取得的本机签名可以长期使用,网络失败时仍能检查权限。如果许可证本身只有 30 天,仍会在 30 天到期;永久离线不会把期限授权变成永久授权。
接口用 offline_mode 表示这三种选择,依次为 limited、none、permanent。指定时长时,offline_seconds 为 1–31536000 秒;另外两种方式填 0。
这几个组合会怎样运行?
| 心跳 / 离线设置 | 实际行为 |
|---|---|
| 10 分钟 / 24 小时 | 有网时每 10 分钟验证。断网后按最后一次成功验证的时间,最多继续使用 24 小时 |
| 10 分钟 / 永久离线 | 仍然每 10 分钟尝试心跳,成功时更新规则;联系不上时继续使用有效签名 |
| 0 / 永久离线 | 有有效缓存时,启动可直接使用,不定时发送请求。应用主动验证或刷新时仍会联系服务器 |
| 0 / 24 小时 | 不定时心跳,但缓存仍会到期;应用要自行安排验证,不能靠关闭心跳延长离线时间 |
建议离线时间长于心跳间隔。如果设成“每小时心跳,但只允许离线 1 分钟”,签名会在下一次定时心跳前过期,应用必须另行验证。
修改策略后,旧许可证会更新吗?
会。修改心跳或离线设置后,关联的旧许可证和新许可证都在下一次成功激活、验证或心跳时采用新规则,SDK 用新签名替换缓存。相同 Idempotency-Key 的重试仍会返回原来那次请求的结果。
持续断网的设备暂时不知道规则变了,仍按已有签名使用。仅仅接上网络也不会自动改写缓存,要有一次成功的请求。应用可以在网络恢复时主动刷新。
许可证的有效天数、设备上限和功能权益保存在各份许可证中,修改策略不会自动重写这些权益。要改某份许可证的权益,在许可证页面操作。
主动联网刷新
下面的方法始终尝试联系服务器,包括“永久离线 + 关闭心跳”的情况。成功后更新签名;网络失败会报刷新失败,同时保留仍允许使用的旧缓存;明确被吊销或释放时清除缓存。
| 语言 | 调用方式 |
|---|---|
| Go | client.RefreshOnline(ctx) |
| Java | client.refreshOnline() |
| C | ln_refresh_online(client) |
| C++ | client.RefreshOnline() |
| C# | await client.RefreshOnline() |
| Python | client.refresh_online() |
| JavaScript | await client.refreshOnline() |
刷新成功计一次激活 API 请求。如果刚更新的策略开启了原来关闭的心跳,再调用对应语言的 StartHeartbeat 启动调度。持续离线时,设备也无法收到吊销或释放通知。
设备绑定
一份许可证设置为单设备后,客户把软件和缓存复制到另一台普通电脑,不能直接继续使用原电脑的授权。
SDK 怎么知道是哪一台电脑?
每次启动时,SDK 读取操作系统的机器身份,结合产品 ID 计算设备哈希。服务器签名中包含这个哈希,本地检查也会核对它。
Windows 读取 MachineGuid,Linux 读取 machine-id,macOS 读取 IOPlatformUUID。原始机器身份不会上传,只发送计算出的哈希;配置文件中的旧 device_id 不会覆盖实际读取的本机身份。
把机器 A 的文件复制到 B 会怎样?
- A 激活后,拿到的签名绑定 A 的设备哈希。
- B 读取自己的系统身份,得到不同的哈希,因此拒绝 A 的缓存。
- B 必须联网重新激活。如果设备上限为 1,且 A 还没释放,服务器会拒绝增加 B。
客户要换电脑,怎么处理?
让客户在旧电脑上主动解绑,再在新电脑激活。如果旧电脑损坏,你也可以在控制台的“设备激活”页面释放旧绑定。换电脑不会重置许可证的到期时间。
操作系统重装后机器身份可能变化,需要按新设备处理。完整克隆操作系统、伪造系统身份或修改客户端程序属于更强的攻击,单靠这个设备哈希不能保证防住。
允许长期离线时,A 已持有的旧签名无法马上得知它被释放了;它要再次成功联系服务器,才能更新这个状态。离线时间越长,远程限制越难立即生效。
跨语言设备哈希算法
SHA256(UTF8(
"LicenovaDevice/v2\n"
+ lower(product_id) + "\n"
+ lower(trim(OS_machine_identity))
))额度与计费
设备额度统计用了多少台设备,API 额度统计向服务器成功请求了多少次。两者分别计算,不能用设备数推算 API 次数。
哪些操作会用掉一次 API 额度?
| 操作 | 是否计次数 |
|---|---|
| 成功激活、验证、心跳或释放 | 每次成功各计 1 次;同一天的多次心跳分别计算 |
| 失败请求,例如授权码错误、额度不足 | 不计 |
| 使用同一 Idempotency-Key 重试并返回原结果 | 不重复计 |
| 本地 CheckFeature 或检查签名缓存 | 不计,没有向服务器发请求 |
| 通过管理 API Key 查询或签发许可证 | 不计入这项运行 API 额度 |
一个设备很多次请求,设备额度会重复扣吗?
不会。同一计费周期内,同一产品的同一台设备只计一次设备用量,但它的每次成功心跳都计 API 用量。释放设备不会减掉已经产生的用量。
例如 100 台设备,每天运行 8 小时,每月 22 天,每 10 分钟心跳一次,仅心跳就有 105,600 次。再加上 100 次激活和每天每台一次额外验证,总共是 107,900 次。
这些额外验证是例子里的假设,实际次数取决于你的程序。可在 在线用量估算中修改设备数、运行时间和请求间隔。
超过套餐额度以后怎么收钱?
以“包含 10 万次 API,超出每次 0.0001 USD”为例:周期内成功调用了 12 万次,超出 2 万次,API 超额费是 2 USD。
允许超额的套餐按管理员设置的单价,从余额扣除。系统把累计金额四舍五入到分,只扣新增的差额,因此很小的单次价格不会因每次舍入而被重复收费。
设备超额费另算:超额设备数 × 设备单价。某一项设成硬上限时,用完后拒绝该项超额请求;需要扣款但余额不足时,也会拒绝下一次请求。该失败请求不增加用量。
API 标为“不限次”时不产生 API 超额费。额度为 0 则没有免费调用,从首次成功请求开始按超额规则处理。具体包含额度、单价和限制请看 当前套餐,这里的数字只是计算示例。
套餐期限和额度从哪天算?
可以购买 1、3、6 或 12 个月。付款成功后立即生效,一次支付所选期限的总价。
设备与 API 额度按月重新计算,从套餐生效时刻开始。1 月 31 日生效,下一次重置在 2 月最后一天,再下一次在 3 月 31 日;未用完的额度不累积。
没有购买套餐时,使用管理员设置的默认规则,按 UTC 自然月统计。默认设备费用沿用月结账单;默认 API 超额费即时扣余额。购买后使用购买周期的额度,不与默认额度叠加。
运行请求因额度或余额不足被拒绝后,已有的离线签名仍按它原来的规则有效。厂商可以在控制台释放设备,不需要占用客户的运行 API 额度。
浮动席位
浮动授权限制同时运行的程序数量。它适合多人轮流使用同一款软件,不要求为每台电脑单独买授权。
一个例子
客户有 100 台电脑,你签发 10 个浮动席位。前 10 个程序可以启动,第 11 个收到“席位已满”。其中一个正常退出并关闭 SDK 后,其他程序就能取得这个席位。同一台电脑启动两个程序,也占两个席位。
在策略里设置
选择“浮动席位”,把设备 / 席位上限设为 10。心跳间隔最低 600 秒;席位租约必须比心跳间隔长,例如 1200 秒。每次心跳续租,崩溃或断网后,租约到期会回收席位。
浮动授权可以在租约内短暂断网。离线使用时间会同时受到席位租约约束,不能允许永久离线,也不能用文件完成完全离线首次激活。
代码需要额外处理什么?
使用 Open / Start 和正常关闭方法即可。SDK 为每个 client 生成独立会话 ID;不要为一次导出操作创建一个新 client。把一个 client 保留到整个程序退出。
客户端的旧浮动缓存不会在下一次进程启动时自动恢复席位。租约已经过期时,先重新激活,取得席位后再恢复业务。
离线文件激活
目标电脑完全不能联网时,用申请文件和签名响应完成首次激活。申请文件可以通过 U 盘带到另一台联网电脑。
操作步骤
- 在目标电脑加载 SDK 配置,调用 OfflineRequest("activate"),保存申请文件。生成申请不需要访问服务器。
- 在联网电脑打开控制台的“离线文件激活”,上传申请并下载响应。终端客户也可以在自己的客户门户中办理。
- 把响应带回目标电脑,调用 ImportOffline,传入原申请和响应。SDK 检查签名、申请编号、版本以及本机身份,通过后保存缓存。
- 用 CheckFeature 检查授权。离线电脑无需启动在线心跳;允许离线多久,由授权策略决定。
文件激活只支持允许离线的绑定设备授权。申请有效期 30 天。限时许可证从服务器批准申请时开始计时,因为服务器无法知道响应文件什么时候被导入。
如何离线解绑?
在原电脑生成 OfflineRequest("deactivate")。SDK 会先删除本地缓存,再把申请交给厂商或客户门户办理释放。删除原电脑缓存不能证明没有其他历史副本;需要立即阻止旧授权时,应使用需要定期联网的策略。
各语言的名字
| 语言 | 生成申请 | 导入响应 |
|---|---|---|
| Go | OfflineRequest("activate") → []byte | ImportOffline(requestBytes, responseBytes) |
| Java | offlineRequest("activate") → JSON 文本 | importOffline(requestText, responseText) |
| JavaScript | offlineRequest("activate") → object | importOffline(requestObject, responseObject) |
| C | ln_offline_request(client, "activate") | ln_import_offline(client, requestText, responseText) |
| C++ / C# | OfflineRequest("activate") → JSON 文本 | ImportOffline(C++ 传文本,C# 传字节) |
| Python | offline_request("activate") → dict | import_offline(requestDict, responseDict) |
C 返回的文本由调用方使用 ln_free_string 释放。响应文件传入的是文件本身的内容,不是网页 API 外层的 data 包装。
功能次数
“允许导出”与“每月允许导出 500 次”是两条不同的规则。RunFeature 只检查权限;RunMeteredFeature 在业务成功后确认次数。
设置每月 500 次导出
在策略的功能列表中添加 export,再增加一条功能额度:功能 export、额度 500、周期“每月”。默认用完就拒绝。允许额外使用时,填写最多额外次数;系统会记录超出量,供你的订单系统处理。
每天和每月额度按 UTC 零点重置;累计额度不重置。修改额度不会删除已经使用的次数。
导出成功后,才确认扣次
把自己的业务函数传给对应 SDK 方法。新任务使用新编号,同一任务重试使用原编号、功能和数量;不同进程不要同时执行同一编号。
| 语言 | 计次业务调用 |
|---|---|
| Go | client.RunMeteredFeature(ctx, "export", 1, jobID, exportReport) |
| Java | client.runMeteredFeature("export", 1, jobID, this::exportReport) |
| Node.js | await client.runMeteredFeature("export", 1, jobID, exportReport) |
| Python | client.run_metered_feature("export", 1, job_id, export_report) |
| C# | await client.RunMeteredFeature("export", 1, jobID, ExportReport) |
| C++ | client.RunMeteredFeature("export", 1, jobID, exportReport) |
| Rust | client.run_metered_feature("export", 1, &job_id, || export_report())? |
| Ruby | client.run_metered_feature("export", 1, job_id) { export_report } |
| C | ln_run_metered_feature(client, "export", 1, job_id, export_report, context) |
先设置 export 的功能额度,再用快速开始的脚本加上 -Metered -OperationID export-job-001(Windows),或 --metered --operation-id export-job-001(Linux / macOS)。
业务成功而确认失败时,再次调用同一任务只重试确认,不重复执行业务。若程序在业务执行中途崩溃,SDK 会停止自动重做;核对自己的持久业务结果后,用 ResolveMeteredFeature 确认或取消。远程扣次和本地业务不构成同一个事务。
需要自己控制预留流程时
- 为这次导出生成并保存一个业务编号。
- Reserve 预留 1 次额度,返回 pending 后再导出。返回 committed 表示已完成过,不能再次执行。
- 导出成功并保存结果后,Commit 确认扣次。
- 导出失败且没有完成业务时,Cancel 取消预留,不扣次数。
预留默认有效 15 分钟,遇到 UTC 周期重置会提前结束。直接调用 API 可用 reservation_seconds 设置 30–3600 秒。
| 语言 | 预留 | 确认 / 取消 |
|---|---|---|
| Go | Reserve(ctx, "export", 1, jobID) | Commit / Cancel(ctx, hold.ID, jobID) |
| Java / Node.js | reserve("export", 1, jobID) | commit / cancel(id, jobID) |
| C | ln_reserve(client, "export", 1, jobID) | ln_commit / ln_cancel(client, id, jobID) |
| C++ / C# | Reserve("export", 1, jobID) | Commit / Cancel(id, jobID) |
| Python | reserve("export", 1, jobID) | commit / cancel(id, jobID) |
返回字段怎么读?
| 字段 | 含义 |
|---|---|
| reservation_id / operation_id | 预留编号与原业务编号,重试继续使用原值。 |
| status / expires_at | 预留状态和截止时间;pending 待完成,committed 已扣次,canceled 已取消,expired 已过期。 |
| consumption.used / reserved | 本周期已确认次数 / 所有有效任务正在预留的次数。 |
| consumption.limit / remaining | 基础额度 / 可用基础次数。remaining = max(0, limit - used - reserved),不包含允许超出的部分。 |
| consumption.quantity / overage | 本任务次数 / 已确认的基础额度超出量;不会替厂商向客户收款。 |
| consumption.period / reset_at | UTC 用量周期 / 下次重置时间;终身额度的 reset_at 为 null。 |
发送什么、返回什么和每个字段的类型,见授权验证接口中的预留、确认和取消。
业务已成功时,不取消预留,也不重新导出。保存原编号和结果,继续调用 Commit;若预留已过期,保留任务供人工核对。远程扣次无法与你本地业务组成同一个事务。
Consume 仍可用于直接记账,但会立即扣次。需要避免业务失败仍扣次时,请用预留流程。所有计次接口都需要联网,不计入平台四类 API 调用额度。
版本与维护期
买断软件可以永久使用,但不一定永久免费升级。维护期用来决定客户可以使用哪些发布时间的版本。
买断当前版本,免费更新一年
策略设为永久授权,维护天数设为 365。客户首次激活时开始计算维护期。打开“软件版本”发布 1.0.0、1.1.0 等版本,填写实际发布时间。
维护期内发布的版本可以继续使用;维护期结束后发布的新版本会被拒绝,旧版本仍可使用。客户续维护后,再到许可证“权益与客户”中延长维护期。
软件如何传自己的版本?
在 sdk-demo.json 中填写 app_version,例如 1.1.0。使用维护期时,这个版本必须先在控制台发布。版本格式暂时只接受三个数字,例如 1.2.3。版本记录不能改发布时间,避免改日期改变已经售出的权益。
只想限制 1.x 版本时,也可以设置最小 1.0.0 和最大 1.999.999,不启用维护期。软件更新后重新联网取得新版本的签名授权,旧版本缓存不能直接用于新版本。
提供下载
版本记录可以附上 HTTPS 下载地址和 SHA-256 校验值。客户门户只展示这份授权有资格使用的版本。当前记录提供下载链接,不代你上传安装包,也不会自动给客户安装更新。
客户门户
客户可以自己查授权、查看设备和解绑,不用进入厂商控制台。
厂商先关联客户邮箱
签发许可证时填写客户邮箱,或打开许可证的“权益与客户”补填。把 客户门户链接发给客户。客户输入这个邮箱,邮件中会收到一次性登录链接,15 分钟内有效;登录状态保留 24 小时。
客户只看到关联到这个邮箱的许可证,不会看到你的产品管理、账单或其他客户的数据。邮件服务需要先在管理员设置中接好。
客户换了电脑,但找不到原来的密钥,怎么办?
- 客户输入许可证关联的邮箱,打开邮件里的登录链接。链接只负责登录,不会直接解绑。
- 在“我的设备 / 会话”找到旧电脑,点击“解绑并换机”,核对设备后确认。
- 页面显示临时激活码,有效期最长 15 分钟,只能成功激活一次。客户可以复制,也可以勾选发送到自己的邮箱。
- 在新电脑打开你的软件,在激活界面输入临时码。SDK 会识别新电脑,绑定原许可证并保存设备凭证。此后重启和心跳使用设备凭证,临时码到期不影响已激活的新电脑。
这一步更换的是设备绑定。许可证编号、原到期时间、功能、客户归属和已用次数都保留;不会另外签发一份许可证。新设备必须符合原授权策略,也必须有空余设备名额。
软件厂商需要改哪里?
升级到当前 SDK。原来接受许可证密钥的输入框,也可以接受 lv_tmp_ 开头的临时码;传入配置的 license_key 即可,后面的 Start、验证和心跳接口照常使用。SDK 会在私有缓存目录保存设备凭证文件;cache_path 可留空。这个文件要保存在当前用户的应用数据目录,不要跟安装包分发。
成功兑换后,软件可以把 license_key 留空;保持同一产品、公钥和 cache_path,重启时 SDK 会恢复设备凭证。客户不用保存或反复输入临时码。兑换尚未成功时,软件应保留输入,以便断线重试。
激活码过期,或关闭了这个页面
再次登录门户,可以看到尚未使用的临时码。未使用的码过期后,在对应的已释放设备旁点击“查看 / 获取激活码”重新取得;这不会再扣一次换机次数。已经成功使用的码无法再次兑换,也不能从旧设备记录反复领取来绕过次数限制。再次换机请解绑当前的新设备。
新电脑完全离线时
在新电脑的软件里填入临时码,用 SDK 导出激活申请。在临时码过期前,把文件带到联网电脑并通过门户生成响应,再带回新电脑导入。策略必须允许设备绑定模式的离线激活;期限仍按原许可证计算。响应文件包含这台机器的授权与凭证,需要妥善保管。
解绑次数和旧电脑的状态
自助释放次数可以在授权策略中设置,按每份许可证每个 UTC 月计数;设置为 0 表示关闭新的门户自助解绑。重复点击、查看已有码、过期后重发未用码不会多扣次数。此限制覆盖客户门户及客户离线释放申请,厂商手工释放和软件直接使用原许可证密钥调用释放接口不受它限制。
远程解绑会阻止旧设备下一次联网验证;旧电脑上已有的离线缓存仍需等联网或到期才会失效。永久离线缓存无法立即远程停用,需要及时撤销授权的产品应选择定期联网策略。正常复制配置、凭证和缓存到另一台电脑,SDK 会因机器身份不同拒绝使用。
客户门户管理授权使用,厂商的订单和软件销售收款仍由厂商自己的系统完成。
授权事件通知
当许可证激活、续期或被吊销时,Licentivo 可以向你的服务器发一条通知,用来同步订单、客户和售后系统。
设置接收地址
打开“授权事件通知”,添加你服务器的 HTTPS 地址,选择需要的事件。保存时会显示一次签名密钥,把它保存在接收通知的服务器上。正式环境不允许请求本机或内网地址。
接收后检查签名
读取 X-Licentivo-Timestamp、X-Licentivo-Event 和原始请求正文。拼接为“时间戳 + 点号 + 事件 ID + 点号 + 正文”,用签名密钥计算 HMAC-SHA256。加上 sha256= 前缀后,与 X-Licentivo-Signature 做恒定时间比较。时间戳与当前时间相差超过 5 分钟时拒绝。
事件 ID 是去重依据。先检查签名,再把事件放入自己的事务或队列,持久化成功后返回 2xx。重复事件返回 2xx 即可,不重复执行订单操作。
失败会怎样?
系统最多自动尝试 8 次,逐步延长重试间隔。控制台能查看每次 HTTP 状态、尝试时间和错误,也可以手动重新投递。事件与授权操作在同一个数据库事务里记录,服务重启后会继续投递。
支持创建、更新、续期、吊销、激活、释放、功能消耗、即将到期、到期和维护期变化事件。通知只带授权前缀,不包含完整许可证密钥。
批量与策略变更
同一批客户需要相同授权时,可以一次签发、续期或吊销。需要改变旧许可证权益时,先预览影响。
批量签发或导入
打开“许可证”页面,点击“批量签发 / 导入”,选择产品和策略。每行填写客户名称、邮箱,或者上传带 customer,customer_email 表头的 CSV。每批最多 100 条。成功后下载签发结果,保存完整授权码。
一个批次中的任意一条失败,整批都不会生效。网络超时后保持当前内容重试;系统按这次操作的请求编号返回原结果,不会再次签发。
批量续期或吊销
在许可证列表左侧勾选记录,再点击“批量续期”或“批量吊销”。表头的复选框选择当前页,翻页后已选记录会保留,每批最多 100 份。更换筛选条件、离开许可证页面或刷新页面会清空选择。
续期时填写增加的天数;尚未激活的许可证增加有效天数,已激活的延长原到期时间,已经过期的从当前时间延长。吊销前请展开所选记录核对客户,吊销无法恢复。只读成员不能执行这些操作。
把新策略应用到已有许可证
- 在“授权策略”中保存新规则。
- 在“许可证”页面勾选同一产品的旧许可证,点击“应用策略”并指定新策略。
- 检查有效期、设备或席位数、功能、次数和维护期的变化。已经占用的席位多于新上限,或切换绑定方式时还有活跃设备,系统会要求先处理设备。
- 确认后应用。客户端下一次联网验证取得新的签名授权;当前完全离线缓存不会被远程修改。
预览在 10 分钟内有效。预览之后许可证或策略有变化,需要重新预览。应用新有效天数会按原首次激活时间重新计算到期日,请先检查预览中的日期。
常见问题
先看返回的错误码,再核对对应设置。响应里的 request_id 可以帮助你从服务器日志中找到这次操作,不要把完整密钥贴进日志或截图。
激活失败,先检查什么?
检查服务器地址能否访问、产品 ID 是否正确、授权码是否完整,以及许可证是否属于这个产品。如果服务器还没部署,客户电脑不能用你的 127.0.0.1 地址连接,它表示客户自己的电脑。
| 错误码 | 先做什么 |
|---|---|
LICENSE_INVALID | 检查产品 ID、完整许可证密钥和所属产品 |
LICENSE_EXPIRED | 查看许可证到期时间,需要继续使用时由厂商续期 |
LICENSE_REVOKEDDEVICE_RELEASED | 授权已被吊销或设备已释放;客户端会清除旧缓存 |
DEVICE_LIMIT_REACHED | 许可证的设备名额已占满;释放旧设备,或修改该许可证的设备上限 |
API_QUOTA_EXCEEDED | API 硬额度已用完;查看当前周期和套餐限制 |
API_BALANCE_INSUFFICIENT | API 超额余额不足;检查对应环境和币种的余额 |
MONTHLY_QUOTA_EXCEEDED | 设备硬额度已达到;它与许可证的设备上限是两项不同限制 |
IDEMPOTENCY_CONFLICT | 同一 Key 被用于不同正文;新操作换新 Key,重试保留原正文 |
IDEMPOTENCY_EXPIRED | 旧响应或设备绑定已失效;检查当前状态后,为新的操作使用新 Key |
断网后还能用,是否没有验证?
允许离线时,SDK 仍会在本机检查服务器签名、产品、设备、功能和到期时间。它只是没有联系服务器。签名缓存过期,或已联网收到吊销、释放等明确拒绝后,就不能继续使用。
为什么验签或设备检查不通过?
公钥不匹配、使用了其他产品的缓存、把缓存复制到另一台电脑,都可能导致失败。核对 trusted_keys 的密钥 ID 和公钥是否来自当前产品,再检查是否更换或重装了系统。
SDK 报 clock rollback 时,检查系统时间是否被调回。修正时间后,尝试主动联网刷新授权。
完整的错误响应
{
"error": {
"code": "LICENSE_REVOKED",
"message": "License was revoked"
},
"request_id": "99999999-9999-4999-8999-999999999999"
}| 字段 | 含义与处理 |
|---|---|
error.code · string | 稳定的错误标识,程序按它判断如何处理,不依赖 message 的文字。 |
error.message · string | 说明失败原因的文字,适合诊断或转换为面向客户的提示。 |
request_id · UUID | 服务器为这次 HTTP 请求生成的编号,供查日志。它不是许可证 ID,也不是 Idempotency-Key。 |
400 / 415 先修正字段或 Content-Type;401 检查管理 Key;403 按具体错误码处理授权;409 检查额度、会话或幂等冲突;429 按 Retry-After 等待。超时或 5xx 可以有间隔地重试,写操作必须沿用原幂等编号和正文,避免重复计次。
请求返回的 JSON 怎么看?
成功返回 data 和 request_id;失败返回 error.code、error.message 和 request_id。保留错误码和 request_id 就能定位多数问题。
需要检查邮件、支付或部署设置时,继续看 支持与排查。
接口的全部字段可在 OpenAPI 文件中查看。