/v1/deposits
上报会员入金
deposits:write
代会员调用 · 必带 x-on-behalf-of
需 x-idempotency-key
动钱 · 增会员 available,同额增 merchant.custody(我方替你记的负债)
失败处置见下方响应表。超时(504)用同一把幂等键重试——我方可能已经处理完;业务失败要换新键,同键会原样返回那次失败。
全系统唯一一个凭空产生会员余额的端点。 六道闸一道都不能省:
1. 强制签名 —— 即便这把 Key 配了 signature_required=false,
此端点仍强制。少了 x-signature / x-timestamp / x-nonce
一律 401,且时间戳容差 ±300 秒、nonce 不可重放;
2. 幂等键必须带租户前缀且由商户提供业务流水号(reference)——
重复上报同一笔链上入金 = 双倍记账;
3. 单笔与日累计上限(按商户配,总后台设);
4. 资产必须在该商户的白名单内,认不出的一律拒绝而不是挂账;
5. 照常过平台风控与准入 —— 上报入金不等于绕开风控;
6. 全量审计 + 商户后台可见。
凭证是两条腿:member.available credit W / merchant.custody debit W。
merchant.available 不参与 —— 上报入金不是一笔成本。
两把键,别混
x-idempotency-key(请求头,UUID)挡的是你的网络重试:
同键同体 24 小时内原样回放首次结果,同键异体 409。
reference(请求体)挡的是同一笔链上入金被上报两次:
它进的是账本幂等键 mdep:<商户>:<reference>,
和你换没换幂等键无关。所以 reference 必须是你那边那笔
链上交易的稳定标识(txid + vout、或你自己的入金单号),
不许每次调用现生成一个。
重复命中时返回 200 + replayed: true,首次成功是 201。
replayed 这一位让你不用去比对余额就知道「这笔我方早就记过了」。
白名单的语义
一行都没配 = 该商户尚未启用白名单,按平台资产目录放行。
一旦配了任意一行,就必须逐资产命中且启用 —— 没有那一行的资产
是拒绝,不是「没配限额」。
失败之后怎么办
- 收到 504 / 网络超时:用同一把
x-idempotency-key重试,
我方可能已经记过了;
- 收到明确的 4xx:换新键重试,同键会把那次失败原样回放给你;
- 收到
limit_exceeded:看limit_type(single单笔 /daily
日累计),这不是重试能解决的,去商户后台看额度。
前置条件
- 请求已签名(此端点强制,不受 Key 的 signature_required 影响)
- 资产在平台目录内且已启用;若该商户配了白名单,还必须逐资产命中且启用
- 会员未被封禁 / 拉黑 / 冻结(照常过平台准入)
请求头
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
x-on-behalf-of |
string | 必填 | 给哪个会员记这笔入金 |
x-idempotency-key |
string | 必填 | UUID。格式不对直接 400 idempotency_key_invalid。 |
x-signature |
string | 必填 | HMAC 签名。此端点不可关闭签名,与 Key 上的
signature_required 配置无关。 |
x-timestamp |
string | 必填 | Unix 秒。容差 ±300 秒,超窗回 timestamp_out_of_range。 |
x-nonce |
string | 必填 | 一次性随机串。重放窗口内重复使用回 nonce_reused。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
asset |
string | 必填 | 资产代码。我方会自动转成大写,所以 usdt 与 USDT
等价。不在目录/白名单内一律 asset_not_allowed。 |
amount |
string | 必填 | 入账数量,必须 > 0。字符串定点,位数 = 该资产
ledger_scale。小数位多于 scale 且尾数非全 0 时
直接 400 —— 我方不做四舍五入,宁可让你自己决定。USDT(ledger_scale=6)形如 1500.000000 |
reference |
string | 必填 | 你那边这笔入金的稳定业务流水号,≤ 120 字符。 它进账本幂等键,是「同一笔钱只入账一次」的唯一依据。 ⚠ 每次调用现生成一个 = 这道闸完全失效。 |
响应
{
"id": "dep_0xa3f1c2...e9:0",
"external_member_id": "u_10023",
"asset": "USDT",
"amount": "1500.000000",
"ledger_scale": 6,
"status": "credited",
"replayed": true
}{
"id": "dep_0xa3f1c2...e9:0",
"external_member_id": "u_10023",
"asset": "USDT",
"amount": "1500.000000",
"ledger_scale": 6,
"status": "credited",
"replayed": false
}invalid_request 缺字段 / 金额非法 / reference 超 120 字符 ·
asset_not_allowed 资产不在目录或不在该商户白名单内 ·
limit_exceeded 超限(看 limit_type = single 或 daily,
limit_scope = merchant) ·
request_rejected 会员被平台准入拦下(不下发原因) ·
insufficient_balance 过账时余额类 CHECK 被撞 ·
idempotency_key_required / idempotency_key_invalid 幂等键问题signature_required 没带签名 · invalid_signature 签名不匹配 ·
timestamp_out_of_range 时间戳超窗 · nonce_reused nonce 重放idempotency_key_reused 同一把幂等键配了不同的请求体 ·
idempotency_in_progress 首次请求还在处理中,稍后同键重试curl -X POST 'https://api.zise.com/v1/deposits' \
-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": "1500.000000",
"reference": "0xa3f1c2...e9:0"
}'
const res = await fetch(
"https://api.zise.com/v1/deposits",
{
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": "1500.000000",
"reference": "0xa3f1c2...e9:0"
}),
},
);
// 金额一律按字符串读,不要 JSON.parse 成 number
const data = await res.json();
import requests
res = requests.post(
"https://api.zise.com/v1/deposits",
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": "1500.000000",
"reference": "0xa3f1c2...e9:0"
},
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()