Z Zise Developers

新建或关联收款人(建档不碰上游

POST /v1/remit/payees scope: remittances:write
代会员调用 · 必带 x-on-behalf-of x-idempotency-key

收款人是上游无关的实体:这一刻我方一个上游接口都不调,只落库。

在建档这一刻替用户选一个 provider 建过去,等于把一条本该跨上游共用

的记录钉死在今天启用的那一家。上游建档发生在下单分发那一步。

推论有两条,都会影响你的产品设计:

① 建档成功不代表这个收款人一定汇得出去 —— 格式我方尽力校了

(见 GET /v1/remit/payee-form-schema 下发的 pattern),

但上游还有它自己的判据;

② 建档很快、很便宜、可以让用户随时来一遍。

推论 ① 的代价落在你的用户身上,而且落得很晚。 上游拒绝的理由

不止格式(重复添加、制裁名单命中、它自己的银行目录里查不到这家行),

而它被发现的时刻是下单分发那一刻 —— 那时钱已经从会员的可用余额

冻进锁定桶,订单停在处理中,只能等我方人工处置。

建完收款人调一次 POST /v1/remit/payees/{id}/verify

把那次失败提前到没有钱牵涉其中的时候。它不动一分钱。

三种结果,看 outcome

  • created 全新的收款人;
  • linked 这个账户我方已经有了,且户名一致 —— 关联,不是重复创建
  • review 这个账户我方已经有了,但户名不一致。仍然会关联成功

201),同时把 name_mismatch 落库。你应当在自己的界面上

让用户复核一次户名。

判据是收款人指纹:由走廊四段 + 账户标识(账号 / IBAN /

路由码 / proxy)算出来,户名走副指纹。所以「同一个账户填两遍」

不会造出两条收款人,而「同一个账户换个户名」会被抓出来。

fields 的三条纪律

契约之外的键一律丢弃(不报错,就是不存)—— 别把你自己的

业务字段塞进来;

② 每个值最长 256 字符,超出截断不报错;

③ 我方先归一再校验alnum_upper 的字段会剥掉横杠空格并转大写

(用户从银行 App 复制的账号一定带分隔符,而上游的 pattern

连横杠都不许)。用户键入的原文我方另存一份,争议留证读那份。

⚠ IBAN 除了 pattern,服务端还会做逐国长度 + mod-97 校验位

错误 reason 分档给出(iban_length / iban_checksum),

那决定用户是去数位数还是去核对原件。这道校验不是多余的洁癖:

一个少一位的 IBAN 本地一路放行,到分发那一刻才被上游拒,

而那时收款人已被钉成终态。

前置条件

  • 走廊可用(同 GET /v1/remit/payee-form-schema 的判据,这里再验一遍)
  • 账号 / IBAN / proxy 三个标识至少有一个非空
字段类型必填说明
x-on-behalf-of string 必填 代哪个会员调用。收款人关联落在这个会员名下。
x-idempotency-key string 必填 UUID v4。24 小时内同键同体回放首次结果(含错误)。

请求体

字段类型必填说明
country_code string 必填 收款银行所在国家,两位大写。别名 country
currency string 必填 到账币种,三位大写。
payment_method string 必填 LOCALSWIFT。别名 method
clearing_system string 必填 清算网络,大小写原样。别名 clearing
entity_type "INDIVIDUAL" | "COMPANY" 必填 别名 entity
nickname string 可选 会员给这个收款人起的名字。只属于这一条关联, 别的会员看不到。超过 64 字符截断。
fields object 必填 字段契约里那些 key 的取值,扁平的一层 (键里带点号,不是嵌套对象)。契约之外的键丢弃。{"bank_details.iban": "GB33BUKB20201555555555", …}

响应

201建档成功。三种 outcome 都是 201 —— linked / review 不是错误,是「我方认出这个账户已经在了」。
{
  "id": "pye_1042",
  "outcome": "created",
  "status": "active"
}
400invalid_fields 逐字段校验失败,fields 数组 (每项 {key, reason}reason 取值 required / too_long / pattern / not_in_enum / iban_length / iban_checksum / account_identifier_required / unknown_clearing_system)。 一句笼统的「信息有误」在一张十几个框的表上等于没说。 corridor_not_supported 走廊四段不全 / 未启用 / 受限辖区 / 没有可确认的路由码类型。
409idempotency_key_reused 同一把键换了请求体(或打到了别的端点)。
请求
curl -X POST 'https://api.zise.com/v1/remit/payees' \
  -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 '{
    "country_code": "GB",
    "currency": "GBP",
    "payment_method": "LOCAL",
    "clearing_system": "FASTER PAYMENTS",
    "entity_type": "INDIVIDUAL",
    "nickname": "房东",
    "fields": {
      "first_name": "MARY",
      "last_name": "SMITH",
      "bank_details.account_holder": "MARY SMITH",
      "bank_details.bank_name": "Barclays",
      "bank_details.bank_address": "1 Churchill Place, London",
      "bank_details.swift_code": "BUKBGB22",
      "bank_details.iban": "GB33BUKB20201555555555",
      "bank_details.routing_code_value1": "202015",
      "bank_details.routing_code_type1": "sort_code",
      "address.country": "GB",
      "address.state": "Greater London",
      "address.city": "London",
      "address.street_address": "221B Baker Street",
      "address.postal_code": "NW1 6XE"
    }
  }'
