Skip to content

PIX Cash-Out 按密钥

使用收款方的 PIX 密钥执行 PIX 转账。

端点

POST /api/external/pix/cash-out

请求头

类型必填描述
AuthorizationStringApiKey {client_id}:{client_secret}
Content-TypeStringapplication/json
hmacStringbody 的 HMAC-SHA512 签名(十六进制)
Idempotency-KeyString避免重复处理的唯一键(最大 256 字符)

身份验证

参见 身份验证。HMAC 签名必须按照 HMAC-SHA512 中的说明生成。

Idempotency-Key - 重放行为

提供时,API 会在 24 小时内存储响应(仅 2xx),并为具有相同 (method, path, Idempotency-Key) 组合的任何新 POST 返回缓存的响应。缓存按端点作用域(/cash-out/refund 中的相同键不冲突)。

  • 在重放响应中,API 包含 X-Idempotent-Replay: true 头并回显发送的 Idempotency-Key
  • 超过 256 字符的键返回 400 Bad Request
  • 键是可选的。如果不发送,API 将每个 POST 作为新交易处理(确定性的 end_to_end_id 仍然保证 BACEN/SPI 层的幂等性,但如果第一次尝试已结算,可能会被拒绝并返回 failure_reason: "DUPL")。

必需权限

API Key 必须具有 transfer:write 权限才能发送 PIX。没有该权限,请求返回 403 Forbidden。参见 如何配置权限

请求体

字段类型必填描述
amountInteger金额,以**分(centavos)**为单位。R$ 30,00 = 3000
pix_keyString收款方 PIX 密钥
pix_key_typeString密钥类型:cpfcnpjemailphoneevp。如省略,根据密钥值自动检测。
descriptionString转账描述(最多 140 个字符)
external_idString您系统中的标识符,用于追踪。trim 后最多 128 字符。仅 a-zA-Z0-9._:- 字符。在响应和 webhook 中返回。无效值(不允许的字符、> 128 字符、trim 后为空)被静默丢弃 - 交易继续,external_id: null。如需确保持久化,请在发送前自行验证。
recipient_ispbString目的地机构的 ISPB,用于手动路由(8 位数字)。提供时,将付款定向到指定的 PSP。不要发送 Minha Konta 的 ISPB(04838403 - 机构内请求返回 same_institution 错误(不支持内部 PIX)。
end_to_end_idStringBACEN 格式的 End-to-End ID(E{ISPB}{YYYYMMDDHHmm}{entropy})。建议省略 - 后端每次尝试生成确定性的 E2E(相同的 amount + pix_key + merchant_id → 相同的 E2E)。此确定性即使没有 Idempotency-Key 也能保证 SPI/BACEN 的幂等性。仅在协调的重新处理场景中手动发送。
purposeString转账目的(用于内部使用和合规的自由文本字段)。

货币值

输入值以分为单位(R$ 1,00 = 100)。响应值以基础单位为单位(R$ 1,00 = 10000)。要将响应转换为雷亚尔,请除以 10.000。绝不使用浮点数。

示例

bash
curl -X POST https://api.minhakonta.com/api/external/pix/cash-out \
  -H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "hmac: $HMAC" \
  -d '{
    "amount": 3000,
    "pix_key": "12345678901",
    "pix_key_type": "cpf",
    "description": "Pagamento fornecedor",
    "external_id": "order-9876"
  }'

成功响应 -- 200 / 202

json
{
  "worked": true,
  "final": false,
  "transaction_id": "PIXOUT20260309a1b2c3d4e5f6",
  "end_to_end_id": "E04838403202603091530abcdef01",
  "external_id": "order-9876",
  "amount": 300000,
  "fee_amount": 350,
  "net_amount": 300350,
  "status": "accepted",
  "detail": "PIX enviado para processamento"
}

HTTP 200 vs 202

  • HTTP 200:交易已结算(final: true,status: "settled")。
  • HTTP 202:交易已被接受处理(final: false)。通过轮询或 webhook 跟踪状态。status 可能为 "accepted"(正常流程)、"queued"(应用了 rate-limit - 每 3 秒自动重试最多 120 分钟)或 "pending_approval"(等待双重授权工作流批准,启用时)。
