# Webhook 事件目录

本页列出商户会收到的全部 Webhook 事件：触发条件、`data` 字段和载荷示例。接收端的实现、验签、重试与回调地址要求见 [Webhook 对接指南](https://docs.potabank.com/guide/webhooks.md)；信封与请求头的机器可读契约见 [API 参考](https://docs.potabank.com/reference/merchant-webhook.md)。

## 事件一览

| `event` | 触发条件 | 带分类字段 |
|---|---|---|
| [`payment.intent.completed`](#代收事件) | 代收订单收款完成（含多笔付款累计到全额） | 是 |
| [`payment.intent.partially_paid`](#代收事件) | 代收订单收到部分付款、尚未付满 | 是 |
| [`payment.intent.expired`](#代收事件) | 代收订单超时过期 | 是 |
| [`payment.intent.canceled`](#代收事件) | 商户或系统取消代收订单 | 是 |
| [`payment.transfer.completed`](#代付事件) | 代付成功 | 是 |
| [`payment.transfer.failed`](#代付事件) | 代付失败（含风控拒绝） | 是 |
| [`payment.transfer.returned`](#代付事件) | 已成功的代付被银行退回 | 是 |
| [`creditcard_invoice.captured`](#信用卡事件) | 信用卡账单扣款成功 | 是 |
| [`creditcard_invoice.3ds_timeout`](#信用卡事件) | 3DS 验证超时未完成 | 是 |
| [`payment.refund.completed`](#退款完成) | 代收订单的退款完成 | 否 |
| [`payment.transfer_refund.completed`](#退款完成) | 代付退回（transfer refund）完成 | 否 |
| [`account.frozen`](#账户冻结与解冻) / `account.unfrozen` | 账户被冻结 / 解冻 | 否 |
| [`merchant_settlement.completed.v1`](#商户结算完成) | 商户结算项结算完成 | 不适用（信封不同） |
| [`withdrawal.completed`](#提现与旧版结算状态事件) / `withdrawal.failed` | 提现完成 / 失败 | 不适用（扁平体） |
| [`settlement.completed`](#提现与旧版结算状态事件) / `settlement.failed` / `settlement.rejected` / `settlement.canceled` | 旧版结算流程的状态变更 | 不适用（扁平体） |

> **并非所有事件在所有环境开启**
>
`withdrawal.*`、`settlement.*` 只在对应的提现、旧版结算流程启用时才会发送。`account.frozen`、`account.unfrozen` 和 `merchant_settlement.completed.v1` 依赖 PSP 的“通知源”兜底通道，是否在你的环境启用，请向接入团队确认后再依赖。你收到的事件以 `X-Webhook-Event` 为准；**遇到本页未列出的 `event`，请记录并返回 `2xx`，不要报错**，PSP 后续可能新增事件类型。

## 通用约定

**信封。** 支付、代付、退款、信用卡与账户事件的请求体都是：

```json
{ "event": "…", "timestamp": "2026-01-01T00:00:00Z", "data": { } }
```

**例外：以下事件不使用上面的信封。** 事件类型都在 `event_type`（与请求头 `X-Webhook-Event` 相同），没有 `event` 字段；事件时间的字段名因类型而异（见下）：

- 新版结算 [`merchant_settlement.completed.v1`](#商户结算完成)：有自己的顶层字段（`schema_version`、`event_id`、`event_type`、`occurred_at` 等），事件时间是 `occurred_at`，没有 `timestamp`；**业务内容仍在顶层 `data` 对象里**。
- [提现与旧版结算状态事件](#提现与旧版结算状态事件)（`withdrawal.*`、`settlement.*`）：完全扁平，**没有 `data`**，业务字段都在顶层，**有顶层 `timestamp`**（状态变更时间）。

接收端应先读取请求头 `X-Webhook-Event`，再按事件选择解析方式，不要假定所有请求体都有 `event` 与 `data`。

`timestamp` 是事件发生时间（RFC3339，可能带小数秒），与 `X-Timestamp`（发送时间）不是一回事。

**金额与币种。** 金额字段都是 **JSON 整数，单位为最小货币单位**（`100000` 且 `currency` 为 `BRL` 表示 `R$ 1,000.00`）。`currency` 是 ISO 4217 三位大写字母。**以下金额是十进制字符串，不是整数**：新版结算 `merchant_settlement.completed.v1` 的 `gross_amount`/`merchant_fee_amount`/`net_amount`，旧版结算 `settlement.*` 的 `net_amount`（最小货币单位的整数值，可能为负），以及提现 `withdrawal.*` 的可选字段 `amount`。

**空值。** 字符串缺失时输出 `""`，不是 `null`；对象缺失时输出 `null`。

**字段别名。** 为兼容旧接入，部分字段有同值别名，二选一使用即可：`intent_number` 与 `invoice_number`、`provider_transaction_id` 与 `provider_tx_id`、`merchant_account_id` 与 `account_id`；信用卡事件的 `captured_amount` 与 `paid_amount`、`captured_at` 与 `completed_at`。有些事件只带其中一个名字，以各事件表格为准。

**支付分类。** 标注“带分类字段”的事件，`data` 中固定包含：

| 字段 | 说明 |
|---|---|
| `classification_schema_version` | 整数，当前固定为 `1` |
| `classification.classification_status` | 固定为 `resolved` |
| `classification.payment_category` | 支付大类，小写下划线，如 `bank_transfer` |
| `classification.payment_method` | 支付方式，如 `pix`、`spei` |
| `classification.rail_variant` | 通道变体，如 `pix_qr`、`standard` |

分类在订单创建时固化，之后不会改变。

**重放与重发。** 运营侧“重发”会原样重放已存储的载荷，`X-Webhook-Event-ID` 不变；“刷新回调”会按订单当前状态重新生成载荷，字段与首次投递一致（含 `classification`）。无法如实还原的事件（例如分多笔付款的订单的“代收完成”、SPEI 订单的“代收完成”、代收“部分支付”）不会被重新生成，而是直接拒绝。在此之前生成并存储的旧载荷可能缺少个别字段（如退款的 `original_intent_type`、`fee_amount`），请按可选字段处理；金额与状态始终以 Query API 回查结果为准。

## 代收事件

### payment.intent.completed

代收订单收款完成时发送；多笔付款累计到全额时，在最后一笔到账后发送。

| 字段 | 类型 | 说明 |
|---|---|---|
| `intent_id` | string(uuid) | 代收订单 ID |
| `intent_number` / `invoice_number` | string | 订单号（别名，值相同） |
| `merchant_id` | string(uuid) | 商户 ID |
| `merchant_account_id` / `account_id` | string(uuid) | 商户账户 ID（别名，值相同） |
| `amount` | integer | 订单金额 |
| `paid_amount` | integer | 累计已付金额 |
| `fee_amount` | integer | 商户手续费 |
| `net_amount` | integer | 净额：允许不同金额时为 `paid_amount - fee_amount`，否则为 `amount - fee_amount` |
| `current_payment_amount` | integer | 本次到账金额 |
| `remaining_amount` | integer | 剩余应付金额，完成时为 `0` |
| `is_partial_payment` / `is_final_payment` | boolean | 恒为 `false` / `true` |
| `payment_policy` | object | `allow_different_amount`、`allow_multiple_payments`（boolean） |
| `currency` | string | 币种 |
| `description` | string | 订单描述，可为 `""` |
| `merchant_order_id` | string | 你的订单号，可为 `""` |
| `provider_transaction_id` / `provider_tx_id` | string | 渠道流水号（别名），可为 `""` |
| `payer_info` | object 或 null | 付款人信息：`cpf_cnpj`、`name`、`email`、`phone`、`account`；无付款人时为 `null` |
| `payer_name` / `payer_tax_id` | string | 付款人姓名 / 证件号，可为 `""` |
| `completed_at` | string(date-time) | 完成时间 |
| `classification_*` | — | 支付分类，见上文 |

```json
{
  "event": "payment.intent.completed",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "intent_id": "019ba648-1d00-7000-8000-000000000001",
    "intent_number": "PI_20260101_001",
    "invoice_number": "PI_20260101_001",
    "merchant_id": "019ba648-1d00-7000-8000-000000000002",
    "merchant_account_id": "019ba648-1d00-7000-8000-000000000003",
    "account_id": "019ba648-1d00-7000-8000-000000000003",
    "amount": 100000,
    "paid_amount": 100000,
    "fee_amount": 500,
    "net_amount": 99500,
    "current_payment_amount": 100000,
    "remaining_amount": 0,
    "is_partial_payment": false,
    "is_final_payment": true,
    "payment_policy": {
      "allow_different_amount": false,
      "allow_multiple_payments": false
    },
    "currency": "BRL",
    "description": "Test invoice",
    "merchant_order_id": "ORDER_20260101_001",
    "provider_transaction_id": "pix-tx-001",
    "provider_tx_id": "pix-tx-001",
    "payer_info": {
      "cpf_cnpj": "00000000000",
      "name": "Test Payer",
      "email": "payer@example.com",
      "phone": "",
      "account": ""
    },
    "payer_name": "Test Payer",
    "payer_tax_id": "00000000000",
    "completed_at": "2026-01-01T00:00:00Z",
    "classification_schema_version": 1,
    "classification": {
      "classification_status": "resolved",
      "payment_category": "bank_transfer",
      "payment_method": "pix",
      "rail_variant": "pix_qr"
    }
  }
}
```
### payment.intent.partially_paid

允许分多笔付款的订单收到一笔付款但尚未付满时发送。与 `completed` 相比，没有 `merchant_account_id`、`fee_amount`、`net_amount`、`description`、`merchant_order_id`、`provider_transaction_id`、`payer_info` 和 `completed_at`。

| 字段 | 类型 | 说明 |
|---|---|---|
| `intent_id` | string(uuid) | 代收订单 ID |
| `intent_number` / `invoice_number` | string | 订单号（别名） |
| `merchant_id` / `account_id` | string(uuid) | 商户 ID / 账户 ID |
| `amount` | integer | 订单金额 |
| `paid_amount` | integer | 累计已付金额 |
| `current_payment_amount` | integer | 本次到账金额 |
| `remaining_amount` | integer | 剩余应付金额 |
| `is_partial_payment` / `is_final_payment` | boolean | 恒为 `true` / `false` |
| `payment_policy` | object | 同 `completed` |
| `currency` | string | 币种 |
| `provider_tx_id` | string | 渠道流水号 |
| `payer_name` / `payer_tax_id` | string | 付款人姓名 / 证件号 |
| `partially_paid_at` | string(date-time) | 本次部分付款时间 |
| `classification_*` | — | 支付分类 |

```json
{
  "event": "payment.intent.partially_paid",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "intent_id": "019ba648-1d00-7000-8000-000000000001",
    "intent_number": "PI_20260101_002",
    "invoice_number": "PI_20260101_002",
    "merchant_id": "019ba648-1d00-7000-8000-000000000002",
    "account_id": "019ba648-1d00-7000-8000-000000000003",
    "amount": 100000,
    "paid_amount": 70000,
    "current_payment_amount": 30000,
    "remaining_amount": 30000,
    "is_partial_payment": true,
    "is_final_payment": false,
    "payment_policy": {
      "allow_different_amount": true,
      "allow_multiple_payments": true
    },
    "currency": "BRL",
    "provider_tx_id": "pix-tx-002",
    "payer_name": "Test Payer",
    "payer_tax_id": "00000000000",
    "partially_paid_at": "2026-01-01T00:00:00Z",
    "classification_schema_version": 1,
    "classification": {
      "classification_status": "resolved",
      "payment_category": "bank_transfer",
      "payment_method": "pix",
      "rail_variant": "pix_qr"
    }
  }
}
```
### payment.intent.expired

代收订单在有效期内未付款而过期时发送。**不含订单号和 `merchant_order_id`**，请用 `intent_id` 关联。

| 字段 | 类型 | 说明 |
|---|---|---|
| `intent_id` | string(uuid) | 代收订单 ID |
| `merchant_id` / `account_id` | string(uuid) | 商户 ID / 账户 ID |
| `expired_at` | string(date-time) | 过期时间 |
| `classification_*` | — | 支付分类 |

```json
{
  "event": "payment.intent.expired",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "intent_id": "019ba648-1d00-7000-8000-000000000001",
    "merchant_id": "019ba648-1d00-7000-8000-000000000002",
    "account_id": "019ba648-1d00-7000-8000-000000000003",
    "expired_at": "2026-01-01T00:00:00Z",
    "classification_schema_version": 1,
    "classification": {
      "classification_status": "resolved",
      "payment_category": "bank_transfer",
      "payment_method": "pix",
      "rail_variant": "pix_qr"
    }
  }
}
```
### payment.intent.canceled

商户或系统取消代收订单时发送。订单引用来自原订单：没有提供时为 `""`；历史事件中部分引用可能缺失。请在账户范围内优先用 `intent_id` 或 `merchant_order_id` 关联，不要要求一定存在渠道流水号。

| 字段 | 类型 | 说明 |
|---|---|---|
| `intent_id` | string(uuid) | 代收订单 ID |
| `intent_number` / `invoice_number` | string | 订单号（别名）；历史事件可能为空 |
| `merchant_order_id` | string | 你的订单号，可为 `""` |
| `provider_transaction_id` / `provider_tx_id` | string | 渠道流水号（别名），提交渠道前为 `""` |
| `merchant_id` / `account_id` | string(uuid) | 商户 ID / 账户 ID |
| `cancel_reason` | string | 取消原因 |
| `canceled_at` | string(date-time) | 取消时间 |
| `classification_*` | — | 支付分类 |

```json
{
  "event": "payment.intent.canceled",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "intent_id": "019ba648-1d00-7000-8000-000000000001",
    "intent_number": "PI_20260101_003",
    "invoice_number": "PI_20260101_003",
    "merchant_order_id": "ORDER_20260101_003",
    "provider_transaction_id": "",
    "provider_tx_id": "",
    "merchant_id": "019ba648-1d00-7000-8000-000000000002",
    "account_id": "019ba648-1d00-7000-8000-000000000003",
    "cancel_reason": "customer_request",
    "canceled_at": "2026-01-01T00:00:00Z",
    "classification_schema_version": 1,
    "classification": {
      "classification_status": "resolved",
      "payment_category": "bank_transfer",
      "payment_method": "pix",
      "rail_variant": "pix_qr"
    }
  }
}
```
## 代付事件

### payment.transfer.completed

代付成功时发送。

| 字段 | 类型 | 说明 |
|---|---|---|
| `transfer_id` / `transfer_number` | string | 代付订单 ID / 订单号 |
| `merchant_id` | string(uuid) | 商户 ID |
| `merchant_account_id` / `account_id` | string(uuid) | 商户账户 ID（别名） |
| `amount` | integer | 代付本金 |
| `transferred_amount` | integer | 实际转出金额 |
| `fee_amount` | integer | 商户手续费 |
| `total_amount` | integer | `amount + fee_amount` |
| `currency` | string | 币种 |
| `description` | string | 描述，可为 `""` |
| `merchant_order_id` | string | 你的订单号 |
| `provider_transaction_id` / `provider_tx_id` | string | 渠道流水号（别名） |
| `beneficiary_info` | object 或 null | 收款人：`key_type`（恒为 `""`）、`key_value`、`name`、`document`、`email`、`phone`、`bank_code`、`branch_code` |
| `e2e_id` | string | 端到端标识，**仅在有值时出现** |
| `settlement_id` | string | 结算 ID，**仅在有值时出现** |
| `completed_at` | string(date-time) | 完成时间 |
| `classification_*` | — | 支付分类 |

```json
{
  "event": "payment.transfer.completed",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "transfer_id": "019ba648-1d00-7000-8000-000000000001",
    "transfer_number": "TI_20260101_001",
    "merchant_id": "019ba648-1d00-7000-8000-000000000002",
    "merchant_account_id": "019ba648-1d00-7000-8000-000000000003",
    "account_id": "019ba648-1d00-7000-8000-000000000003",
    "amount": 50000,
    "transferred_amount": 50000,
    "fee_amount": 250,
    "total_amount": 50250,
    "currency": "BRL",
    "description": "Test transfer",
    "merchant_order_id": "PAYOUT_20260101_001",
    "provider_transaction_id": "pix-transfer-001",
    "provider_tx_id": "pix-transfer-001",
    "beneficiary_info": {
      "key_type": "",
      "key_value": "recipient@example.com",
      "name": "Maria Santos",
      "document": "00000000000",
      "email": "recipient@example.com",
      "phone": "",
      "bank_code": "001",
      "branch_code": "0001"
    },
    "e2e_id": "E00000000202601010000abcdefghijk",
    "completed_at": "2026-01-01T00:00:00Z",
    "classification_schema_version": 1,
    "classification": {
      "classification_status": "resolved",
      "payment_category": "bank_transfer",
      "payment_method": "pix",
      "rail_variant": "pix_qr"
    }
  }
}
```
### payment.transfer.failed

代付失败时发送；风控拒绝时 `failure_code` 为 `RISK_REJECTED`。

| 字段 | 类型 | 说明 |
|---|---|---|
| `transfer_id` / `transfer_number` | string | 代付订单 ID / 订单号 |
| `merchant_id` | string(uuid) | 商户 ID |
| `merchant_account_id` / `account_id` | string(uuid) | 商户账户 ID |
| `amount` | integer | 代付金额 |
| `currency` | string | 币种 |
| `merchant_order_id` | string | 你的订单号 |
| `failure_reason` | string | 失败原因 |
| `failure_code` | string | 失败代码 |
| `failed_at` | string(date-time) | 失败时间 |
| `classification_*` | — | 支付分类 |

```json
{
  "event": "payment.transfer.failed",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "transfer_id": "019ba648-1d00-7000-8000-000000000001",
    "merchant_id": "019ba648-1d00-7000-8000-000000000002",
    "merchant_account_id": "019ba648-1d00-7000-8000-000000000003",
    "account_id": "019ba648-1d00-7000-8000-000000000003",
    "transfer_number": "TI_20260101_002",
    "amount": 50000,
    "currency": "BRL",
    "merchant_order_id": "PAYOUT_20260101_002",
    "failure_reason": "Insufficient funds",
    "failure_code": "INSUFFICIENT_FUNDS",
    "failed_at": "2026-01-01T00:00:00Z",
    "classification_schema_version": 1,
    "classification": {
      "classification_status": "resolved",
      "payment_category": "bank_transfer",
      "payment_method": "pix",
      "rail_variant": "pix_qr"
    }
  }
}
```
### payment.transfer.returned

已成功的代付被银行退回时发送。`returned_amount` 是**累计**退回金额，多次部分退回会多次推送。

| 字段 | 类型 | 说明 |
|---|---|---|
| `transfer_id` / `transfer_number` | string | 代付订单 ID / 订单号 |
| `merchant_id` | string(uuid) | 商户 ID |
| `merchant_account_id` / `account_id` | string(uuid) | 商户账户 ID |
| `amount` | integer | 原代付金额 |
| `currency` | string | 币种 |
| `merchant_order_id` | string | 你的订单号 |
| `returned_amount` | integer | 累计退回金额 |
| `return_reason` | string | 退回原因 |
| `returned_at` | string(date-time) | 退回时间 |
| `classification_*` | — | 支付分类 |

```json
{
  "event": "payment.transfer.returned",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "transfer_id": "019ba648-1d00-7000-8000-000000000001",
    "merchant_id": "019ba648-1d00-7000-8000-000000000002",
    "merchant_account_id": "019ba648-1d00-7000-8000-000000000003",
    "account_id": "019ba648-1d00-7000-8000-000000000003",
    "transfer_number": "TI_20260101_003",
    "amount": 50000,
    "currency": "BRL",
    "merchant_order_id": "PAYOUT_20260101_003",
    "returned_amount": 50000,
    "return_reason": "Account closed",
    "returned_at": "2026-01-01T00:00:00Z",
    "classification_schema_version": 1,
    "classification": {
      "classification_status": "resolved",
      "payment_category": "bank_transfer",
      "payment_method": "pix",
      "rail_variant": "pix_qr"
    }
  }
}
```
## 信用卡事件

### creditcard_invoice.captured

信用卡账单扣款成功时发送。

| 字段 | 类型 | 说明 |
|---|---|---|
| `invoice_id` / `invoice_number` | string | 账单 ID / 账单号 |
| `merchant_id` | string(uuid) | 商户 ID |
| `merchant_account_id` / `account_id` | string(uuid) | 商户账户 ID |
| `channel_id` | string | 通道 ID |
| `amount` | integer | 账单金额 |
| `captured_amount` / `paid_amount` | integer | 扣款金额（别名） |
| `fee_amount` | integer | 商户手续费 |
| `channel_fee` | integer | 通道费用 |
| `net_amount` | integer | 净额 |
| `currency` | string | 币种 |
| `description` | string | 描述 |
| `merchant_order_id` | string | 你的订单号 |
| `provider_transaction_id` / `provider_tx_id` | string | 渠道流水号（别名） |
| `captured_at` / `completed_at` | string(date-time) | 扣款时间（别名） |
| `classification_*` | — | 支付分类 |

### creditcard_invoice.3ds_timeout

3DS 验证在规定时间内未完成时发送。

| 字段 | 类型 | 说明 |
|---|---|---|
| `invoice_id` | string | 账单 ID |
| `merchant_id` | string(uuid) | 商户 ID |
| `merchant_account_id` / `account_id` | string(uuid) | 商户账户 ID |
| `three_ds_transaction_id` | string | 3DS 交易 ID |
| `timeout_seconds` | integer | 超时时长（秒） |
| `initiated_at` | string(date-time) | 3DS 发起时间 |
| `timeout_at` | string(date-time) | 超时时间 |
| `classification_*` | — | 支付分类 |

## 退款完成

`payment.refund.completed`（代收退款）与 `payment.transfer_refund.completed`（代付退回）使用同一结构，**不带支付分类字段**。`data` 中没有账户 ID，所属账户请读取请求头 `X-Merchant-Account-ID`。代付退回完成时，除本事件外还会发送 `payment.transfer.returned`。

| 字段 | 类型 | 说明 |
|---|---|---|
| `refund_id` | string(uuid) | 退款 ID |
| `merchant_id` | string(uuid) | 商户 ID |
| `original_intent_id` | string(uuid) | 原订单 ID |
| `original_intent_type` | string | `payment_intent`（代收）或 `transfer_intent`（代付） |
| `refunded_amount` | integer | 退款金额 |
| `fee_amount` | integer | 手续费，可为 `0` |
| `currency` | string | 币种 |
| `provider_refund_id` | string | 渠道退款流水号，可为 `""` |
| `completed_at` | string(date-time) | 完成时间 |

```json
{
  "event": "payment.refund.completed",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "refund_id": "019ba648-1d00-7000-8000-000000000001",
    "merchant_id": "019ba648-1d00-7000-8000-000000000002",
    "original_intent_id": "019ba648-1d00-7000-8000-000000000004",
    "original_intent_type": "payment_intent",
    "refunded_amount": 9800,
    "fee_amount": 0,
    "currency": "BRL",
    "provider_refund_id": "refund-001",
    "completed_at": "2026-01-01T00:00:00Z"
  }
}
```
退款失败不会产生首次 Webhook 通知（`payment.refund.failed` 只可能在运营重放既有记录时出现，载荷为 `refund_id`、`failure_reason`、`failure_code`、`failed_at`）。请通过 Query API 查询退款状态。

## 账户冻结与解冻

账户被冻结或解冻时发送，不带支付分类字段。

| 字段 | 类型 | 说明 |
|---|---|---|
| `account_id` | string(uuid) | 商户账户 ID |
| `reason` | string | 冻结原因（仅 `account.frozen`） |
| `frozen_at` | string(date-time) | 冻结时间（仅 `account.frozen`） |
| `unfrozen_at` | string(date-time) | 解冻时间（仅 `account.unfrozen`） |

```json
{
  "event": "account.frozen",
  "timestamp": "2026-01-01T00:00:00Z",
  "data": {
    "account_id": "019ba648-1d00-7000-8000-000000000003",
    "reason": "compliance_review",
    "frozen_at": "2026-01-01T00:00:00Z"
  }
}
```
## 商户结算完成

`merchant_settlement.completed.v1` 的**信封与其他事件不同**：顶层没有 `event` 和 `timestamp`，事件类型在 `event_type`（同时也在请求头 `X-Webhook-Event`），`X-Webhook-Event-ID` 等于 `event_id`。金额是**十进制字符串**，时间为 UTC 微秒精度。

特别注意投递规则：这类事件收到**任何非 `2xx`** 响应都会重试，并且忽略 `Retry-After`。

| 字段 | 类型 | 说明 |
|---|---|---|
| `schema_version` | string | 当前为 `1.0.0` |
| `event_id` | string(uuid) | 事件 ID，去重键 |
| `event_type` | string | `merchant_settlement.completed.v1` |
| `occurred_at` | string(date-time) | 事件发生时间（UTC，微秒精度） |
| `aggregate_type` / `aggregate_id` / `aggregate_version` | string / string(uuid) / integer | 结算项聚合标识与版本 |
| `data.merchant_item_id` | string(uuid) | 结算项 ID |
| `data.merchant_account_id` | string(uuid) | 商户账户 ID |
| `data.source_type` | string | 结算来源，如 `invoice` |
| `data.status` | string | 结算状态，如 `settled` |
| `data.gross_amount` / `merchant_fee_amount` / `net_amount` | string | 总额 / 手续费 / 净额，满足 `gross = fee + net`，十进制字符串 |
| `data.currency` | string | 币种 |
| `data.due_at` / `data.settled_at` | string(date-time) | 应结算时间 / 实际结算时间 |
| `data.parent_item_id` / `data.relation_type` | string 或 null | 关联的父结算项及关系，无则为 `null` |

```json
{
  "schema_version": "1.0.0",
  "event_id": "019ba648-1d00-7000-8000-0000000000a1",
  "event_type": "merchant_settlement.completed.v1",
  "occurred_at": "2026-01-01T04:00:00.123456Z",
  "aggregate_type": "merchant_settlement_item",
  "aggregate_id": "019ba648-1d00-7000-8000-0000000000a2",
  "aggregate_version": 3,
  "data": {
    "merchant_item_id": "019ba648-1d00-7000-8000-0000000000a2",
    "merchant_account_id": "019ba648-1d00-7000-8000-000000000003",
    "source_type": "invoice",
    "status": "settled",
    "gross_amount": "100000",
    "merchant_fee_amount": "500",
    "net_amount": "99500",
    "currency": "BRL",
    "due_at": "2026-01-01T03:00:00Z",
    "settled_at": "2026-01-01T04:00:00.123456Z",
    "parent_item_id": null,
    "relation_type": null
  }
}
```

## 提现与旧版结算状态事件

以下事件的请求体是**扁平结构**，没有 `event` / `data` 信封；事件类型在 `event_type`（与请求头 `X-Webhook-Event` 相同），状态名等于事件类型的后缀。它们只在对应的提现、旧版结算流程启用时发送，请向接入团队确认你的环境是否会收到。旧版结算的 `settlement.*` 与新版 `merchant_settlement.completed.v1` 是两套不同的事件，不要混淆。

### withdrawal.completed / withdrawal.failed

| 字段 | 类型 | 说明 |
|---|---|---|
| `event_type` | string | `withdrawal.completed` 或 `withdrawal.failed` |
| `withdrawal_id` | string(uuid) | 提现 ID |
| `status` | string | `completed` 或 `failed`（终态） |
| `timestamp` | string(date-time) | 状态变更时间，UTC，秒级精度 |
| `amount`、`currency`、`channel_ref`、`e2e_id`、`error_code`、`error_message` | string | 发送器支持，但当前提现流程**不会填充**，缺失时不出现，请按可选字段处理 |

```json
{
  "event_type": "withdrawal.completed",
  "withdrawal_id": "019ba648-1d00-7000-8000-0000000000b1",
  "status": "completed",
  "timestamp": "2026-01-01T00:00:00Z"
}
```

### settlement.completed / settlement.failed / settlement.rejected / settlement.canceled

| 字段 | 类型 | 说明 |
|---|---|---|
| `event_type` | string | 上述四个事件之一 |
| `settlement_id` | string(uuid) | 结算 ID |
| `status` | string | `completed`、`failed`、`rejected` 或 `canceled` |
| `timestamp` | string(date-time) | 状态变更时间，UTC，秒级精度 |
| `net_amount` | string | 结算金额，最小货币单位的整数，**十进制字符串**，可能为负数 |
| `currency` | string | 币种；结算没有币种时不出现 |

```json
{
  "event_type": "settlement.completed",
  "settlement_id": "019ba648-1d00-7000-8000-0000000000c1",
  "status": "completed",
  "timestamp": "2026-01-01T00:00:00Z",
  "net_amount": "99500",
  "currency": "BRL"
}
```

## 处理建议

- 先按 `X-Webhook-Event` 分发，再按事件选择解析方式，共三类：
  1. 普通 `event`/`timestamp`/`data` 信封：解析 `data`。
  2. `merchant_settlement.*.v1`：先读顶层的 `event_id`、`event_type` 等字段，业务内容再解析顶层的 `data`。
  3. `withdrawal.*`、`settlement.*`：完全扁平，直接读取顶层字段，没有 `data`。

  未知事件记录后返回 `2xx`。
- 同一订单可能先后收到多个事件（例如 `partially_paid` 之后是 `completed`，`transfer.completed` 之后又出现 `transfer.returned`），不保证顺序，请按[状态机原则](https://docs.potabank.com/guide/webhooks.md#状态机原则)处理。
- 对支付、代付等带 `data` 的事件，入账前核对 `amount`、`currency`、`merchant_order_id` 与账户是否和本地订单一致。
