POST
/v1/remit/payees
新建或关联收款人(建档不碰上游)
Scope
remittances:write
代会员调用 · 必带 x-on-behalf-of
需 x-idempotency-key
收款人是上游无关的实体:这一刻我方一个上游接口都不调,只落库。
在建档这一刻替用户选一个 provider 建过去,等于把一条本该跨上游共用
的记录钉死在今天启用的那一家。上游建档发生在下单分发那一步。
推论有两条,都会影响你的产品设计:
① 建档成功不代表这个收款人一定汇得出去 —— 格式我方尽力校了
(见 GET /v1/remit/payee-form-schema 下发的 pattern),
但上游还有它自己的判据;
② 建档很快、很便宜、可以让用户随时来一遍。
三种结果,看 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 | 必填 | LOCAL 或 SWIFT。别名 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"
}400
invalid_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 走廊四段不全 / 未启用 / 受限辖区 /
没有可确认的路由码类型。409
idempotency_key_reused 同一把键换了请求体(或打到了别的端点)。
调用样例
curl -X POST 'https://api.zise.com/v1/remit/payees' \
-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 '{
"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: {
"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({
"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.parse 成 number
const data = await res.json();
import requests
res = requests.post(
"https://api.zise.com/v1/remit/payees",
headers={
"authorization": "Bearer $TOKEN",
"x-zise-merchant": "$MERCHANT_ID",
"x-on-behalf-of": "$MEMBER_ID",
"x-idempotency-key": "$(uuidgen)",
"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()