字段类型描述
workedBooleantrue 表示请求已被接受
finalBoolean当交易达到终止状态(已结算或已拒绝)时为 true。仍在处理中时为 false
transaction_idString交易唯一标识
end_to_end_idStringSPI/BACEN 的 End-to-End 标识(格式 E{ISPB}...
external_idString您的标识符,按发送原样返回。未提供时为 null
amountInteger转账值,以基础单位(÷ 10.000 得到雷亚尔)。300000 = R$ 30,00
fee_amountInteger收取的手续费,以基础单位(÷ 10.000 得到雷亚尔)
net_amountInteger付款账户的总借记值,以基础单位。计算为 amount + fee_amount(总借记包括手续费)。不是收款方收到的值 - 他们只收到 amount。示例:amount=300000 + fee_amount=350net_amount=300350(从您账户借记 R$ 30,035,收款方入账 R$ 30,00)
statusString其中之一:accepted(HTTP 202,正常同步处理)、settled(HTTP 200,立即结算 - 快速通道中罕见)、queued(HTTP 202,因 DICT 限制等待自动重处理)、pending_approval(HTTP 202,等待批准)。参见终止状态:按 ID 查询 Cash-Out -- status 字段的值
detailString描述消息

cash-out 中的 net_amount 语义与 cash-in 不同

在 cash-out 中,net_amount = amount + fee_amount(付款账户的总借记)。在 cash-in(QR Code 支付)中,后端将 net_amount 视为扣除手续费后的净入账值。这种不对称性是历史性的 - 始终将 net_amount 视为"该方向上账户的实际变动"。对于会计对账,建议单独操作 amountfee_amount 字段。

拒绝代码

API 可能因输入验证(发送到 SPI 之前)、与 provider / DICT 的集成错误(在同步发送期间)或带自动重试队列的 rate-limit 拒绝 cash-out。通过 PACS.002 RJCT 的 BACEN 拒绝是异步到达的,仅通过 状态查询 或 webhook pix.payout.rejected 出现。

错误响应格式

cash-out 的同步拒绝以两种不同的格式返回 - 根据错误的来源选择正确的 parser:

格式 A -- 验证或集成:HTTP 400 或 422,body {"status": "failed", "errors": [{"code": "<代码>", "params": [...]}]}。常见代码:same_institution_transferinsufficient_balancedict_key_not_founddict_rate_limiteddict_bucket_exhausted

格式 B -- payload 验证错误:HTTP 400,body {"errors": {"bad_request": "消息"}}。示例:invalid or missing amountambiguous key

通过 data.status === "failed"(格式 A)vs data.errors.bad_request(格式 B)路由。

验证错误 (HTTP 400 / 422)

HTTP格式带代码的字段含义
400Berrors.bad_request: "invalid or missing amount"amount 缺失、零、负数或非整数
422Aerrors[0].code: "pix_key_ambiguous"没有 pix_key_type 的 11 位密钥 - 可能是 CPF 或电话。通过 CPF 验证 解决并明确提供 pix_key_type
400Berrors.bad_request: "invalid pix_key"密钥未通过格式规则(无效的 CPF 校验和、格式错误的电子邮件等)
422Aerrors[0].code: "same_institution_transfer"recipient_ispb 是 Minha Konta 自己的 ISPB(04838403)。不支持机构内 PIX - 使用内部 TEF。:此验证返回 HTTP 422(非 400),结构为 {status: "failed", errors: [{code: "same_institution_transfer", params: []}]}
422Aerrors[0].code: "insufficient_balance"可用余额小于 amount + fee_amount。考虑活动的 hold(gotcha min(TB, PG)
422Aerrors[0].code: "ceiling_exceeded"amount exceeds the configured per-transaction limit. errors[0].message = "PIX out limit reached: amount R$ X exceeds the R$ Y per-transaction limit"; errors[0].params.ceiling is the limit in subcents

same_institution 的结构变化

这些文档的早期版本声称 HTTP 400 带 detail: "same_institution"。实际行为是HTTP 422 带格式 A 结构(errors 作为 {code, params} 数组)。执行 if (status === 400 && body.detail === "same_institution") 的客户端在实践中不会触发 - 使用 if (status === 422 && body.errors?.[0]?.code === "same_institution_transfer")

与 provider / DICT 的集成错误 (HTTP 400)

当提供方在 BACEN 确认前返回同步错误时,API 返回格式 A,HTTP 400

代码(errors[0].code含义建议操作
dict_key_not_foundDICT/BACEN 中未找到 PIX 密钥与付款方核实;密钥可能已被删除或从未注册
dict_key_blocked密钥被封锁,例如疑似欺诈联系密钥持有人
dict_lookup_failed查询 DICT 失败5-30 秒后重试
dict_rate_limitedDICT 查询临时限制等待自动重处理或在新请求前退避
dict_bucket_exhaustedDICT 限制暂时不可用等待自动重处理;避免突发流量
provider_rejectedprovider 以未分类的 4xx 通用错误拒绝查看 errors[0].params 获取上下文;通过 Minha Konta 支持重新打开案例
provider_schema_error集成 payload 不兼容立即报告,未经 Minha Konta 指引不要重试
provider_unknown_error进入此路径的 400..499 之外的状态支持处提供完整日志

HTTP 是 400(不是 429)

这些文档的早期版本在同步路径中对 dict_rate_limiteddict_bucket_exhausted 显示 HTTP 429。当前合同是:同步集成错误返回 HTTP 400;带自动重处理的 DICT 限制返回 HTTP 202 queued

带自动重试的 rate-limit (HTTP 202 queued)

当 Minha Konta 在发送交易到服务商前检测到 DICT 查询限制时,PIX OUT 会保持处理中并进入自动重试。此路径会在容量不可用时避免新的 DICT 调用。

两种场景触发队列:

来源reason_code(webhook pix.payout.queued原因
客户额度DICT_CLIENT_RATE_LIMITED客户 DICT 查询量超过 Minha Konta 保护策略。
服务商 DICT 限制DICT_BUCKET_EXHAUSTEDDICT 查询能力暂时不可用。

队列时的 HTTP 响应

json
{
  "status": "queued",
  "type": "pix",
  "transaction_id": "PIXOUT20260309a1b2c3d4e5f6",
  "end_to_end_id": "E04838403202603091530abcdef01",
  "outbound_request_id": "0A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D",
  "amount": 300000,
  "message": "Payment rate-limited, enqueued for automatic retry (TTL 120 min)",
  "estimated_retry_seconds": 3,
  "queue_ttl_seconds": 7200
}

重试机制

  • Minha Konta 会在重试窗口有效期间自动重试。
  • 总 TTL:7200 秒(120 分钟)。过期后,客户会收到 pix.payout.failedreason_code: "DICT_QUEUE_TIMEOUT"
  • 立即 webhook:进入队列时,Minha Konta 触发 pix.payout.queued,包含 reason_codeDICT_CLIENT_RATE_LIMITEDDICT_BUCKET_EXHAUSTED)和 reason_description。下一个事件将是 pix.payout.confirmedpix.payout.failed

非终态状态

始终将 queuedaccepted 视为非终态。请通过 webhook 或交易查询接口跟踪最终结果。

权限与身份验证 (HTTP 401 / 403)

HTTPdetail含义
401Invalid HMAC signatureHMAC 签名不匹配。检查序列化 body 中字段的字母顺序 - 参见 HMAC-SHA512
401Invalid API Keyclient_id:client_secret 不正确
403permission 'transfer:write' requiredAPI Key 没有 PIX 权限
403IP not whitelisted源 IP 不在 API Key 白名单中

代码词汇 - UPPERCASE × lowercase

cash-out 的结构化代码来自不同词汇表:

命名空间约定来源示例
BACEN SPIUPPERCASE通过 PACS.002 RJCT 的异步拒绝(在 202 之后到达)- 在 GET /transactions/:id 和 webhook pix.payout.rejected 中可见AC03AB03ED05DUPLAM02FF08BE01
提供方 / DICTlowercase snake_caseBACEN 确认前的同步拒绝dict_key_not_founddict_rate_limitedsame_institution_transferprovider_schema_error
重试队列UPPERCASE(前缀 DICT_当有自动重试时的 webhook pix.payout.queued / pix.payout.failedDICT_CLIENT_RATE_LIMITEDDICT_BUCKET_EXHAUSTEDDICT_QUEUE_TIMEOUT

在程序化错误 switch 时,在您的一侧将其归一化为大写或小写以避免重复分支。不要期望在同步响应中出现 AM02 - BACEN 代码仅在 post-acceptance GET 查询中出现。

对应的 webhook

  • 同步拒绝(上面的格式 A/B)不触发 webhook - 客户端已经在 HTTP 响应中收到错误。
  • 按 rate-limit 入队(HTTP 202 queued立即触发 pix.payout.queued,带 reason_code + reason_description
  • 异步拒绝(202 接受后的 PACS.002 RJCT)触发 pix.payout.rejected,带 BACEN reason_code(AC03、AB03、ED05、DUPL 等)和英文 reason_description
  • 孤儿 void(>30min 无 PACS.002)触发 pix.payout.failed,带 reason_code: "orphan_force_voided"
  • 重试队列过期(120min)触发 pix.payout.failed,带 reason_code: "DICT_QUEUE_TIMEOUT"

PIX 密钥类型

类型格式示例
cpf11 位数字(无标点)12345678901
cnpj14 位数字(无标点)12345678000199
email电子邮件地址nome@empresa.com.br
phone区号 + 号码(11 位数字)11999998888
evpUUID v4a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d

11 位密钥 - CPF vs 电话歧义

恰好 11 位的密钥可能是 CPF,也可能是手机号(DDD + 9xxxx-xxxx)。当密钥有歧义时,API 以 HTTP 400 和 failure_reason: "ambiguous key" 拒绝。

建议解决方案:

  1. 使用 CPF 验证 端点(POST /api/external/cpf/validate)检查 11 位是否形成有效的 CPF
  2. 如果 valid: true → 在 cash-out 中发送 pix_key_type: "cpf"
  3. 如果 valid: false → 是电话,发送 pix_key_type: "phone"(API 自动添加前缀 +55
javascript
// 自动化流程示例
async function resolveKeyType(key) {
  if (key.length !== 11 || /\D/.test(key)) return null; // 无歧义
  
  const { data } = await api.post('/api/external/cpf/validate', { cpf: key });
  return data.valid ? 'cpf' : 'phone';
}

提示:以纯 11 位(DDD + 号码)发送电话。API 自动添加前缀 +55。避免手动发送 +55 - 可能在某些客户端中导致 HMAC 验证失败。

下一步

创建转账后,通过以下方式跟踪状态:

或通过 Webhook 自动接收确认。

Minha Konta Instituição de Pagamento - ISPB 39929224