Z Zise Developers

换取访问令牌

POST /v1/connect/token scope: —
商户自身

两段式的第一段。这是整条链上唯一不带 x-auth-token 的端点

也是唯一不需要 scope 的。

三件与常见实现不同、写错了会当天返工的事:

  • 多令牌可并存。 新换一把不会让旧的失效 —— 多进程的接入方不必

担心两个 worker 互相把对方踢下线。所以请缓存令牌复用到过期前

而不是每次调用都换一把。

  • 签发限流 20 次/分钟/client_id,超了回 rate_limited

高频换取本身就是接入错误的信号。

  • 令牌里那份 scopes 只作提示。 判定用的是每请求实时读的

Key 行 —— 后台改 scope / 停用 Key 即时生效,不必等令牌过期。

scopes 出参是展开后的集合:勾了 members:write 会同时看到

members:read。展开发生在判定时而不是入库时,所以规则日后收紧,

存量 Key 会跟着变。

跨环境凭据一律拒,且明确回 environment_mismatch 而不是

「凭据无效」—— 上游同类接口在这里回的是 Invalid client id,

接入方普遍误判成「密钥抄错了」。沙盒 Key 解析到的是一个**影子商户

主体**,与 live 是两批数据:在沙盒里建的会员、记的账,live 上一条都没有。

轮换有重叠窗口:旧密钥在 prev_valid_until 之前仍可换取令牌,

你可以从容换完再让它自然失效。⚠ 那是硬边界不是提醒 ——

过点即刻失效,不留宽限。

前置条件

  • API Key 状态为 active 且未过 expires_at
  • Key 的环境与你打的主机名一致(api-sandbox.* 对沙盒 Key)
  • live 环境的 Key 必须配了 IP 白名单,且本次出口 IP 命中
字段类型必填说明
x-client-id string 必填 API Key 的 client_id(商户后台创建 Key 时下发)
x-api-key string 必填 API Key 的密钥。⚠ 它不是验签用的那把。 一把 Key 下发两个值: api_key 只在这里用(我方只存哈希),signing_key 用于 x-signature。拿错了的表现是签名恒不匹配。

响应

200已签发
{
  "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expired_at": 1786000800,
  "token_type": "Bearer",
  "scopes": [
    {
      "members": "read"
    },
    {
      "members": "write"
    },
    {
      "merchant": "read"
    }
  ]
}
400environment_mismatch 沙盒凭据打在了 live 主机上(或反过来)
401invalid_credentials —— 五种情况同一个响应:头缺失 · client_id 不存在 · Key 已停用 · Key 已过期 · 密钥不匹配。 区分开就等于给了一个 client_id 探测接口。
429rate_limited 每个 client_id 每分钟 20 次
请求
curl -X POST 'https://api.zise.com/v1/connect/token' \
  -H 'x-auth-token: Bearer $TOKEN'
const res = await fetch("https://api.zise.com/v1/connect/token", {
  method: "POST",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
  },
});
// 金额按字符串读,别让它变成 number
const data = await res.json();
import requests

res = requests.post(
    "https://api.zise.com/v1/connect/token",
    headers={
        "x-auth-token": "Bearer $TOKEN",
    },
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zise.com/v1/connect/token",
    nil)
req.Header.Set("x-auth-token", "Bearer $TOKEN")
res, err := http.DefaultClient.Do(req)
// 金额字段用 string 接,不要 float64
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.zise.com/v1/connect/token"))
    .header("x-auth-token", "Bearer $TOKEN")
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
// 金额字段用 String / BigDecimal,不要 double
$ch = curl_init('https://api.zise.com/v1/connect/token');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'x-auth-token: Bearer $TOKEN',
  ],
]);
$res = curl_exec($ch);
// 金额用 bcmath / 字符串,不要 floatval
200
{
  "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expired_at": 1786000800,
  "token_type": "Bearer",
  "scopes": [
    {
      "members": "read"
    },
    {
      "members": "write"
    },
    {
      "merchant": "read"
    }
  ]
}