> ## Documentation Index
> Fetch the complete documentation index at: https://docs.waffo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 错误处理

> 优雅地处理 API 错误

## 错误格式

所有错误响应遵循统一的结构：

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Missing required field: name",
      "layer": "product"
    }
  ]
}
```

### 读取 errors 数组

`errors` 数组按从根因到顶层调用方的顺序排列：

* `errors[0]` -- 故障的**根因**（对调试最有用）
* `errors[n]` -- 暴露错误的顶层调用方

大多数情况下，您只需检查 `errors[0].message` 即可获取可操作的错误描述。

***

## HTTP 状态码

| 状态码 | 含义    | 说明           |
| --- | ----- | ------------ |
| 200 | 成功    | 请求处理成功       |
| 400 | 请求错误  | 参数无效或请求体格式错误 |
| 401 | 未授权   | 缺少或无效的认证凭证   |
| 403 | 禁止访问  | 凭证有效但权限不足    |
| 404 | 未找到   | 请求的资源不存在     |
| 409 | 冲突    | 幂等请求正在处理中    |
| 429 | 频率限制  | 短时间内请求过多     |
| 500 | 服务器错误 | 内部服务器错误      |
| 501 | 未实现   | 请求的功能尚未可用    |
| 502 | 网关错误  | 上游服务错误       |

***

## 错误 `layer` 字段

每个错误包含一个 `layer` 字符串，指示系统的哪个部分产生了该错误。调试时可将 `layer` 值与 `message` 结合使用来定位根因。

***

## 常见错误场景

### 认证错误 (401)

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Token has expired",
      "layer": "user"
    }
  ]
}
```

**解决方案：**

* 验证您的 Merchant ID 和私钥是否正确
* 确保 SDK 初始化时使用了有效的凭证
* 检查您的私钥是否与已注册的公钥匹配

### 验证错误 (400)

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Store name must be between 1 and 100 characters",
      "layer": "store"
    }
  ]
}
```

**解决方案：**

* 检查必填字段是否已提供
* 验证字段值的格式和约束
* 确保金额为显示格式字符串（例如 "29.00"）

### 权限错误 (403)

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Only store owner can delete store",
      "layer": "store"
    }
  ]
}
```

**解决方案：**

* 验证 API Key 具有所需的权限
* 确保用户属于正确的门店

### 幂等冲突 (409)

```json theme={"system"}
{
  "data": null,
  "errors": [
    {
      "message": "Request with this idempotency key is already being processed",
      "layer": "gateway"
    }
  ]
}
```

**解决方案：**

* 等待原始请求完成
* 使用不同的 `X-Idempotency-Key` 发起新请求

***

## 在代码中处理错误

### TypeScript SDK

```typescript theme={"system"}
import { WaffoPancake, WaffoPancakeError } from "@waffo/pancake-ts";

const client = new WaffoPancake({
  merchantId: process.env.WAFFO_MERCHANT_ID!,
  privateKey: process.env.WAFFO_PRIVATE_KEY!,
});

try {
  const { store } = await client.stores.create({ name: "My Store" });
} catch (err) {
  if (err instanceof WaffoPancakeError) {
    const rootCause = err.errors[0];
    console.error(`Error [${rootCause.layer}]: ${rootCause.message}`);
    // err.status contains the HTTP status code
  }
}
```

***

## 重试策略

对以下状态码使用指数退避重试：

* **429** -- 频率限制（如有 `Retry-After` 请求头则遵循）
* **500** -- 内部服务器错误
* **502** -- 网关错误

```javascript theme={"system"}
async function retryRequest(fn, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const status = error.status;
      if (![429, 500, 502].includes(status) || attempt === maxRetries - 1) {
        throw error;
      }
      const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
```

<Note>
  不要重试 `400`、`401`、`403` 或 `404` 错误。这些错误表示请求本身存在问题，需要修正。
</Note>

***

## 幂等性保障安全重试

使用 `X-Idempotency-Key` 请求头安全地重试写操作，避免创建重复记录：

```bash theme={"system"}
curl -X POST https://api.waffo.ai/v1/actions/checkout/create-session \
  -H "Content-Type: application/json" \
  -H "X-Store-Slug: your-store-slug" \
  -H "X-Environment: test" \
  -H "X-Idempotency-Key: order-abc-123" \
  -d '{"productId": "...", "productType": "onetime", "currency": "USD"}'
```

* 幂等键缓存 **24 小时**
* 使用相同的键将返回缓存的响应
* 如果原始请求仍在处理中，返回 `409 Conflict`
