PuppyIP

PuppyIP / Developer Docs

客户 API 签名文档

PuppyIP 客户 API 使用 HMAC-SHA256 签名。每个请求都需要带上 App ID、时间戳、随机 nonce 和签名,服务端会校验请求内容、防重放并按 API Key 权限放行。

当前可用接口

现在客户 API 只开放只读账号接口:GET /api/v1/account。API 下单、续费、充值、修改代理账号密码和切换 IP 暂未开放。

API Key 只代表 PuppyIP 客户 API 凭证,客户请求和文档中只使用 PuppyIP 发放的 App ID 和 API Secret。生产 App ID 以 live_app_ 开头,沙盒 App ID 以 sbx_app_ 开头,两个环境不能混用。

请求头

Header 说明 示例
X-API-AppId 账号页生成的 App ID。 live_app_01J2EXAMPLE000000000000000
X-API-Timestamp 绝对时间戳,必须带时区,允许 5 分钟内的时钟偏差。 2026-06-28T12:00:00+00:00
X-API-Nonce 每次请求唯一随机值,重复使用会被拒绝。 nonce-empty-body
X-API-Signature 用 API Secret 对 canonical string 计算出的 HMAC-SHA256 小写十六进制值。 e3c27a2055ad38f16861790294802b9fac81e02e8d72fbeaa87299598785832d

Canonical String

签名前先把请求拆成 7 行,用换行符 \n 连接:

METHOD
host
path
canonical_query
timestamp
nonce
sha256(body)

METHOD 使用大写,例如 GET

host 使用小写,不包含协议,例如 puppyip.com

path 必须包含开头斜杠,例如 /api/v1/account

canonical_query 对 query 的 key 和 value 分别 URL 解码后再 rawurlencode,按 key、value 升序排序,重复 key 不合并。没有 query 时这一行留空。

body 使用原始请求体计算 SHA-256。GET 请求没有 body 时,使用空字符串的 SHA-256。

GET /api/v1/account 示例

GET
puppyip.com
/api/v1/account

2026-06-28T12:00:00+00:00
nonce-empty-body
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

上面这组固定示例使用文档测试 Secret puppyip-docs-test-secret 计算出的签名是 e3c27a2055ad38f16861790294802b9fac81e02e8d72fbeaa87299598785832d

代码示例

以下示例只使用假 App ID 和假 Secret。请不要把真实 API Secret 写入前端页面、公开仓库或日志。

curl

curl https://puppyip.com/api/v1/account \
  -H "X-API-AppId: live_app_01J2EXAMPLE000000000000000" \
  -H "X-API-Timestamp: 2026-06-28T12:00:00+00:00" \
  -H "X-API-Nonce: nonce-empty-body" \
  -H "X-API-Signature: e3c27a2055ad38f16861790294802b9fac81e02e8d72fbeaa87299598785832d"

PHP

$method = 'GET';
$host = 'puppyip.com';
$path = '/api/v1/account';
$query = '';
$timestamp = gmdate('Y-m-d\TH:i:s\Z');
$nonce = 'nce_'.bin2hex(random_bytes(16));
$body = '';
$secret = getenv('PUPPYIP_API_SECRET') ?: '';

$canonical = implode("\n", [
    $method,
    strtolower($host),
    $path,
    $query,
    $timestamp,
    $nonce,
    hash('sha256', $body),
]);

$signature = hash_hmac('sha256', $canonical, $secret);

Node.js

import crypto from 'node:crypto';

const method = 'GET';
const host = 'puppyip.com';
const path = '/api/v1/account';
const query = '';
const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
const nonce = `nce_${crypto.randomBytes(16).toString('hex')}`;
const body = '';
const secret = process.env.PUPPYIP_API_SECRET ?? '';

const canonical = [
  method,
  host.toLowerCase(),
  path,
  query,
  timestamp,
  nonce,
  crypto.createHash('sha256').update(body).digest('hex'),
].join('\n');

const signature = crypto
  .createHmac('sha256', secret)
  .update(canonical)
  .digest('hex');

常见错误

invalid_signature
签名缺失、App ID 错误、canonical string 拼接不一致,或使用了错误的 API Secret。
stale_timestamp
时间戳没有时区、格式不是 ISO 8601,或客户端时间与服务器相差超过 5 分钟。
replayed_nonce
同一个 API Key 下重复使用了相同 nonce。每次请求都应该生成新的随机 nonce。
insufficient_scope
API Key 没有所需权限,或请求来源 IP 不在允许范围内。