Files
jczxw-pc/content/docs/api/webhook.md
2026-04-23 16:30:57 +08:00

207 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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))
})
```