POST
/v1/withdrawals
提现扣账(available → withdrawing)
Scope
withdrawals:write
代会员调用 · 必带 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,
不要把这里当成「可以填任意地址」的接口用。
前置条件
- 资产在平台目录内且已启用
- 会员该资产的可用余额 ≥ amount
- 会员未被封禁 / 拉黑 / 冻结
请求头
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
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 字符。会原样快照进订单行, 之后查询返回的就是这一份,改不了。 ⚠ 我方在这个端点上不做地址格式校验、不查地址簿、 不判冷静期(见上方「已知偏差」)—— 校验责任在你那一侧。 |
响应
201已扣账,等你上链
{
"id": "wdr_9f1c0b2a-4d33-4a51-9f2e-7c1b0a5d6e88",
"status": "locked",
"asset": "USDT",
"amount": "200.000000",
"ledger_scale": 6,
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}400
invalid_request 缺字段 / 金额非法 / 地址超 200 字符 ·
asset_not_allowed 资产不在目录 ·
insufficient_balance 会员可用余额不足 ·
limit_exceeded 撞会员侧限额(limit_scope = member) ·
request_rejected 平台准入拦下(不下发原因)409
idempotency_key_reused · idempotency_in_progress
调用样例
curl -X POST 'https://api.zise.com/v1/withdrawals' \
-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 '{
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}'
const res = await fetch(
"https://api.zise.com/v1/withdrawals",
{
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({
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
}),
},
);
// 金额一律按字符串读,不要 JSON.parse 成 number
const data = await res.json();
import requests
res = requests.post(
"https://api.zise.com/v1/withdrawals",
headers={
"authorization": "Bearer $TOKEN",
"x-zise-merchant": "$MERCHANT_ID",
"x-on-behalf-of": "$MEMBER_ID",
"x-idempotency-key": "$(uuidgen)",
"content-type": "application/json"
},
json={
"asset": "USDT",
"amount": "200.000000",
"to_address": "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
},
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()