API 同时支持:
GET + MD5 签名推荐模式(v2):POST + HMAC-SHA256 + 时间戳/随机数 防重放
生产环境域名: https://commu.fun/api (示例)
注意: 为了方便演示,本文档以下示例均使用
https://commu.fun/api作为基础 URL。
| 字段名 | 说明 |
|---|---|
| code | 错误码,请求成功为 0 |
| msg | 返回的消息内容,一般为错误信息 |
| data | 接口返回数据 |
| encrypted_data | (可选) 加密后的数据,如果开启了加密模式 |
enc_ver=2:使用 AES-GCM 解密 encrypted_dataenc_ver:视为 legacy AES-CBC(仅用于兼容旧客户端)POST /api/v2/check(推荐)
GET /check
这个API 用于验证卡密状态、激活新卡密以及绑定机器码。
| 参数名 | 说明 |
|---|---|
| key | 卡密字符串 (例如: KEY-XXXX) |
| hwid | 机器码 (设备唯一标识) |
| instance_id | 软件实例ID (Software Instance ID) |
| sign | 签名 (算法见下文) |
v2 版本改为 JSON Body:
{"key":"KEY-XXXX","hwid":"HWID-...","instance_id":"12345678"}
并携带请求头:
X-Timestamp: 秒级时间戳X-Nonce: 随机字符串(一次性)X-Signature: HMAC-SHA256 签名{
"code": 0,
"msg": "success",
"data": {
"status": "valid",
"expire_date": "2026-12-31 23:59:59",
"hwid": "HWID-ABC-123",
"announcement": {
"title": "系统公告",
"content": "欢迎使用本系统!",
"time": "2026-01-01 12:00:00"
}
}
}
公告不需要单独调用接口获取:系统会在以下接口的成功响应中自动携带 announcement 字段(可能为 null):
/api/v2/check、/check/api/v2/unbind、/unbind/api/v2/info、/info/cloudvar/log进入后台管理面板 → 公告管理:
announcement.titleannouncement.content服务端会从公告表中取出最近一条启用公告作为下发内容:
is_active = true 且(software_id = 当前实例ID 或 software_id is null)created_at 倒序,取最新 1 条也就是说:如果你后创建了一条全局公告,它会覆盖旧的实例公告(因为更“新”)。
POST /api/v2/unbind(推荐)
GET /unbind
用于解绑卡密的机器码绑定。
| 参数名 | 说明 |
|---|---|
| key | 卡密字符串 |
| hwid | 当前绑定的机器码 |
| instance_id | 软件实例ID |
| sign | 签名 |
{
"code": 0,
"msg": "success",
"data": {
"status": "unbound_success"
}
}
当卡密已激活且新设备上的 hwid 与已绑定的不同:
unbind_limit,且尚未用尽:hwid)unbind_count 加 1code=6,msg="当前卡密已绑定,无可换绑次数"说明:
- 每张卡密仅允许同时绑定一个机器码;服务端使用行级锁保证并发下的唯一性。
- 如未设置 unbind_limit 或设置为负数,视为不限制;建议在后台为新卡密设置明确上限。
POST /api/v2/info(推荐)
GET /info
获取软件的最新版本号和更新内容。
| 参数名 | 说明 |
|---|---|
| instance_id | 软件实例ID |
| sign | 签名 |
{
"code": 0,
"msg": "success",
"data": {
"version": "1.0.2",
"update_content": "1. 修复了已知BUG\n2. 优化了性能"
}
}
GET /cloudvar
获取软件配置的云端变量(配置项)。
| 参数名 | 说明 |
|---|---|
| key | 变量名 (Key) |
| instance_id | 软件实例ID |
| sign | 签名 |
{
"code": 0,
"msg": "success",
"data": {
"value": "这里是云变量的值",
"announcement": null
}
}
GET /log
客户端上报自定义事件日志到后台。
| 参数名 | 说明 |
|---|---|
| message | 日志内容 (需 URL 编码) |
| instance_id | 软件实例ID |
| sign | 签名 |
{
"code": 0,
"msg": "success",
"data": {
"status": "logged",
"announcement": null
}
}
用于后台“卡密管理”页面对选中卡密进行批量停用/启用。该接口依赖后台登录的 Session Cookie,不用于客户端 SDK 对接。
POST /admin/card/command
停用卡密:
{"action":"deactivate","keys":["KEY-1","KEY-2"]}
启用卡密:
{"action":"activate","keys":["KEY-1","KEY-2"]}
{
"code": 0,
"msg": "success",
"data": {
"action": "deactivate",
"affected_count": 2,
"requested_count": 2,
"missing_count": 0,
"results": [
{"key":"KEY-1","ok":true,"before":"unused","after":"banned"},
{"key":"KEY-2","ok":true,"before":"unused","after":"banned"}
]
}
}
| code | 说明 |
|---|---|
| 0 | success |
| 1 | 验证失败(或“卡密不存在”,当启用详细错误时) |
| 2 | 卡密不属于该实例(启用详细错误时) |
| 3 | 机器码不匹配(主动解绑接口) |
| 4 | 卡密已过期 |
| 5 | 卡密已封禁 |
| 6 | 当前卡密已绑定,无可换绑次数(自动重绑失败,需要人工处理) |
separators=(',', ':'))body_sha256 = sha256(body_bytes)METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY_SHA256
其中 PATH 为请求路径(不含域名,不含 query),例如:/api/v2/check。
signature = hmac_sha256(secret_key, message)(十六进制小写)X-Timestamp/X-Nonce/X-Signature所有 legacy 接口都需要 sign 参数,计算方法如下:
sign 本身)按照参数名的 ASCII 码从小到大排序。key1=value1&key2=value2...Secret Key。示例:
假设参数为 a=1, b=2,密钥为 secret。
1. 排序并拼接: a=1&b=2
2. 加密钥: a=1&b=2secret
3. MD5: md5("a=1&b=2secret") -> sign
whisper_config.py 中的 API_BASE_URL/INSTANCE_ID/SECRET_KEY/ENCRYPT_KEY 替换为你的真实值模块 文件夹置于你的项目根目录或包路径下pip install pycryptodome certififrom 模块 import get_default_client
client = get_default_client(verify_ssl=True)
res = client.check("KEY-XXXX...", "HWID-...")
print(res.ok, res.message, res.payload)
从响应中读取公告(若存在):
if res.ok:
data = (res.payload or {}).get("data") or {}
anno = data.get("announcement") or None
if anno:
print("公告标题:", anno.get("title"))
print("公告内容:", anno.get("content"))
读取云变量与上报日志:
v = client.cloudvar("some_key")
print(v.ok, v.payload)
r = client.log("client started")
print(r.ok, r.payload)
.版本 2
.支持库 spec
.子程序 生成签名, 文本型
.参数 参数表, 文本型
.局部变量 待签文本, 文本型
.局部变量 通信密钥, 文本型
通信密钥 = “YOUR_SECRET_KEY”
待签文本 = 参数表 + 通信密钥
返回 (取数据摘要 (到字节集 (待签文本)))
import hashlib
def get_sign(params, secret_key):
sorted_keys = sorted([k for k in params.keys() if k != 'sign'])
param_str = "&".join([f"{k}={params[k]}" for k in sorted_keys])
raw = param_str + secret_key
return hashlib.md5(raw.encode('utf-8')).hexdigest()