下载接口 POST
文件下载接口
请求方法POST
认证方式V1 MD5 参数签名
请求格式application/x-www-form-urlencoded
成功判断HTTP 2xx,并按响应 code 与 data 判断
请求地址
POST
https://gnyanzheng.cn/api/download/YOUR_FILE_ID
认证方式
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
app | string | 必填 | 程序 ID(legacy_appid),兼容 appid | YOUR_APP_ID |
t | number | 视配置 | 秒级时间戳,兼容 timestamp | 1720000000 |
s | string | 视配置 | 签名,兼容 sign;开启签名时必填 | A1B2... |
safe_code | string | 选填 | 不参与签名,可原样回传 | hello |
app必填
程序 ID
t视配置
秒级时间戳
s视配置
MD5 签名(大写)
safe_code选填
不参与签名
响应与成功判断
客户端必须按此判断:HTTP 2xx,并按响应 code 与 data 判断。收到 HTTP 200 或收到 JSON,本身都不等于业务成功。
失败响应示例
{
"code": 403,
"msg": "请求签名校验失败。",
"data": null,
"request_id": "01JEXAMPLE123456789"
}
可复制代码示例
/* 接口: download
* 依赖: 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/download/YOUR_FILE_ID";
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"},
{"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;
}
/* 接口: download
* 依赖: 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/download/YOUR_FILE_ID";
const std::string sign_key = "YOUR_SIGN_KEY";
std::map<std::string, std::string> fields;
fields["app"] = "YOUR_APP_ID";
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/download/YOUR_FILE_ID" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "app=YOUR_APP_ID" \
--data-urlencode "t=1720000000" \
--data-urlencode "s=YOUR_MD5_SIGN_UPPER"
<?php
// 接口: download
// 签名与平台一致:ksort → key=value&...(排除 s/sign/safe_code/_ 前缀/空值)→ MD5 大写
$apiUrl = 'https://gnyanzheng.cn/api/download/YOUR_FILE_ID';
$signKey = 'YOUR_SIGN_KEY';
$params = [
'app' => 'YOUR_APP_ID',
't' => time(),
];
ksort($params);
$parts = [];
foreach ($params as $k => $v) {
if ($k === 's' || $k === 'sign' || $k === 'safe_code' || str_starts_with((string) $k, '_')) {
continue;
}
if ($v === '' || $v === null) {
continue;
}
$parts[] = $k . '=' . $v;
}
$params['s'] = strtoupper(md5(implode('&', $parts) . $signKey));
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($params),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
curl_close($ch);
$result = json_decode((string) $body, true);
var_export($result);
常见错误码
| 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。