① 验证接口
请求(JSON 或表单都支持,参数名兼容 card / key / card_key):
POST /api/card/verify
Content-Type: application/json
{"card":"你的卡密","device":"设备唯一ID"}
成功返回:
{"ok":true,"code":0,"msg":"验证成功","expire":1767225600,"expire_at":1767225600,
"card":"你的卡密","device":"设备ID","unbind_left":3,"unbind_used":0,
"max_unbind":3,"token":"签名令牌","server_time":1764600000}
失败返回:
{"ok":false,"code":404,"msg":"卡密不存在","expire":0}
{"ok":false,"code":410,"msg":"卡密已过期","expire":1764000000}
{"ok":false,"code":409,"msg":"该卡密已绑定其他设备,请先解绑(剩余解绑 2 次)","unbind_left":2}
{"ok":false,"code":403,"msg":"卡密已被禁用","expire":0}
公益模式开启时:
{"ok":true,"code":0,"msg":"公益模式·直接进入","public":true,"expire":0,"token":"..."}
② 解绑接口(必须输入密码)
POST /api/card/unbind
Content-Type: application/json
{"card":"你的卡密","device":"设备ID","password":"解绑密码"}
成功:{"ok":true,"code":0,"msg":"解绑成功","unbind_left":2,"unbind_used":1,"max_unbind":3}
密码错:{"ok":false,"code":401,"msg":"解绑密码错误"}
没次数:{"ok":false,"code":429,"msg":"该卡密解绑次数已用完(上限 3 次)","unbind_left":0}
密码规则:单卡设了独立密码就用独立密码,没设就用系统设置里的全局解绑密码。当前全局密码在「系统设置」里可改。
③ 心跳接口 · 到期强制退出登录
验证成功后,客户端每 60 秒调一次。返回 action:"logout" 就立刻清本地登录态、回到登录页;返回 action:"keep" 是网络/服务异常,不要踢人,继续重试。
POST /api/card/heartbeat
Content-Type: application/json
{"card":"你的卡密","device":"设备唯一ID"}
在线:{"ok":true,"code":0,"action":"ok","left":2592000,"expire":1767225600,"heartbeat":60,"server_time":1764600000}
到期:{"ok":false,"code":410,"action":"logout","expired":true,"msg":"卡密已到期,请重新获取卡密登录"}
被踢:{"ok":false,"code":401,"action":"logout","msg":"已被管理员强制下线,请重新登录"}
禁用:{"ok":false,"code":403,"action":"logout"}
换设备:{"ok":false,"code":409,"action":"logout"}
次数用尽:{"ok":false,"code":429,"action":"logout"}
服务异常:{"ok":false,"code":500,"action":"keep"}
安卓(Java)定时心跳,到期回登录页:
final Handler h = new Handler(Looper.getMainLooper());
Runnable beat = new Runnable() {
@Override public void run() {
new Thread(() -> {
boolean stop = false;
try {
JSONObject body = new JSONObject();
body.put("card", card);
body.put("device", device);
HttpURLConnection c = (HttpURLConnection) new URL(BASE + "/api/card/heartbeat").openConnection();
c.setRequestMethod("POST");
c.setRequestProperty("Content-Type", "application/json");
c.setDoOutput(true);
c.getOutputStream().write(body.toString().getBytes("UTF-8"));
String txt = new String(c.getInputStream().readAllBytes(), "UTF-8");
JSONObject r = new JSONObject(txt);
if ("logout".equals(r.optString("action"))) {
stop = true;
h.post(() -> {
Toast.makeText(ctx, r.optString("msg", "登录已失效"), Toast.LENGTH_LONG).show();
startActivity(new Intent(ctx, LoginActivity.class)); // 回登录页
// finishAffinity(); // 需要直接关掉 App 就用这句
});
}
} catch (Exception e) { /* 网络问题:不踢人,下一轮再试 */ }
if (!stop) h.postDelayed(this, 60000);
}).start();
}
};
h.post(beat);
易语言:
.局部变量 返回, 文本型
返回 = 到文本 (网页_访问 (“http://你的域名/api/card/heartbeat”, 1, “{\"card\":\"” + 卡密 + “\",\"device\":\"” + 设备ID + “\"}”, , , “Content-Type: application/json”))
.如果真 (寻找文本 (返回, “logout”, , 假) ≠ -1)
信息框 (“卡密已到期,请重新登录”, 0, , )
载入 (登录窗口, , 真)
销毁 ()
.如果真结束
后台「在线设备」页可以看到所有心跳会话、剩余时间,并能一键强制下线某台设备或全部设备。
④ 对接代码模板
Python
import requests
def verify(card, device):
r = requests.post("http://你的域名/api/card/verify",
json={"card": card, "device": device}, timeout=8).json()
if r.get("ok"):
if r.get("public"):
print("公益模式放行")
else:
print("到期时间", r["expire"], "剩余解绑", r["unbind_left"])
return True
print("失败:", r.get("msg"))
return False
def unbind(card, device, password):
r = requests.post("http://你的域名/api/card/unbind",
json={"card": card, "device": device, "password": password},
timeout=8).json()
return r.get("ok"), r.get("msg")
JavaScript / Node
const res = await fetch("http://你的域名/api/card/verify", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({card: card, device: deviceId})
});
const data = await res.json();
if (!data.ok) return alert(data.msg);
PHP
$ch = curl_init("http://你的域名/api/card/verify");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["card"=>$card, "device"=>$device], JSON_UNESCAPED_UNICODE),
]);
$res = json_decode(curl_exec($ch), true);
if (!$res["ok"]) exit($res["msg"]);
④ 解绑密码模板
完整模板随系统一起交付:解绑密码模板.md,含全局密码改法、单卡独立密码、校验流程、各语言示例、返回码对照表。
流程:
1) 用户在客户端点「解绑设备」
2) 弹出输入框 → 输入解绑密码
3) 客户端 POST /api/card/unbind {card, device, password}
4) 服务端先校验密码(单卡密码优先,否则全局密码)
5) 密码错误 → 401「解绑密码错误」,不消耗次数
6) 次数用完 → 429,提示联系管理员
7) 成功 → 清空绑定设备,解绑次数 -1,返回剩余次数