新建或关联收款人(建档不碰上游)
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 | 必填 | LOCAL 或 SWIFT。别名 method。 |
clearing_system |
string | 必填 | 清算网络,大小写原样。别名 clearing。 |
entity_type |
"INDIVIDUAL" | "COMPANY" | 必填 | 别名 entity。 |
nickname |
string | 可选 | 会员给这个收款人起的名字。只属于这一条关联, 别的会员看不到。超过 64 字符截断。 |
fields |
object | 必填 | 字段契约里那些 key 的取值,扁平的一层
(键里带点号,不是嵌套对象)。契约之外的键丢弃。{"bank_details.iban": "GB33BUKB20201555555555", …} |
响应
outcome 都是 201 —— linked / review
不是错误,是「我方认出这个账户已经在了」。{
"id": "pye_1042",
"outcome": "created",
"status": "active"
}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 走廊四段不全 / 未启用 / 受限辖区 /
没有可确认的路由码类型。idempotency_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 接,不要 float64HttpRequest 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
{
"id": "pye_1042",
"outcome": "created",
"status": "active"
}