哨 哨卡网络验证 · 卡密发行
开发者文档

把卡密验证接进你的程序

一个 HTTP GET 就能跑通:带上 AppID、卡密、机器码,拿回到期时间、剩余次数、最新版本。下面按「先跑通、再加固」的顺序写,示例可直接抄。

01接入准备

只需要三样东西:接口地址、AppID、AppSecret。前两个可以放进客户端;AppSecret 只用来在服务端或你的签名逻辑里算 sign,不要打包进分发给最终用户的程序。

接口地址
https://key.dlam.cc/api/verify.php
AppID
后台「应用管理」里复制
AppSecret
同一页,可随时重置
GEThttps://key.dlam.cc/api/verify.php

GET 带查询参数,或 POST 表单 / JSON 都行,参数名完全一致。会员门户在 https://key.dlam.cc/user/,与接口读的是同一套数据,会员在门户里看到的到期时间与客户端里必然一致。

应用可以在后台关掉「开启验证」:关掉后请求会直接放行并返回 ok=true,方便你先接流程、后发码。

02请求与返回约定

公共参数(不是每个 act 都要,按需取用):

字段说明
appid必填。应用编号,后台「应用管理」可见。缺失或写错会直接拒绝。
act要做的操作,默认 verify。可选 verify / renew / unbind / register / login / info / logout。
code卡密原文。用户手输,或从本地配置读出的那张卡。
machine机器码。应用开启「绑定机器码」时必传;建议用 CPU + 主板 + 磁盘序列号做哈希,重装系统尽量不变。
ver客户端当前版本号,用来和后台发布版本比对,决定是否推送更新。
username账号名。走账号体系时可用它代替 code 验证。
sign / ts可选签名与时间戳,见 签名校验。

返回永远是同一个信封,先判 code 再取 data:

字段说明
code200 成功、400 失败。与 HTTP 状态码无关,请以此判断成败。
ok布尔值,等价于 code==200,脚本里直接取更省事。
msg一句话结论;失败时就是给用户看的提示,可原样展示。
data业务数据对象,失败时为空对象。
别把 HTTP 200 当成授权通过,也别把结果缓存超过一天——后台封卡、改到期日会失效。

03验证授权 · act=verify

主接口。程序启动时调一次,运行中按周期复验。卡密与账号二选一:只传 code 走纯卡密模式;传 username 会自动取该账号名下最晚到期、仍在有效期内的一张卡。校验通过才会扣量,被拒的请求不动卡密余额。

最简一次验证
curl "https://key.dlam.cc/api/verify.php?appid=你的AppID&act=verify&code=卡密&machine=A1B2C3D4E5F6&ver=1.0.0"
成功返回
{
  "code": 200,
  "ok": true,
  "msg": "验证通过",
  "data": {
    "auth": {
      "code": "SHAO3M9SLQG388RY",
      "type": "会员",
      "days": 30,
      "times_left": 0,
      "points": 0,
      "expire_at": "2026-11-11 06:25:03",
      "days_left": 29,
      "activated_at": "2026-10-12 06:25:03",
      "username": "",
      "machine": "A1B2C3D4E5F6",
      "verify_total": 1,
      "unbind_left": 2
    },
    "app":    { "name": "星云助手", "version": "1.2.0", "notice": "更新请先到官网下载", "down_url": "", "force_update": 0 },
    "update": null,
    "notice": null,
    "server_time": "2026-10-12T06:25:03+08:00"
  }
}

返回字段:

字段说明
auth.type卡密种类:会员 / 单码 / 次数 / 积分 / 充值。
auth.expire_at到期时间。会员卡首次验证才起算(按卡面天数),未激活为 null。
auth.days_left剩余天数,向下取整;无到期概念时为 null。
auth.times_left剩余次数,次卡每成功验证一次减 1。
auth.points剩余积分,积分卡每次成功验证扣掉应用设定的步长。
auth.machine当前绑定的机器码,为空表示这张卡还没绑设备。
auth.verify_total这张卡累计成功验证次数。
auth.unbind_left还能自助解绑几次;应用没开放解绑时为 null。
server_time服务器时间,用它校准本地时钟,防止改系统时间白嫖。
设备与 IP 拦截发生在扣量之前:换电脑被拒「该卡已绑定其他设备」,换网络被拒「该卡已绑定其他网络」;多开上限由后台的「最多可开机台数」控制,超出直接拒。

