Skip to Content
API 参考身份验证
View as Markdown

身份验证

InfraIO Pay 有两套 API 表面,鉴权模型不同。挑选与调用方匹配的那一套:

表面路径前缀受众鉴权
商户 B2B/b2b/v1/*你的服务器HMAC-SHA256 请求签名
仪表板供商户仪表板使用商户仪表板的浏览器会话Bearer JWT

本页面介绍 B2B 表面,也就是你用 API 密钥对从服务端调用的那一套。 仪表板表面供 InfraIO Pay 商户仪表板使用,不是公开的集成接口。

请始终发送包含 /b2b 前缀的完整路径,并对同一路径签名(见下文)。

端点

环境Base URL
Testhttps://api-dev.infraio.xyz
Livehttps://api.infraio.xyz

URL 模式相同 — 环境由密钥前缀(pk_test_… vs pk_live_…)控制, 而不是 URL。

密钥对

你从商户仪表板(Developers → API keys → + Add key)获得两个值:

  • Publishable key (pk_test_… 或 pk_live_…)— 标识你的账户。 作为 X-Client-ID 发送。可安全嵌入浏览器 bundle(SDK 已经在用)。
  • Secret key (sk_test_… 或 sk_live_…)— HMAC 签名密钥。仅限 服务端。请像数据库密码一样对待。

如果 secret key 不慎进入浏览器 bundle、git 仓库、日志行,或被分享 到聊天 — 立刻从仪表板吊销。 吊销立即生效,没有重叠窗口。请重新签发并重新部署。

对请求签名

每次 /b2b/v1/* 调用都携带三个标头:

X-Client-ID: pk_live_70d0becbd7a53047b0dfe88a960d10ad X-Timestamp: 1715990400 X-Signature: 9a8b7c6d… (hex HMAC-SHA256)

签名在一个规范化字符串上计算:

METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODY
  • METHOD — 大写的 HTTP 动词(POST、GET、……)。
  • PATH — 请求路径包含 /b2b 前缀,不含 host 且不含查询字符串 (例如 /b2b/v1/checkout-sessions/quick)。前缀必须在。查询参数不参与签名 — 对于 GET …?cursor=…&limit=20,只对路径签名,不要包含 ?… 部分。
  • TIMESTAMP — Unix 秒,十进制字符串(例如 "1715990400"),与 X-Timestamp 完全一致。
  • BODY — 原始请求 body 字节。GET/DELETE 为空字符串。

用 secret key 作为密钥的 HMAC-SHA256 签名,输出 hex:

import { createHmac } from "node:crypto"; function sign({ method, path, body, secret }: { method: string; path: string; body: string; secret: string; }) { const timestamp = Math.floor(Date.now() / 1000).toString(); const input = [method.toUpperCase(), path, timestamp, body].join("\n"); const signature = createHmac("sha256", secret).update(input).digest("hex"); return { timestamp, signature }; }

为什么选 HMAC,而不是 Bearer?

裸 Bearer-token API 会在每次请求里把你唯一的 secret 经线传输。任何 拿到某个 TLS 终止代理日志的人都能拿到你账户的钥匙。HMAC 签名意味着 secret 永远不上路 — 上路的只是其衍生签名,而且一次性(绑定到该次 请求 + 该分钟)。

代价是:每次调用都要计算签名。目前还没有服务端 SDK,但上面的 helper 每种语言大约 15 行。

时间戳容差

容差为 ±5 分钟(300 秒)。超出该窗口的请求会被以 401 invalid_signature 拒绝。两点含义:

  1. 同步服务器时钟到 NTP。时钟漂移的长跑 cron 会间歇失败。
  2. 不要预先计算并入队签名。 如果请求在重试队列里坐了 > 5 分钟, 它的签名就过期了。

密钥 scope

Secret key 携带一个或多个下面的 scope 包:

Scope预期用途
read列出/读取订单、会话、退款
write_order创建结账会话、订单
write_refund发起退款、铸造退款申请 token
webhook_manage创建/更新/删除 Webhook 端点

仪表板默认签发”全权限”密钥(全部四个 scope)。你可以从 Developers → API keys → + Add key 铸造受限 scope 的密钥,仅勾选 集成需要的 scope。

scope 目前尚未强制执行。 scope 会记录在密钥上并显示在仪表板中, 但任何有效的 sk_… 密钥都可以调用你商户的任意 /b2b/v1/* 端点。 请勿把 scope 当作安全边界。请通过轮换或吊销密钥来限制访问。

验证失败

如果签名、X-Client-ID 或时间戳无效,请求会在到达 API 之前被以 401 INVALID_SIGNATURE 拒绝。只有 /b2b/v1/* 请求使用这种方式签名。 Webhook 使用另一套方案(见下文)。

下一步

  • 错误 — 4xx/5xx 的响应形态。
  • 安全 → API 密钥 — 轮换、吊销,以及 secret 泄露时的处理。
  • Webhooks → 签名验证 — 使用不同的 HMAC 方案(标头 X-Signature: sha256=…,签名 X-Timestamp + "." + raw_body,加上 24 小时轮换宽限窗口内可选的 X-Signature-Prev)。不要把两种方案搞混 — 它们共用哈希算法, 但签名字节和 secret 家族(whsec_… vs sk_…)不同。