Skip to content

PIX 违规(完整流程)

Owem Pay 中 PIX 违规生命周期的端到端视图 — 它如何产生、如何自动处理、何时封锁余额、如何抗辩以及如何在您的系统中对账。

一句话总结

PIX 违规是对方针对您收到的 PIX 报告的争议。它可能在解决之前对争议值产生预防性封锁。订阅 webhook pix.infraction.* 以在期限内采取行动。


1. 什么是 PIX 违规?

违规(BACEN 词汇中的 REFUND_REQUEST 类型)是付款方机构在 DICT 中注册的正式报告,声称您收到的 PIX 显示出欺诈、诈骗、操作错误或退款请求的迹象。

官方渠道是 BACEN DICT — 没有客户可以直接对您发起违规;它始终通过付款方机构和 BACEN 到达 Owem。

技术字段描述
infraction_typeBACEN 中的违规类型。值:REFUND_REQUEST(退款请求)和 REFUND_CANCELLED(取消先前的请求)
statusBACEN 中的状态。UPPERCASE 值:ACKNOWLEDGED(已识别,正在分析)、CLOSED(已完成)、CANCELLED(在分析前被对方取消)
amount以子分计的争议值(不一定是原始 PIX 的总值)
e2e_id被争议的原始 PIX 的 End-to-End ID
defense_deadline提交抗辩的 BACEN 期限(ISO 8601 UTC)
counterpart_ispb发起违规的付款方机构的 ISPB
analysis_result关闭时的最终决定(CLOSED):AGREED(执行退款)或 DISAGREED(抗辩被接受,无退款)

来源:schemas/pix_compliance/pix_infraction.ex

不要与 MED 混淆

MED(特殊退款机制)是在确认的欺诈违规需要偿还时执行退款的监管机制 — 它是已接受违规的结果,不是初始事件。参见下方的 违规 ↔ MED 关系 部分。


2. Owem 如何接收违规

Owem 不通过推送接收违规。后端通过 worker Fluxiq.Workers.ComplianceSyncWorker15 分钟主动轮询 DICT OnZ/BACEN。

发现流程:

BACEN DICT

  │ GET /v3/dict/infracao/list(每次调用 1000 条分页)

ComplianceSyncWorker (*/15 * * * *)

  ├─ upsert_infraction/2 → 持久化到 pix_infractions
  │     ├─ 如果是新的(is_new=true):dispatch "pix.infraction.created"
  │     └─ 如果是更新(status/analysis_result 改变):dispatch "pix.infraction.resolved"

  └─ maybe_auto_deny_or_block/2 → 路由决定(见第 3 节)

来源:workers/compliance_sync_worker.exuse_cases/pix_compliance/pix_compliance.ex

BACEN → Webhook 延迟

在对方开启的违规最多在 15 分钟内到达 Owem(ComplianceSyncWorker 的下一个周期)。一旦行在 pix_infractions 中插入/更新,就会触发 webhook pix.infraction.created


3. 分类与自动操作

一旦检测到具有 ACKNOWLEDGEDOPEN 状态的 REFUND_REQUEST 违规,后端会在没有手动干预的情况下将其分类到三条自动路径之一:

3.1 您的交易中不存在 E2E → auto-deny

如果违规引用的 e2e_id 不在您的 transactions 表中(没有带该 E2E 的 PIX IN 到达您的系统),后端假设这是对方的操作错误并立即执行 auto-deny

  • OnZ 操作:POST /v3/dict/infracao/{id},AnalysisResult=DISAGREED
  • 更新 pix_infractionsstatus=CLOSEDanalysis_result=DISAGREEDresolved_at=now
  • 触发 webhook pix.infraction.resolved
  • 不创建预防性封锁
  • 不执行退款

3.2 值 ≤ R$ 1.000(阈值可配置)→ auto-deny

低值违规自动被拒绝。默认阈值为 R$ 1.000,0010_000_000 子分,常量 @default_auto_deny_threshold),但可通过表 med_configurations(字段 min_threshold_amount)按账户merchant 配置。

  • 应用与 3.1 相同的 auto-deny 操作
  • 发送给 BACEN 的标准理由是:"Verificado pelo time de compliance e sem evidencias concretas nao temos como fazer devolucao"
  • 触发 webhook pix.infraction.resolved
  • 不创建预防性封锁
  • 收到的 PIX 值继续可用在余额中

