# 幂等与重试

所有改变状态的请求都必须携带 `X-Idempotency-Key` 或 `Idempotency-Key`，至少一个。两者同时发送时必须相等；签名优先覆盖 `X-Idempotency-Key`。

## 生成规则

为一次业务操作生成稳定的键，并在重试时沿用。`merchant_order_id` 是商户订单身份，与 Header 幂等键是两个不同字段。创建 Invoice 以账户内的 `merchant_order_id` 和请求内容识别重试，不使用网关的 Header 响应缓存；Transfer 与退款还会以 Header 键判断业务重试。信用卡授权、捕获和退款另有请求体 `idempotency_key`，建议与 Header 使用相同值。

```text
invoice:create:ORDER_20260827_001
transfer:create:PAYOUT_20260827_0042
invoice:refund:REFUND_20260827_0007
```

## 安全重试流程

1. 请求超时或连接断开时，不要立即生成新订单号。
2. 使用相同 body、商户订单号和 Header 幂等键重试。
3. 刷新 `X-Timestamp` 并重新签名。
4. 若结果仍不确定，通过 Query API 按资源 ID 或商户订单号确认。

## 冲突处理

同一 Header 幂等键在网关响应缓存有效期内配合不同 body 会返回 `422 VALIDATION_ERROR`；业务层的订单号或幂等键冲突可能返回 `409 CONFLICT`。两种情况都应停止自动重试，核对原请求。创建 Invoice 的重试首先依据账户内的 `merchant_order_id`，不能仅凭更换 Header 键创建新订单。

| 状态 | 是否自动重试 | 建议 |
|---|---:|---|
| 网络超时 / 连接重置 | 是 | 相同幂等键，指数退避并加入 jitter |
| `429 Too Many Requests` | 是 | 遵循 `Retry-After` |
| `500` / `502` / `503` / `504` | 有限重试 | 相同幂等键，最终转 Query 确认 |
| `400` / `401` / `403` | 否 | 修复请求或凭证配置 |
| `409` | 否 | 检查幂等键与原始 payload |
| `422` | 否 | 检查幂等键是否配合了不同 body，或支付 Rail 是否可用 |
