Z Zise Developers
POST /v1/cards/bind

绑定实体卡(三要素;不挂强认证

Scope cards:write 代会员调用 · 必带 x-on-behalf-of x-idempotency-key

把一张已经寄到用户手上、还不属于任何人的实体卡认到这个会员名下。

入参是卡面三要素:卡号 / 有效期 / CVV。

不挂强认证是刻意的 —— CVV 只印在卡背面,三要素本身就是占有权证明;

再加一道验证只会让「拿到卡的人绑不上」。真正不可逆的那一步是激活,

提权加在那里。

三要素一个字都不落库、不进日志。 CVV 只在这一次请求的内存里比对,

比完即弃(我方存的是卡号的带密钥 HMAC,不是卡号本身)。你的服务器上

同样不该留下它们 —— 转发一次就等于你也进了 PCI 的射程。

换绑在上游是一次性的:换错人那张卡就废了,没有第二次机会,

上游也没有解绑接口。所以「一张卡只许有一次在途/成功的换绑」是

库级约束,你这一侧不要做「先查一次再提交」的重试。

⚠ 这条接口天然是卡号爆破口,所以按人限流:一小时内 5 次真实错误

(网络抖动与被限流本身不计次)。撞上之后要等窗口过去。

status 恒为 binding —— 上游换绑是异步的,终态由回调收敛。

前置条件

  • 这张卡在库存里且已发出(shipped
  • 该会员名下有一张状态为 shippedawaiting_bind 的实体卡申请单
  • 会员 KYC 已通过 L1(我方要拿它去上游建用卡人)
  • 一小时内的失败次数未达 5 次
字段类型必填说明
x-on-behalf-of string 必填 代哪个会员调用

请求体

字段类型必填说明
pan string 必填 完整卡号。空格与连字符会被剥掉;长度 12~19 位(不同卡组织不同,不要按 16 位校验)。
expiry string 必填 有效期。MM/YY / MMYY / MM-YY / MMYYYY 都收,认不出按校验失败处理。
cvv string 必填 卡背面安全码,3~4 位。恒定时间比对,比完即弃。
application_id string 可选 这张卡对应的申请单 id(带不带 cap_ 前缀都收)。省略则由我方按该会员的待绑单自动找。

响应

201已受理,上游换绑处理中
{
  "application_id": "cap_1b7d90c4-5e2a-4f18-83b6-0c7a4d1e9f22",
  "masked_pan": "524012******7890",
  "status": "binding"
}
400product_not_available 该 BIN 或产品不支持绑定 · invalid_request body 不是合法 JSON · idempotency_key_required / idempotency_key_invalid三要素对不上、卡号格式不对、被限流、这张卡已被别人绑走、 找不到待绑申请单、KYC 未过 —— 目前都以 500 api_error 返回 (内部有区分到「是有效期不对还是 CVV 不对」的码,未登记进对外目录)。 在修好之前,给终端用户的提示只能笼统写「卡面信息不正确」, 并且要自己数次数:连错 5 次会被锁一小时。
409idempotency_key_reused · idempotency_in_progress
调用样例
curl -X POST 'https://api.zise.com/v1/cards/bind' \
  -H 'authorization: Bearer $TOKEN' \
  -H 'x-zise-merchant: $MERCHANT_ID' \
  -H 'x-on-behalf-of: $MEMBER_ID' \
  -H 'x-idempotency-key: $(uuidgen)' \
  -H 'content-type: application/json' \
  -d '{
    "pan": "5240121234567890",
    "expiry": "08/29",
    "cvv": "123"
  }'
const res = await fetch(
  "https://api.zise.com/v1/cards/bind",
  {
    method: "POST",
    headers: {
      "authorization": "Bearer $TOKEN",
      "x-zise-merchant": "MERCHANT_ID",
      "x-on-behalf-of": "MEMBER_ID",
      "x-idempotency-key": "crypto.randomUUID()",
      "content-type": "application/json"
    },
    body: JSON.stringify({
      "pan": "5240121234567890",
      "expiry": "08/29",
      "cvv": "123"
    }),
  },
);
// 金额一律按字符串读,不要 JSON.parse 成 number
const data = await res.json();
import requests

res = requests.post(
    "https://api.zise.com/v1/cards/bind",
    headers={
        "authorization": "Bearer $TOKEN",
        "x-zise-merchant": "$MERCHANT_ID",
        "x-on-behalf-of": "$MEMBER_ID",
        "x-idempotency-key": "$(uuidgen)",
        "content-type": "application/json"
    },
    json={
        "pan": "5240121234567890",
        "expiry": "08/29",
        "cvv": "123"
    },
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()