04五种卡密的差别

客户端调用方式完全相同,只是服务端扣量方式不同。定价、生成、发货都在后台完成。

字段说明
会员按天授权。首次验证起算 days 天,之后每次验证只校验是否过期,不扣量。适合订阅制软件。
单码不带天数时一次一用:验证过一次即作废,第二次直接拒。带天数时等同会员卡,适合试用码、一次性激活码。
次数每成功验证一次扣 1 次,用完提示「卡密次数已用完」。适合按量收费的工具。
积分每次扣掉后台设定的「积分步长」,默认 1 分,可按应用调整。
充值不能用于验证。会员在门户「我的余额」里兑换成金额,走接口验证会被拒。

05续期 · act=renew

用户手里有张新卡,直接叠到当前卡上,不用重新绑定设备。两张卡类型必须一致,新卡会被消耗。

请求
https://key.dlam.cc/api/verify.php?appid=你的AppID&act=renew&code=当前卡密&newcode=另一张同类型的新卡
类型叠加规则
会员 / 单码取「当前到期时间」与「现在」的较晚者,再加新卡天数;days 累加。已过期的用户因此能无损续上。
次数times_left 直接相加。
积分points 直接相加。
msg成功时形如「已续 30 天」,可直接提示给用户。

06解绑设备 · act=unbind

让用户自己换电脑,省掉一半客服。前提是后台把该应用的「自助解绑次数」设为大于 0,否则一律提示联系客服。解绑会清掉机器码、IP 与设备记录。

请求
https://key.dlam.cc/api/verify.php?appid=你的AppID&act=unbind&code=卡密&sig_machine=当前机器码
sig_machine 可选:传了就和卡上的绑定记录核对,避免别人拿卡号恶意解绑。返回里的 auth.unbind_left 是剩余可解绑次数,建议在界面上显示出来。

07注册账号 · act=register

想要账号体系(余额、代理价、名下多张卡)就让用户先注册。可以同时带 code,注册即绑定,省一步。

请求
https://key.dlam.cc/api/verify.php?appid=你的AppID&act=register&username=用户账号&password=至少6位&nickname=显示名&qq=12345678&code=卡密
字段说明
username4-20 位,只允许字母、数字、下划线;不能与后台管理员重名。
password6 位以上,入库用强哈希,接口永不返回密码。
code可选。绑给新账号,卡仍需在客户端首次验证才起算。充值卡不能在这里用。
限制后台关闭「允许注册」时直接拒;同一账号或 IP 失败过多会暂时拒绝。

08登录 · act=login

账号密码换一枚 token,之后用 username 验证即可,不必让用户重复输卡密。

请求
https://key.dlam.cc/api/verify.php?appid=你的AppID&act=login&username=用户账号&password=登录密码
成功返回(截断)
{
  "ok": true, "msg": "登录成功",
  "data": {
    "token":  "56ad48797ff148f757a054ec79aeca93",
    "user":   { "username": "xiaoyu", "nickname": "小雨", "balance": 128.5, "spent": 671.5 },
    "cards":  [ { "code": "...", "type": "会员", "state": "已使用", "expire_at": "2026-11-11 06:25:03" } ],
    "app":    { "name": "星云助手", "version": "1.2.0" },
    "update": null
  }
}
token 存在本地即可;同一账号再次登录会换发新 token,旧值失效。密码错误会计入风控,多次失败后 15 分钟内禁止再试。

09查询状态 · act=info

只读:不扣次数、不绑设备。适合做「剩余天数」悬浮窗、到期提醒和用户自助查询页。

请求
https://key.dlam.cc/api/verify.php?appid=你的AppID&act=info&code=卡密

比 verify 多一个 state:未使用 / 已使用 / 已过期 / 已用尽 / 已封禁,用来给用户一句人话。

10退出登录 · act=logout

清掉服务端 token,配合本地删配置。token 传了会先核对,避免别人拿账号名顶掉你。

