文档
将 SublimeKeys 接入你的应用所需的一切。无需账户即可阅读。
$SUBLIMEKEYS_API_KEY
下面的每个请求都用它作为你真实密钥的占位符。 登录 (免费,无需信用卡)获取属于你自己的密钥,并查看用你自己的产品填充后的这些示例。
快速上手 — 3 次调用
1. 注册你的产品 (只需在“产品”中做一次)
curl -X POST https://api.sublimearts.io/v1/products \
-H "Authorization: Bearer $SUBLIMEKEYS_API_KEY" \
-d '{"slug":"my-app","key_prefix":"MYAPP"}'2. 每次销售时签发一个密钥
curl -X POST https://api.sublimearts.io/v1/licenses \
-H "Authorization: Bearer $SUBLIMEKEYS_API_KEY" \
-d '{"product":"my-app","email":"buyer@mail.com","max_activations":3}'
→ {"key":"MYAPP-K3F7Q-9WZ2M-P8RT4-XN5CB", ...}| product | 必填——来自第 1 步的产品 slug |
| 可选——会显示在你控制台的许可证列表中 | |
| max_activations | 可选,默认值为 1——这一个密钥可以同时在多少个不同的 machine_id 上处于激活状态。请按照你自己的定价承诺来设置(例如“个人使用,3 台设备”套餐设为 3)。上限由你自己的 SublimeKeys 套餐决定,而非你终端客户的套餐——Free 为 5,Pro 为 25,Business 为 100(详见价格)。 |
| expires_at | 可选——ISO 日期;留空则为永久密钥 |
| notes | 可选——自由文本,仅供你自己参考 |
或者完全跳过这次调用——见下方 AUTOMATE。
3. 在你的应用内验证
# pip install sublimekeys
from sublimekeys import SublimeKeysClient
client = SublimeKeysClient(product_id="my-app")
result = client.activate(license_key=key) # first run
result = client.verify(license_key=key) # every launch after — offline-first,
# verifies a signed lease locally,
# zero network calls for up to 7 days
if result.valid:
unlock_full_version()// npm install sublimekeys
import { SublimeKeysClient } from "sublimekeys";
const client = new SublimeKeysClient("my-app");
const activated = await client.activate(key); // first run
const result = await client.verify(key); // every launch after — offline-first,
// verifies a signed lease locally,
// zero network calls for up to 7 days
if (result.valid) unlockFullVersion();不用 Python 或 Node.js,或者想自己调用原始 API? PyPI 上的 Python SDK · npm 上的 Node.js SDK 或直接使用下方的接口。
import requests
r = requests.post("https://api.sublimearts.io/activate", json={
"license_key": key,
"machine_id": machine_id,
"product_id": "my-app",
})
if r.json()["valid"]:
unlock_full_version()const r = await fetch("https://api.sublimearts.io/activate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ license_key, machine_id, product_id: "my-app" }),
});
const { valid } = await r.json();
if (valid) unlockFullVersion();curl -X POST https://api.sublimearts.io/activate \
-d '{"license_key":"...","machine_id":"...","product_id":"my-app"}'▸ 第一次接触?这些调用在你的应用中实际应该放在哪里
上面的示例只单独展示了 /activate ——在实际应用中,你会在不同时刻调用不同的接口:
应用启动
├─ 尚未保存许可证密钥(首次运行)
│ → 显示你的“输入许可证密钥”界面
│ → 用户提交后 → 调用一次 /activate → 将结果保存在本地
│
├─ 已保存许可证密钥(之后每次启动)
│ → 改为调用 /verify(而不是再次调用 /activate)——确认它
│ 在这台机器上依然有效
│
├─ API 无法访问(用户处于离线状态)
│ → /activate 和 /verify 都会返回一个已签名的“租约”(有效期 7 天)
│ → 在本地验证其签名,而不是盲目信任上一次的
│ 结果——Python 和 Node.js SDK 会自动完成这一步;其他
│ 语言可以自行验证 Ed25519 签名(公钥
│ 位于 GET /public-key)
│
└─ 用户卸载/退出登录(可选)
→ 调用 /deactivate,让名额释放给另一台机器在 Python 或 Node.js/Electron 中,官方 SDK(PyPI·npm)已经自动处理了这一切——包括对离线租约的真实加密验证。下方的原始版本跳过了这一步(离线时只是简单信任上一次保存的结果)——更简单,但没有经过加密校验。如果你不使用 Python/Node.js,或者想要完全掌控,可以使用它。
import requests, json, os
STATE_FILE = "license_state.json"
def check_license(machine_id):
saved = json.load(open(STATE_FILE)) if os.path.exists(STATE_FILE) else None
try:
if saved is None:
# first run — ask the user for their key once
key = prompt_for_license_key()
r = requests.post("https://api.sublimearts.io/activate", json={
"license_key": key, "machine_id": machine_id, "product_id": "my-app",
}, timeout=5)
else:
# every later launch — re-check the saved key, don't ask again
r = requests.post("https://api.sublimearts.io/verify", json={
"license_key": saved["license_key"], "machine_id": machine_id, "product_id": "my-app",
}, timeout=5)
result = r.json()
json.dump(result, open(STATE_FILE, "w"))
return result["valid"]
except requests.RequestException:
# offline — trust the last known result instead of locking the user out
return saved["valid"] if saved else False已经熟悉这个概念了?可以跳过这部分——上面的调用就是你所需要的全部。
官方 SDK
🐍 Python — sublimekeys
封装了整个 API,并为你处理离线验证——通过本地校验的签名租约完成 activate/verify/deactivate,因此大多数启动都不产生任何网络请求,试用检查也能挺过一段离线时间。还会安装一个 sublimekeys 命令行工具,方便你从终端测试集成。
pip install sublimekeys
⚡ Node.js / Electron — sublimekeys
同样具备离线能力的设计,零运行时依赖——Ed25519 验证使用 Node 内置的加密模块,因此不会有任何东西让 Electron 的 asar 打包机制出问题。
npm install sublimekeys
更多语言正在路线图中。与此同时,下面的每个接口都是一个普通的 REST 调用——任何能发起 HTTPS 请求的东西都可以调用它。
接口参考 — 公开(由你的应用调用)
POST /activate
请求体: { "license_key", "machine_id", "product_id" }
▸ 详情
首次运行——将一个许可证密钥绑定到某个 machine_id。在同一台机器上重复调用是安全的——不会消耗额外的激活名额。如果无法完成(例如在另一台机器上已达到激活上限),会返回 valid:false 并附带说明信息。成功时,响应中的 lease 字段还会返回一个签名租约(7 天离线信任窗口)——Python SDK 会在之后的启动中本地验证它,而不是每次都调用 API。
POST /verify
请求体: { "license_key", "machine_id", "product_id" }
▸ 详情
此后每次启动都会调用——确认该密钥在这台机器上依然有效。不会影响你的激活计数,因此可以随意频繁调用。每次成功调用还会刷新签名租约(参见 /activate)。
POST /deactivate
请求体: { "license_key", "machine_id", "product_id" }
▸ 详情
用户退出登录/卸载——释放名额给另一台机器使用。
POST /trial/start
请求体: { "machine_id", "product_id" }
▸ 详情
为某台机器获取或创建一个 7 天试用期。幂等操作——重新安装永远不会重置计时。
POST /trial/status
请求体: { "machine_id", "product_id" }
▸ 详情
只读的试用状态检查——绝不会启动一个新的试用。
这些接口不需要 Authorization 请求头——license_key 本身就是凭证。完整的账户级 API(创建/列出/吊销)位于 /v1/* 下,详见控制台。
⚡ AUTOMATE — 无需后端的密钥发放
完全跳过上面的第 2 步。将你支付服务商的 webhook 指向你产品的 URL,每笔销售就会自动签发一个密钥并通过邮件发送给你的买家。
- Stripe 控制台 → Developers → Webhooks → Add endpoint
- 粘贴你产品的 URL (登录并创建一个产品即可获取你的 URL)
- 选择事件: checkout.session.completed
- 保存。就这么简单——无需服务器,无需代码。
- 可选但推荐:Stripe 在此之后会显示一个 Signing secret——把它粘贴到“产品”页面的“Signature verification”中,这样我们就能拒绝任何并非真正来自 Stripe 的请求(而不只是拥有这个 URL 的任何人)。
🔔 也想在每次销售时收到通知邮件?在“产品”页面此区块旁打开“Notify me”。
从 GUMROAD、KEYGEN 或自建服务器迁移
将现有客户导出为一个只含 email 列的 CSV 文件,然后针对每个产品运行一次这段脚本。它会为每一行批量签发一个全新的、带有你品牌前缀的 SublimeKeys 许可证。
import csv, os, requests
API_KEY = os.environ["SUBLIMEKEYS_API_KEY"]
PRODUCT = "my-app"
with open("customers.csv") as f:
for row in csv.DictReader(f):
r = requests.post("https://api.sublimearts.io/v1/licenses",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"product": PRODUCT, "email": row["email"]})
print(row["email"], "->", r.json()["key"])这会签发全新的密钥——不会保留你原有的密钥字符串(出于设计考虑,我们不接受自定义密钥值)。可以将新密钥通过邮件发给客户,或者在过渡期内于应用中并行运行新旧两种校验,之后再淘汰旧系统。
套餐限制
FREE
1 个产品 · 100 个密钥 · 每个密钥 5 台设备
PRO
5 个产品 · 1,000 个密钥 · 每个密钥 25 台设备
BUSINESS
产品和密钥数量不限 · 每个密钥 100 台设备
达到限制永远不会破坏已有的密钥——它们会一直持续验证下去。你只是无法签发新密钥,直到升级为止。降级或取消同样也永远不会删除任何东西。