SingleLogin 卡密登录
用卡密 + 机器码完成一次授权验证。这是 A7验证最常用的接口:终端用户只需要输入一串卡密,首次使用时服务端自动创建激活记录并把卡密绑定到当前设备。
| 项目 | 值 |
|---|---|
| action | SingleLogin |
| 入口 | Handle POST https://www.a7p.cn/api/verify/{ApiPassword} |
| 是否计入验证统计 | 是(SingleLogin 与 UserLogin 会写入验证日志) |
| 典型调用时机 | 软件启动 / 用户点击「登录」按钮 |
调用步骤
- 完成 Init 握手,拿到
ApiPassword。 - 采集当前设备的机器码(见设备指纹与硬件绑定)。
- 拼接明文:
action=SingleLogin&Card=卡密&Mac=机器码&DeviceName=设备名。 - RC4 加密明文得到
Data(HEX 大写),计算Sign = MD5(ApiPassword + TimeStamp + Data)。 - 表单 POST 到 Handle 接口。
code == 200→ 缓存data.token,读取data.expire_at展示到期时间。- 若
data.is_times == true,同时展示remaining剩余次数。 - 启动定时器,每 5 分钟用
token调用一次UserHeartbeat(Type=card)。
请求参数
明文参数(RC4 加密前):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
action | string | 是 | 固定值 SingleLogin |
Card | string | 是 | 卡密密钥,格式 XXXX-XXXX-XXXX-XXXX |
Mac | string | 是 | 机器码,用于绑定设备。等价别名:mac、DeviceId |
DeviceName | string | 否 | 设备名称,仅用于后台展示。等价别名:Device |
明文示例:
text
action=SingleLogin&Card=UTDE-7TCX-N5AT-ZDSP&Mac=TEST-ABC-123&DeviceName=MyPC业务规则
- 首次使用:卡密未激活时,本次调用会创建激活记录,把卡密绑定到
Mac对应的设备,并按卡密类型计算到期时间。 - 同卡同机再次登录:时长卡刷新心跳即可;按次卡每次登录都会消耗一次次数。
- 同卡换机:卡密授权模式下,一张卡密与一个
Mac一一对应,换机需要在后台处理或改用账号授权模式(ChangeBind面向的是账号,不是卡密)。 - 黑名单:若产品启用了黑名单且当前 IP 或机器码命中,返回
1023。 - 按次卡限制:按次卡密只能用于
SingleLogin,用于UserRegin/UserLogin充值 /UserRecharge都会返回1024。
请求示例
bash
# 前置:已通过 Init 拿到 API_PW
API_PW="A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6"
TS=$(date +%s)
# RC4 + 签名借助 python3 完成
read -r DATA_HEX SIGN <<< "$(API_PW="$API_PW" TS="$TS" python3 - <<'PY'
import hashlib, os
api_pw, ts = os.environ["API_PW"], os.environ["TS"]
plain = "action=SingleLogin&Card=UTDE-7TCX-N5AT-ZDSP&Mac=TEST-ABC-123&DeviceName=MyPC"
key = api_pw.encode()
s = list(range(256)); j = 0
for i in range(256):
j = (j + s[i] + key[i % len(key)]) % 256
s[i], s[j] = s[j], s[i]
i = j = 0
out = bytearray()
for b in plain.encode():
i = (i + 1) % 256; j = (j + s[i]) % 256
s[i], s[j] = s[j], s[i]
out.append(b ^ s[(s[i] + s[j]) % 256])
data_hex = out.hex().upper()
print(data_hex, hashlib.md5(f"{api_pw}{ts}{data_hex}".encode()).hexdigest())
PY
)"
curl -s -X POST "https://www.a7p.cn/api/verify/${API_PW}" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "TimeStamp=${TS}&Sign=${SIGN}&Data=${DATA_HEX}"python
# 复用 /api/handle 页面中的 handle() 与 rc4() 函数
API_PW = "A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6"
CARD = "UTDE-7TCX-N5AT-ZDSP"
MAC = "TEST-ABC-123"
plain = f"action=SingleLogin&Card={CARD}&Mac={MAC}&DeviceName=MyPC"
result = handle(API_PW, plain)
if result["code"] == 200:
data = result["data"]
token = data["token"]
print(f"登录成功,卡类型 {data['card_type']},到期 {data['expire_at']}")
if data.get("is_times"):
print(f"按次卡:共 {data['total']} 次,已用 {data['used']},剩余 {data['remaining']}")
else:
print(f"登录失败 {result['code']}: {result['message']}")csharp
// 复用 /api/handle 页面中的 A7Client
var client = new A7Client(apiPassword);
string card = "UTDE-7TCX-N5AT-ZDSP";
string mac = "TEST-ABC-123";
string plain = $"action=SingleLogin&Card={card}&Mac={mac}&DeviceName=MyPC";
JsonElement result = await client.HandleAsync(plain);
if (result.GetProperty("code").GetInt32() == 200)
{
JsonElement data = result.GetProperty("data");
string token = data.GetProperty("token").GetString() ?? "";
string expireAt = data.GetProperty("expire_at").GetString() ?? "";
bool isTimes = data.GetProperty("is_times").GetBoolean();
Console.WriteLine($"登录成功,到期 {expireAt}");
if (isTimes)
{
Console.WriteLine($"剩余次数 {data.GetProperty("remaining").GetInt32()}");
}
}
else
{
Console.WriteLine($"登录失败: {result.GetProperty("message").GetString()}");
}text
.版本 2
.子程序 A7_卡密登录, 逻辑型
.参数 ApiPassword, 文本型
.参数 卡密, 文本型
.参数 机器码, 文本型
.局部变量 TimeStamp, 文本型
.局部变量 加密参数, 文本型
.局部变量 Sign, 文本型
.局部变量 返回文本, 文本型
TimeStamp = 到文本 (时间_取现行时间戳 ())
加密参数 = RC4加密 (ApiPassword, "action=SingleLogin&Card=" + 卡密 + "&Mac=" + 机器码 + "&DeviceName=" + 取计算机名 ())
Sign = 取数据摘要 (到字节集 (ApiPassword + TimeStamp + 加密参数))
返回文本 = 到文本 (网页_访问 ("https://www.a7p.cn/api/verify/" + ApiPassword, 1, ;
"TimeStamp=" + TimeStamp + "&Sign=" + Sign + "&Data=" + 加密参数))
.如果真 (JSON_解析 (返回文本, "code") = "200")
全局_Token = JSON_解析 (返回文本, "data.token")
全局_到期时间 = JSON_解析 (返回文本, "data.expire_at")
返回 (真)
.如果真结束
信息框 ("验证失败:" + JSON_解析 (返回文本, "message"), 0, )
返回 (假)成功响应
时长卡
json
{
"code": 200,
"message": "success",
"data": {
"token": "b86fbe17a3e5492f...",
"status": "active",
"is_times": false,
"card_type": "月卡",
"expire_at": "2026-07-22 16:30:00"
}
}按次卡
json
{
"code": 200,
"message": "success",
"data": {
"token": "b86fbe17a3e5492f...",
"status": "active",
"is_times": true,
"card_type": "10次卡",
"expire_at": "2036-06-22 16:30:00",
"total": 10,
"used": 3,
"remaining": 7
}
}响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
token | string | 登录凭证,40 位随机串。后续心跳(Type=card)必须携带 |
status | string | active = 正常使用中 |
is_times | bool | true = 按次卡 / false = 时长卡 |
card_type | string | 卡密类型名称,如「月卡」「10次卡」 |
expire_at | string | 到期时间,格式 YYYY-MM-DD HH:mm:ss。按次卡为远未来日期(无时间过期) |
total | int | 总可用次数,仅 is_times = true 返回 |
used | int | 已使用次数,仅 is_times = true 返回 |
remaining | int | 剩余可用次数,为 0 时卡密失效,仅 is_times = true 返回 |
失败响应
json
{ "code": 1004, "message": "卡密无效", "data": null }json
{ "code": 1005, "message": "卡密已过期", "data": null }json
{ "code": 1005, "message": "卡密次数已用完", "data": null }json
{ "code": 1006, "message": "卡密已禁用", "data": { "remark": "违规使用" } }json
{ "code": 1013, "message": "参数不完整: Card, Mac 必填", "data": null }json
{ "code": 1023, "message": "已被列入黑名单,请联系管理员", "data": null }错误码
| 错误码 | 含义 | 解决方案 |
|---|---|---|
1004 | 卡密无效 / 卡密已退款 | 卡密不存在、不属于当前产品,或已退款作废 |
1005 | 卡密已过期 / 次数已用完 | 引导用户续费或购买新卡密 |
1006 | 卡密已禁用 / 已冻结 / 设备已被禁用 | 读取 data.remark 展示原因,联系管理员处理 |
1013 | 参数不完整 | Card 与 Mac 均为必填 |
1023 | 已被列入黑名单 | 当前 IP 或机器码在产品黑名单中 |
完整列表见错误码对照表。
相关文档
- UserHeartbeat 心跳检测 —— 登录后必须做的事
- GetCardInfo 卡密信息 —— 不消耗次数地查询卡密状态
- 设备指纹与硬件绑定 —— 机器码怎么取才稳
- SDK 实现:易语言 · C# · Python · Go · Node.js