应用接口 POST

应用通用接口,支持多种操作类型。通过act参数指定操作。

标识:app · 参数 6 个 · Content-Type:application/x-www-form-urlencoded

请求方法POST
认证方式V1 MD5 参数签名
请求格式application/x-www-form-urlencoded
成功判断HTTP 2xx,并按响应 code 与 data 判断
请求地址
POST https://gnyanzheng.cn/api/app

请将域名替换为实际部署域名,并保持本页显示的请求路径和请求方法不变。

认证方式
参数名类型必填说明示例
appstring必填 程序 ID(legacy_appid),兼容 appidYOUR_APP_ID
tnumber视配置 秒级时间戳,兼容 timestamp1720000000
sstring视配置 签名,兼容 sign;开启签名时必填A1B2...
safe_codestring选填 不参与签名,可原样回传hello
app必填
程序 ID
t视配置
秒级时间戳
s视配置
MD5 签名(大写)
safe_code选填
不参与签名
查看完整签名算法 →
业务参数
参数名类型必填标签说明示例值
act string 必填 操作类型 操作类型: notice=获取公告, ini=获取配置, userreg=注册账号, userlogon=登录账号, logongetapp=获取应用信息, kmlogon=卡密登录, kmunmachine=卡密解绑 ini
kami string 选填 卡密 kmlogon/kmunmachine时必填 ABC123XYZ789
imei string 选填 设备码 kmlogon/kmunmachine时必填,设备唯一标识 device_001
user string 选填 用户名 userreg/userlogon时必填 testuser
pwd string 选填 密码 userreg/userlogon时必填 pass123
act 必填
操作类型: notice=获取公告, ini=获取配置, userreg=注册账号, userlogon=登录账号, logongetapp=获取应用信息, kmlogon=卡密登录, kmunmachine=卡密解绑
示例:ini
kami 选填
kmlogon/kmunmachine时必填
示例:ABC123XYZ789
imei 选填
kmlogon/kmunmachine时必填,设备唯一标识
示例:device_001
user 选填
userreg/userlogon时必填
示例:testuser
pwd 选填
userreg/userlogon时必填
示例:pass123
最小业务字段(另加上方认证参数)
Content-Type: application/x-www-form-urlencoded
app=YOUR_APP_ID&act=ini
响应与成功判断

客户端必须按此判断:HTTP 2xx,并按响应 code 与 data 判断。收到 HTTP 200 或收到 JSON,本身都不等于业务成功。

成功响应
{ "code": 1, "msg": "ok", "data": { "version_name": "1.0.0", "version_code": 1, "announcement": "系统公告" } }
失败响应示例
{ "code": 403, "msg": "请求签名校验失败。", "data": null, "request_id": "01JEXAMPLE123456789" }

request_id 用于平台故障定位。业务错误先按 code/msg 或 code/reason 处理;联系作者、客服或管理员时再提供请求 ID。

