Webhook 载荷
每种事件类型发送的 payload 示例。所有 webhook 都以 HTTP POST 发送,Content-Type: application/json。
安全头
每个通知都包含 X-Owem-Signature(HMAC-SHA256)、X-Owem-Timestamp、X-Owem-Event-Id 和 X-Owem-Event-Type 头。参见 Webhooks — 概述 了解验证的详细信息。
状态参考
并非所有事件都意味着交易已完成。使用下表了解何时资金实际结算。
| 事件 | 状态 | 含义 | 资金已结算? |
|---|---|---|---|
pix.charge.created | created | QR 码已生成或 cash-in 已启动。等待付款。 | 否 — 仅创建 |
pix.charge.paid | paid | PIX 已接收并在账户中结算。余额已更新,手续费已收取。 | 是 |
pix.charge.expired | expired | QR 码在未付款情况下过期。 | N/A |
pix.charge.cancelled | cancelled | merchant 在付款前明确取消了 QR 码。 | N/A |
pix.payout.queued | queued | 已发送的 PIX 由于速率限制(ClientLimiter 或 DICT bucket)入队。尚无借记。 | 否 — 等待配额 |
pix.payout.processing | processing | PIX 已发送,等待目的地确认。余额已保留(hold)。 | 否 — 可能撤销 |
pix.payout.confirmed | settled | PIX 已发送并被目的地确认。最终借记。 | 是 |
pix.payout.failed | rejected | PIX 已发送,被目的地拒绝。Hold 已释放,余额已恢复。 | 否 |
pix.payout.returned | returned | 已发送的 PIX 在结算后退回。 | 是(反向) |
pix.refund.requested | requested | 已请求 PIX 退款(MED)。创建了预防性封锁。 | 部分 |
pix.refund.completed | settled / completed | PIX 退款已完成并结算。最终借记。 | 是 |
pix.return.received | settled | 收到 PIX 退款并已结算(在账户中贷记)。 | 是 |
pix.infraction.created | ACKNOWLEDGED | 对您报告 PIX 违规。需要操作。 | 部分 — 如果 >R$1k 则预防性封锁 |
pix.infraction.resolved | CLOSED / CANCELLED | 违规已解决(退款已执行或已拒绝)。 | N/A — 影响在其他事件中 |
pix.infraction.defense_submitted | defense_submitted | merchant 已提交抗辩。等待 BACEN。 | N/A |
webhook.test | test | 通过 Admin/Merchant portal 手动触发的测试事件。 | N/A |
对账规则:
- 仅在以下状态中考虑进入余额:
paid(PIX IN 贷记)和returned(先前发送的 PIX OUT 的反向)。 - 仅在以下状态中考虑离开余额:
settled(已确认的 PIX OUT 借记)、completed(最终 MED refund 借记)以及pix.return.received的settled(先前收到的 PIX IN 的反向)。 - 所有其他状态(
created、queued、processing、rejected、expired、requested、ACKNOWLEDGED、defense_submitted等)都是中间的 — 不会触发您那边的会计移动。 - 不要将
pix.payout.processing视为确认;等待终止事件(pix.payout.confirmed或pix.payout.failed)。
公共字段
所有 webhook 载荷都包含这些字段:
| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 触发 webhook 的事件类型(例:pix.charge.paid) |
status | string | 操作状态 — 见 状态参考 |
account_id | integer | 您在 Owem 的账户号码 |
entity_id | string (UUID) | Owem 实体标识符 |
货币值:所有值都以子分(1 BRL = 10.000 子分)。要转换为雷亚尔:值 / 10000。示例:300000 / 10000 = R$ 30,00。
pix.charge.paid
在收到 PIX 并在账户中结算时发送。此事件确认资金已进入。
示例 — 与 QR 码关联
{
"event_type": "pix.charge.paid",
"status": "paid",
"account_id": 10014,
"amount": 300000,
"fee_amount": 400,
"end_to_end_id": "E9040088820260402095758709999671",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"tx_id": "u5f26sfyrq4plkw7tjwa",
"qr_code_id": "f401d5e3-a2b1-4c8e-9f3d-1234567890ab",
"counterparty_name": "MARIA SANTOS",
"payer_document": "12345678901",
"payer_ispb": "60701190",
"payer_bank_name": "Itau Unibanco S.A.",
"external_id": "order-9876",
"paid_at": "2026-04-02T09:58:05Z",
"recipient_key": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"recipient_key_type": "evp",
"receiver": {
"name": "PENHOTA GESTAO E INTERMEDIACAO LTDA",
"document": "62188010000150",
"account": "0000000019",
"ispb": "37839059",
"institution_name": "OWEM PAY IP"
}
}示例 — 直接转账(无 QR)
{
"event_type": "pix.charge.paid",
"status": "paid",
"account_id": 10014,
"amount": 300000,
"fee_amount": 400,
"end_to_end_id": "E9040088820260402095758709999671",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"tx_id": null,
"qr_code_id": null,
"counterparty_name": "JOAO SILVA",
"payer_document": "98765432100",
"payer_ispb": "00000000",
"payer_bank_name": "Banco do Brasil S.A.",
"external_id": null,
"paid_at": "2026-04-02T10:15:22Z",
"recipient_key": "12345678901",
"recipient_key_type": "cpf",
"receiver": {
"name": "PENHOTA GESTAO E INTERMEDIACAO LTDA",
"document": "62188010000150",
"account": "0000000019",
"ispb": "37839059",
"institution_name": "OWEM PAY IP"
}
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.charge.paid |
status | string | 始终为 paid |
account_id | integer | 收到 PIX 的账户号码 |
amount | integer | 收到的值,以子分。300000 = R$ 30,00 |
fee_amount | integer | 收取的手续费,以子分。400 = R$ 0,04 |
end_to_end_id | string | BACEN E2E 标识符(每笔 PIX 交易唯一) |
entity_id | string (UUID) | Owem 实体标识符 |
tx_id | string 或 null | 交易 ID。与 QR 码关联时存在。直接转账时为 null |
qr_code_id | string 或 null | 关联的 QR 码 UUID。直接转账时为 null |
counterparty_name | string 或 null | 付款方(发送方)名称 |
payer_document | string 或 null | 付款方 CPF/CNPJ(仅数字) |
payer_ispb | string 或 null | 付款方机构的 ISPB(8 位数字) |
payer_bank_name | string 或 null | 付款方机构名称,通过 BCB 缓存解析(896 家银行) |
external_id | string 或 null | 您的外部标识符。当 QR 码通过带 external_id 的 API 创建时存在。直接转账或无 external_id 的 QR 时为 null |
paid_at | string (ISO 8601) | 结算日期/时间(UTC) |
recipient_key | string 或 null | 收到付款的 PIX 密钥(EVP、CPF、CNPJ、email 或电话) |
recipient_key_type | string 或 null | 接收方 PIX 密钥类型:evp、phone、email、cpf、cnpj |
receiver | object | 接收方(您)的完整数据。包括 name、document、account、ispb、institution_name |
载荷变化:部署后对账
在少数情况下(后端 pod 在 webhook 发出前死亡、事件后回溯重放),worker PostDeployReconciliation 可能触发 pix.charge.paid,字段减少 — 通常没有 receiver、payer_ispb、payer_bank_name、recipient_key 或 recipient_key_type。始终存在的字段:event_type、status、account_id、amount、end_to_end_id、fee_amount、counterparty_name、payer_document、external_id、paid_at、tx_id(与 QR 关联时)。
您的消费者应将所有非必需字段视为可选(nil/缺失),并通过 end_to_end_id 对账。
qr_code_id 是规范的 UUID v4
qr_code_id 字段始终以规范格式的 UUID v4 序列化(36 字符带连字符:f401d5e3-a2b1-4c8e-9f3d-1234567890ab)— 从不作为原始二进制、base64 或无连字符的十六进制。用于与 POST /api/external/pix/cash-in 响应直接关联(您请求中的 transaction_id 返回 QR 的 tx_id,这里的 qr_code_id 是内部主键)。
pix.charge.expired
QR 码在未付款情况下过期时自动触发。由 worker QrExpirationChecker 执行,cron */5 * * * *(每 5 分钟一次,针对 expires_at 已过的 QR 码)。
{
"event_type": "pix.charge.expired",
"status": "expired",
"account_id": 10014,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"tx_id": "abc123def456ghi789",
"amount": 500000,
"external_id": "order-9876",
"expired_at": "2026-04-02T14:30:00Z"
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.charge.expired |
status | string | 始终为 expired |
account_id | integer | 发出 QR 码的账户 |
entity_id | string (UUID) | Owem 实体标识符 |
tx_id | string | 收款/QR 码 ID |
amount | integer | 预期值,以子分(未收取) |
external_id | string 或 null | 您的外部标识符,如果在创建时发送 |
expired_at | string (ISO 8601) | worker 检测到过期的时刻(UTC)— 可能比 QR 的真实 expires_at 晚 ~5 分钟 |
pix.charge.cancelled
在 QR 码被 merchant 在付款前通过门户操作(后端:Fluxiq.UseCases.Pix.QRCodes.cancel_qrcode/2)明确取消时发送。在自动过期(使用 pix.charge.expired)或付款(pix.charge.paid)时不触发。
{
"event_type": "pix.charge.cancelled",
"status": "cancelled",
"tx_id": "abc123def456ghi789",
"amount": 500000,
"account_id": 10014,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"external_id": "order-9876",
"cancelled_at": "2026-04-23T12:30:00Z"
}| 字段 | 类型 | 描述 |
|---|---|---|
tx_id | string | 收款/QR 码 ID |
amount | integer | 预期值,以子分(未收取) |
external_id | string 或 null | 您的外部标识符,如果在创建时发送 |
cancelled_at | string (ISO 8601) | 取消生效的时刻(UTC) |
cancelled、expired 和 paid 之间的区别
pix.charge.cancelled:merchant 在付款前有意取消pix.charge.expired:QR 生命周期耗尽(workerQrExpirationChecker每 5 分钟)pix.charge.paid:收款成功结算
pix.charge.created
在 QR 码生成或 cash-in 启动时发送。没有财务移动发生。
{
"event_type": "pix.charge.created",
"status": "created",
"account_id": 10014,
"amount": 500000,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"tx_id": "abc123def456ghi789",
"external_id": "order-9876"
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.charge.created |
status | string | 始终为 created |
amount | integer | 预期值,以子分 |
tx_id | string | 收款/QR 码 ID |
external_id | string 或 null | 您的外部标识符,按发送原样返回。未提供或通过门户生成的 QR 时为 null |
pix.payout.confirmed
在已发送的 PIX 被目的地机构确认时发送。最终借记。
{
"event_type": "pix.payout.confirmed",
"status": "settled",
"account_id": 10014,
"amount": 500000,
"fee_amount": 200,
"end_to_end_id": "E3783905920260402101500000001",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "payment-456",
"pix_key": "destinatario@email.com",
"pix_key_type": "EMAIL",
"description": "Pagamento fornecedor",
"initiated_at": "2026-04-02T10:14:59Z",
"recipient": {
"name": "EMPRESA DESTINO LTDA",
"document": "12345678000199",
"ispb": "60701190",
"account": "12345678",
"agency": "0001",
"institution_name": "Itau Unibanco S.A."
},
"sender": {
"name": "MINHA EMPRESA LTDA",
"document": "98765432000100",
"ispb": "37839059",
"account": "00001234",
"agency": "0001"
}
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.payout.confirmed |
status | string | 始终为 settled — 最终借记 |
amount | integer | 发送值,以子分 |
fee_amount | integer | 收取的手续费,以子分 |
end_to_end_id | string | BACEN E2E 标识符 |
transaction_id | string (UUID) | 交易唯一标识 |
external_id | string 或 null | 您的外部标识符 |
pix_key | string | 收款方 PIX 密钥 |
pix_key_type | string | 密钥类型:CPF、CNPJ、EMAIL、PHONE、EVP |
description | string 或 null | 发送方填写的描述 |
initiated_at | string (ISO 8601) | 此 webhook 触发的时刻(UTC)。不是 cash-out 原始 request 或 BACEN settlement 的时间戳。要与您发送 POST 的时刻关联,请使用 GET /api/external/transactions/ref/{external_id} 的 created_at;对于 webhook 投递的确切时刻,请使用头 X-Owem-Timestamp |
recipient | object | 收款方的银行数据(通过 DICT 解析) |
recipient.name | string 或 null | 目标账户持有人姓名 |
recipient.document | string 或 null | 收款方 CPF/CNPJ(仅数字) |
recipient.ispb | string 或 null | 目标机构的 ISPB |
recipient.account | string 或 null | 目标账户号码 |
recipient.agency | string 或 null | 目标账户分行 |
recipient.institution_name | string 或 null | 目标机构名称(通过 BCB 缓存解析) |
sender | object | 发送方账户(您的 Owem 账户)的银行数据 |
sender.name | string 或 null | 发送方账户持有人姓名 |
sender.document | string 或 null | 发送方 CPF/CNPJ(仅数字) |
sender.ispb | string 或 null | Owem Pay 的 ISPB(37839059) |
sender.account | string 或 null | 发送方账户号码 |
sender.agency | string 或 null | 发送方账户分行 |
pix.payout.processing
在已发送的 PIX 正在处理时发送。余额已保留(hold)但不是最终的。此事件是可选的 — 如果您只想在终止状态时通知,请忽略它并等待 pix.payout.confirmed 或 pix.payout.failed。
载荷字段与 pix.payout.confirmed 相同,但 status: "processing" — 余额已保留,可能撤销。
事件顺序
pix.payout.processing 始终跟随(几秒到几分钟后)pix.payout.confirmed 或 pix.payout.failed。在快速交易中(立即结算),processing 可能被省略,您直接收到终止事件。
pix.payout.failed
在发送的 PIX 被拒绝时发送。Hold 已释放,余额已恢复。
2026-04-10 更新
载荷包含结构化字段 reason_code(BACEN SPI 代码 2-6 字符)和 reason_description(英文描述)。新集成应使用这些字段进行故障的程序化路由。
互斥:当后端可以从拒绝中提取 BACEN 代码(例:"rejected: AC03")时,载荷仅发送 reason_code + reason_description — 遗留字段 reason 被移除。当故障没有可解析的 BACEN 代码(例:内部超时、无代码的 provider 错误)时,载荷仅发送 reason(自由字符串)— 没有 reason_code。在您的消费者中处理两种格式。
{
"event_type": "pix.payout.failed",
"status": "rejected",
"account_id": 10014,
"amount": 500000,
"fee_amount": 200,
"end_to_end_id": "E3783905920260402101500000001",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"external_id": "payment-456",
"pix_key": "destinatario@email.com",
"pix_key_type": "EMAIL",
"description": "Pagamento fornecedor",
"initiated_at": "2026-04-02T10:14:59Z",
"reason_code": "AC03",
"reason_description": "Invalid creditor account number",
"reason": "Conta destinatario nao encontrada",
"recipient": {
"name": "EMPRESA DESTINO LTDA",
"document": "12345678000199",
"ispb": "60701190",
"account": "12345678",
"agency": "0001",
"institution_name": "Itau Unibanco S.A."
},
"sender": {
"name": "MINHA EMPRESA LTDA",
"document": "98765432000100",
"ispb": "37839059",
"account": "00001234",
"agency": "0001"
}
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.payout.failed |
status | string | 始终为 rejected — hold 已释放,余额已恢复 |
amount | integer | 值,以子分 |
fee_amount | integer | 手续费,以子分。显示的手续费是会被收取的值 — 在 TB ledger 中,pending 转账会自动撤销,因此在被拒绝的交易中实际上没有手续费借记 |
end_to_end_id | string | BACEN E2E 标识符 |
transaction_id | string (UUID) | 交易唯一标识 |
external_id | string 或 null | 您的外部标识符 |
pix_key | string | 收款方 PIX 密钥 |
pix_key_type | string | 密钥类型:CPF、CNPJ、EMAIL、PHONE、EVP |
description | string 或 null | 发送方填写的描述 |
initiated_at | string (ISO 8601) | 此 webhook 的 dispatch 时刻(UTC) |
reason_code | string 或缺失 | 结构化 BACEN SPI 代码(2-6 字符)。示例:AC03、ED05、AM02、BE01、MD06、FOCR。当后端从拒绝中提取 BACEN 代码时存在。用于程序化路由 |
reason_description | string 或缺失 | reason_code 的英文描述。与 reason_code 一起存在。示例:"Invalid creditor account number" |
reason | string 或缺失 | [Legacy] 原因的自由描述。仅在拒绝没有可解析的 BACEN 代码时存在 — 与 reason_code 互斥 |
recipient.* | object | 同 pix.payout.confirmed(收款方银行数据) |
sender.* | object | 同 pix.payout.confirmed(发送方账户银行数据) |
dispatch site 之间的载荷变化
pix.payout.failed 由多个路径触发 — pix.ex 中的主路径从字符串 "rejected: <CODE>" 中提取 BACEN 代码并应用上述 reason vs reason_code 互斥。次要路径(stale checker、retry worker、部署后对账)可能在同一载荷中发送 reason 和 reason_code,或仅发送 reason 而无结构。始终将两个字段视为可选,并在 reason_code 存在时优先使用。
最常见的 reason_code 代码(BACEN SPI)
| 代码 | 英文含义 | 建议操作 |
|---|---|---|
AC03 | Invalid creditor account number | 与最终客户确认收款方银行数据 |
AC06 | Creditor account blocked | 目标账户已封锁 — 不要重试 |
AM02 | Not allowed amount (limit exceeded) | 值超过目标或源的 PIX 限制 |
AM04 | Insufficient funds | 源端余额不足 |
BE01 | End customer not in whitelist | 收款方标识符未识别 |
ED05 | Settlement failed | Settlement 失败 — 调查后可重试 |
MD06 | Refund requested by end customer | 最终客户请求的退款 |
FOCR | Forbidden credit return | 禁止贷记返回 |
完整列表:查询 BACEN SPI 消息目录。
pix.payout.returned
在您发送的 PIX 被目的地银行在结算后退回时发送。罕见,但可能发生在几天后。merchant 的余额增加(进入)。
命名区别
三个不同的流程可能混淆:
pix.return.received:您收到的 PIX 正在退回给原始付款方。余额减少。pix.payout.returned(这个):您发送的 PIX 正在返回给您。余额增加。pix.refund.requested:您收到的 PIX 上的 MED 预防性封锁。资金冻结。
相同载荷,两个事件,两种不同的状态
后端在同一调用(文件 return_in_handler.ex)中触发 pix.return.received 和 pix.payout.returned,使用相同的基础载荷,只重写 status 字段:
pix.return.received→status: "settled"(您收到的 PIX 正在退回 → 余额减少)pix.payout.returned→status: "returned"(您发送的 PIX 正在返回 → 余额增加)
如果您的对账逻辑按 status 过滤或按 (e2e, event_type) 去重,请确保先区分 event_type — 载荷几乎相同。
{
"event_type": "pix.payout.returned",
"status": "returned",
"account_id": 10014,
"amount": 500000,
"original_amount": 500000,
"refunded_amount": 500000,
"fee_amount": 0,
"net_amount": 500000,
"is_partial": false,
"total_refunded": 500000,
"remaining_refundable": 0,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"return_e2e_id": "D3783905920260410111500000001",
"end_to_end_id": "E3783905920260402101500000001",
"original_transaction_id": "PIXOUTa1b2c3d4e5f67890abcdef1234567890",
"external_id": "payment-456",
"return_reason": "MD06",
"return_reason_description": "Refund requested by end customer",
"counterparty_ispb": "60701190",
"counterparty_name": "EMPRESA DESTINO LTDA",
"counterparty_document": "12345678000199",
"counterparty_institution_name": "Itau Unibanco S.A.",
"returned_at": "2026-04-10T11:15:00Z"
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.payout.returned |
status | string | 始终为 returned — 退款已结算并在您账户中贷记 |
amount | integer | 与 refunded_amount 相同(为兼容性保留) |
original_amount | integer | 原始 PIX OUT 的值,以子分 |
refunded_amount | integer | 此次退款中实际退回的值(可能为部分) |
fee_amount | integer | 此次退款中收取的手续费(通常为 0) |
net_amount | integer | refunded_amount - fee_amount |
is_partial | boolean | 当 refunded_amount < original_amount 或仍有余额要退回时为 true |
total_refunded | integer | 此原始交易的所有退款之和(包括此次) |
remaining_refundable | integer | max(original_amount - total_refunded, 0) — 仍可退回的余额 |
return_e2e_id | string | 退款的 E2E(前缀 D) |
end_to_end_id | string | 原始 PIX OUT 交易的 E2E(前缀 E) |
original_transaction_id | string | 原始 PIX OUT 的 transaction_id。用于与您的系统对账 |
external_id | string 或 null | 原始交易的您的外部标识符(如适用) |
return_reason | string | 退款的 BACEN 代码:MD06、BE08、FR01、SL02 |
return_reason_description | string | return_reason 的英文描述 |
counterparty_ispb | string | 发起退款的机构 ISPB |
counterparty_name | string | 对手方名称(原始 PIX 的目标机构) |
counterparty_document | string 或 null | 对手方 CPF/CNPJ |
counterparty_institution_name | string 或 null | 对手方机构名称(BCB 缓存) |
returned_at | string (ISO 8601) | 此 webhook 的 dispatch 时刻(UTC) |
手续费不退还
原始 cash-out 的手续费在 pix.payout.returned 中不退还。手续费是为成功发送而收取的,它确实发生了。如果业务规则要求向最终客户退还手续费,merchant 必须单独进行。
pix.refund.requested
在通过 MED(特殊退款机制)请求 PIX 退款时发送。资金已在收到原始 PIX 的 merchant 账户中预防性封锁。
仅 PIX In
此事件仅适用于收到的 PIX(cash-in)。如果您发送了 PIX 并被退回,您将收到 pix.return.received 而不是 pix.refund.*。
{
"event_type": "pix.refund.requested",
"status": "requested",
"account_id": 10014,
"requested_amount": 300000,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"block_id": "b1c2d3e4-f5g6-7890-hijk-lm1234567890",
"infraction_report_id": "INF20260402001",
"e2e_id": "E9040088820260402095758709999671",
"external_id": null,
"blocked_amount": 300000,
"fee_amount": 0,
"fraud_category": "OTHER",
"deadline": "2026-04-09T14:30:00Z",
"scenario": "cautelar",
"created_at": "2026-04-02T14:30:00Z"
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.refund.requested |
status | string | 始终为 requested — 预防性封锁活跃 |
requested_amount | integer | 请求退款的值,以子分 |
block_id | string (UUID) | 预防性封锁标识符 |
infraction_report_id | string | OnZ 中违规的标识符 |
e2e_id | string | 正在争议的原始 PIX 交易的 E2E |
external_id | string 或 null | 您的外部标识符(如适用) |
blocked_amount | integer | 实际封锁的值,以子分 |
fee_amount | integer | MED 手续费,以子分 |
fraud_category | string | 声称的欺诈类别。可能值:SCAM、ACCOUNT_TAKEOVER、COERCION、FRAUDULENT_ACCESS、OTHER。当对方未发送特定 FraudType 时,值为 OTHER(REFUND_REQUEST 的默认)。 |
deadline | string (ISO 8601) | 分析/抗辩的截止日期(UTC) |
scenario | string | MED 场景:cautelar 或 fraude |
created_at | string (ISO 8601) | 封锁的日期/时间(UTC) |
pix.refund.completed
在 MED 退款成功完成时触发。在 MED 接受周期中通过 med/processor.ex:915 触发。
{
"event_type": "pix.refund.completed",
"status": "settled",
"account_id": 10014,
"amount": 300000,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"block_id": "b1c2d3e4-f5g6-7890-hijk-lm1234567890",
"infraction_report_id": "INF20260402001",
"e2e_id": "E9040088820260402095758709999671",
"external_id": null,
"reason": "analysis_unfounded",
"completed_at": "2026-04-02T14:30:00Z"
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.refund.completed |
status | string | 始终为 completed — MED 退款已完成 |
amount | integer | 退回的值,以子分 |
block_id | string (UUID) | 预防性封锁标识符 |
infraction_report_id | string | OnZ 违规标识符 |
e2e_id | string | 原始 PIX 交易的 E2E |
external_id | string 或 null | 您的外部标识符(如适用) |
reason | string | 释放原因(例:analysis_unfounded、manual_release) |
completed_at | string (ISO 8601) | 完成日期/时间(UTC) |
pix.return.received
在收到 PIX 退款时发送。此事件在您之前收到的 PIX(cash-in)正在退回给原始付款方时生成。merchant 的余额减少。
命名区别
pix.return.received(这个):您收到的 PIX 正在退回给原始付款方。余额减少。pix.payout.returned:您发送的 PIX 正在返回给您。余额增加。
名称与常识含义相反 — 请注意。
{
"event_type": "pix.return.received",
"status": "settled",
"account_id": 10014,
"amount": 300000,
"original_amount": 300000,
"refunded_amount": 300000,
"fee_amount": 0,
"net_amount": 300000,
"is_partial": false,
"total_refunded": 300000,
"remaining_refundable": 0,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"return_e2e_id": "D9040088820260402111500000001",
"end_to_end_id": "E9040088820260402095758709999671",
"original_transaction_id": "PIXINE9040088820260402095758709999671",
"external_id": null,
"return_reason": "MD06",
"return_reason_description": "Refund requested by end customer",
"counterparty_ispb": "60701190",
"counterparty_name": "EMPRESA X LTDA",
"counterparty_document": "98765432100",
"counterparty_institution_name": "Itau Unibanco S.A.",
"returned_at": "2026-04-02T11:15:00Z"
}字段列表与 pix.payout.returned 相同,但语义相反 — 详见上方"命名区别"。
去重
要对 webhook 重试去重,请使用头 X-Owem-Event-Id 或组合 (return_e2e_id, end_to_end_id)。return_e2e_id 以 D(退款)开头,end_to_end_id 以 E(原始)开头。
webhook.test
手动触发的测试事件,用于验证 webhook 配置。
{
"event_type": "webhook.test",
"status": "test",
"account_id": 10014,
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7",
"message": "Webhook test event"
}pix.infraction.created
在对方(通过 BACEN DICT)报告 PIX 违规时触发。需要在 defense_deadline 前抗辩或自动 MED 预防性封锁。
由 pix_compliance.ex:463 触发(函数 upsert_infraction,当 is_new=true 时)。
{
"event_type": "pix.infraction.created",
"infraction_id": "e7f4d23a-6f2a-4d1e-a3e6-fe8b32bba95d",
"e2e_id": "E0416201020260404113012abcdef1234",
"status": "ACKNOWLEDGED",
"infraction_type": "REFUND_REQUEST",
"amount": 1500000,
"analysis_result": null,
"analysis_details": null,
"defense_deadline": "2026-04-21T23:59:59Z",
"counterpart_ispb": "60701190",
"account_id": 10011,
"merchant_id": "1b2db911-972f-4466-9be9-60a7c5450064",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7"
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.infraction.created |
infraction_id | string (UUID) | 违规内部 ID |
e2e_id | string | 争议交易的 E2E |
status | string | BACEN 状态:ACKNOWLEDGED、CLOSED、CANCELLED |
infraction_type | string | BACEN 类型:REFUND_REQUEST、REFUND_CANCELLED、FRAUD |
amount | integer | 值,以子分 |
defense_deadline | string (ISO 8601) | 抗辩提交截止日期 |
counterpart_ispb | string (8 位) | 对手方机构的 ISPB |
account_id | integer | 您受影响的账户 |
merchant_id | string (UUID) | 您的 merchant_id |
需要操作
状态为 ACKNOWLEDGED 且值 > R$1.000 的违规会自动生成 MED 预防性封锁。您必须在 defense_deadline 前通过 POST /api/merchant/infractions/{id}/defense 响应,否则将执行退款。
pix.infraction.resolved
在违规被解决(admin 关闭或 auto-deny)时触发。如有则释放预防性封锁。
由 pix_compliance.ex:664 和 admin/infractions/resolve_controller.ex:84 触发。
{
"event_type": "pix.infraction.resolved",
"infraction_id": "e7f4d23a-6f2a-4d1e-a3e6-fe8b32bba95d",
"e2e_id": "E0416201020260404113012abcdef1234",
"status": "CLOSED",
"infraction_type": "REFUND_REQUEST",
"amount": 1500000,
"analysis_result": "DISAGREED",
"analysis_details": "Verificado pelo time de compliance e sem evidencias concretas nao temos como fazer devolucao",
"defense_deadline": "2026-04-21T23:59:59Z",
"counterpart_ispb": "60701190",
"account_id": 10011,
"merchant_id": "1b2db911-972f-4466-9be9-60a7c5450064",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7"
}| 字段 | 类型 | 描述 |
|---|---|---|
analysis_result | string | AGREED(退回)、DISAGREED(拒绝) |
analysis_details | string | 决定的理由 |
| 其他字段 | 与 pix.infraction.created 相同 |
pix.infraction.defense_submitted
在 merchant 针对违规提交抗辩时触发(通过门户或 API)。
由 admin/infractions/defense_controller.ex:67 和 merchant/infractions/defense_controller.ex:178 触发。
{
"event_type": "pix.infraction.defense_submitted",
"infraction_id": "e7f4d23a-6f2a-4d1e-a3e6-fe8b32bba95d",
"e2e_id": "E0416201020260404113012abcdef1234",
"status": "defense_submitted",
"account_id": 10011,
"merchant_id": "1b2db911-972f-4466-9be9-60a7c5450064",
"entity_id": "26a48541-edce-4581-8c6e-564e7f2e6cd7"
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.infraction.defense_submitted |
status | string | 始终为 defense_submitted |
infraction_id | string (UUID) | 被抗辩违规的 ID |
等待 BACEN 分析
提交后,BACEN 分析抗辩 + 对方证据。通过 pix.infraction.resolved 返回结果。
pix.payout.queued
在 PIX OUT 自动加入重试队列时触发(retry queue pix_out_retry_queue_enabled 功能,自 session 155 起可用)。原因:每 merchant 的 ClientLimiter 速率限制或共享的 DICT BACEN bucket 耗尽。
由 pix.ex:315(模块 Fluxiq.UseCases.Payments.OutboundPayment.Pix)触发。
{
"event_type": "pix.payout.queued",
"status": "queued",
"account_id": 10011,
"merchant_id": "1b2db911-972f-4466-9be9-60a7c5450064",
"transaction_id": "PIXOUT0200806193e0984f830569",
"end_to_end_id": "E3783905920260421133012abcdef1234",
"amount": 200,
"external_id": "payment-456",
"reason": "dict_client_rate_limited",
"reason_code": "DICT_CLIENT_RATE_LIMITED",
"reason_description": "Merchant exceeded per-minute DICT lookup quota",
"queued_at": "2026-04-21T13:30:12Z",
"estimated_retry_seconds": 3,
"queue_ttl_seconds": 7200
}| 字段 | 类型 | 描述 |
|---|---|---|
event_type | string | 始终为 pix.payout.queued |
status | string | 始终为 queued |
account_id | integer | 发起 PIX OUT 的账户 |
merchant_id | string (UUID) | 您的 merchant_id |
transaction_id | string | Owem 交易标识符 |
end_to_end_id | string | 为 PIX OUT 生成的 BACEN E2E |
amount | integer | 值,以子分 |
external_id | string 或 null | 您的外部标识符(如果在原始 request 中发送) |
reason | string | 入队原因(snake_case)。已知值:dict_client_rate_limited(每 merchant 限制)、dict_bucket_exhausted(共享 DICT BACEN bucket 耗尽)、dict_rate_limited(通用回退) |
reason_code | string | 与 reason 对应的 UPPERCASE 内部代码。值:DICT_CLIENT_RATE_LIMITED、DICT_BUCKET_EXHAUSTED、DICT_RATE_LIMITED。不是 BACEN SPI 代码(如 AC03、AM02)— 入队发生在发送到 BACEN 之前,因此代码是 Owem 内部的 |
reason_description | string | 原因的英文描述 |
queued_at | string (ISO 8601) | 进入队列的时刻(UTC) |
estimated_retry_seconds | integer | worker 的重试间隔(默认 3 秒);队列不保证 3 秒 — 如果 bucket 释放延迟,可能需要更长 |
queue_ttl_seconds | integer | 队列中的最大 TTL(秒)(7200 = 2 小时)。过期后,请求变为 failed,原因 queue_ttl_expired |
这里的 reason_code 不是 BACEN SPI
请注意,在 pix.payout.queued 中,reason_code 是 UPPERCASE 的内部 Owem 代码(DICT_CLIENT_RATE_LIMITED 等)。在 pix.payout.failed 中,reason_code 是 BACEN SPI 代码(例:AC03、AM02、ED05)。这两个字段同名但词汇不同 — 在您的消费者中分别处理每个事件。
自动排空
入队的请求会在 TTL 有效期间由 worker 约每 3 秒自动重试。正常情况下,当每 merchant 限制或 DICT BACEN bucket 释放容量后会继续处理,但这不是 3-10 分钟 SLA。下一个事件:pix.payout.processing(当它离开队列并发送到 BACEN 时)。如果 2 小时 TTL 过期而未成功,您会收到 pix.payout.failed,原因 reason="queue_ttl_expired"。
如何解释 webhook
要确认资金已进入账户:等待 pix.charge.paid,status: "paid"。这是保证值已入账且手续费已收取的唯一事件。
要确认资金已离开账户:等待 pix.payout.confirmed,status: "settled"。状态 processing 是中间的 — 余额已保留但如果被拒绝可能撤销。
对于退款:pix.return.received,status: "settled" 确认已结算并在账户中贷记的退款。
去重:使用头 X-Owem-Event-Id 或字段 end_to_end_id 作为幂等性键。