Skip to content

5 分钟接入示例

本页给出一份可以直接复制运行的最小接入代码,跑通「Init 握手 → SingleLogin 卡密登录 → UserHeartbeat 心跳」三步。

开始之前请先准备好 AppIdAppKey,参见 获取 AppID 与 API 密钥

调用步骤

  1. 取当前 Unix 秒级时间戳 TimeStamp
  2. 计算 Sign = MD5(AppId + AppKey + TimeStamp + AppKey)
  3. POST https://api.a7p.cn/api/verify,表单提交 AppIdAppKeyTimeStampSign
  4. 从响应中取出 data.api_password,存进内存变量 ApiPassword
  5. 拼接明文参数 action=SingleLogin&Card=卡密&Mac=机器码
  6. Data = HEX(RC4(ApiPassword, 明文参数))
  7. 重新取时间戳,计算 Sign = MD5(ApiPassword + TimeStamp + Data)
  8. POST https://api.a7p.cn/api/verify/{ApiPassword},表单提交 TimeStampDataSign
  9. 从响应中取出 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 返回 1015action 拼写或大小写错误对照 action 列表区分大小写
Handle 返回 1001ApiPassword 已失效重新调用 Init 后重试一次

更多请看 完整错误码对照表常见问题 FAQ

下一步

A7验证 · 软件授权与网络验证平台