请求
https://key.dlam.cc/api/verify.php?appid=你的AppID&act=logout&username=用户账号&token=登录时拿到的token

11签名校验(可选)

不传 sign 时接口照常工作,接入门槛最低。想防止别人扒走 AppID 白嫖,就加上签名:把除 sign 外的全部参数按参数名升序拼成 k=v,用 & 连接,再以 AppSecret 做 HMAC-SHA256,十六进制小写。

PHP 完整示例(可直接用作启动校验)
<?php
$endpoint = 'https://key.dlam.cc/api/verify.php';
$appid    = '你的 AppID';
$secret   = '你的 AppSecret';   // 只参与签名,不出现在返回值与日志里

function kami_sign(array $p, $secret) {
    unset($p['sign']);
    ksort($p);
    $parts = [];
    foreach ($p as $k => $v) { $parts[] = $k . '=' . $v; }
    return hash_hmac('sha256', implode('&', $parts), $secret);
}

function kami_verify($endpoint, $appid, $secret, $code, $machine, $ver = '1.0.0') {
    $p = ['appid' => $appid, 'act' => 'verify', 'code' => $code,
          'machine' => $machine, 'ver' => $ver, 'ts' => time()];
    $p['sign'] = kami_sign($p, $secret);
    $ch = curl_init($endpoint . '?' . http_build_query($p));
    curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 8]);
    $raw = curl_exec($ch);
    if ($raw === false) { curl_close($ch); return ['ok' => false, 'msg' => '网络异常,请检查网络后重试']; }
    curl_close($ch);
    return json_decode($raw, true) ?: ['ok' => false, 'msg' => '返回解析失败'];
}

$r = kami_verify($endpoint, $appid, $secret, '卡密', '机器码');
if ($r['ok']) {
    $a = $r['data']['auth'];
    echo '授权成功,到期:' . ($a['expire_at'] ?: '不限') . PHP_EOL;
} else {
    echo $r['msg'] . PHP_EOL;      // 直接把提示给用户
}
Python 示例
import hashlib, hmac, time, requests
from urllib.parse import urlencode

ENDPOINT = "https://key.dlam.cc/api/verify.php"
APPID, SECRET = "你的 AppID", "你的 AppSecret"

def sign(params):
    p = {k: v for k, v in params.items() if k != "sign"}
    canon = "&".join(f"{k}={p[k]}" for k in sorted(p))
    return hmac.new(SECRET.encode(), canon.encode(), hashlib.sha256).hexdigest()

def verify(code, machine, ver="1.0.0"):
    params = {"appid": APPID, "act": "verify", "code": code,
              "machine": machine, "ver": ver, "ts": int(time.time())}
    params["sign"] = sign(params)
    r = requests.get(ENDPOINT, params=params, timeout=8)
    data = r.json()
    return data["data"]["auth"] if data["ok"] else data["msg"]
字段说明
sign对除 sign 外的全部参数做的 HMAC-SHA256,十六进制小写。
ts秒级时间戳。带上后服务端只接受 ±300 秒内的请求,防止重放。
注意布尔和空值都按字符串拼;参数名区分大小写;不要对 value 做 URL 编码后再签。
签名对参数集合敏感:客户端多传或漏传任何一个参数都会导致校验失败。若后续会加新参数,建议只固定签几个字段(appid、act、code、machine、ts),服务端同样按这套字段拼。

12更新与公告下发

后台「版本发布」发新版、「公告管理」发公告后,客户端不用改代码就能收到,靠的是 verify / login / info 附带的两个字段。

字段说明
update把 ver 和最新版本比较;已是最新为 null,落后时返回版本详情。
update.version / log新版本号与更新说明,可原样贴进更新弹窗。
update.url / size下载地址与包体大小;版本里留空时自动回落到应用配置的下载地址。
update.force_update1 表示强制更新:建议只留「立即更新」,不放行进入主界面。
notice最新一条公告(本应用优先,其次全站公告),null 表示没有。
notice.level普通 / 提醒 / 紧急,用来决定弹窗口还是角标。
app.notice应用上常驻的一句话提示,适合放「加群领福利」这类长期文案。
更新弹窗判定(PHP)
$r = kami_verify($endpoint, $appid, $secret, $code, $machine, '1.0.0');
if (!empty($r['ok'])) {
    $u = $r['data']['update'];
    if ($u) {
        if ((int)$u['force_update'] === 1) {
            kami_forced_update($u['version'], $u['log'], $u['url']);   // 不给关闭
        } else {
            kami_ask_update($u['version'], $u['log'], $u['url']);      // 用户可跳过
        }
    }
    $n = $r['data']['notice'] ?? null;
    if ($n) kami_show_notice_once($n['id'], $n['title'], $n['content'], $n['level']);
}
公告的 id 请存到本地,同一条只弹一次;每次成功验证会给该公告 views 加一,用来统计触达。

