Z Zise Developers
POST /v1/remittances

下单(付款重试沿用同一把幂等键)

Scope remittances:write 代会员调用 · 必带 x-on-behalf-of x-idempotency-key 动钱 · 冻结会员该资产的可用余额(locked_amount,含滑点预留)+ 同步冻结你的预付备付金
这个端点会动钱

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

这是这条线上钱开始动的那一刻。 成功返回意味着:会员的可用余额

已经按 locked_amount 冻进 locked 桶(含滑点预留),你的预付

备付金已经按批发价同步冻结,限额已经预留。

走廊不从请求里读

收款人自带的那一份为准。少一条可被构造的入参 ——

你传什么走廊我方都不看。

只有「收款人收多少」这一个方向

下单没有 source_amount。理由是上游那一侧 payout_amount

是定死边,必须逐字等于报价的买入额。要按「我出多少」下单,

先用 POST /v1/remit/quotes 反算出 payout_amount,再拿它下单。

幂等键 = 一次「确认」一把

付款重试沿用同一把键(拿回首次那笔,duplicated: true)。

换新键 == 第二笔真实付款。收到 504/网络错误时必须同键重试,

别新生成 —— 我方可能已经建单并冻了钱。

三种成功状态,处置完全不同

  • dispatching 已放行,分发执行器在跑;
  • reviewing / platform_reviewing 等我方人工审核

(商户单一律先过一遍总后台,这是制度性关卡,不是出了问题);

  • pending = 内部的 pending_merchant_funds你的预付余额不足

此时会员的钱一分未动、限额未预留、上游没有订单,

只有一张排队的订单行。去充预付,我方会自动推进它。

队列深度看 GET /v1/merchant/pending

会员侧永远看不到 pending 的原因。 你的界面上对终端用户

只能说「处理中」,不能说「商户余额不足」。

duplicated: true没有 quote(这一次没有真的报价),

status 是那张既有订单此刻的状态 —— 可能已经是 completed 了。

别把它当成「刚刚下单成功」。

前置条件

  • 收款人在这个会员名下且状态可用
  • 会员该资产可用余额 ≥ locked_amount(= 客户实付 × (1 + 滑点))
  • 会员已过这条线要求的 KYC 层级,且未被封禁/拉黑
  • 金额在最低起汇额、单笔上限、日限额之内
  • purpose_code 在这条产品线允许的用途码清单内
  • 个人线(pobo)还要求这个会员已有一个 active 的上游子账户
字段类型必填说明
x-on-behalf-of string 必填 代哪个会员调用。钱从他的余额里扣。
x-idempotency-key string 必填 UUID v4。这把键就是「一笔付款」的身份 —— 重试用同一把,新的一笔换新的一把。24 小时后同键视为新请求。

请求体

字段类型必填说明
payee_id string 必填 收款人号。pye_ 前缀可带可不带;剥掉后必须是纯数字。
payout_amount string 必填 收款人收多少(目标币)。位数必须与该币种一致 —— 多给一位在下单这一刻就被拒(这道校验刻意前移到冻结之前: 晚一步的话,钱已经冻上、订单卡在 dispatching, 而那个状态不解冻也不可取消)。最长 32 字符。GBP 为 2 位:"380.00";JPY 为 0 位:"50000"
slippage_bps integer 必填 滑点上限,基点。必填整数,50 ~ 1000(0.5% ~ 10%)。 成交价相对下单快照上浮超过它时,订单停在 needs_reconfirm 等用户决定,而不是默默多扣。它同时决定冻多少: locked_amount = customer_total × (1 + slippage_bps/10000)
purpose_code string 必填 汇款用途。必填,取值来自我方给你的用途码清单 (上游固定枚举 23 个,如 FAMILY_SUPPORT / GOODS_PURCHASED / EDUCATION_TRAINING)。 留空或写错在下单这一刻被拒 —— 上游那个字段是 required, 漏到分发那一步就是钱冻完了才被打回来。
reference string 可选 附言,会带给收款方。超过 200 字符截断。
line "express" | "pobo" 可选 缺省 express认不出的值静默按 express 处理
asset string 可选 从哪个资产扣款。缺省 USDT

响应

201已受理。幂等命中也是 201(带 duplicated: truequote: null)—— 这个端点不用 200 区分首次与重放,判据是 duplicated 和响应头 X-Idempotent-Replay
{
  "id": "rmt_9c1f0a7e-3b2d-4f81-9a55-1d2e3f4a5b6c",
  "status": "reviewing",
  "quote": {
    "source_asset": "USDT",
    "customer_total": "499.980000",
    "fee": "2.480000",
    "locked_amount": "504.979800",
    "indicative_rate": "1.2899",
    "applied_rate": "1.2743"
  }
}
400invalid_request 请求体不是合法 JSON,或 payee_id 不是数字。 resource_not_found 收款人不在这个会员名下。 corridor_not_supported 收款人所在走廊已不可用(下架 / 受限辖区)。 product_not_available 业务线未开通,或源资产不可用。 invalid_fields payout_amount 位数与币种不符 / 超长 / 算不出来。 request_rejected 风控拒绝。不带原因、不带规则名、不带评分 (判据永不出境)。别重试,走商户后台联系我方。 service_unavailable 你的预付余额不足这条线不排队时的对外形态。 汇款线正常情况下走排队(201 + status: pending)。
409idempotency_key_reused 同键换了请求体 —— 我方拒绝, 因为那意味着你有两笔不同的付款用了同一个身份。 idempotency_in_progress 首次请求还在处理中,稍后同键重试
500当前实现的一处缺陷,别把它当契约。 下列业务拒绝目前没有 登记进对外码目录,于是以 api_error(500)返回: 会员可用余额不足、超日限额、超单笔上限、低于最低起汇额、 KYC 层级不足、用途码非法、slippage_bps 缺失或越界、 账户被封禁/拉黑、个人线缺子账户。 对这些 500 重试是没有意义的(同键会一直失败), 请先用 POST /v1/remit/quotes 把金额与最低额那一档挡在前面, 其余带 request_id 报给我方。修复后它们会变成上面那些 400 码。

触发的事件

调用样例
curl -X POST 'https://api.zise.com/v1/remittances' \
  -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 '{
    "payee_id": "pye_1042",
    "payout_amount": "380.00",
    "slippage_bps": 100,
    "purpose_code": "FAMILY_SUPPORT",
    "reference": "Rent Aug",
    "line": "express",
    "asset": "USDT"
  }'
const res = await fetch(
  "https://api.zise.com/v1/remittances",
  {
    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({
      "payee_id": "pye_1042",
      "payout_amount": "380.00",
      "slippage_bps": 100,
      "purpose_code": "FAMILY_SUPPORT",
      "reference": "Rent Aug",
      "line": "express",
      "asset": "USDT"
    }),
  },
);
// 金额一律按字符串读,不要 JSON.parse 成 number
const data = await res.json();
import requests

res = requests.post(
    "https://api.zise.com/v1/remittances",
    headers={
        "authorization": "Bearer $TOKEN",
        "x-zise-merchant": "$MERCHANT_ID",
        "x-on-behalf-of": "$MEMBER_ID",
        "x-idempotency-key": "$(uuidgen)",
        "content-type": "application/json"
    },
    json={
        "payee_id": "pye_1042",
        "payout_amount": "380.00",
        "slippage_bps": 100,
        "purpose_code": "FAMILY_SUPPORT",
        "reference": "Rent Aug",
        "line": "express",
        "asset": "USDT"
    },
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()