提现扣账(available → withdrawing)
x-on-behalf-of
需 x-idempotency-key
动钱 · 会员 available 减少,同额进 withdrawing(merchant.custody 不动)
失败处置见下方响应表。超时(504)用同一把幂等键重试——我方可能已经处理完;业务失败要换新键,同键会原样返回那次失败。
把会员的钱从 available 挪进 withdrawing。此刻钱不可再用,
但 merchant.custody 不动 —— 钱还在你手上,你还欠着会员。
为什么必须先调这一步再上链:会员余额得在我方账本上真的减少,
否则同一笔钱能在我方产品上(汇款、兑换、发卡、理财)再花一次。
上链成功后调 POST /v1/withdrawals/{id}/confirm,
失败调 .../fail 退回 available。没有第三条路 ——
钱不会自己从 withdrawing 里出来,你不推它就永远停在那里。
⚠⚠ 已知偏差,不要照着它设计产品
这个端点目前直接收请求体里的 to_address:不查地址簿、
不判 24 小时冷静期、不要求强认证。而同一条线的设计纪律写的是
「地址簿是提现主线里唯一的地址来源,主线里没有输入地址的输入框」——
代码与纪律在这一处不一致。
所有者已决定把会员充值提现从商户面撤掉,所以本端点后续可能下线。
在那之前:请你在自己那一侧先调 GET /v1/withdraw-addresses
取会员已备案且 usable=true 的地址,把它填进 to_address,
不要把这里当成「可以填任意地址」的接口用。
── 这条线可以被我方停掉 ──
本端点受你的 withdraw 业务线开关约束(同 GET /v1/merchant/lines
里的那一条)。线没开、被人工停售、或预付余额掉进低水位触发自动停售时,
这里一律回 400 service_unavailable,不排队:它是即时成交线,
没有「等你充值之后自动继续」这一档,扣账要么当场成功要么当场失败。
处置:先看 GET /v1/merchant/lines。halted=true 的会随着预付充值
在下一轮巡检自己解除;enabled=false 只能找客户经理。
⚠ 已经 locked 的单不受影响 —— confirm / fail 照常可调,
停售的意思是「别再开新的了」,不是把在途的钉死在 withdrawing 里。
前置条件
- 资产在平台目录内且已启用
- 会员该资产的可用余额 ≥ amount
- 会员未被封禁 / 拉黑 / 冻结
- 你的 withdraw 业务线已开通、未被停售,且商户资产账户状态正常
请求头
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
x-on-behalf-of |
string | 必填 | 给哪个会员扣账 |
x-idempotency-key |
string | 必填 | UUID。这把键是防重复扣账的唯一保护 —— 与入金不同,
这个端点没有 reference 那样的业务流水号,
重发时换了新键就是第二次真实扣账。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
asset |
string | 必填 | 资产代码,自动转大写。不在目录内回 asset_not_allowed。 |
amount |
string | 必填 | 扣账数量,必须 > 0。字符串定点,位数 = 该资产
ledger_scale;超出位数且尾数非全 0 直接 400。USDT(ledger_scale=6)形如 200.000000 |
to_address |
string | 必填 | 收款地址,≤ 200 字符。会原样快照进订单行, 之后查询返回的就是这一份,改不了。 ⚠ 我方在这个端点上不做地址格式校验、不查地址簿、 不判冷静期(见上方「已知偏差」)—— 校验责任在你那一侧。 |
响应
{
"id": "wdr_9f1c0b2a-4d33-4a51-9f2e-7c1b0a5d6e88",
"status": "locked",
"asset": "USDT",
"amount": "200.000000",
"ledger_scale": 6,
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}invalid_request 缺字段 / 金额非法 / 地址超 200 字符 ·
asset_not_allowed 资产不在目录 ·
insufficient_balance 会员可用余额不足 ·
limit_exceeded 撞会员侧限额(limit_scope = member) ·
request_rejected 平台准入拦下(不下发原因) ·
service_unavailable 你的 withdraw 线没开 / 已停售 /
商户资产账户被处置 —— 原样重试不会变,先看
GET /v1/merchant/linesidempotency_key_reused · idempotency_in_progresscurl -X POST 'https://api.zise.com/v1/withdrawals' \
-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 '{
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}'const res = await fetch("https://api.zise.com/v1/withdrawals", {
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({
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}),
});
// 金额按字符串读,别让它变成 number
const data = await res.json();import requests
res = requests.post(
"https://api.zise.com/v1/withdrawals",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
"x-idempotency-key": "$IDEMPOTENCY_KEY",
"content-type": "application/json",
},
json={
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
},
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()req, _ := http.NewRequest("POST", "https://api.zise.com/v1/withdrawals",
strings.NewReader(`{
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}`))
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/withdrawals"))
.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("""
{
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}
"""))
.build();
// 金额字段用 String / BigDecimal,不要 double$ch = curl_init('https://api.zise.com/v1/withdrawals');
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'
{
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}
JSON,
]);
$res = curl_exec($ch);
// 金额用 bcmath / 字符串,不要 floatval
{
"id": "wdr_9f1c0b2a-4d33-4a51-9f2e-7c1b0a5d6e88",
"status": "locked",
"asset": "USDT",
"amount": "200.000000",
"ledger_scale": 6,
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}