Pix 密钥出金保护机制
概述
在发送密钥出金之前,清算提供商会在 Pix 目录(DICT)中查询该密钥。该提供商限制查询次数,并可能临时封锁反复查询不存在密钥的账户。一旦发生,账户下的所有出金都会受到影响。
为避免这种情况,平台会立即用已知的结果响应,并避免会导致提供商封锁账户的重复查询。共有三种行为,可在 POST /api/pix/cash-out 的响应中看到:
| 情况 | API 响应 | 是否创建交易 | 是否发送 Webhook |
|---|---|---|---|
| 提供商已告知不存在的密钥 | 立即返回 422 | 否 | 否 |
| 提供商正在封锁查询 | 立即返回 429,带 Retry-After | 否 | 否 |
| 提供商速率限制 | 正常返回 201,发送最多延迟 90 秒 | 是 | 是,与往常一致 |
重复使用已被否定的密钥不会封锁账户或客户:只有该密钥的结果会被保存(15 分钟;若提供商在 2 小时内再次否定则最长 30 分钟),平台返回同样的否定结果而不向提供商查询。其他密钥均正常。
该行为取决于您账户的清算提供商:在部分清算提供商中启用,在其他清算提供商中流程不变。如对您的账户有疑问,请联系您的集成经理。
密钥不存在(422)
触发时机: 针对不存在密钥的首次尝试按您账户的正常流程处理:异步接受时,API 返回 201,随后您会收到说明密钥未找到的失败 Webhook;同步接受时,错误会直接在响应中返回。如果提供商最近已答复该密钥不存在,拒绝结果在您账户的首次尝试时就可能返回。当提供商回复该密钥不存在时,平台会将该结果保存 15 分钟。在此期间,对该密钥的新出金会直接由平台返回同样的否定结果(422 PIX_KEY_NOT_FOUND,details.cached: true),不会向提供商查询。
- 到期后,如果提供商在 2 小时内再次否定同一密钥,新的记录有效期为 30 分钟。这是上限,不会更长。
- 由平台应答的尝试不计数,也不会延长时长。只有提供商自身的新否定才会计数。
- 仅该密钥受影响。账户、客户和所有其他密钥均正常。这不是封锁,也不是惩罚。
- 存在原因:当同一个不存在的密钥被反复查询时,提供商的 DICT 规则会暂停该账户所有密钥出金约 30 分钟。保存结果可保护整个账户。
- 新注册的密钥最多可能需要等到该时长结束才会被接受。
- 不创建交易,不预留余额,也不发送 Webhook。
- 被拒绝的请求不会占用
externalId,可以重复使用。
{
"code": "PIX_KEY_NOT_FOUND",
"message": "Chave Pix não encontrada",
"details": {
"retryAfterSeconds": 780,
"source": "provider-guard",
"cached": true
}
}retryAfterSeconds(以及 Retry-After 头)始终给出实际剩余时间。
处理方式: 与通过 Webhook 到达的"密钥未找到"失败完全一样处理:请用户核对密钥。如果密钥刚注册,请等待 retryAfterSeconds 后重试。
密钥不存在的否定结果可能以两种方式到达。请将两者视为同一情况(密钥不存在):先更正再重新发送。
| 否定来源 | 到达方式 | 标识 |
|---|---|---|
| 提供商 | 出金失败(Webhook) | 清算提供商返回的 errorCode 和 errorMessage(errorCode 因清算提供商而异,不应用于业务逻辑) |
| 平台记录 | 响应中的 422 | code 为 PIX_KEY_NOT_FOUND,details.cached: true 及 details.retryAfterSeconds |
时间线示例:
10:00 向密钥 X 出金
-> 201,随后收到失败 Webhook"密钥未找到"
10:02 再次向密钥 X 出金
-> 由记录立即返回 422,retryAfterSeconds ~ 780,cached: true
10:15 记录到期
10:16 再次向密钥 X 出金
-> 重新向提供商查询
-> 若再次被否定,新记录 30 分钟(至 10:46)已被接受的出金: 在异步受理的账户中(响应 201,结果通过 Webhook 返回),如果密钥在出金被接受之后、发送给提供商之前被记录为不存在(例如同时发送多笔向同一密钥的出金),该出金会以密钥不存在的错误结束,预留金额被释放,与提供商否定时完全一致。通知通过正常的失败 Webhook 到达。在同步受理的账户中,同样的检查会在调用本身中完成,错误直接在响应中返回。
未提供 details.document 的出金由提供商异步校验:拒绝结果稍后通过 Webhook 到达,通常不说明原因。因此密钥主要通过带证件的出金进入记录;一旦被记录,保存的结果对发往该密钥的任何出金都有效,无论是否带证件。请尽可能传入收款方证件:错误会立即返回并附带原因。
提供商正在封锁查询(429)
触发时机: 提供商临时封锁了密钥查询。只要封锁持续,提供了收款方证件的密钥出金中,大部分会被立即拒绝,不创建交易、不预留余额,也不发送 Webhook。
- 仅在提供商封锁期间持续。
- 每 30 秒最多放行一笔出金,用来测试提供商是否恢复。该出金会被正常受理(
201),如果封锁仍在持续,会通过 Webhook 以错误结束,预留金额退回。测试成功后,保护机制结束。 - 该封锁来自提供商(其 DICT 查询规则),不是平台的惩罚。该保护机制正是为了避免它发生。
响应包含 Retry-After 头和 details.retryAfterSeconds(60)。
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json{
"code": "TOO_MANY_PIX_KEYS_NOT_FOUND",
"message": "Muitas tentativas com chaves Pix inexistentes. Saque por chave bloqueado temporariamente; tente novamente em instantes.",
"details": {
"retryAfterSeconds": 60,
"source": "provider-guard"
}
}处理方式: 等待 Retry-After 指定的时间后重试,若仍被拒绝请使用退避策略。没有创建或预留任何内容。
10:00 提供商封锁密钥查询
10:00 带证件的密钥出金 -> 立即 429,Retry-After: 60
10:00:30 放行一笔出金作为测试 -> 201,若提供商仍封锁,则返回失败 Webhook
10:01 再次尝试 -> 429(仍被封锁)
10:03 提供商放开;平台自动检测到
10:03 再次尝试 -> 201,正常流程提供商速率限制(发送变慢)
触发时机: 查询量接近提供商的限制。
API 的响应: 出金被正常接受(201),发送给提供商可能最多延迟 90 秒。结果仍通过 Webhook 送达。该等待仅适用于已提供收款方证件的出金,且仅限异步接受的账户;未带证件的出金不会被延迟。
处理方式: 无需任何操作。请勿因延迟而重复发送出金,请先查询其状态(参见状态监控)。
10:00:00 出金被接受 -> 201 (PENDING)
10:01:20 发送给提供商(最多延迟 90 秒)
10:01:23 Webhook 返回结果对集成方的变化
| 之前 | 现在 | |
|---|---|---|
| 重复使用不存在的密钥 | 每次尝试都返回 201 并收到失败 Webhook | 密钥记录有效期间立即 422,无交易,无 Webhook |
| 提供商正在封锁查询 | 201,数分钟后收到失败 Webhook | 立即 429,无交易,无 Webhook,几乎所有出金如此。每 30 秒放行一笔出金作为测试,可能通过 Webhook 失败 |
| 速率限制 | 201 和 Webhook | 201 和 Webhook,发送最多延迟 90 秒 |
您必须在请求响应中直接处理同步拒绝(422 和 429)。对于 422 和 429 拒绝,不会发送任何 Webhook,请勿等待。封锁期间收到的 201 是一笔真实出金(提供商测试),按正常流程处理,包括 Webhook。
最佳实践
- 将带
cached: true的PIX_KEY_NOT_FOUND与通过 Webhook 到达的"密钥未找到"失败完全一样处理:请用户核对密钥。 - 收到
PIX_KEY_NOT_FOUND时,请先更正密钥再重新发送。不要对同一密钥自动重试,并遵守retryAfterSeconds。 - 不要循环重复同一个无效密钥:在时长结束前,响应都不会变。
- 遵守
Retry-After和details.retryAfterSeconds,若仍被拒绝请使用退避策略。 - 只要知道收款方证件,就传入
details.document:错误会立即返回,而不是数分钟后才通过 Webhook 到达。 - 未确认状态前不要重复发送同一笔出金。幂等性基于
externalId,参见 PIX Cash-Out。