可复制代码示例
/* 接口: app
 * 依赖: libcurl + OpenSSL
 * 编译: gcc gn_demo.c -o gn_demo -lcurl -lcrypto
 * 签名: ksort 字典序, 排除 s/sign/safe_code/_ 前缀与空值, MD5(拼接+密钥) 大写
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <curl/curl.h>
#include <openssl/md5.h>

typedef struct { const char *k; const char *v; } kv_t;

static int kv_cmp(const void *a, const void *b) {
    return strcmp(((const kv_t *)a)->k, ((const kv_t *)b)->k);
}

static int should_skip_sign_key(const char *k) {
    if (!k) return 1;
    if (strcmp(k, "s") == 0 || strcmp(k, "sign") == 0 || strcmp(k, "safe_code") == 0) return 1;
    if (k[0] == '_') return 1;
    return 0;
}

static void md5_upper_hex(const char *src, char out[33]) {
    unsigned char dig[MD5_DIGEST_LENGTH];
    MD5((const unsigned char *)src, strlen(src), dig);
    for (int i = 0; i < MD5_DIGEST_LENGTH; i++) sprintf(out + i * 2, "%02X", dig[i]);
    out[32] = '\0';
}

struct mem_buf { char *data; size_t len; };
static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
    size_t n = size * nmemb;
    struct mem_buf *m = (struct mem_buf *)userdata;
    char *p = (char *)realloc(m->data, m->len + n + 1);
    if (!p) return 0;
    m->data = p;
    memcpy(m->data + m->len, ptr, n);
    m->len += n;
    m->data[m->len] = '\0';
    return n;
}

int main(void) {
    const char *api_url  = "https://gnyanzheng.cn/api/app";
    const char *sign_key = "YOUR_SIGN_KEY";
    char tbuf[32];
    snprintf(tbuf, sizeof(tbuf), "%ld", (long)time(NULL));

    kv_t items[] = {
    {"app", "YOUR_APP_ID"},
    {"act", "ini"},
    {"kami", "ABC123XYZ789"},
    {"imei", "device_001"},
    {"user", "testuser"},
    {"pwd", "pass123"},
    {"t", tbuf},
    };
    size_t n = sizeof(items) / sizeof(items[0]);
    qsort(items, n, sizeof(kv_t), kv_cmp);

    /* 拼签名串 */
    char sign_src[4096];
    sign_src[0] = '\0';
    for (size_t i = 0; i < n; i++) {
        if (should_skip_sign_key(items[i].k)) continue;
        if (!items[i].v || items[i].v[0] == '\0') continue;
        if (sign_src[0]) strncat(sign_src, "&", sizeof(sign_src) - strlen(sign_src) - 1);
        strncat(sign_src, items[i].k, sizeof(sign_src) - strlen(sign_src) - 1);
        strncat(sign_src, "=", sizeof(sign_src) - strlen(sign_src) - 1);
        strncat(sign_src, items[i].v, sizeof(sign_src) - strlen(sign_src) - 1);
    }
    strncat(sign_src, sign_key, sizeof(sign_src) - strlen(sign_src) - 1);
    char sign_hex[33];
    md5_upper_hex(sign_src, sign_hex);

    /* 组 form body */
    char body[8192];
    body[0] = '\0';
    CURL *curl = curl_easy_init();
    if (!curl) return 1;
    for (size_t i = 0; i < n; i++) {
        char *ek = curl_easy_escape(curl, items[i].k, 0);
        char *ev = curl_easy_escape(curl, items[i].v ? items[i].v : "", 0);
        if (body[0]) strncat(body, "&", sizeof(body) - strlen(body) - 1);
        strncat(body, ek, sizeof(body) - strlen(body) - 1);
        strncat(body, "=", sizeof(body) - strlen(body) - 1);
        strncat(body, ev, sizeof(body) - strlen(body) - 1);
        curl_free(ek); curl_free(ev);
    }
    {
        char *ek = curl_easy_escape(curl, "s", 0);
        char *ev = curl_easy_escape(curl, sign_hex, 0);
        strncat(body, "&", sizeof(body) - strlen(body) - 1);
        strncat(body, ek, sizeof(body) - strlen(body) - 1);
        strncat(body, "=", sizeof(body) - strlen(body) - 1);
        strncat(body, ev, sizeof(body) - strlen(body) - 1);
        curl_free(ek); curl_free(ev);
    }

    struct mem_buf resp = {0};
    curl_easy_setopt(curl, CURLOPT_URL, api_url);
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
    CURLcode rc = curl_easy_perform(curl);
    if (rc != CURLE_OK) {
        fprintf(stderr, "curl error: %s\n", curl_easy_strerror(rc));
    } else {
        printf("%s\n", resp.data ? resp.data : "");
    }
    free(resp.data);
    curl_easy_cleanup(curl);
    return 0;
}
/* 接口: app
 * 依赖: libcurl + OpenSSL
 * 编译: g++ gn_demo.cpp -o gn_demo -lcurl -lcrypto -std=c++17
 */
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <ctime>
#include <curl/curl.h>
#include <openssl/md5.h>

static std::string md5_upper(const std::string &src) {
    unsigned char dig[MD5_DIGEST_LENGTH];
    MD5(reinterpret_cast<const unsigned char *>(src.data()), src.size(), dig);
    char out[33];
    for (int i = 0; i < MD5_DIGEST_LENGTH; ++i) std::sprintf(out + i * 2, "%02X", dig[i]);
    out[32] = '\0';
    return out;
}

static bool skip_sign_key(const std::string &k) {
    return k == "s" || k == "sign" || k == "safe_code" || (!k.empty() && k[0] == '_');
}

static std::string build_sign(std::map<std::string, std::string> params, const std::string &sign_key) {
    std::string s;
    for (const auto &kv : params) {
        if (skip_sign_key(kv.first) || kv.second.empty()) continue;
        if (!s.empty()) s += '&';
        s += kv.first + "=" + kv.second;
    }
    s += sign_key;
    return md5_upper(s);
}

static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
    auto *out = static_cast<std::string *>(userdata);
    out->append(ptr, size * nmemb);
    return size * nmemb;
}