const res = await fetch("https://api.zise.com/v1/remit/payees", {
  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({
    "country_code": "GB",
    "currency": "GBP",
    "payment_method": "LOCAL",
    "clearing_system": "FASTER PAYMENTS",
    "entity_type": "INDIVIDUAL",
    "nickname": "房东",
    "fields": {
      "first_name": "MARY",
      "last_name": "SMITH",
      "bank_details.account_holder": "MARY SMITH",
      "bank_details.bank_name": "Barclays",
      "bank_details.bank_address": "1 Churchill Place, London",
      "bank_details.swift_code": "BUKBGB22",
      "bank_details.iban": "GB33BUKB20201555555555",
      "bank_details.routing_code_value1": "202015",
      "bank_details.routing_code_type1": "sort_code",
      "address.country": "GB",
      "address.state": "Greater London",
      "address.city": "London",
      "address.street_address": "221B Baker Street",
      "address.postal_code": "NW1 6XE"
    }
  }),
});
// 金额按字符串读,别让它变成 number
const data = await res.json();
import requests

res = requests.post(
    "https://api.zise.com/v1/remit/payees",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-on-behalf-of": "$MEMBER_ID",
        "x-idempotency-key": "$IDEMPOTENCY_KEY",
        "content-type": "application/json",
    },
    json={
      "country_code": "GB",
      "currency": "GBP",
      "payment_method": "LOCAL",
      "clearing_system": "FASTER PAYMENTS",
      "entity_type": "INDIVIDUAL",
      "nickname": "房东",
      "fields": {
        "first_name": "MARY",
        "last_name": "SMITH",
        "bank_details.account_holder": "MARY SMITH",
        "bank_details.bank_name": "Barclays",
        "bank_details.bank_address": "1 Churchill Place, London",
        "bank_details.swift_code": "BUKBGB22",
        "bank_details.iban": "GB33BUKB20201555555555",
        "bank_details.routing_code_value1": "202015",
        "bank_details.routing_code_type1": "sort_code",
        "address.country": "GB",
        "address.state": "Greater London",
        "address.city": "London",
        "address.street_address": "221B Baker Street",
        "address.postal_code": "NW1 6XE"
      }
    },
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zise.com/v1/remit/payees",
    strings.NewReader(`{
  "country_code": "GB",
  "currency": "GBP",
  "payment_method": "LOCAL",
  "clearing_system": "FASTER PAYMENTS",
  "entity_type": "INDIVIDUAL",
  "nickname": "房东",
  "fields": {
    "first_name": "MARY",
    "last_name": "SMITH",
    "bank_details.account_holder": "MARY SMITH",
    "bank_details.bank_name": "Barclays",
    "bank_details.bank_address": "1 Churchill Place, London",
    "bank_details.swift_code": "BUKBGB22",
    "bank_details.iban": "GB33BUKB20201555555555",
    "bank_details.routing_code_value1": "202015",
    "bank_details.routing_code_type1": "sort_code",
    "address.country": "GB",
    "address.state": "Greater London",
    "address.city": "London",
    "address.street_address": "221B Baker Street",
    "address.postal_code": "NW1 6XE"
  }
}`))
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 接,不要 float64
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.zise.com/v1/remit/payees"))
    .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("""
{
  "country_code": "GB",
  "currency": "GBP",
  "payment_method": "LOCAL",
  "clearing_system": "FASTER PAYMENTS",
  "entity_type": "INDIVIDUAL",
  "nickname": "房东",
  "fields": {
    "first_name": "MARY",
    "last_name": "SMITH",
    "bank_details.account_holder": "MARY SMITH",
    "bank_details.bank_name": "Barclays",
    "bank_details.bank_address": "1 Churchill Place, London",
    "bank_details.swift_code": "BUKBGB22",
    "bank_details.iban": "GB33BUKB20201555555555",
    "bank_details.routing_code_value1": "202015",
    "bank_details.routing_code_type1": "sort_code",
    "address.country": "GB",
    "address.state": "Greater London",
    "address.city": "London",
    "address.street_address": "221B Baker Street",
    "address.postal_code": "NW1 6XE"
  }
}
"""))
    .build();
// 金额字段用 String / BigDecimal,不要 double
$ch = curl_init('https://api.zise.com/v1/remit/payees');
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'
{
  "country_code": "GB",
  "currency": "GBP",
  "payment_method": "LOCAL",
  "clearing_system": "FASTER PAYMENTS",
  "entity_type": "INDIVIDUAL",
  "nickname": "房东",
  "fields": {
    "first_name": "MARY",
    "last_name": "SMITH",
    "bank_details.account_holder": "MARY SMITH",
    "bank_details.bank_name": "Barclays",
    "bank_details.bank_address": "1 Churchill Place, London",
    "bank_details.swift_code": "BUKBGB22",
    "bank_details.iban": "GB33BUKB20201555555555",
    "bank_details.routing_code_value1": "202015",
    "bank_details.routing_code_type1": "sort_code",
    "address.country": "GB",
    "address.state": "Greater London",
    "address.city": "London",
    "address.street_address": "221B Baker Street",
    "address.postal_code": "NW1 6XE"
  }
}
JSON,
]);
$res = curl_exec($ch);
// 金额用 bcmath / 字符串,不要 floatval
201
{
  "id": "pye_1042",
  "outcome": "created",
  "status": "active"
}