# Webhooks

配置 Webhook 接收来自 FluxiQ PIX 的实时事件通知。

## 前提条件

- FluxiQ PIX 后端可访问的 HTTPS 端点
- 能够验证 HMAC-SHA256 签名
- 了解幂等事件处理

## Webhook 架构

```mermaid
graph LR
    PIX[FluxiQ PIX] -->|HTTPS POST| WH[您的 Webhook 端点]
    WH -->|200 OK| PIX
    PIX -->|失败时重试| WH
```

## 安全

### HMAC-SHA256 签名

每个 Webhook 请求包含三个安全头：

| 头 | 描述 |
|----|------|
| `X-FluxiQ-Signature` | 使用共享密钥对请求体的 HMAC-SHA256 |
| `X-FluxiQ-Timestamp` | 事件生成时的 Unix 时间戳 |
| `X-FluxiQ-Event-Id` | 唯一事件标识符（UUID）用于幂等性 |

### 签名验证

```python
import hmac
import hashlib

def verify_signature(payload, signature, secret, timestamp):
    # 验证时间戳在 5 分钟内
    if abs(time.time() - int(timestamp)) > 300:
        return False

    # 计算 HMAC
    message = f"{timestamp}.{payload}".encode()
    expected = hmac.new(secret.encode(), message, hashlib.sha256).hexdigest()

    return hmac.compare_digest(signature, expected)
```

```elixir
# Elixir 验证
def verify_webhook(payload, signature, secret, timestamp) do
  message = "#{timestamp}.#{payload}"
  expected = :crypto.mac(:hmac, :sha256, secret, message) |> Base.encode16(case: :lower)
  Plug.Crypto.secure_compare(signature, expected)
end
```

## 事件类型

### 交易事件

| 事件 | 触发条件 |
|------|----------|
| `transaction.created` | 新 PIX 交易创建 |
| `transaction.processing` | 交易被接受处理（ACSP） |
| `transaction.settled` | 交易已清算（STLD） |
| `transaction.rejected` | 交易被拒绝（RJCT） |
| `transaction.returned` | 交易已退回（RTRN） |
| `transaction.cancelled` | 交易已取消（CANC） |

### DICT 事件

| 事件 | 触发条件 |
|------|----------|
| `key.created` | PIX 密钥已注册 |
| `key.deleted` | PIX 密钥已删除 |
| `claim.created` | 所有权认领已发起 |
| `claim.resolved` | 认领已解决 |

### 清算事件

| 事件 | 触发条件 |
|------|----------|
| `settlement.cycle_completed` | 清算周期已完成 |
| `settlement.reconciliation_discrepancy` | 发现差异 |

## 负载格式

```json
{
  "event": "transaction.settled",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-02-09T14:30:00Z",
  "data": {
    "transaction_id": "uuid",
    "end_to_end_id": "E12345678202602091430abcdef123456",
    "amount": 10000,
    "status": "STLD",
    "debtor": {
      "name": "John Doe",
      "cpf_cnpj": "12345678901",
      "ispb": "12345678"
    },
    "creditor": {
      "name": "Jane Doe",
      "cpf_cnpj": "98765432100",
      "ispb": "87654321"
    },
    "settled_at": "2026-02-09T14:30:01.200Z"
  }
}
```

## 重试策略

| 尝试 | 延迟 | 累计等待 |
|------|------|----------|
| 1 | 立即 | 0秒 |
| 2 | 10 秒 | 10秒 |
| 3 | 30 秒 | 40秒 |
| 4 | 2 分钟 | 2分40秒 |
| 5 | 10 分钟 | 12分40秒 |
| 6（最终） | 30 分钟 | 42分40秒 |

所有重试用尽后，事件发送到死信队列。

## 幂等性

使用 `X-FluxiQ-Event-Id` 在您端进行事件去重。由于重试，同一事件可能被多次投递。

```python
def handle_webhook(event):
    event_id = request.headers["X-FluxiQ-Event-Id"]

    # 检查是否已处理
    if already_processed(event_id):
        return Response(status=200)

    # 处理事件
    process(event)
    mark_processed(event_id)
    return Response(status=200)
```

## 预期结果

配置 Webhook 后：

- 实时事件通知投递到您的端点
- HMAC-SHA256 签名验证确保安全
- 使用事件 ID 实现幂等处理
- 投递失败时自动重试