static std::string http_post_form(const std::string &url, const std::map<std::string, std::string> &fields) {
    CURL *curl = curl_easy_init();
    if (!curl) return {};
    std::string body;
    for (const auto &kv : fields) {
        char *ek = curl_easy_escape(curl, kv.first.c_str(), 0);
        char *ev = curl_easy_escape(curl, kv.second.c_str(), 0);
        if (!body.empty()) body += '&';
        body += ek; body += '='; body += ev;
        curl_free(ek); curl_free(ev);
    }
    std::string resp;
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
    curl_easy_perform(curl);
    curl_easy_cleanup(curl);
    return resp;
}

int main() {
    const std::string api_url  = "https://gnyanzheng.cn/api/app";
    const std::string sign_key = "YOUR_SIGN_KEY";

    std::map<std::string, std::string> fields;
    fields["app"] = "YOUR_APP_ID";
    fields["act"] = "ini";
    fields["kami"] = "ABC123XYZ789";
    fields["imei"] = "device_001";
    fields["user"] = "testuser";
    fields["pwd"] = "pass123";
    fields["t"] = std::to_string(std::time(nullptr));
    fields["s"] = build_sign(fields, sign_key);

    std::string body = http_post_form(api_url, fields);
    std::printf("%s\n", body.c_str());
    return 0;
}
# 1) 先按签名算法计算 s(见「签名算法」文档)
# 2) 用 form 提交(Content-Type: application/x-www-form-urlencoded)

curl -X POST "https://gnyanzheng.cn/api/app" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "app=YOUR_APP_ID" \
  --data-urlencode "act=ini" \
  --data-urlencode "kami=ABC123XYZ789" \
  --data-urlencode "imei=device_001" \
  --data-urlencode "user=testuser" \
  --data-urlencode "pwd=pass123" \
  --data-urlencode "t=1720000000" \
  --data-urlencode "s=YOUR_MD5_SIGN_UPPER"
<?php
$apiUrl = 'http://your-domain.com/api.php?r=/app';
$appId = 'YOUR_APP_ID';
$signKey = 'YOUR_SIGN_KEY';

$params = [
    'app' => $appId,
    'act'  => 'ini',
    't' => time(),
];

ksort($params);
$signParts = [];
foreach ($params as $k => $v) {
    if ($k === 's' || $k === 'safe_code' || str_starts_with($k, '_')) continue;
    $signParts[] = $k . '=' . $v;
}
$signStr = implode('&', $signParts);
$params['s'] = md5($signStr . $signKey);

$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => http_build_query($params),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 30,
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
if ($result['code'] === 1) {
    echo "操作成功" . PHP_EOL;
    print_r($result['data']);
} else {
    echo "操作失败: " . $result['msg'] . PHP_EOL;
}
常见错误码
code含义处理
0 成功(卡密登录 / 域名验证 / 探测 / 空结果) card_login 单码设备登录、check 域名验证、system 探测、暂无公告/版本等。客户端:code===0 且看 msg
1 成功(通用业务) 解绑、用户注册/登录、公告有数据、版本有数据、ini 等。客户端:code===1 或用 isSuccess(0|1)
2 业务失败 卡密不存在、已封禁、设备上限、参数不全、额度不足等,以 msg 为准
101 缺少或无效 app(程序 ID) 检查 legacy_appid / 参数 app
102 应用已关闭 项目 status 非 active
103 验证功能未开启 项目关闭 verify_enabled,或 notauth_msg
172 接口未开通 / 不存在 / 未开放 用户中心→接口中心开通;或 act/api 名称错误
-1 访问拒绝 签名失败、时间戳过期/缺失、密钥未配置、通用拒绝
-3 授权 Key 已到期 legacy.core 站点 key 过期
-4 IP 策略拒绝 黑名单命中、不在白名单、或 IP 与绑定不符
-8 请求过于频繁 项目 rate_limit(次/分钟)触发,稍后重试
401 需要登录 管理端内部 API 等
404 接口或资源不存在 路径或 apikey 错误
501 动态路由暂不支持 legacy 动态映射未实现
注意事项
  • 应用须处于 active 状态,且已在接口中心开通本接口(否则返回码为 172)。
  • 签名、时间戳、IP 策略以项目安全配置为准。
  • 客户端建议 30 秒超时;失败优先展示服务端 msg。
  • C/C++ 使用 libcurl;Legacy V1 参数签名通过 OpenSSL 计算大写 MD5。
已复制