提前把收款人送去上游建档(不动一分钱)
x-on-behalf-of
它解决的是什么
我方的收款人是上游无关的实体:建档那一刻一个上游接口都不调
(提前替用户选一家供应商建过去,会把这条记录钉死在今天这一家)。
上游建档默认发生在下单分发那一刻。
代价是失败点在冻结之后:上游对收款人的拒绝是终态,
而订单此时已经把会员的钱从可用余额冻进锁定桶,只能等我方人工处置。
上游拒绝的理由不止格式(重复添加、制裁名单命中、它自己的银行目录里
查不到这家行)—— 格式那一类我方本地能挡,其余的**第一次发生时一定
要花掉一次冻结**。
这个端点就是那个问句:把上游建档提前到没有钱牵涉其中的时候。
建议在 POST /v1/remit/payees 成功之后立刻调一次,
并在 upstream_status 转 active 之前不要放行下单。
它做什么、不做什么
- 不动钱:不建单、不冻结、不扣你的预付;
- 不接受你指定供应商或上游账户。那两维由我方按
line推导 ——
一个拼错的值会在上游建出第二条指向同一个银行账户的记录,
而此后没有任何一条业务路径会用到它(上游没有删除接口);
- 不重试
failed。那是终态,原样再发一次一定还是拒。
幂等
这个端点不要 x-idempotency-key,可以放心轮询。
原因是幂等层会在 24 小时内回放首次响应 —— 你拿同一把键轮询会永远
读到那一次的 pending,而收款人可能早就 active 了。
真正的幂等保证更强且在我方这一侧:送上游那把键**落在我方库里、
重试沿用**,所以同一条收款人无论调多少次都只会在上游产生一条记录。
返回什么
upstream_status 与 GET /v1/remit/payees/{id} 同一套取值
(这里只会出现 pending / active / failed)。
failed 时 reason 是我方自己的词表:
upstream_rejected上游拒了这条数据。具体理由要人看 ——
上游没有错误码表,只有一句英文自由文本,逐字转发只会让你按一句
会变的话写分支。处置:让用户改收款人重建一条,或联系我方;
upstream_gone上游那一侧的记录不见了(曾建成、后被删);payee_not_found/no_beneficiary_id我方这一侧的异常,请报工单。
⚠ 沙盒同样打真实上游(这条线没有上游沙盒)。也就是说你在沙盒里
verify 出来的 beneficiary 是真的,而上游没有删除接口。
联调时请用真实、可用的收款账户,别灌测试数据。
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
string | 必填 | 收款人号。pye_ 前缀可带可不带;剥掉后必须是纯数字。 |
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
line |
"express" | "pobo" | 可选 | 按哪条线建档。同一条收款人在两条线下是两条独立的上游记录 (个人线走该会员自己的子账户上下文)—— 两条线都要用就调两次。 |
请求头
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
x-on-behalf-of |
string | 必填 | 代哪个会员调用。必须是这条收款人关联到的那个会员。 |
响应
{
"id": "pye_1042",
"upstream_status": "pending",
"reason": ""
}product_not_available 这条线现在没有可用供应商,或(个人线)
这个会员还没有可用的上游子账户 —— 这不是收款人的问题 ·
service_unavailable 我方尚未配置上游。not_found 这条收款人不在这个会员名下(或 id 不是数字)。rate_limited 独立的一道闸(每商户 30 次/分钟)。
每一次未命中都是一次真实的上游写调用,而它在上游产生的是
不可撤销的实体。upstream_error 上游明确拒了这次调用(凭据 / 参数 / 限流)。upstream_timeout 结果不明。
⚠ 这一档重投是安全的:送上游那把幂等键是我方生成并落库的,
同键重投拿回的是首次那一条,不会在上游建出第二个收款人。curl -X POST 'https://api.zise.com/v1/remit/payees/{id}/verify' \
-H 'x-auth-token: Bearer $TOKEN' \
-H 'x-on-behalf-of: $MEMBER_ID'const res = await fetch("https://api.zise.com/v1/remit/payees/{id}/verify", {
method: "POST",
headers: {
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
},
});
// 金额按字符串读,别让它变成 number
const data = await res.json();import requests
res = requests.post(
"https://api.zise.com/v1/remit/payees/{id}/verify",
headers={
"x-auth-token": "Bearer $TOKEN",
"x-on-behalf-of": "$MEMBER_ID",
},
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()req, _ := http.NewRequest("POST", "https://api.zise.com/v1/remit/payees/{id}/verify",
nil)
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
res, err := http.DefaultClient.Do(req)
// 金额字段用 string 接,不要 float64HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.zise.com/v1/remit/payees/{id}/verify"))
.header("x-auth-token", "Bearer $TOKEN")
.header("x-on-behalf-of", "$MEMBER_ID")
.method("POST", HttpRequest.BodyPublishers.noBody())
.build();
// 金额字段用 String / BigDecimal,不要 double$ch = curl_init('https://api.zise.com/v1/remit/payees/{id}/verify');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'x-auth-token: Bearer $TOKEN',
'x-on-behalf-of: $MEMBER_ID',
],
]);
$res = curl_exec($ch);
// 金额用 bcmath / 字符串,不要 floatval
{
"id": "pye_1042",
"upstream_status": "pending",
"reason": ""
}