开发文档 {{ tabNames[tab] }}
返回后台
主题
V2 授权验证系统采用"客户端采集 + 服务端判定"架构,支持 Windows / Android 双端,RSA-2048加密传输,JWT令牌认证

接入流程

1
初始化
调用 init.php 接口获取RSA公钥,用于后续数据加密传输。这是接入的第一步,必须先完成初始化才能进行后续操作。
2
采集指纹
采集设备硬件信息生成唯一机器码。Windows采集CPU、硬盘、MAC地址、主板序列号;Android采集Android ID、设备品牌、型号等。
3
激活授权
调用 activate.php 接口,使用授权码+机器指纹激活,成功后获取JWT令牌和刷新令牌,令牌有效期24小时。
4
心跳保活
调用 heartbeat.php 接口,固定每5秒发送一次心跳维持在线状态。连续15秒无心跳将被判定为离线。

统一响应格式

所有API接口返回统一的JSON格式,包含code、message/msg、data、timestamp字段
JSON
{ "code": 200, // 状态码,200表示成功 "message": "成功", // 消息说明 "msg": "成功", // 消息说明(兼容字段) "data": { ... }, // 业务数据 "timestamp": 1705123456 // 服务器时间戳 }

注意:message 和 msg 字段内容相同,为兼容不同版本客户端,建议优先使用 message 字段

POST
http://ape.cqyidc.com/api/v2/init.php

获取RSA公钥和服务器配置,这是接入的第一步。

请求参数

响应示例

JSON复制
{ "code": 200, "message": "成功", "data": { "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhki...\n-----END PUBLIC KEY-----", "server_time": 1705123456, "heartbeat_interval": 5 } }
POST
http://ape.cqyidc.com/api/v2/activate.php
data字段需要RSA公钥加密后Base64编码,app_key明文传输

请求参数(外层)

加密数据内容(data解密后)

fingerprint 必须包含 final 和 components 两个字段,components 至少需要2个组件,每个组件值支持32位MD5或64位SHA256十六进制哈希

fingerprint 对象结构

JSON复制
{ "final": "32位MD5或64位SHA256十六进制字符串", "components": { "cpu": "32位MD5或64位SHA256(CPU序列号的哈希)", "mac": "32位MD5或64位SHA256(MAC地址的哈希)", "disk": "可选 - 硬盘序列号的哈希", "board": "可选 - 主板序列号的哈希" } } // 示例1(易语言SDK - 使用MD5,2个组件): { "final": "df2c7aa05ffabd0787db4d86feeb909e", "components": { "cpu": "b014b7d94b33ea5181da4e54ca40730d", "mac": "141df94074a9704483b51d2fe774e6ca" } } // final 计算方法: MD5(cpu哈希 + mac哈希) // 示例2(完整版 - 使用SHA256,4个组件): { "final": "a1b2c3d4e5f6...共64位", "components": { "cpu": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "disk": "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", "mac": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "board": "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2" } } // final 计算方法: SHA256(cpu + disk + mac + board)

device_info 对象结构

JSON复制
{ "platform": "windows", // 平台: windows / android "os_version": "Windows 10", // 操作系统版本 "is_virtual": false, // 是否虚拟机 "virtual_type": "" // 虚拟机类型: vmware/virtualbox/hyper-v 等 }

响应示例

JSON复制
{ "code": 200, "message": "激活成功", "data": { "token": "eyJhbGciOiJSUzI1NiJ9...", "refresh_token": "rf_abc123...", "expire_time": "2026-02-13 12:00:00", "remain_days": 30 } }
POST
http://ape.cqyidc.com/api/v2/heartbeat.php
心跳间隔固定5秒,连续15秒无心跳判定离线

请求参数

响应示例

JSON复制
{ "code": 200, "data": { "status": "online", "server_time": 1705123456, "expire_time": "2026-02-13 12:00:00", "remain_seconds": 2592000, "cloud": { "vars": { "download_url": "https://example.com/download", "notice_text": "欢迎使用" }, "announcements": [ { "id": 1, "title": "系统维护通知", "content": "系统将于今晚22:00-24:00进行维护升级,期间可能影响正常使用,请提前保存工作。", "type": "notice", "is_popup": 1, "show_once": 1 }, { "id": 2, "title": "新版本发布", "content": "v2.1.0版本已发布,修复了若干已知问题,建议及时更新。", "type": "update", "is_popup": 0, "show_once": 1 } ], "latest_version": { "version": "2.0.0", "version_code": 200, "download_url": "https://...", "force_update": 0 }, "switches": { "feature_a": true, "maintenance": false } } } }

云控制数据说明

公告处理示例

公告数据在心跳响应的 cloud.announcements 数组中,客户端应根据 type 和 is_popup 字段进行相应处理
C#复制
// 已读公告ID列表(建议持久化存储到本地文件或注册表) private static HashSet<int> _readAnnouncementIds = new HashSet<int>(); private static bool _isFirstHeartbeat = true; // 处理心跳响应中的公告数据 if (response.data.cloud?.announcements != null) { foreach (var announcement in response.data.cloud.announcements) { int id = announcement.id; int showOnce = announcement.show_once; // 1=只显示一次, 0=每次启动显示 int isPopup = announcement.is_popup; // 判断是否需要显示 bool shouldShow = false; if (showOnce == 1) { // 只显示一次模式:检查是否已读 if (!_readAnnouncementIds.Contains(id)) { shouldShow = true; } } else { // 每次启动显示模式:只在首次心跳时显示 if (_isFirstHeartbeat) { shouldShow = true; } } if (shouldShow) { string typeText = announcement.type switch { "notice" => "通知", "warning" => "警告", "update" => "更新", "ad" => "广告", _ => "公告" }; if (isPopup == 1) { // 弹窗显示 MessageBox.Show( announcement.content, $"[{typeText}] {announcement.title}", MessageBoxButtons.OK, announcement.type == "warning" ? MessageBoxIcon.Warning : MessageBoxIcon.Information ); } else { // 添加到公告列表 AddAnnouncementToList(id, announcement.title, announcement.content, announcement.type); } // 标记为已读(只对show_once=1的公告记录) if (showOnce == 1) { _readAnnouncementIds.Add(id); SaveReadIds(); // 持久化保存 } } } _isFirstHeartbeat = false; }
Java (Android)复制
// 处理心跳响应中的公告数据 JSONArray announcements = cloudData.optJSONArray("announcements"); if (announcements != null) { for (int i = 0; i < announcements.length(); i++) { JSONObject announcement = announcements.getJSONObject(i); int id = announcement.getInt("id"); String title = announcement.getString("title"); String content = announcement.getString("content"); String type = announcement.getString("type"); int isPopup = announcement.getInt("is_popup"); // 如果需要弹窗显示 if (isPopup == 1) { // 在主线程显示弹窗 runOnUiThread(() -> { new AlertDialog.Builder(this) .setTitle(getTypeText(type) + " " + title) .setMessage(content) .setPositiveButton("确定", null) .show(); }); } else { // 添加到公告列表 addAnnouncementToList(id, title, content, type); } } } private String getTypeText(String type) { switch (type) { case "notice": return "📢 通知"; case "warning": return "⚠️ 警告"; case "update": return "🔄 更新"; case "ad": return "📺 广告"; default: return "📋 公告"; } }
POST
http://ape.cqyidc.com/api/v2/refresh.php

JWT令牌24小时有效,过期后1小时内可刷新

请求参数

响应示例

JSON复制
{ "code": 200, "message": "刷新成功", "data": { "token": "eyJhbGciOiJSUzI1NiJ9...", "refresh_token": "rf_new123..." } }
POST
http://ape.cqyidc.com/api/v2/trial.php
每个设备只能试用一次,data字段需RSA加密

请求参数(外层)

加密数据内容

响应示例

JSON复制
{ "code": 200, "message": "试用激活成功", "data": { "token": "eyJhbGciOiJSUzI1NiJ9...", "refresh_token": "rf_trial...", "expire_time": "2026-01-13 12:30:00", "remain_seconds": 1800, "is_trial": true } }
POST
http://ape.cqyidc.com/api/v2/deduct_points.php
点卡扣点接口,用于按次或按时扣除用户点数

请求参数

响应示例

JSON复制
{ "code": 200, "msg": "扣点成功", "data": { "remaining_points": 99, "deduct_amount": 1, "before_points": 100 } }
POST
http://ape.cqyidc.com/api/v2/query_points.php
查询点卡剩余点数和使用情况

请求参数

响应示例

JSON复制
{ "code": 200, "msg": "查询成功", "data": { "card_type": 1, "total_points": 100, "remaining_points": 99, "used_points": 1, "deduct_type": "per_use", "expire_time": "2026-12-31 23:59:59" } }
POST
http://ape.cqyidc.com/api/v2/recharge_points.php
使用充值卡密为现有授权充值点数,充值卡密使用后自动失效

请求参数

响应示例

JSON复制
{ "code": 200, "msg": "充值成功", "data": { "before_points": 99, "recharge_points": 100, "after_points": 199 } }

防重放攻击

所有请求需携带 timestamp + nonce + sign 三个参数

重要:签名算法只拼接参数的值(value),不包含参数名(key) 服务器同时支持 SHA256(推荐)和 MD5(兼容模式)两种签名算法。
• SHA256:64位十六进制字符串,安全性更高,推荐使用
• MD5:32位十六进制字符串,兼容易语言等不方便使用SHA256的客户端
签名算法步骤复制
1. 将业务参数按key字母顺序排序 2. 只拼接参数的值(不是key=value格式) 3. 拼接字符串: timestamp + nonce + 参数值 + client_secret 4. 计算哈希得到sign(SHA256推荐,MD5兼容) 示例(激活接口): 业务参数: app_key="SK_abc123", auth_code="AUTH001" 排序后: app_key, auth_code(按字母顺序) 拼接值: "SK_abc123" + "AUTH001" = "SK_abc123AUTH001" 最终字符串: "1705123456" + "randomnonce123" + "SK_abc123AUTH001" + "your_client_secret" SHA256签名: sign = SHA256("1705123456randomnonce123SK_abc123AUTH001your_client_secret") MD5签名: sign = MD5("1705123456randomnonce123SK_abc123AUTH001your_client_secret") 示例(心跳接口): 业务参数: token="eyJhbGci..." 最终字符串: timestamp + nonce + token值 + client_secret sign = SHA256或MD5("1705123456randomnonce123eyJhbGci...your_client_secret")

各接口签名参数

机器指纹采集

指纹容错:当硬件变更导致指纹匹配率≥80%时触发人工审核,低于80%则拒绝
易语言SDK已单独开发完成,如需获取完整示例工程,请联系鼠大侠获取。

SDK功能说明

精易模块、spec支持库、EThread多线程支持库 MD5签名(服务端同时支持MD5和SHA256) 明文模式(不使用RSA加密,适合易语言环境) 初始化、激活授权、心跳保活、机器指纹采集
以下是完整的SDK实现代码,可直接复制使用。代码中的API地址已自动替换为当前服务器地址。
{{ sdkLangs[tab] }}
{{ codes[tab] }}