每账户自定义阈值

处理合法高额票据的账户(例:高值市场)可以通过 Owem admin 申请增加阈值。对任何值都倾向于手动抗辩的账户可以申请 threshold=0(不自动拒绝)。

3.3 值 > R$ 1.000 且 E2E 存在 → MED 预防性封锁

这是影响您余额的路径。在检测到合格违规时,后端:

  1. 更新 pix_infractions.statusPROCESSING(避免在下个 sync 周期再次进入)
  2. med_cautelar_blocks 中创建一行,带争议值
  3. 在 TigerBeetle 中创建 phantom hold — 针对受影响账户钱包的 pending 转账,减少可用余额余额 中的 available)按争议值
  4. 触发 webhook pix.refund.requested查看载荷
  5. 触发 webhook pix.infraction.created查看载荷

值会被封锁直到:

  • BACEN 抗辩期限到期(见第 6 节)
  • 您通过门户提交抗辩(见第 5 节)
  • BACEN 解决违规(第 7 节)
  • 对方在分析前取消status=CANCELLED

来源:use_cases/pix_compliance/pix_compliance.ex:495-537use_cases/pix_compliance/med/processor.ex


4. 违规 ↔ MED 关系

违规和 MED(特殊退款机制)是同一周期的不同层

它是什么主要 webhook
违规BACEN DICT 中的正式报告pix_infractionspix.infraction.created
MED 预防性封锁分析期间在 TigerBeetle 中的余额保留med_cautelar_blockspix.refund.requested
MED 退款AGREED 决定后执行的 PACS.004transactions(type=pix_returnpix.refund.completed + pix.payout.returned
[BACEN 中创建违规]


  ComplianceSyncWorker 检测(15 分钟轮询)

          ├─ pix.infraction.created(始终,当 is_new=true 时)

          │ 如果 > R$ 1.000 且 E2E 存在:

  MED Processor → cautelar block + TB phantom hold

          ├─ pix.refund.requested(封锁已创建)

          │ 等待决定(merchant 抗辩或期限过期或 auto-accept session 135)

  [解决]

          ├─ AGREED → PACS.004 dispatched
          │     ├─ pix.refund.completed
          │     └─ pix.payout.returned(E2E 中的 D 前缀)

          └─ DISAGREED → 封锁已释放
                └─ BlockRelease.release_for_e2e — TB phantom 的 void + 余额的释放


  pix.infraction.resolved(关闭时始终 — AGREED 或 DISAGREED)

关系是按 E2E 的 1 对 1,不是按 ID 的 1 对 1

同一笔 PIX 交易(相同 e2e_id不能同时有两个活跃的违规 — ComplianceSyncWorker 在 cautelar_block_exists_for_infraction?/1 中做 guard 以避免封锁重复。如果对方取消并开启具有相同 E2E 的新违规,新的封锁取代前一个。

另请参见:med-listmed-detail 用于通过 External API 查询预防性封锁。


5. merchant 如何抗辩

抗辩通过 External API 提供

抗辩提交(上传证据 + 理由)仅通过 merchant portalhttps://merchant.owem.com.br)。今天没有 External API 端点 POST /api/external/infractions/:id/defense。这是已知的限制 — 见第 8 节。

merchant portal 中的抗辩流程:

  1. 认证的操作员访问门户中的 Compliance → Infrações/compliance
  2. 查看 defense_deadline 接近的 ACKNOWLEDGED 违规列表
  3. 点击违规并填写:
    • 抗辩文本(最多 ~5000 字符,自由 — 作为 AnalysisDetails 发送给 BACEN)
    • 附件(最多 5 个文件,每个 10MB — 如截屏、合同、对话历史等证据)
  4. 提交 → 后端:
    • 将附件上传到 GCS 存储
    • 调用 OnZ POST /v3/dict/infracao/{id}/defense,带文本 + 附件 URL
    • 本地更新 pix_infractions.status=defense_submitted
    • 触发 webhook pix.infraction.defense_submitted

来源:controllers/merchant/infractions/defense_controller.ex

谁可以抗辩

在 merchant portal 中具有 infractions:write 权限的任何用户。子账户 operators(非 primary holder)只要受影响的账户在他们的 account_ids 中,也可以提交抗辩。primary holders 可以为 merchant 的任何账户抗辩。


6. 期限与自动过期

6.1 defense_deadline(BACEN 期限)

BACEN 定义 merchant 响应的期限。它在 webhook pix.infraction.createddefense_deadline 字段(ISO 8601 UTC)中提供,并传播到 pix_infractions.defense_deadline

通常,此期限为违规创建后的 7 个日历日,但可能因类型(REFUND_REQUEST vs 严重欺诈)而不同。

6.2 通过 worker MedDefenseExpiration 自动过期

为避免机构的监管风险(因未在期限内响应而受 BACEN 罚款),Owem 每 5 分钟通过 Oban cron 运行 worker Fluxiq.Workers.MedDefenseExpiration

  • 搜索 deadline 在接下来 30 分钟内且 analysis_status 仍为 pendingdefense_submitted 的预防性封锁
  • 对于每个封锁,强制裁决 founded(接受争议)并触发 Med.execute_return/1 → PACS.004 发送到 BACEN
  • 释放封锁的余额(见 TB phantom void)
  • 审计日志的 actor 是 system,不是 admin

结果:如果您没有及时抗辩,系统在 BACEN 期限到期前自动接受争议并退回值。

来源:workers/med_defense_expiration.ex(cron */5 * * * *)。

auto-accept 是保守的

默认行为是保护机构免受监管风险 — 这就是为什么默认是接受争议并退回。如果您需要不同的逻辑(例:总是自动抗辩),请联系 compliance@owem.com.br


7. 解决与最终 webhook

违规总是在以下三个终止状态之一结束:

最终状态Analysis result余额发生了什么触发的 webhook
CLOSEDAGREED值通过 PACS.004 退回给对方pix.infraction.resolved + pix.refund.completed + pix.payout.returned
CLOSEDDISAGREED封锁已释放,值返回 availablepix.infraction.resolved
CANCELLEDnull对方在分析前取消 — 封锁已释放pix.infraction.resolved

在所有情况下,TB 预防性封锁通过 Fluxiq.UseCases.PixCompliance.Med.BlockRelease.release_for_e2e/2 释放,它在 TigerBeetle 中执行 pending 转账的 void 并更新 med_cautelar_blocks.status

来源:use_cases/pix_compliance/med/block_release.ex

AGREED 和 DISAGREED 之间的区别

  • AGREED:您同意(明确或通过 auto-accept)退款。PIX OUT(pacs.004)被执行,值离开您的账户,您收到 pix.payout.returned,status="returned"
  • DISAGREED:您抗辩,BACEN 接受了抗辩,或违规被 auto-denied(≤R$1k 或 E2E 不存在)。值保留给您,不执行 PIX OUT。

8. 当前限制(External API)

功能 gap — 通过 External API 的操作

今天,在 /api/external/* 命名空间中只有通过 MED 的间接查询可用:

  • 存在 GET /api/external/med列出 MED)和 GET /api/external/med/:idMED 详情)— 都返回 MED 封锁,不直接返回违规
  • 不存在 GET /api/external/infractionsPOST /api/external/infractions/:id/defense
  • 违规的完整列表和抗辩流程仅通过 merchant portal 提供(JWT-based,非 API Key)
  • Webhook pix.infraction.* 是自动通知的官方形式

如果您的用例需要通过 External API 自动化(例:以编程方式提交抗辩),请联系 compliance@owem.com.br 优先处理。


9. 相关 Webhook

以下三个事件是您在违规周期中的眼睛和耳朵。在 注册 Webhook 中订阅所有这些:

事件何时触发完整载荷
pix.infraction.created检测到新违规且创建预防性封锁(>R$1k 且 E2E 存在)webhooks-payloads#pix-infraction-created
pix.infraction.resolved状态转为 CLOSEDCANCELLED(AGREED、DISAGREED、auto-deny、auto-accept)webhooks-payloads#pix-infraction-resolved
pix.infraction.defense_submitted通过门户提交抗辩(merchant 或 admin)webhooks-payloads#pix-infraction-defense-submitted

相关事件(MED 层):

事件何时触发
pix.refund.requestedmed_cautelar_blocks 中创建了预防性封锁(账户已保留余额)
pix.refund.completedPACS.004 退款已执行(AGREED 或 auto-accept)
pix.payout.returnedPIX 已退回,E2E 前缀 D — 您的余额已借记以补偿付款方

≤R$1k 的静默 auto-deny

在 auto-deny ≤R$1k/E2E 不存在路径(第 3 节的路径 3.1 和 3.2)中,webhook pix.infraction.created 不触发。仅在关闭时触发 pix.infraction.resolved。这避免了在没有决定可做的情况下向 merchant 发送噪音。

如果您想要完全可见性(包括 auto-denies),请通过 merchant portal /compliance 查询 — 那里显示所有违规,与路径无关。


10. 在您的系统中对账

在您的 DRE / ERP 中对账违规的建议:

10.1 订阅 webhook

  • pix.infraction.created → 将原始 PIX IN 标记为"已争议"(内部 flag)
  • pix.refund.requested → 在您的 ledger 中将值保留为"有争议"
  • pix.infraction.resolved
    • 如果 analysis_result=DISAGREED → 释放保留,PIX IN 保持最终
    • 如果 analysis_result=AGREED → 将 PIX IN 标记为"已退回",创建出账
  • pix.refund.completed → 确认退款 PIX OUT 的离开

10.2 使用 e2e_id 作为关联键

每个违规都携带指向原始 PIX IN 的 e2e_id。在您的系统中将此字段用作 FK 以:

10.3 保留 analysis_details 的历史

字段 analysis_details 包含发送给 BACEN 的理由。在 auto-deny 情况下,它带有标准消息。在提交抗辩的情况下,它带有您的操作员编写的文本。这些数据是可审计的,应保留以供监管可追溯性(LGPD Art. 46、BCB Resolução 4893)。


技术参考


FAQ

我可以通过 Owem API 对另一个 merchant 发起违规吗?

不可以。 违规仅由付款方机构开启,而不是收款方。如果您是 PIX 的付款方并想争议,请使用您自己的客户服务渠道(银行应用、ombudsman)。Owem 仅在收款方端操作。

如果我不订阅 webhook pix.infraction.* 会发生什么?

系统继续工作 — auto-deny 和 auto-accept 自动发生。但您只有通过查看 /balance 中的 available 才会发现封锁(它会比安全的 balance 小)。要列出活跃封锁,请查询 GET /api/external/med

为什么我的余额在没有解释的情况下下降?

如果您看到 available 小于 balance,可能有活跃的 MED 预防性封锁(>R$1k + 有效 E2E)。查询 GET /api/external/med 查看所有。订阅 pix.refund.requested 以在创建新封锁时实时通知。

我可以增加或禁用 R$1.000 阈值吗?

可以。请联系 compliance@owem.com.br。阈值存储在 med_configurations.min_threshold_amount 中,可以配置:

  • 按 merchant(适用于所有账户)
  • 按特定账户(merchant 的覆盖)
  • 系统默认:R$1.000(10.000.000 子分)

增加阈值减少预防性封锁的数量(更多 auto-denies)。将阈值归零消除 auto-deny,使任何争议值都转到手动分析。

Owem 以我的名义做过决定吗?

是,在两个明确自动化的场景中:

  1. Auto-deny ≤R$1k 或 E2E 不存在 — 系统以 merchant 名义自动拒绝。发送给 BACEN 的标准理由是 "Verificado pelo time de compliance e sem evidencias concretas nao temos como fazer devolucao"
  2. 接近 BACEN 期限的 auto-accept — 如果预防性封锁距离 defense_deadline <30 分钟且 merchant 没有提交抗辩,worker MedDefenseExpiration 接受争议作为 founded 裁决并触发退款。

这两种行为都是保守的,以保护机构免受 BACEN 监管罚款。

merchant 的历史是否通过 API 可见?

部分 — 通过 GET /api/external/med(封锁)和 GET /api/external/transactions/ref/:external_id(原始交易和退款)。今天带有所有 BACEN 字段(包括 analysisDetailsfraudTypesituationType)的违规完整历史仅在 merchant portal(/compliance)中可用。

Owem Pay Instituição de Pagamento — ISPB 37839059