13提示语对照

msg 已经是给人看的话,直接展示即可。下面标注了哪些该让用户重试、哪些该找客服。

提示语(msg)该怎么处理
缺少参数 appid / 请提交卡密或账号客户端调用漏参,属于开发问题。
AppID 不存在,请核对后台应用管理AppID 写错或应用已被删除。
该应用已被停用后台关了这个应用;提示「服务暂停,请联系作者」。
卡密不存在 / 这张卡密属于「其他应用」卡号打错,或拿的是别的软件的卡;让用户核对,不要自动重试。
卡密已被封禁,请联系客服作弊或退款回收;给出你的联系方式。
卡密已于 2026-10-01 到期引导续费:卖新卡或走 renew。
卡密次数已用完 / 积分已用完引导复购,可在门户里直接下单。
单码已于 … 使用过,请更换卡密一次一用的码被重复使用;补发一张新卡。
客户端需上报机器码应用开了设备绑定,但客户端没传 machine。
该卡已绑定其他设备,请解绑后再试换机场景;开放了自助解绑就直接调 act=unbind。
已达到最多可开机台数(N 台)多开超限。
该应用未开放自助解绑,请联系客服后台把解绑次数设成了 0。
账号或密码错误不区分「账号不存在」与「密码错误」,避免撞库探测。
尝试次数过多,请 15 分钟后再试风控生效;客户端应停止自动重试。
站点尚未连接授权放行云 / 服务已暂停这套系统自身的授权中断,属于站长运维问题,与用户无关。

14客户端落地建议

事项建议
调用时机启动必须验一次;运行中每 30 分钟~2 小时复验,防止长时间挂机绕过到期。
超时与重试8 秒超时;网络类失败最多重试 2 次,业务失败(ok=false)不要重试。
离线宽限断网时可用上次成功结果宽限 1~3 天,本地记录 server_time 与到期时间做判断,别只信本地时钟。
机器码取硬件指纹做 SHA1/MD5,长度固定,重装系统尽量不变;不要用纯 MAC(虚拟机可改)。
本地存储卡密与 token 存到用户目录并做简单混淆;别放安装目录,避免同机多用户串号。
首次激活会员卡是从第一次成功验证起算天数的,卖卡前提醒用户「装好再激活」。
错误处理把 msg 原样显示,再补一句客服入口;用户对「卡密不存在」这种短句最容易反复提交。
版本策略ver 要随发布递进,后台强更才有意义。
最小可用流程(伪代码)
启动:
  本地卡密 = 读配置()
  本地卡密为空 → 弹窗输入卡密 → 校验格式 → 保存
  r = 调 verify(code, machine, ver)
  r.ok 为假 → 显示 r.msg → 询问是否重输卡密 → 回到上一步
  r.data.auth.expire_at 存在且已过期 → 视为失败(防本地缓存脏数据)
  r.data.update 非空 → 弹更新(force_update=1 时不给关闭)
  r.data.notice 非空 且 id 未读过 → 弹公告,记录 id
运行:
  每 1 小时 → 调 verify → 失败立即锁功能并显示 msg
  网络失败 → 用离线宽限,超过宽限同样锁功能
接口之外还有会员门户:https://key.dlam.cc/user/。激活、续费、解绑、下单、工单都能在那里自助完成,客户端只需要把用户引导过去。
联系站长

拿卡、续费、定制,都可以直接说

有问题走门户工单,一般在 12 小时内回复。

会员门户
工单中心
在门户里提工单,附上卡密后两半,处理更快。