一次登录换令牌,之后用心跳保活。这一页讲清楚四件事:签名怎么算、每条接口传什么、返回里那些字段是什么、以及用户报「验证失败」时该看哪个码。示例可直接抄,口径全按表里的写。
三样东西就够:接口基址、app_key、app_secret。后两个在控制台「软件管理」里,一个软件一对,app_secret 可以随时重置。
基址 + 端点路径,路径同时是签名要素。发卡、建账号、看调用流水都在 开发者控制台 里做;接口不需要你点头,客户端自己就能把卡激活。
一律打到 https://api.dlam.cc/v1/…。写动作只认 POST;允许 GET 的只有下面这几条只读接口,其余用 GET 打过去就是 404。参数用表单或 JSON 都收,同名时请求体覆盖查询串——建议全放请求体,别把卡密和签名挂进 URL。
只开 GET 的:/v1/card/config /v1/user/config /v1/software/config /v1/software/notice /v1/software/latest_ver /v1/af/remote_var /v1/af/remote_data
每条请求都要带的公共参数(它们也全部参与签名):
| 参数 | 要求 | 说明 |
|---|---|---|
app_key | 必传 | 软件的 Key。缺失直接 400,errs 里点名是哪个参数。 |
timestamp | 必传 | 秒级 10 位。比服务器快超过 5 秒 → 10013;与服务器相差 60 秒以上 → 10011。客户端时钟不可信,用登录返回的 server_time 校准。 |
nonce | 必传 | 不超过 36 位的随机串,每次都要新(UUID 去横线就行)。服务端按软件维度记 60 秒,重复即 10014。空串与超长串一律判重复。 |
sign | 必传 | 请求签名,见下一节。缺了先回 400 而不是 10010。 |
GET /v1/ping:不签名、不要 app_key,正文就是一句裸文本 Pong。它给客户端在「还没定位到软件」的时候判断这台 API 是否活着,不通就换备用域名。除 GET 以外一律按未找到处理。五个要素按顺序拼成一串,做一次 MD5,十六进制小写:
sign = md5( METHOD + host + path + k1=v1&k2=v2&…&kn=vn + app_secret )| 要素 | 怎么取 |
|---|---|
METHOD | 大写,POST 或 GET。它参与签名,所以方法用错必算不过。 |
host | 只含域名,不带协议与端口。必须是你实际访问的那个 Host:换成 IP、换成别名,签名就废了(这是 10010 最常见的来源)。软件自建在非 80/443 端口上时,请求地址照旧带端口,签名里那一段要把 :端口 去掉——服务端读 Host 时也是从第一个冒号截断的。 |
path | 带 /v1 的原始路径,例 /v1/card/login。 |
k=v 串 | 除 sign 外的全部参数,格式化成 k=v 后排序,用 & 连接。 |
app_secret | 软件密钥,接在末尾,无分隔符。 |
k=v 字符串,不是参数名。差别在数字后缀上:app_key2=b 排在 app_key=a 前面(因为 2 的字节小于 =)。&、=、空格时尤其容易错。params),先自己 JSON 化成字符串再传。<?php
$HOST = 'api.dlam.cc'; // 只写域名:不带 https://、不带端口。它参与签名
$SECRET = '你的 app_secret'; // 只参与计算,不打印、不写日志
function scx_sign($method, $path, array $p, $host, $secret)
{
$kv = [];
foreach ($p as $k => $v) {
if (is_array($v)) continue; // 数组不参与签名
$kv[] = $k . '=' . $v; // value 原样拼,不做 URL 编码
}
usort($kv, 'strcmp'); // 排的是整条 k=v,不是参数名
return md5(strtoupper($method) . $host . $path . implode('&', $kv) . $secret);
}
function scx_call($path, array $p, $method = 'POST')
{
global $HOST, $SECRET;
$p['app_key'] = '你的 app_key';
$p['timestamp'] = time();
$p['nonce'] = bin2hex(random_bytes(8)); // 每次都要新,≤36 位
$p['sign'] = scx_sign($method, $path, $p, $HOST, $SECRET);
$ch = curl_init('https://' . $HOST . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_POSTFIELDS => http_build_query($p),
]);
$raw = curl_exec($ch);
if ($raw === false) {
$err = curl_error($ch); curl_close($ch);
return ['code' => -1, 'message' => '网络异常:' . $err];
}
curl_close($ch);
$j = json_decode((string)$raw, true);
return is_array($j) ? $j : ['code' => -2, 'message' => '返回不是合法 JSON'];
}
$r = scx_call('/v1/card/login', ['card' => '卡密', 'device_id' => '机器码']);
if (isset($r['code']) && (int)$r['code'] === 0) {
$res = $r['result'];
echo '可用到 ' . ($res['expires'] !== '' ? $res['expires'] : '不限时长')
. ',每 ' . $res['hg'] . ' 秒发一次心跳' . PHP_EOL;
// 之后:scx_call('/v1/card/heartbeat', ['card'=>'卡密','token'=>$res['token']]);
} else {
echo $r['code'] . ' · ' . $r['message'] . PHP_EOL; // 含义见「返回码对照」
}import hashlib, hmac, json, time, uuid, requests
HOST = "api.dlam.cc" # 只写域名,它参与签名
SECRET = "你的 app_secret"
APP_KEY = "你的 app_key"
def sign(method, path, params):
kv = [f"{k}={params[k]}" for k in params if k != "sign" and not isinstance(params[k], (list, dict))]
kv.sort() # 排的是整条 k=v,不是参数名
raw = method.upper() + HOST + path + "&".join(kv) + SECRET
return hashlib.md5(raw.encode()).hexdigest()
def call(path, params, method="POST"):
p = dict(params)
p.update(app_key=APP_KEY, timestamp=int(time.time()), nonce=uuid.uuid4().hex)
p["sign"] = sign(method, path, p)
r = requests.request(method, f"https://{HOST}{path}", data=p, timeout=10)
return r.json()
res = call("/v1/card/login", {"card": "卡密", "device_id": "机器码"})
if res["code"] == 0:
print("可用到", res["result"]["expires"], "心跳", res["result"]["hg"], "秒")
else:
print(res["code"], res["message"])app_secret 必然存在于你分发出去的程序里。它能挡住「别人不知道密钥就来白嫖你的 app_key」,挡不住铁了心拆包的人。要更强的控制,请把敏感逻辑放到 远程函数 里,让答案只在服务端算出来。返回永远是这一个信封。成功是 code = 0,不是 200;HTTP 状态码一律 200,成败只看 code。
{
"code": 0,
"message": "ok",
"result": {
"card_type": "会员",
"token": "k3f9dq2m1x7b5p0c",
"expires": "2026-11-19 14:20:31",
"expires_ts": 1795742431,
"config": "",
"server_time": 1792548031,
"hg": 600
},
"nonce": "bqzp3k1k00a00",
"sign": "0d4f6a9c2b1e5837a0c94d2f6b1e0735"
}| 字段 | 说明 |
|---|---|
code | 结果码,0 才算成功。没有 result 这个 key 也是正常:成功且无额外数据时它整个缺席,不是空对象、不是 null。 |
message | 给人看的一句话,和 返回码对照 里的文案一致。可以原样显示给用户,除了 10543 这类给开发者看的。 |
result | 业务数据,只有非空时出现。 |
errs | 只在 400 时出现:逐字段说清哪个参数没带对。只有公共参数缺失那类 400 带它,业务参数缺失的 400 由 message 点名缺的是哪个。 |
nonce | 服务端 nonce,趋势递增。客户端记下上一个,比它小或相等的响应就是重放。 |
sign | 响应签名,用同一个 app_secret 验。 |
sign = md5( code + message + k1=v1&k2=v2&… + nonce + app_secret )中间的 k=v 串取的是响应 result 里所有非数组字段,同样按整条 k=v 排序后连接;拼接处没有分隔符。result 为空时那一段是空字符串——不是 0,也不是 null。失败响应照样签名,SDK 里对 code≠0 不校验也不亏。
按组排开,每条列请求参数、成功返回的字段、以及实践里真会撞上的失败码。参数名一字不差照抄——多带一个不认识的参数,签名就算不过。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 必传 | 卡密原文,控制台「卡密管理」生成或导入的那一串,原样传,不用带软件前缀之外的加工。 |
device_id | 看情况 | 机器码,最长 120 位。软件开了「绑定机器码」时必传;建议用 CPU+主板+磁盘序列号做哈希,重装系统尽量不变,别用纯 MAC(虚拟机可改)。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
card_type | string | 这张卡的卡类名;没挂卡类时是类型本身(会员 / 单码 / 次数 / 积分 / 充值)。适合显示在「当前授权」那一栏。 |
token | string | 会话令牌。心跳、退出、解绑、读带校验的远程变量都要带它。 |
expires | string | 到期时间 Y-m-d H:i:s。次数卡与积分卡没有到期概念,回空串。 |
expires_ts | int | 同一个到期时间的秒级时间戳;0 表示这张卡不限时长。 |
config | string | 这张卡的配置串,和 GET /v1/card/config 读到的是同一个值。 |
server_time | int | 服务器当前秒级时间戳。拿它校准本地时钟,别只信客户端时间。 |
hg | int | 心跳间隔,单位秒(最少 60)。按这个值发心跳,别写死。 |
可能的失败:400 参数错误 10230 软件不存在,请检查 app_key 10218 卡密不可用 10212 卡密已被冻结 10210 卡密已过期 10215 只能在首次登录绑定的设备上使用 10213 卡密超过多开上限
首次登录那一刻就是激活:会员与单码的到期时间从这时起算,卖卡前提醒用户「装好再激活」。同一台设备重复登录会复用还没过期的那个令牌,不会多占一个多开名额;软件开了「顶号」时反过来——每次登录把其它在线会话撤掉。次数卡与积分卡在这一步扣一次,心跳不扣量。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 必传 | 登录时用的那张卡密。 |
token | 必传 | 登录返回的会话令牌。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
expires | string | 这个令牌的下一次生死线:现在 + token 有效期。不是卡的到期时间——卡的到期时间在登录那一步就定下来了。 |
expires_ts | int | 同上,秒级时间戳。 |
server_time | int | 服务器当前秒级时间戳。 |
可能的失败:400 参数错误 10214 登录状态已失效 10218 卡密不可用 10212 卡密已被冻结
心跳只续令牌,不续卡。超过 token 有效期没打心跳,令牌自己失效,下一次要回登录;卡在这期间到期或被冻结,心跳同样回失败,客户端要立刻锁功能,不能拿登录时的旧结果继续跑。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 看情况 | 传了就和令牌所属的卡核对一次,不传只按令牌处理。 |
token | 必传 | 要作废的令牌。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
已经失效的令牌再退一次也回 10214。这不是「退出失败」,本地配置清干净就行,别弹错误给用户看。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 必传 | 要换机的卡密。 |
token | 必传 | 当前在线令牌。解绑成功后这个令牌一并作废。 |
device_id | 看情况 | 要解掉的机器码。不传就解掉发起这次请求的那个令牌所绑的设备。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
可能的失败:400 参数错误 10216 软件未开启解绑设备功能 10255 该卡密不可解绑设备 10257 卡密剩余时间已不足以解绑扣除 10214 登录状态已失效 10212 卡密已被冻结 10218 卡密不可用
解绑成功后这个令牌一并作废,客户端要重新走登录。软件设了「解绑扣时」的话,卡的到期时间会往前挪那个分钟数;剩余时间不足以扣时就直接拒(10257)。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 必传 | 卡密。 |
token | 必传 | 当前在线令牌。 |
password | 必传 | 6-10 位解绑密码,明文存在这张卡上。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
可能的失败:400 参数错误 10214 登录状态已失效 10218 卡密不可用
解绑密码存在卡上,是给人「忘带电脑」用的兜底,不要拿它当账号密码,也别用它替代 token。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 必传 | 卡密。 |
password | 必传 | 解绑密码:优先认这张卡自己设过的,卡上没设过就回落到软件上的默认解绑密码。 |
device_id | 看情况 | 要解掉的机器码。新用户常常拿新机器码来,那也行——先解旧设备,再回「登录」重新绑。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
可能的失败:400 参数错误 10216 软件未开启解绑设备功能 10254 解绑密码不正确 10255 该卡密不可解绑设备 10257 卡密剩余时间已不足以解绑扣除 10212 卡密已被冻结 10218 卡密不可用
这条是免登录换机的入口:在新机器上直接调,解完还要回 /v1/card/login 才能真正登录,它自己不签发令牌。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 必传 | 要被续期的那张卡:必须已经激活(登录过一次)。 |
use_card | 必传 | 拿来烧的那张新卡:必须是「未使用」,且多开上限与目标卡一致。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
可能的失败:400 参数错误 10240 卡密不存在或已被使用 10241 卡密已被使用 10244 不能对未使用的卡密进行充值 10256 卡密多开配置不相等,不能充值 10212 卡密已被冻结
供体卡的天数(含零头分钟)、次数、积分都并进目标卡,随后供体变成「已使用」。时长是从现在往后加,不追回目标卡已经过掉的那几天。成功只有 code=0,想知道续到了哪天,再调一次登录看 expires。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 必传 | 卡密。 |
config | 看情况 | 只在 POST 写的时候必传,最长 512 位,内容随你(想存结构化数据就自己 JSON 化)。GET 读的时候不用带。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
config | string | 只有 GET 读的时候有:这张卡的配置串原文,和登录返回里那个 config 是同一个值。POST 写成功没有 result。 |
可能的失败:400 参数错误 10218 卡密不可用 10212 卡密已被冻结
这对接口只验软件签名,不认任何卡密凭证:拿着 app_secret 的人能读写任意一张卡的配置。别在这串里放密钥。另外 GET 成功不写用卡记录,POST 才写一行。
| 参数 | 要求 | 说明 |
|---|---|---|
card | 必传 | 用来开号的卡密,最长 45 位。注册成功这张卡就被烧进这个账号名下。 |
username | 必传 | 账号名,不超过 20 位。 |
password | 必传 | 登录密码,6-30 位,入库走强哈希,接口永不返回。 |
device_id | 必传 | 注册时的设备,最长 120 位。这台会直接成为首个绑定设备。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
可能的失败:400 参数错误 10240 卡密不存在或已被使用 10241 卡密已被使用 10242 账号已存在 10210 卡密已过期 10212 卡密已被冻结
注册即激活,不需要先调 /v1/card/login:卡当场变成「已使用」并挂进账号名下,账号与这张主卡共用同一个到期点。注册成功后直接走用户登录拿令牌。
| 参数 | 要求 | 说明 |
|---|---|---|
username | 必传 | 账号名。 |
password | 必传 | 登录密码。 |
device_id | 看情况 | 机器码。软件开了「绑定机器码」时必传。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
card_type | string | 账号名下那张主卡的卡类名;名下没有卡时回空串。 |
token | string | 账号侧会话令牌。 |
expires | string | 账号的到期时间。账号与名下主卡共用同一个到期点。 |
expires_ts | int | 同上,秒级时间戳。 |
config | string | 账号的配置串,和 GET /v1/user/config 同源。 |
server_time | int | 服务器当前秒级时间戳。 |
hg | int | 心跳间隔,单位秒。 |
可能的失败:400 参数错误 10243 用户登录密码错误 10250 用户已到期 10252 用户已被冻结 10218 卡密不可用 10253 用户超过多开上限 10215 只能在首次登录绑定的设备上使用
用户侧的扣量发生在登录:账号名下若有带次数或积分的卡,这次登录从其中一张扣一次;心跳只保活。名下卡池烧空时回 10218,这时该引导用户「用户充值」补一张进去。
| 参数 | 要求 | 说明 |
|---|---|---|
username | 必传 | 账号名,必须和令牌持有者是同一个。 |
token | 必传 | 用户登录返回的令牌。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
expires | string | 令牌的下一次生死线(现在 + token 有效期),不是账号到期时间。 |
expires_ts | int | 同上,秒级时间戳。 |
server_time | int | 服务器当前秒级时间戳。 |
可能的失败:400 参数错误 10214 登录状态已失效 10250 用户已到期 10252 用户已被冻结
心跳期间账号到期(10250)或被冻结(10252)都会在这里爆出来,和卡密侧一样要立刻锁功能。
| 参数 | 要求 | 说明 |
|---|---|---|
username | 必传 | 账号名。 |
token | 必传 | 要作废的令牌。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
| 参数 | 要求 | 说明 |
|---|---|---|
username | 必传 | 账号名。 |
password | 必传 | 当前登录密码,用来验身份。 |
new_password | 必传 | 新密码,6-30 位。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
改密成功即下线:这个账号名下所有在线令牌全部作废,其它设备下一次心跳就是 10214,得重新登录。这是设计,不是 bug。
| 参数 | 要求 | 说明 |
|---|---|---|
username | 必传 | 要充的账号。 |
card | 必传 | 拿来烧的那张新卡:必须「未使用」,且多开上限与账号名下那张主卡一致。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
可能的失败:400 参数错误 10240 卡密不存在或已被使用 10241 卡密已被使用 10244 不能对未使用的卡密进行充值 10252 用户已被冻结 10256 卡密多开配置不相等,不能充值
充进去的是账号名下那张主卡的额度:天数并进到期时间,次数与积分留在供体卡上、变成卡池里可烧的一张。账号侧的流水记在「以卡充卡记录」里,充值对象那一列写的是用户名。
| 参数 | 要求 | 说明 |
|---|---|---|
username | 必传 | 账号名。 |
token | 必传 | 当前在线令牌。 |
device_id | 看情况 | 要解掉的机器码,不传就用这个令牌所绑的那台。 |
成功只有 code=0,没有 result——别去取返回体里的字段。
可能的失败:400 参数错误 10216 软件未开启解绑设备功能 10255 该卡密不可解绑设备 10257 卡密剩余时间已不足以解绑扣除 10214 登录状态已失效
上限取账号上的多开数,超限回 10253(卡密侧同类情况回 10213)。解掉的是这台设备在账号侧与卡侧的全部绑定记录。
| 参数 | 要求 | 说明 |
|---|---|---|
username | 必传 | 账号名。 |
config | 看情况 | 只在 POST 写的时候必传,最长 512 位。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
config | string | 只有 GET 读的时候有:这个账号的配置串原文,和用户登录返回里那个 config 同源。POST 写成功没有 result。 |
可能的失败:400 参数错误 10243 用户登录密码错误 10252 用户已被冻结
和卡密配置同样的信任模型:只认签名不认用户凭证,别放敏感数据。GET 成功不写用卡记录。
除公共参数外不带业务参数。
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
config | string | 软件级配置串,控制台「配置软件 → 软件配置」里那一栏写的原文。 |
可能的失败:400 参数错误 10230 软件不存在,请检查 app_key 10212 卡密已被冻结
除公共参数外不带业务参数。
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
notice | string | 公告正文原文。没填就是空串,不是 null。 |
可能的失败:400 参数错误 10230 软件不存在,请检查 app_key 10212 卡密已被冻结
| 参数 | 要求 | 说明 |
|---|---|---|
version | 必传 | 客户端当前版本号,写成 1.2 或 1.2.3 这种语义化形状,服务端按段比大小(1.10 比 1.9 新)。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
version | string | 服务端当前版本号。 |
notice | string | 这条版本的更新说明;那条没写说明时回落到软件公告。 |
url | string | 安装包下载地址;这条版本没填时回落到软件上的默认下载地址。 |
server_time | int | 服务器当前秒级时间戳。 |
可能的失败:400 参数错误 10304 已经是最新版本 10230 软件不存在,请检查 app_key 10212 卡密已被冻结
已是最新时回 10304,不是 code=0 加一个空结果——把 10304 当「没更新」处理,别弹错误窗口。当前版本取「配置软件 → 版本配置」里最新那条,历史版本只用来查说明与下载地址。成功不写用卡记录。
| 参数 | 要求 | 说明 |
|---|---|---|
device_id | 必传 | 机器码,最长 120 位。试用不记名,这个值就是身份,换一台机器就是一个新的试用名额。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
expires | string | 这次试用的到期时间:登录那一刻 + 软件上设的试用时长。 |
expires_ts | int | 同上,秒级时间戳。 |
server_time | int | 服务器当前秒级时间戳。 |
hg | int | 心跳间隔,单位秒。试用的心跳照这个值发。 |
可能的失败:400 参数错误 10401 软件未开启试用 10402 试用中,请勿重复登录 10403 已试用过了 10407 本周期试用次数已用完
试用没有令牌:device_id 就是身份,心跳带同一个设备码。正在试用中重复登录回 10402,那是「你已经在试用里」,改用心跳即可;软件开了「一台设备只能试用一次」时用过就永久 10403,否则按周期次数计,用完 10407。
| 参数 | 要求 | 说明 |
|---|---|---|
device_id | 必传 | 和试用登录时同一个机器码。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
expires | string | 续后的试用到期时间(现在 + 试用时长)。 |
expires_ts | int | 同上,秒级时间戳。 |
server_time | int | 服务器当前秒级时间戳。 |
可能的失败:400 参数错误 10401 软件未开启试用 10405 试用已到期 10406 本周期试用已到期
试用心跳把到期时间整段往前推,所以「一直开着程序」能一直用到周期上限;到期后回 10405/10406,该引导付费了。
| 参数 | 要求 | 说明 |
|---|---|---|
key | 必传 | 变量名,最长 64 位,控制台「远程变量」里写好的那一个。注意参数名是 key,不是 name。 |
card | 看情况 | 这个变量在控制台勾了「校验 token」时变为必传,和 token 成对。 |
token | 看情况 | 同上。没勾校验 token 时带上也没坏处,服务端不看。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
value | string | 变量的值原文。服务端不按类型解释它:数字、JSON、句子都是这一串,客户端自己解。 |
可能的失败:400 参数错误 10501 远程变量不存在 10214 登录状态已失效 10601 本月高级功能调用量已用完
参数名是 key。变量勾了「校验 token」时,card+token 变成必传,读的人必须是正在用的卡。成功不写用卡记录(这是热路径),但照样进高级功能的月调用量。
| 参数 | 要求 | 说明 |
|---|---|---|
key | 必传 | 数据键,最长 64 位。GET 读回时只认这一个参数。 |
value | 看情况 | create/update 时必传(最长 256 位);delete 时不要带,带了也不参与删除,还会让签名多算一项。 |
action | 看情况 | 只在 POST 用:create / update / delete 三选一。GET 读回不带。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
value | string | GET 读回时是存着的那串值;POST 写成功没有 result。 |
可能的失败:400 参数错误 10521 远程数据不存在 10522 远程数据 key 已存在 10601 本月高级功能调用量已用完
读写同一个 URL,靠 METHOD 区分:读用 GET+key,增改删用 POST+action。delete 不要带 value——带了会让签名多算一项、必报 10010(服务端虽然会抹掉空值,但你的签名是在抹掉之前算的)。写成功会留下一行用卡记录,写着 create · 键名。
| 参数 | 要求 | 说明 |
|---|---|---|
func_name | 必传 | 函数名,最长 64 位。注意参数名是 func_name,不是 name。 |
params | 看情况 | 实参列表,必须是「JSON 数组序列化后的字符串」,例如 [3,4]。对象、嵌套数组、标量都拒(10542)。函数没声明形参可以不带。 |
成功时 result 里带:
| 字段 | 形态 | 说明 |
|---|---|---|
return | string | 返回值先被 JSON 编码成字符串再放进信封,形如 {"return":7}。客户端要再解一次才是 7。整数不会带小数点(4 而不是 4.0)。 |
可能的失败:400 参数错误 10541 远程函数不存在 10542 参数(params)必须为 json 列表序列化后的 string 10543 远程函数调用错误 10601 本月高级功能调用量已用完
跑的是控制台里那条受限表达式,白名单解释器,不执行 PHP、读不到服务器文件。实参按声明的形参名按位置绑定。返回值是 JSON 字符串,要再解一次。撞 10543 时 message 里是具体原因(哪一步算错了)。
客户端的调用方式对五种卡完全一样,差别只在服务端扣什么、expires 有没有值。类型在控制台生成卡密时选定。
| 类型 | 扣量与计时 |
|---|---|
| 会员 | 首次登录起算「天数+零头分钟」,之后每次登录只校验有没有到期,不扣量。到期时间回在 expires。适合订阅制。 |
| 单码 | 带天数时等同会员;天数与分钟都是 0 就是一次一用:登录过一次即作废,第二次直接拒。适合发放体验码。 |
| 次数 | 每成功登录一次扣 1 次,扣到 0 变「已用尽」。心跳不扣。没有到期概念,expires 是空串、expires_ts 是 0。 |
| 积分 | 每成功登录一次扣掉软件上的「积分步长」(默认 1 分)。适合按量计费的工具。 |
| 充值 | 额度是金额,不是时长、次数、积分:接口侧的登录与心跳都不消费它,也不算到期时间。它该在会员门户里兑换,不该出现在客户端的验证入口上。 |
时长按「天数+零头分钟」两个字段给,所以能出 30 天的卡,也能出 45 分钟的卡。时长从激活那一刻起算,不是从卖出去那天。
| 能做什么 | 走哪条接口 |
|---|---|
| 续时长 | /v1/card/recharge:烧一张新卡,把它的天数、次数、积分并进正在用的那张。已过期的卡也能充,从现在往后加。 |
| 换绑设备 | /v1/card/unbind_device(带令牌)或 /v1/card/unbind_device/by_password(免令牌,用解绑密码)。 |
| 账号侧 | 一个账号名下是一个卡池:主卡管到期时间,带次数或积分的卡排队被烧。/v1/user/recharge 往这个池子里补卡。 |
这一节是「为什么用户说他还开着程序,却突然不能用了」的答案。
| 事项 | 口径 |
|---|---|
| 令牌有效期 | 软件上设的「token 有效期」(分钟)。登录签发、心跳整段续到 现在 + 有效期。超时没心跳就自动失效,回 10214,得重新登录。 |
| 心跳间隔 | 登录响应里的 hg(秒,最少 60),来自软件的「心跳间隔」。照 hg 发,别写死 60 秒——心跳打太密只会把用卡记录刷满。 |
| 扣量时机 | 次数卡与积分卡在登录时扣一次,心跳不扣。所以「挂着程序不动」不烧次数,重启一次算一次。 |
| 多开上限 | 卡侧取这张卡的多开数(0 为不限),超限回 10213;账号侧取账号的多开数,超限回 10253。同一台设备重复登录复用令牌,不额外占名额。 |
| 顶号 | 软件开了「顶号」时,每次登录把同一主体的其它在线令牌全撤掉——被顶掉那一台下一次心跳就是 10214。 |
| 解绑 | 开关、次数上限、每次扣多少分钟,都设在软件上。扣时先判剩余时间够不够,不够直接拒(10257),不会把卡扣成负数。 |
| 改密 | /v1/user/password 成功即撤掉该账号全部在线令牌。这是设计:改完密码不该还有别的设备继续在线。 |
三条接口解决「不改客户端也能调整」的三件事:读一个服务端写的值、存一个小值、让服务端算一道题。
| 能力 | 用来干什么 |
|---|---|
| 远程变量 | 开发者在控制台维护,客户端只读。改完立刻生效,不用发版。变量可以勾「校验 token」,勾了就只有正在用的卡读得到。 |
| 远程数据 | 客户端自己写的键值对,读用 GET、增改删用 POST 的 action。适合存设备唯一的计数、开关、灰度标记。 |
| 远程函数 | 客户端调名字,服务端跑控制台里那条受限表达式并回返回值。白名单解释器:不执行 PHP、读不到服务器文件、也不给租户代码留口子。 |
这一组按自然月计调用量,控制台给每个开发者设了每月上限,撞上限后整组回 10601。当前阶段只挡量、不收费。
message 已经是给人看的话,直接显示即可;下表这一列告诉你该让用户做什么。码出自服务端那张表,和 message 永远一致。
| code | message | 客户端该怎么办 |
|---|---|---|
| 400 | 参数错误 | 调用方的问题:漏参数、参数超长、格式不合。按 message 或 errs 里那个字段名改代码,别提示给用户。 |
| 404 | 资源未找到 | 路径或方法不对。写动作只认 POST,用 GET 打过来就是这个。核对基址与 /v1/ 前缀。 |
| 500 | 服务器错误,请稍后重试 | 服务端异常。可以让用户重试一次,连续出现就把时间、软件名报给开发者,控制台的用卡记录里有那一行。 |
| 10010 | 无效的签名,请检查签名算法 | 签名不对。九成是三个口径之一:排的是整条 k=v 不是参数名、host 写成了常量或被换成了 IP、value 提前 URL 编码了。用本页的示例逐字对照。 |
| 10011 | 签名已过期 | 时间戳和服务器差了 60 秒以上。客户端别用本地时间,用登录返回的 server_time 校准。 |
| 10013 | 时间戳大于当前服务器时间,请检查 timestamp | 时间戳比服务器还快,多半是用户把系统时间往前调了。同样用 server_time。 |
| 10014 | 重复的 nonce | nonce 重复。每次请求换新随机串;空串或超过 36 位一律按重复处理。重试同一个请求必须重算 nonce。 |
| 10210 | 卡密已过期 | 卡到期了。给续费入口,别再自动重试。 |
| 10212 | 卡密已被冻结 | 卡被冻结(也可能是软件或开发者账号被停用)。原样把 message 显示给用户,引导联系客服。 |
| 10213 | 卡密超过多开上限 | 卡密侧多开超限。提示「已达最多可开机台数」,把当前台数显示出来。 |
| 10214 | 登录状态已失效 | 令牌没了:超时没心跳、被顶号、改密下线、解绑作废。回登录流程重取,别当业务失败弹错。 |
| 10215 | 只能在首次登录绑定的设备上使用 | 这张卡只允许首次登录绑定的设备。引导走解绑,或者联系开发者。 |
| 10216 | 软件未开启解绑设备功能 | 软件没开自助解绑。这条不该出现在界面上,把按钮置灰。 |
| 10217 | 超出可绑定设备上限,请在已绑定设备上使用 | 保留码,本平台不会返回它;同一件事用 10213 表达。SDK 里当多开超限处理即可。 |
| 10218 | 卡密不可用 | 这张卡现在不能用(不存在、不属于这个软件、已用尽)。让用户核对卡号,不要自动重试。 |
| 10230 | 软件不存在,请检查 app_key | app_key 认不出来:写错了,或软件在控制台被删了。这是接入期问题,与用户无关。 |
| 10240 | 卡密不存在或已被使用 | 卡密或账号查不到。用于充值与注册:核对用户抄的那一串。 |
| 10241 | 卡密已被使用 | 供体卡已经被用过了(慢了一步被别人抢掉,或这张卡本来就不是新的)。不能再拿它充。 |
| 10242 | 账号已存在 | 账号名重名。换个名字重试,或引导直接登录。 |
| 10243 | 用户登录密码错误 | 密码错,或用户名配置查不到账号。故意不区分两种,避免撞库探测。 |
| 10244 | 不能对未使用的卡密进行充值 | 目标卡还没激活,不能给它充值。先让用户登录一次。 |
| 10250 | 用户已到期 | 账号到期。给续费入口。 |
| 10252 | 用户已被冻结 | 账号被冻结。显示 message,引导联系客服。 |
| 10253 | 用户超过多开上限 | 账号侧多开超限。 |
| 10254 | 解绑密码不正确 | 解绑密码不正确。 |
| 10255 | 该卡密不可解绑设备 | 这张卡不给解绑(没绑过设备、或自助解绑次数用完)。把剩余次数显示出来。 |
| 10256 | 卡密多开配置不相等,不能充值 | 两张卡的多开上限不一致,不能充。让两张卡来自同一个卡类。 |
| 10257 | 卡密剩余时间已不足以解绑扣除 | 卡的剩余时间不够「解绑扣时」的,宁可拒绝也不把卡扣成负数。 |
| 10304 | 已经是最新版本 | 已经是最新版本。按「没得升级」处理,不要弹错误。 |
| 10401 | 软件未开启试用 | 软件没开试用。界面上不该有这个入口。 |
| 10402 | 试用中,请勿重复登录 | 这台设备正在试用中,别重复登录,改用心跳续时间。 |
| 10403 | 已试用过了 | 这台设备已经用掉过试用名额(软件开了「只能试用一次」)。引导购买。 |
| 10404 | 请先登录 | 需要先有登录态。 |
| 10405 | 试用已到期 | 试用到期。引导付费。 |
| 10406 | 本周期试用已到期 | 本周期试用到期,下个周期还能试(软件按周期给名额)。 |
| 10407 | 本周期试用次数已用完 | 本周期试用次数用完。 |
| 10501 | 远程变量不存在 | 远程变量不存在:名字写错,或开发者还没在控制台建。客户端该有一个兜底默认值。 |
| 10521 | 远程数据不存在 | 远程数据不存在(读一个没写过的 key,或 update/delete 一个不存在的 key)。 |
| 10522 | 远程数据 key 已存在 | 这个 key 已经存在,create 撞名了。改用 update。 |
| 10541 | 远程函数不存在 | 远程函数不存在。同样是客户端接错了名字。 |
| 10542 | 参数(params)必须为 json 列表序列化后的 string | params 不是 JSON 数组字符串。序列化成 [...] 再传。 |
| 10543 | 远程函数调用错误 | 函数跑挂了,message 里是具体原因。这条消息给开发者看,别原样弹给用户。 |
| 10601 | 本月高级功能调用量已用完 | 这个月的调用量到上限了,被拦的是软件不是用户。让开发者在控制台调额度。 |
| 事项 | 建议 |
|---|---|
| 调用时机 | 启动必验一次;运行中按 hg 发心跳。长时间挂机不心跳的话令牌会自己过期,这是故意的。 |
| 超时与重试 | 10 秒超时。网络类失败最多重试 2 次,而且每次重算 timestamp 与 nonce(照抄上一次的 sign 就是 10014)。业务失败(code≠0)不要自动重试。 |
| 离线宽限 | 断网时可用上次成功的结果宽限一小段时间,但判断依据要存 server_time 与 expires,别只信本地时钟——用户改系统时间就能白嫖。 |
| 机器码 | 取硬件指纹做哈希,长度固定、重装系统尽量不变。不要用纯 MAC,虚拟机一改就换了一台「新设备」。 |
| 本地存储 | 令牌和卡密存用户目录,别存安装目录(同机多用户会串号)。日志里不要写 sign,也不要写 app_secret。 |
| 错误处理 | message 原样显示,后面补一句你的客服入口。10010/10230 这类接入期错误写进本地日志就行,别弹给用户。 |
| 首次激活 | 时长从第一次成功登录起算。卖卡前提醒「装好再激活」,能省掉一半退款。 |
| 版本升级 | /v1/software/latest_ver 用 10304 表示「没得升」,不是错误。客户端上报的版本号要随发布递进。 |
启动:
本地卡密 = 读配置()
没有卡密 → 输入卡密 → 调 /v1/card/login(card, device_id)
code != 0 → 按返回码分支:
10218 / 10240 → 让用户核对卡号,不要自动重试
10215 / 10213 → 走解绑或联系客服
10212 / 10210 → 原样显示 message,给续费或客服入口
10010 附近 → 这是接入代码的问题,写进本地日志,别弹给用户
code == 0 → 存下 result.token / result.expires,用 result.server_time 校准本地时钟
用 result.hg 起一个心跳定时器
运行中:
每 hg 秒 → /v1/card/heartbeat(card, token)
0 → 继续跑
10214 → 令牌没了,回登录流程
其它 → 立即锁功能并显示 message
网络失败 → 最多重试 2 次;期间可用上次成功结果宽限,但宽限不超过 expires
退出/关机:
/v1/card/logout(card, token) → 无论成败都清本地令牌控制台 用卡记录 是排障第一现场:按卡密、账号、机器码或 IP 查那一次调用,写明返回码与人话原因。要区分的是——
GET 卡密配置、GET 用户配置。它们的量只进概览的调用统计。