5 分钟接入示例
本页给出一份可以直接复制运行的最小接入代码,跑通「Init 握手 → SingleLogin 卡密登录 → UserHeartbeat 心跳」三步。
开始之前请先准备好 AppId 和 AppKey,参见 获取 AppID 与 API 密钥。
调用步骤
- 取当前 Unix 秒级时间戳
TimeStamp。 - 计算
Sign = MD5(AppId + AppKey + TimeStamp + AppKey)。 POST https://api.a7p.cn/api/verify,表单提交AppId、AppKey、TimeStamp、Sign。- 从响应中取出
data.api_password,存进内存变量ApiPassword。 - 拼接明文参数
action=SingleLogin&Card=卡密&Mac=机器码。 Data = HEX(RC4(ApiPassword, 明文参数))。- 重新取时间戳,计算
Sign = MD5(ApiPassword + TimeStamp + Data)。 POST https://api.a7p.cn/api/verify/{ApiPassword},表单提交TimeStamp、Data、Sign。- 从响应中取出
data.token,每 5 分钟用它调一次UserHeartbeat。
完整示例代码
python
import hashlib
import json
import time
import urllib.parse
import urllib.request
BASE = "https://api.a7p.cn/api/verify"
APP_ID = "150018"
APP_KEY = "YOUR_APP_KEY"
def rc4_hex(key: str, plain: str) -> str:
"""RC4 加密并返回大写 HEX 字符串。"""
key_bytes = key.encode("utf-8")
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + key_bytes[i % len(key_bytes)]) % 256
s[i], s[j] = s[j], s[i]
i = j = 0
out = bytearray()
for b in plain.encode("utf-8"):
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])
return out.hex().upper()
def md5_hex(text: str) -> str:
return hashlib.md5(text.encode("utf-8")).hexdigest()
def post_form(url: str, fields: dict, headers: dict = None) -> dict:
body = urllib.parse.urlencode(fields).encode("utf-8")
base_headers = {"Content-Type": "application/x-www-form-urlencoded"}
if headers:
base_headers.update(headers)
req = urllib.request.Request(url, data=body, headers=base_headers)
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read().decode("utf-8"))
# ===== Step 1: Init 握手 =====
ts = int(time.time())
init_resp = post_form(BASE, {
"AppId": APP_ID,
"AppKey": APP_KEY,
"TimeStamp": ts,
"Sign": md5_hex(f"{APP_ID}{APP_KEY}{ts}{APP_KEY}"),
})
if init_resp["code"] != 200:
raise SystemExit(f"Init 失败: {init_resp['code']} {init_resp['message']}")
api_password = init_resp["data"]["api_password"]
print("ApiPassword =", api_password)
# ===== Step 2: Handle 通用调用封装 =====
def call(action: str, params: dict = None) -> dict:
pairs = [f"action={action}"]
for k, v in (params or {}).items():
pairs.append(f"{k}={urllib.parse.quote(str(v), safe='')}")
plain = "&".join(pairs)
data = rc4_hex(api_password, plain)
ts2 = int(time.time())
return post_form(BASE, {
"TimeStamp": ts2,
"Data": data,
"Sign": md5_hex(f"{api_password}{ts2}{data}"),
}, headers={"X-Api-Password": api_password})
# ===== Step 3: 卡密登录 =====
login = call("SingleLogin", {
"Card": "UTDE-7TCX-N5AT-ZDSP",
"Mac": "TEST-ABC-123",
"DeviceName": "MyPC",
})
print("SingleLogin =", login)
if login["code"] != 200:
raise SystemExit(f"登录失败: {login['code']} {login['message']}")
token = login["data"]["token"]
# ===== Step 4: 心跳保活(实际项目里放到定时器,每 5 分钟一次)=====
beat = call("UserHeartbeat", {"Token": token, "Type": "card"})
print("Heartbeat =", beat)csharp
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
public static class Quickstart
{
private const string Base = "https://api.a7p.cn/api/verify";
private const string AppId = "150018";
private const string AppKey = "YOUR_APP_KEY";
private static readonly HttpClient Http = new HttpClient
{
Timeout = TimeSpan.FromSeconds(10)
};
private static string ApiPassword = string.Empty;
/// <summary>RC4 加密并返回大写 HEX 字符串。</summary>
public static string Rc4Hex(string key, string plain)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] data = Encoding.UTF8.GetBytes(plain);
int[] s = new int[256];
for (int i = 0; i < 256; i++) s[i] = i;
for (int i = 0, j = 0; i < 256; i++)
{
j = (j + s[i] + keyBytes[i % keyBytes.Length]) % 256;
(s[i], s[j]) = (s[j], s[i]);
}
var sb = new StringBuilder(data.Length * 2);
for (int k = 0, x = 0, y = 0; k < data.Length; k++)
{
x = (x + 1) % 256;
y = (y + s[x]) % 256;
(s[x], s[y]) = (s[y], s[x]);
sb.Append((data[k] ^ s[(s[x] + s[y]) % 256]).ToString("X2"));
}
return sb.ToString();
}
public static string Md5Hex(string text)
{
using var md5 = MD5.Create();
byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(text));
var sb = new StringBuilder(32);
foreach (byte b in hash) sb.Append(b.ToString("x2"));
return sb.ToString();
}
private static long Now() => DateTimeOffset.UtcNow.ToUnixTimeSeconds();
/// <summary>Step 1: Init 握手,换取 ApiPassword。</summary>
public static async Task<bool> InitAsync()
{
long ts = Now();
var form = new Dictionary<string, string>
{
["AppId"] = AppId,
["AppKey"] = AppKey,
["TimeStamp"] = ts.ToString(),
["Sign"] = Md5Hex(AppId + AppKey + ts + AppKey)
};
var resp = await Http.PostAsync(Base, new FormUrlEncodedContent(form));
string json = await resp.Content.ReadAsStringAsync();
Console.WriteLine("Init = " + json);
// 生产环境请改用 System.Text.Json / Newtonsoft.Json 反序列化
const string marker = "\"api_password\":\"";
int start = json.IndexOf(marker, StringComparison.Ordinal);
if (start < 0) return false;
start += marker.Length;
int end = json.IndexOf('"', start);
ApiPassword = json.Substring(start, end - start);
return ApiPassword.Length > 0;
}
/// <summary>Step 2: Handle 通用调用。</summary>
public static async Task<string> CallAsync(string action, Dictionary<string, string> args)
{
var sb = new StringBuilder("action=").Append(action);
foreach (var kv in args)
{
sb.Append('&').Append(kv.Key).Append('=').Append(HttpUtility.UrlEncode(kv.Value));
}
string data = Rc4Hex(ApiPassword, sb.ToString());
long ts = Now();
var form = new Dictionary<string, string>
{
["TimeStamp"] = ts.ToString(),
["Data"] = data,
["Sign"] = Md5Hex(ApiPassword + ts + data)
};
var req = new HttpRequestMessage(HttpMethod.Post, Base)
{
Content = new FormUrlEncodedContent(form)
};
req.Headers.Add("X-Api-Password", ApiPassword);
var resp = await Http.SendAsync(req);
return await resp.Content.ReadAsStringAsync();
}
public static async Task Main()
{
if (!await InitAsync())
{
Console.WriteLine("Init 失败");
return;
}
string login = await CallAsync("SingleLogin", new Dictionary<string, string>
{
["Card"] = "UTDE-7TCX-N5AT-ZDSP",
["Mac"] = "TEST-ABC-123",
["DeviceName"] = "MyPC"
});
Console.WriteLine("SingleLogin = " + login);
// 从 login 里解析 data.token 后,每 5 分钟调用一次心跳
// string beat = await CallAsync("UserHeartbeat",
// new Dictionary<string, string> { ["Token"] = token, ["Type"] = "card" });
}
}text
.版本 2
.程序集 窗口程序集_启动窗口
.子程序 _按钮_登录_被单击
.局部变量 AppId, 文本型
.局部变量 AppKey, 文本型
.局部变量 TimeStamp, 文本型
.局部变量 Sign, 文本型
.局部变量 返回文本, 文本型
.局部变量 ApiPassword, 文本型
.局部变量 加密参数, 文本型
.局部变量 Token, 文本型
AppId = "150018"
AppKey = "你的程序密钥"
' ===== 第一步:Init 握手,换取 ApiPassword =====
TimeStamp = 到文本 (时间_取现行时间戳 ())
Sign = 取数据摘要 (到字节集 (AppId + AppKey + TimeStamp + AppKey))
返回文本 = 到文本 (网页_访问 ("https://api.a7p.cn/api/verify", 1, /
"AppId=" + AppId + "&AppKey=" + AppKey + "&TimeStamp=" + TimeStamp + "&Sign=" + Sign))
ApiPassword = JSON_解析 (返回文本, "data.api_password")
.如果真 (ApiPassword = "")
信息框 ("Init 失败:" + 返回文本, 0, )
返回 ()
.如果真结束
' ===== 第二步:卡密登录 SingleLogin =====
TimeStamp = 到文本 (时间_取现行时间戳 ())
加密参数 = RC4加密 (ApiPassword, "action=SingleLogin&Card=" + 编辑框_卡密.内容 + "&Mac=" + 取机器码 ())
Sign = 取数据摘要 (到字节集 (ApiPassword + TimeStamp + 加密参数))
返回文本 = 到文本 (网页_访问 ("https://api.a7p.cn/api/verify/" + ApiPassword, 1, /
"TimeStamp=" + TimeStamp + "&Sign=" + Sign + "&Data=" + 加密参数))
输出调试文本 (返回文本)
.如果真 (JSON_解析 (返回文本, "code") ≠ "200")
信息框 ("登录失败:" + JSON_解析 (返回文本, "message"), 0, )
返回 ()
.如果真结束
Token = JSON_解析 (返回文本, "data.token")
信息框 ("登录成功,到期时间:" + JSON_解析 (返回文本, "data.expire_at"), 0, )
' ===== 第三步:启动心跳时钟(间隔 300000 毫秒 = 5 分钟)=====
时钟_心跳.时钟周期 = 300000完整 SDK 封装
上面是「能跑起来」的最小实现。生产环境建议使用 SDK 页里的 A7Client 封装,它带了 JSON 解析、ApiPassword 自动续期、错误码映射:C# · Python · Go · Node.js · Java · C++ · 易语言
用 cURL 手工验证 Init
Init 阶段不需要 RC4,可以直接用 cURL 验证凭据是否正确:
bash
TS=$(date +%s)
APP_ID="150018"
APP_KEY="YOUR_APP_KEY"
SIGN=$(printf "%s%s%s%s" "$APP_ID" "$APP_KEY" "$TS" "$APP_KEY" | md5sum | cut -d' ' -f1)
curl -s -X POST "https://api.a7p.cn/api/verify" \
-d "AppId=${APP_ID}" \
-d "AppKey=${APP_KEY}" \
-d "TimeStamp=${TS}" \
-d "Sign=${SIGN}"预期输出:
json
{"code":200,"message":"success","data":{"api_password":"A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6"}}如果返回 1011,说明 AppKey 抄错了;返回 1010,说明签名拼接顺序有问题(注意 AppKey 要出现两次)。
常见首次接入问题
| 现象 | 原因 | 解决 |
|---|---|---|
Init 返回 1010 | 签名拼接漏了结尾的 AppKey | 顺序必须是 AppId + AppKey + TimeStamp + AppKey |
Init 返回 1014 | 用了毫秒时间戳 | 改成秒级 Unix 时间戳 |
Init 返回 1012 | 程序还没审核通过 | 等待审核 / 检查套餐是否到期 |
Handle 返回 1010 | 签名用的时间戳和提交的不是同一个 | 先算好 ts 变量,签名和提交都用它 |
Handle 返回 1015 | action 拼写或大小写错误 | 对照 action 列表,区分大小写 |
Handle 返回 1001 | ApiPassword 已失效 | 重新调用 Init 后重试一次 |
下一步
- API 概述 · 两步模型 —— 了解全部 12 个接口
- 心跳保活机制 —— 心跳频率与离线判定
- 设备指纹与硬件绑定 —— 机器码怎么取才稳定
- 易语言接入 A7验证 完整教程