---
title: Webhook 事件接入
description: 订阅业务事件，处理订单支付、AI 任务完成等异步通知。
category: api
order: 3
---

# Webhook 事件接入

> 订阅业务事件，处理订单支付、AI 任务完成等异步通知。

## 🔔 什么是 Webhook？

Webhook 是一种「反向 API 调用」机制：

- 传统 API：你 → 平台（主动拉取）
- Webhook：平台 → 你（被动接收）

当某个事件发生时（如支付成功），平台主动通知你的服务器。

## 📋 可订阅的事件

| 事件类型 | 说明 |
|----------|------|
| `payment.completed` | 支付完成 |
| `payment.failed` | 支付失败 |
| `subscription.created` | 订阅创建 |
| `subscription.cancelled` | 订阅取消 |
| `ai.task.completed` | AI 任务完成 |
| `ai.task.failed` | AI 任务失败 |
| `file.uploaded` | 文件上传完成 |
| `project.created` | 项目创建 |
| `user.invited` | 用户被邀请 |
| `workflow.triggered` | 工作流触发 |

## 🚀 配置 Webhook

### 步骤 1：创建 Webhook 端点

在你的服务器创建一个 API 端点：

```typescript
// Node.js Express 示例
app.post('/webhook/websopy', express.raw({ type: 'application/json' }), (req, res) => {
  // 验证签名
  const signature = req.headers['x-websopy-signature']
  const timestamp = req.headers['x-websopy-timestamp']
  
  if (!verifySignature(req.body, signature, timestamp)) {
    return res.status(401).send('Invalid signature')
  }
  
  // 处理事件
  const event = JSON.parse(req.body)
  console.log('收到事件:', event.type)
  
  // 立即返回 200（重要！）
  res.status(200).send('OK')
  
  // 异步处理业务逻辑
  processEvent(event)
})
```

### 步骤 2：在控制台配置

1. 进入 **开发者中心 → Webhook**
2. 点击 **添加端点**
3. 填写配置：

| 配置项 | 说明 |
|--------|------|
| URL | 你的服务器地址，如 `https://your-server.com/webhook/websopy` |
| 事件类型 | 选择要订阅的事件 |
| 密钥 | 用于验证签名的密钥 |

### 步骤 3：验证签名

```typescript
import crypto from 'crypto'

function verifySignature(body, signature, timestamp) {
  const secret = process.env.WEBSOPY_WEBHOOK_SECRET
  
  // 检查时间戳（5分钟内有效）
  const ts = parseInt(timestamp)
  if (Date.now() - ts > 5 * 60 * 1000) {
    return false
  }
  
  // 计算签名
  const payload = `${timestamp}.${body}`
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex')
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  )
}
```

## 📨 事件数据格式

### 通用结构

```json
{
  "id": "evt_abc123xyz",
  "type": "payment.completed",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    // 事件相关数据
  }
}
```

### 支付完成事件

```json
{
  "id": "evt_abc123xyz",
  "type": "payment.completed",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "order_id": "ord_xyz789",
    "amount": 29900,
    "currency": "CNY",
    "payment_method": "alipay",
    "status": "completed",
    "user_id": "user_123"
  }
}
```

## ⏰ 失败重试

如果你的服务器返回非 200 状态码，Websopy 会自动重试：

| 重试次数 | 延迟 |
|----------|------|
| 第 1 次 | 1 分钟 |
| 第 2 次 | 5 分钟 |
| 第 3 次 | 30 分钟 |
| 第 4 次 | 2 小时 |
| 第 5 次 | 12 小时 |

超过重试次数仍未成功，事件将被标记为失败，可在控制台手动重试。

### 幂等处理

```typescript
const processedEvents = new Set()

app.post('/webhook/websopy', async (req, res) => {
  const event = JSON.parse(req.body)
  
  // 幂等检查
  if (processedEvents.has(event.id)) {
    return res.status(200).send('Already processed')
  }
  
  try {
    await processEvent(event)
    processedEvents.add(event.id)
    res.status(200).send('OK')
  } catch (error) {
    res.status(500).send('Error')
  }
})
```

## 🧪 本地开发测试

### 使用 ngrok

```bash
# 安装 ngrok
npm install -g ngrok

# 启动本地服务
ngrok http 3000

# 复制输出的 https URL 到 Webhook 配置
# https://abc123.ngrok.io/webhook/websopy
```

## ❓ 常见问题

### Q: 收到重复事件怎么办？

使用事件 ID 做幂等检查，参考上文示例。

### Q: 处理超时怎么办？

```typescript
app.post('/webhook', async (req, res) => {
  // 立即返回
  res.status(200).send('OK')
  
  // 异步处理
  setImmediate(() => processEvent(req.body))
})
```
