HMAC 认证
本页详细介绍用于 SlaunchX API 请求认证的完整 HMAC-SHA256 签名计算流程。所有对 /api/** 端点的请求都必须使用此流程进行签名。
必需的请求头
每个 API 请求必须包含以下请求头:
| 请求头 | 必需 | 说明 |
|---|---|---|
X-Api-Key | 是 | 您的 API 密钥标识符(例如 sk_live_abc123) |
X-Timestamp | 是 | 当前 Unix 时间戳(秒,UTC)。必须与服务器时间相差不超过 60 秒 |
X-Nonce | 是 | 唯一请求标识符。建议使用 UUID v4 以确保唯一性。每个 nonce 在时间戳窗口内只能使用一次 |
X-Signature | 是 | 规范消息的 HMAC-SHA256 签名,Base64 编码。详见下方 签名计算 |
X-LOCALE | 否 | 首选响应语言(例如 en-US、zh-CN)。影响本地化错误消息 |
Content-Type | 按需 | application/json — 对于包含请求体的请求(POST、PUT、PATCH)必需 |
签名计算
签名分三步计算:对请求体进行哈希、构建规范消息字符串、计算 HMAC。
第一步:计算请求体哈希
计算原始请求体的 SHA-256 哈希值,并将其编码为小写十六进制字符串。
bodyHash = lowercase_hex(SHA-256(requestBody))对于包含请求体的请求(POST、PUT、PATCH),对您将发送的完整 JSON 字符串进行哈希:
echo -n '{"sourceWalletId":"w_123","amount":"100.00"}' | openssl dgst -sha256 -hex
# 输出: a1fce4363854ff888cff4b8e7875d600c...对于无请求体的请求(GET、DELETE),使用空字符串的 SHA-256 哈希值:
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855:::caution 请求体编码至关重要 请求体哈希是基于 HTTP 请求体中发送的 精确字节序列 计算的。如果您的 JSON 序列化器生成 {"a": 1}(冒号后有空格),但您哈希的是 {"a":1}(没有空格),签名将不匹配。请务必对传递给 HTTP 客户端的相同字符串进行哈希。 :::
第二步:构建规范消息
将五个组件用换行符(\n)连接:
canonicalMessage = method + "\n" + path + "\n" + timestamp + "\n" + nonce + "\n" + bodyHash| 组件 | 说明 | 示例 |
|---|---|---|
method | HTTP 方法,大写 | POST |
path | 仅请求路径——不包含协议、主机或查询字符串 | /api/v1/transfers |
timestamp | Unix 时间戳(秒,UTC),与 X-Timestamp 请求头的值相同 | 1709337600 |
nonce | 唯一标识符,与 X-Nonce 请求头的值相同(建议使用 UUID v4) | 550e8400-e29b-41d4-a716-446655440000 |
bodyHash | 第一步中的 SHA-256 十六进制摘要 | a1fce436... |
对于 POST /api/v1/transfers 请求,规范消息如下所示:
POST
/api/v1/transfers
1709337600
550e8400-e29b-41d4-a716-446655440000
a1fce4363854ff888cff4b8e7875d600c...:::caution 路径不能包含查询字符串 对于带有查询参数的 GET 请求(例如 /api/v1/wallets?page=0&size=20),在规范消息中只包含 路径 部分:/api/v1/wallets。查询参数不参与签名计算。 :::
第三步:计算 HMAC 签名
使用 HMAC-SHA256 和您的 API 密钥对规范消息进行签名,然后对结果进行 Base64 编码:
signature = Base64(HMAC-SHA256(canonicalMessage, apiSecret))生成的 Base64 字符串放入 X-Signature 请求头。
完整示例
Bash + curl
以下脚本演示了一个完整的签名 API 请求:
#!/bin/bash
# SlaunchX API 集成 -- HMAC-SHA256 认证示例
# ─── 配置 ────────────────────────────────────────────
API_KEY="sk_live_abc123def456"
API_SECRET="your-api-secret-here"
BASE_URL="https://api.slaunchx.cc" # 品牌专属:替换为您的 API 主机地址
# ─── 请求参数 ──────────────────────────────────────
METHOD="POST"
PATH_URI="/api/v1/transfer/command/create"
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]')
# 请求体(必须是通过网络发送的精确字符串)
BODY='{"sourceWalletId":"w_123","targetWalletId":"w_456","amount":"100.00","currency":"USD"}'
# ─── 第一步:请求体哈希 ───────────────────────────────────
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')
# ─── 第二步:规范消息 ────────────────────────────────
CANONICAL="${METHOD}\n${PATH_URI}\n${TIMESTAMP}\n${NONCE}\n${BODY_HASH}"
# ─── 第三步:HMAC-SHA256 签名 ────────────────────────
SIGNATURE=$(echo -ne "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" -binary | openssl base64 -A)
# ─── 发送请求 ─────────────────────────────────────────
curl -X "$METHOD" "${BASE_URL}${PATH_URI}" \
-H "Content-Type: application/json" \
-H "X-Api-Key: $API_KEY" \
-H "X-Signature: $SIGNATURE" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $NONCE" \
-d "$BODY"对于 GET 请求,省略 -d 参数并使用空字符串的请求体哈希:
METHOD="GET"
PATH_URI="/api/v1/wallets"
BODY=""
BODY_HASH="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
CANONICAL="${METHOD}\n${PATH_URI}\n${TIMESTAMP}\n${NONCE}\n${BODY_HASH}"
SIGNATURE=$(echo -ne "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" -binary | openssl base64 -A)
curl -X "$METHOD" "${BASE_URL}${PATH_URI}" \
-H "X-Api-Key: $API_KEY" \
-H "X-Signature: $SIGNATURE" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Nonce: $NONCE"Node.js
用于签名和发送 API 请求的可复用函数:
import crypto from 'crypto';
/**
* 向 SlaunchX API 发送经过认证的请求。
*
* @param {string} method - HTTP 方法(GET、POST、PUT、DELETE)
* @param {string} path - 请求路径(例如 /api/v1/wallets)
* @param {object|null} body - 请求体(GET/DELETE 时为 null)
* @param {string} apiKey - 您的 API 密钥(sk_live_xxx)
* @param {string} apiSecret - 您的 API 密钥
* @returns {Promise<object>} 解析后的 JSON 响应
*/
async function apiRequest(method, path, body, apiKey, apiSecret) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID();
// 第一步:请求体哈希
const bodyStr = body ? JSON.stringify(body) : '';
const bodyHash = crypto.createHash('sha256').update(bodyStr).digest('hex');
// 第二步:规范消息
const canonical = [method, path, timestamp, nonce, bodyHash].join('\n');
// 第三步:HMAC-SHA256 签名
const signature = crypto
.createHmac('sha256', apiSecret)
.update(canonical)
.digest('base64');
// 发送请求
// 发送请求(将 api.slaunchx.cc 替换为您的 API 主机地址)
const res = await fetch(`https://api.slaunchx.cc${path}`, {
method,
headers: {
'Content-Type': 'application/json',
'X-Api-Key': apiKey,
'X-Signature': signature,
'X-Timestamp': timestamp,
'X-Nonce': nonce,
},
body: bodyStr || undefined,
});
return res.json();
}
// ─── 使用示例 ──────────────────────────────────────────
// POST:创建转账
const transfer = await apiRequest(
'POST',
'/api/v1/transfer/command/create',
{
sourceWalletId: 'w_123',
targetWalletId: 'w_456',
amount: '100.00',
currency: 'USD',
},
'sk_live_abc123def456',
'your-api-secret-here'
);
// GET:列出钱包(无请求体)
const wallets = await apiRequest(
'GET',
'/api/v1/wallets',
null,
'sk_live_abc123def456',
'your-api-secret-here'
);服务器验证流水线
当服务器收到 API 请求时,会通过多步流水线验证请求。任何一步失败都会短路处理并返回相应的错误码。
┌─────────────────────────────────────────────────────────────────┐
│ 验证流水线 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. API 密钥查找 │
│ ├── 缺少 X-Api-Key 请求头 → GA2001 (401) │
│ └── 未找到密钥 → GA2011 (401) │
│ │
│ 2. 密钥状态检查 │
│ └── 管理员已禁用密钥 → GA2021 (403) │
│ │
│ 3. 工作区解析 │
│ ├── 未找到工作区 → GA2032 (404) │
│ └── 非工作区成员 → GA2034 (403) │
│ │
│ 4. 时间戳验证 │
│ └── |server_time - timestamp| > 60s → GA2013 (401) │
│ │
│ 5. Nonce 唯一性 │
│ └── Nonce 已被使用 → GA2013 (401) │
│ │
│ 6. HMAC 签名验证 │
│ └── 计算的 HMAC ≠ X-Signature → GA2012 (401) │
│ │
│ 7. IP 白名单(可选) │
│ └── 客户端 IP 不在白名单中 → GA2022 (403) │
│ │
│ 8. 权限范围检查 │
│ └── 缺少所需权限范围 → GA2024 (403) │
│ │
│ ✓ 所有检查通过 → 处理请求 │
└─────────────────────────────────────────────────────────────────┘验证顺序很重要
流水线按固定顺序验证。例如,如果您的 API 密钥有效但时间戳已过期,您将收到 GA2013(时间戳过期)而非签名错误。请使用错误码来准确诊断哪一步失败了。
API 权限范围
API 密钥是 工作区级别 的——每个密钥属于一个工作区,只能访问该工作区内的资源。通过 API 密钥无法进行跨工作区访问。
每个 API 密钥被分配一个或多个权限范围,用于控制其可以访问哪些端点:
| 权限范围 | 说明 |
|---|---|
wallet:read | 查询工作区内的钱包 |
transfer:read | 查询工作区内的转账记录 |
transfer:create | 在工作区内的钱包之间创建转账订单 |
可访问端点
| 权限范围 | 方法 | 端点 | 说明 |
|---|---|---|---|
wallet:read | GET | /api/v1/wallets | 列出钱包 |
wallet:read | GET | /api/v1/wallets/{id} | 获取钱包详情 |
wallet:read | GET | /api/v1/wallets/{id}/balance | 获取钱包余额 |
wallet:read | GET | /api/v1/wallets/{id}/flows | 列出钱包流水 |
transfer:read | GET | /api/v1/transfer/query/orders | 列出转账订单 |
transfer:read | GET | /api/v1/transfer/query/orders/wallet/{walletId} | 按钱包列出转账 |
transfer:read | GET | /api/v1/transfer/query/orders/{bizId} | 获取转账订单 |
transfer:read | GET | /api/v1/transfer/query/orders/{bizId}/completed | 检查转账是否完成 |
transfer:create | POST | /api/v1/transfer/command/create | 创建转账订单 |
未声明 API 权限范围的端点通过 API 链 不可访问(默认拒绝)。对此类端点的请求,或缺少所需权限范围的请求,将收到包含错误码 50090201(API_ACCESS_DENIED)的 403 Forbidden 响应。
工作区隔离
所有 API 响应自动限定在与 API 密钥绑定的工作区范围内。转账查询端点会强制执行工作区所有权验证——如果转账订单属于另一个工作区,请求将返回错误。即使使用有效的订单 ID,也能防止跨工作区数据泄露。
响应示例
成功的 API 响应:
{
"version": "1.3.0",
"timestamp": 1709337600000,
"success": true,
"code": "2000",
"message": "SUCCESS",
"data": {
"transferId": "txn_789xyz",
"status": "COMPLETED",
"amount": "100.00",
"currency": "USD"
}
}认证失败:
{
"version": "1.3.0",
"timestamp": 1709337600000,
"success": false,
"code": "GA2012",
"message": "SIGNATURE_INVALID",
"data": null
}常见陷阱
:::caution 时间戳时钟偏移 服务器会拒绝时间戳与服务器时间相差超过 60 秒 的请求。如果遇到 GA2013 错误,请检查您的服务器时钟是否已通过 NTP 同步。在本地调试时,可以将您机器上的 date +%s 与 NTP 服务器进行比较。 :::
:::caution 空请求体的请求体哈希 GET 和 DELETE 请求没有请求体。但您仍必须在规范消息中包含请求体哈希——使用空字符串的 SHA-256 值:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855。省略它或使用其他值将导致 GA2012。 :::
:::caution JSON 序列化一致性 请求体哈希覆盖请求体的 精确字节。如果您对一个 JSON 字符串计算哈希,但 HTTP 库重新序列化了请求体(改变了键顺序、空格或编码),签名将不匹配。最佳实践:序列化一次,对该字符串计算哈希,然后将该同一字符串作为请求体发送。 :::
:::caution Nonce 重复使用 每个 nonce 在 60 秒的时间戳窗口内必须全局唯一。重复使用 nonce(即使请求体不同)会导致 GA2013。使用 UUID v4 以确保唯一性。 :::
后续步骤
- API 参考与错误码 -- 完整的错误码表、API 密钥管理和技术规格。
- 错误处理 -- 响应信封格式和所有领域的完整错误码目录。