充值到卡(两段异步:到卡以 webhook 为准)
代会员调用 · 必带
x-on-behalf-of
需 x-idempotency-key
动钱 · 扣会员源资产可用余额 + 冻结商户预付;到卡由 webhook 确认
这个端点会动钱
失败处置见下方响应表。超时(504)用同一把幂等键重试——我方可能已经处理完;业务失败要换新键,同键会原样返回那次失败。
── 这个端点的成功不等于钱到卡 ──
上游 recharge 回 succeed 只表示「下单收到了」。钱真的到卡要等
card.topup.credited 这条 webhook。所以这里回的 status 绝大多数时候是
processing,你不能据此给用户放行任何东西;同理,卡的限额也必须
等到账确认之后才下发,提前放开等于限额已开而钱没到。
── 失败与「结果不明」是两件事 ──
上游明确拒绝 → 我方当场解冻,你拿到业务失败码,钱一分没少。
网络层失败/超时 → 单子落 processing 交给巡检查证,我方绝不解冻;
你这一侧也不要重试成第二笔,用同一把幂等键重发即可拿回同一张单。
── 幂等回放的是「原始结论」,不是「永远成功」──
同一把 x-idempotency-key 打进来,第一次失败的单第二次仍然回失败。
要重新发起就换一把新键。(曾经这里一律回成功,表现是「用户第二次点
显示成功、钱没到卡、流水上写着失败」。)
⚠ 扣的是会员的可用余额,同时冻结你的预付备付金。你的预付不够时,
会员看到的只是「暂时不可用」——不许把原因转达给终端用户。
前置条件
- 卡状态在可充值集合内(
active/frozen/pending;过渡态一律不行) - 会员不在资金保护态(限额与上游失步时我方会先锁住这条路)
- 会员该资产可用余额 ≥
customer_total - 你的预付余额够付这一单的批发成本
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
string | 必填 | 卡 id |
请求头
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
x-on-behalf-of |
string | 必填 | 代哪个会员调用 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
amount |
string | 必填 | 基准额,源资产口径的十进制串(与试算同一个口径)。实际扣款是试算里的 customer_total。USDT 为 6 位以内:"100.00" |
source_asset |
string | 可选 | 扣款资产。省略或不可用时按支付优先级自动挑(同试算:偏好而非指令)。 |
响应
201已受理。
status 取值:processing(下单成功/结果待查证/排队中,都是这一档)·
completed(共享额度卡当场结清才会出现)。看到 201 不等于到卡。{
"id": "ctp_4a8e5d21-90bc-4f37-b1e2-8d0c6a3f5719",
"status": "processing"
}400
state_invalid 卡状态不允许充值 · product_not_available 产品或供应商不可用 ·
service_unavailable 发卡线未开通 / 你的资金账户被处置 ·
idempotency_key_required / idempotency_key_invalid
⚠ 余额不足、低于最低额、超单笔上限、上游拒绝、汇率不可用,
目前都以 500 api_error 返回(内部有码,未登记进对外目录)。
对 500 的正确处置是:用同一把键重发一次确认结论,
仍是 500 就当业务失败上报,不要循环重试。409
idempotency_key_reused · idempotency_in_progress504
upstream_timeout 结果不明 —— 用同一把幂等键重试,我方可能已经处理完会触发的事件
绿 = 终局且是好消息 · 红 = 终局且要处置 · 紫 = 中间态。点进去看事件体与验签。
请求
curl -X POST 'https://api.zise.com/v1/cards/{id}/topups' \
-H 'x-auth-token: Bearer $TOKEN' \
-H 'x-on-behalf-of: $MEMBER_ID' \
-H 'x-idempotency-key: $IDEMPOTENCY_KEY' \
-H 'content-type: application/json' \
-d '{
"amount": "100.00",
"source_asset": "USDT"
}'const res = await fetch("https://api.zise.com/v1/cards/{id}/topups", {
method: "POST",
headers: {
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"x-idempotency-key": "$IDEMPOTENCY_KEY",
"content-type": "application/json",
},
body: JSON.stringify({
"amount": "100.00",
"source_asset": "USDT"
}),
});
// 金额按字符串读,别让它变成 number
const data = await res.json();import requests
res = requests.post(
"https://api.zise.com/v1/cards/{id}/topups",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"x-idempotency-key": "$IDEMPOTENCY_KEY",
"content-type": "application/json",
},
json={
"amount": "100.00",
"source_asset": "USDT"
},
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()req, _ := http.NewRequest("POST", "https://api.zise.com/v1/cards/{id}/topups",
strings.NewReader(`{
"amount": "100.00",
"source_asset": "USDT"
}`))
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
req.Header.Set("x-idempotency-key", "$IDEMPOTENCY_KEY")
req.Header.Set("content-type", "application/json")
res, err := http.DefaultClient.Do(req)
// 金额字段用 string 接,不要 float64HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.zise.com/v1/cards/{id}/topups"))
.header("x-auth-token", "Bearer $TOKEN")
.header("x-on-behalf-of", "$MEMBER_ID")
.header("x-idempotency-key", "$IDEMPOTENCY_KEY")
.header("content-type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("""
{
"amount": "100.00",
"source_asset": "USDT"
}
"""))
.build();
// 金额字段用 String / BigDecimal,不要 double$ch = curl_init('https://api.zise.com/v1/cards/{id}/topups');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-auth-token: Bearer $TOKEN',
'x-on-behalf-of: $MEMBER_ID',
'x-idempotency-key: $IDEMPOTENCY_KEY',
'content-type: application/json',
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"amount": "100.00",
"source_asset": "USDT"
}
JSON,
]);
$res = curl_exec($ch);
// 金额用 bcmath / 字符串,不要 floatval
201
{
"id": "ctp_4a8e5d21-90bc-4f37-b1e2-8d0c6a3f5719",
"status": "processing"
}