接入流程
1
初始化
调用 init.php 接口获取RSA公钥,用于后续数据加密传输。这是接入的第一步,必须先完成初始化才能进行后续操作。
2
采集指纹
采集设备硬件信息生成唯一机器码。Windows采集CPU、硬盘、MAC地址、主板序列号;Android采集Android ID、设备品牌、型号等。
3
激活授权
调用 activate.php 接口,使用授权码+机器指纹激活,成功后获取JWT令牌和刷新令牌,令牌有效期24小时。
4
心跳保活
调用 heartbeat.php 接口,固定每5秒发送一次心跳维持在线状态。连续15秒无心跳将被判定为离线。
统一响应格式
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解密后)
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
请求参数
响应示例
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
}
}
}
}
云控制数据说明
公告处理示例
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
请求参数(外层)
加密数据内容
响应示例
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 三个参数
• 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")
各接口签名参数
机器指纹采集
{{ sdkNames[tab] }} SDK 完整示例
复制完整代码
SDK功能说明
{{ sdkLangs[tab] }}
{{ codes[tab] }}