Z Zise Developers
POST /v1/cards/applications

提交开卡申请(虚拟/实体经 form_factor 区分)

Scope cards:write 代会员调用 · 必带 x-on-behalf-of x-idempotency-key 动钱 · 扣开卡费与邮费 + 锁定首充金额(会员可用余额)
这个端点会动钱

失败处置见下方响应表。超时(504)用同一把幂等键重试——我方可能已经处理完;业务失败要换新键,同键会原样返回那次失败。

建单 + 扣开卡费(实体卡另收邮费),同步返回,不外呼上游

真正的开卡是异步的,终局靠 webhook。

回给你的 status 只有三档,是裁剪过的,不是我方的内部状态机。

其中 pending 的真实含义可能是「你的预付余额不够了,这张单在排队」——

我方刻意不在这里说破(会员侧连原因都看不到)。你要看队列深度走

GET /v1/merchant/pending,那是你自己的经营面。

⚠ 有两把幂等键,别混:请求头 x-idempotency-key 是开放 API 那一层

(24 小时窗口,同键回放首次结论);请求体 client_key 是发卡业务自己

那一层(落库、永久)。不传 client_key 我方会随机生成一把 ——

于是超过 24 小时之后同一份 body 重发会真的开出第二张卡。要幂等就自己传。

前置条件

  • 会员 KYC 已通过 L1(产品可单独抬到 L2)
  • 该会员在该供应商/介质下的持卡数未达上限(在途申请也算一个名额)
  • 该会员没有未结清的罚金欠款
  • 实体卡:必须已有默认邮寄地址,且收货国家在发卡与配送清单内
  • 会员可用余额 ≥ 开卡费 + 邮费 + 首充额
  • 你的预付余额够付这一单的批发成本(不够则这张单排队,不报原因)
字段类型必填说明
x-on-behalf-of string 必填 代哪个会员调用

请求体

字段类型必填说明
form_factor string 可选 physical = 实体卡,其余一切取值(含缺省)都按虚拟卡处理。 没有拼写校验 —— 打成 Physical 会静默开出一张虚拟卡。
provider string 必填 发卡供应商代码。当前只有 photonpay(常规卡)与 zeno(共享额度卡)。认不出一律拒。
bin string 必填 卡 BIN,取自 GET /v1/cards/productsbin。该 BIN 必须支持你要的介质与资金模式。
source_asset string 必填 扣款资产代码(如 USDT)。必须在该产品的允许清单内 —— 不在清单里回 product_not_available
first_topup string 可选 首充金额,源资产口径的十进制串。省略 = 不首充。 产品配了首充下限时低于它会被拒。开卡费与首充是两笔独立的钱: 开卡失败退开卡费,而首充那笔在开卡成功后才真的充进卡。USDT 为 6 位以内:"50.00"
address_id string 可选 实体卡的收货地址 id。省略则用该会员的默认地址。地址会快照进单里 —— 会员日后改地址不影响已下的单。
client_key string 可选 业务级幂等键(见上文两把键的区别)。强烈建议传

响应

201已受理
{
  "id": "cap_1b7d90c4-5e2a-4f18-83b6-0c7a4d1e9f22",
  "status": "submitted",
  "form_factor": "virtual"
}
400product_not_available 供应商/产品/BIN 不可用,或 source_asset 不在清单内 · service_unavailable 发卡这条线对你没开通,或你的资金账户被处置 · member_context_required 少了 x-on-behalf-of · idempotency_key_required / idempotency_key_invalid 幂等键缺失或不是 UUID v4 ⚠ 另有一批业务拒绝目前会以 500 api_error 的形态返回(会员余额不足、 KYC 未过、持卡数已达上限、首充低于下限、有未结清罚金、收货国家不支持)—— 它们在内部有明确的码,但还没登记进对外码目录。不要照着 500 无限重试: 重试同一把幂等键只会拿回同一个 500。这是我方的缺陷,已在修, 修好后这些会变成 400 且带各自的 code
409idempotency_key_reused 同一把键换了 body 或换了端点 · idempotency_in_progress 首次请求还在处理

触发的事件

调用样例
curl -X POST 'https://api.zise.com/v1/cards/applications' \
  -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 '{
    "form_factor": "virtual",
    "provider": "photonpay",
    "bin": "5240",
    "source_asset": "USDT",
    "first_topup": "50.00",
    "client_key": "6d0f3a2e-8b41-4c77-9a10-2f5c7e91b3d4"
  }'
const res = await fetch(
  "https://api.zise.com/v1/cards/applications",
  {
    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({
      "form_factor": "virtual",
      "provider": "photonpay",
      "bin": "5240",
      "source_asset": "USDT",
      "first_topup": "50.00",
      "client_key": "6d0f3a2e-8b41-4c77-9a10-2f5c7e91b3d4"
    }),
  },
);
// 金额一律按字符串读,不要 JSON.parse 成 number
const data = await res.json();
import requests

res = requests.post(
    "https://api.zise.com/v1/cards/applications",
    headers={
        "authorization": "Bearer $TOKEN",
        "x-zise-merchant": "$MERCHANT_ID",
        "x-on-behalf-of": "$MEMBER_ID",
        "x-idempotency-key": "$(uuidgen)",
        "content-type": "application/json"
    },
    json={
        "form_factor": "virtual",
        "provider": "photonpay",
        "bin": "5240",
        "source_asset": "USDT",
        "first_topup": "50.00",
        "client_key": "6d0f3a2e-8b41-4c77-9a10-2f5c7e91b3d4"
    },
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()