# 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" } } ``` ### AI 任务完成事件 ```json { "id": "evt_ai123", "type": "ai.task.completed", "created_at": "2024-01-15T10:30:00Z", "data": { "task_id": "task_abc", "task_type": "ai_chat", "result": { "content": "这里是 AI 的回复...", "model": "gpt-4", "usage": { "prompt_tokens": 50, "completion_tokens": 120 } }, "user_id": "user_123" } } ``` ## 🔧 处理业务逻辑 ### 处理支付事件 ```typescript async function processPaymentEvent(event) { const { order_id, amount, status } = event.data switch (status) { case 'completed': // 1. 更新订单状态 await updateOrderStatus(order_id, 'paid') // 2. 发放权益 await grantPremiumAccess(order_id) // 3. 发送通知 await sendConfirmationEmail(order_id) break case 'failed': // 处理失败 await handlePaymentFailure(order_id) break } } ``` ### 处理 AI 任务事件 ```typescript async function processAITaskEvent(event) { const { task_id, result, user_id } = event.data // 保存 AI 回复 await saveChatMessage({ taskId: task_id, userId: user_id, content: result.content, model: result.model, tokens: result.usage.completion_tokens }) // 更新用户用量 await updateUserUsage(user_id, result.usage) } ``` ## ⏰ 失败重试 如果你的服务器返回非 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 ``` ### Webhook 测试工具 控制台提供「发送测试事件」功能,可以手动触发任意事件类型进行测试。 ## ❓ 常见问题 ### Q: 收到重复事件怎么办? 使用事件 ID 做幂等检查,参考上文示例。 ### Q: 处理超时怎么办? ```typescript app.post('/webhook', async (req, res) => { // 立即返回 res.status(200).send('OK') // 异步处理 setImmediate(() => processEvent(req.body)) // 使用 setImmediate 不阻塞 }) ``` ### Q: 如何查看 webhook 日志? 在控制台 → Webhook → 查看每个端点的请求历史和响应状态。 --- **上一步:** [流式输出(SSE)接入](./streaming.md) **下一步:** [REST API 完整参考](./api-reference.md)