# 如何设置用于支付通知的 Webhook

> 了解支付通知中的 Webhook 工作原理，学习如何设置端点、验证签名，并在 SaaS 应用中可靠地处理支付事件。
- **Author**: Ayush Agarwal
- **Published**: 2026-04-13
- **Category**: Developer Guide
- **URL**: https://dodopayments.com/blogs/zh/webhooks-payment-notifications

---

每隔几秒轮询一次 API 来检查支付是否成功，既浪费资源又不可靠。Webhook 通过在支付事件发生的瞬间将事件推送到你的服务器，解决了这个问题。客户完成结账、订阅续费、支付失败、退款处理----你的应用都能立即知晓，无需主动发起请求。

对于 SaaS 企业来说，Webhook 是支付后逻辑的核心。它们可以触发许可证密钥交付、激活订阅、更新用户权限、发送确认邮件，并将账单数据同步到数据库。如果没有 Webhook，你的支付集成在 API 调用之间就无法掌握状态。

本指南将介绍支付 Webhook 的工作原理、如何正确设置 Webhook，以及哪些错误会导致事件遗漏和支付流程中断。

## 支付 Webhook 的工作原理

Webhook 是支付服务商在特定事件发生时发送到你服务器的 HTTP POST 请求。与需要主动拉取数据的 API 不同，Webhook 会将数据主动推送给你。

```mermaid
flowchart LR
    A[客户付款] -->|"事件已创建"| B[支付服务商]
    B -->|"HTTP POST"| C[你的 Webhook 端点]
    C -->|"处理事件"| D[你的应用逻辑]
    D -->|"更新数据库"| E[激活订阅]
    D -->|"发送邮件"| F[向客户发送确认]
    C -->|"返回 200 OK"| B
```

流程非常简单：

1. 事件发生（支付成功、失败、退款等）
2. 支付服务商创建包含所有相关数据的事件负载
3. 服务商向你注册的 Webhook URL 发送 HTTP POST 请求
4. 你的服务器处理事件并执行相应操作
5. 你的服务器返回 200 状态码以确认收到事件
6. 如果你的服务器未返回 200，服务商会重试

## 常见的支付 Webhook 事件

大多数支付服务商都会为以下核心事件发送 Webhook：

| 事件                     | 触发时机       | 你应执行的操作                                                              |
| ------------------------ | -------------- | --------------------------------------------------------------------------- |
| `payment.succeeded`      | 扣款完成       | 激活访问权限，发送收据                                                      |
| `payment.failed`         | 扣款被拒       | 通知客户，触发[催收流程](https://dodopayments.com/blogs/dunning-management) |
| `subscription.active`    | 新订阅开始     | 配置账户，设置套餐                                                          |
| `subscription.renewed`   | 周期性扣款成功 | 延长访问期限                                                                |
| `subscription.cancelled` | 客户取消订阅   | 安排撤销访问权限                                                            |
| `refund.succeeded`       | 退款处理完成   | 撤销访问权限，更新记录                                                      |
| `dispute.opened`         | 提交拒付       | 标记账户，收集证据                                                          |

通过 [Dodo Payments Webhook](https://docs.dodopayments.com/developer-resources/webhooks)，你可以接收完整支付生命周期中的事件，包括一次性支付、订阅、退款和争议。

## 设置 Webhook 端点

### 第 1 步：创建端点

你的 Webhook 端点是一个接受 POST 请求的标准 HTTP 路由。下面是使用 Node.js 和 Express 的基本示例：

```javascript
import express from "express";
import crypto from "crypto";

const app = express();
app.use(express.json());

app.post("/webhooks/payments", async (req, res) => {
  const event = req.body;

  switch (event.type) {
    case "payment.succeeded":
      // 激活客户的订阅或交付产品
      await activateSubscription(event.data.customer_id, event.data.product_id);
      break;
    case "payment.failed":
      // 通知客户支付失败
      await sendFailedPaymentEmail(event.data.customer_email);
      break;
    case "refund.succeeded":
      // 撤销访问权限并更新内部记录
      await revokeAccess(event.data.customer_id);
      break;
    default:
      console.log(`未处理的事件类型：${event.type}`);
  }

  res.status(200).json({ received: true });
});

app.listen(3000);
```

端点的关键要求：

- 必须可公开访问（不能是 localhost）
- 必须接受包含 JSON 请求体的 POST 请求
- 必须在 5-30 秒内返回 200 状态码
- 应具备幂等性（同一事件处理两次应产生相同结果）

### 第 2 步：注册 URL

在支付服务商的控制面板中或通过 [API](https://docs.dodopayments.com/api-reference/introduction) 注册 Webhook URL，并选择你希望接收的事件。先订阅实际需要的事件，而不是一次性订阅所有事件。

使用 Dodo Payments 时，你可以通过控制面板配置 Webhook 端点，也可以参考[集成指南](https://docs.dodopayments.com/developer-resources/integration-guide)进行程序化设置。

### 第 3 步：验证 Webhook 签名

未经验证，绝不要信任传入的 Webhook 数据。支付服务商会使用密钥为每个 Webhook 签名，这样你就能确认请求确实来自服务商，且未被篡改。

```javascript
function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(JSON.stringify(payload))
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature),
  );
}

app.post("/webhooks/payments", (req, res) => {
  const signature = req.headers["x-webhook-signature"];

  if (
    !verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET)
  ) {
    return res.status(401).json({ error: "签名无效" });
  }

  // 处理已验证的事件......
  res.status(200).json({ received: true });
});
```

上面的示例展示了通用模式。Dodo Payments 遵循 [Standard Webhooks](https://docs.dodopayments.com/developer-resources/webhooks) 规范，因此验证时会使用 `webhook-id`、`webhook-signature` 和 `webhook-timestamp` 请求头；每个官方 SDK 都提供 `webhooks.unwrap()` 辅助方法，可为你验证并解析事件。

> Webhook 签名验证不是可选项。如果不进行验证，任何发现你的 Webhook URL 的人都可以向你的服务器发送伪造事件，可能会授予未经授权的访问权限或触发虚假退款。
>
> - Ayush Agarwal，Dodo Payments 联合创始人兼 CPTO

### 第 4 步：处理重试并实现幂等性

Webhook 采用至少一次投递机制，这意味着你可能会多次收到同一个事件。你的处理程序必须具备幂等性：

```javascript
async function handlePaymentSucceeded(event) {
  // 检查我们是否已经处理过此事件
  const existing = await db.webhookEvents.findOne({ eventId: event.id });
  if (existing) {
    return; // 已处理，跳过
  }

  // 处理事件
  await activateSubscription(event.data.customer_id);

  // 记录事件已处理
  await db.webhookEvents.create({ eventId: event.id, processedAt: new Date() });
}
```

保存已处理的事件 ID，并在处理前进行检查。这样可以避免重复激活订阅、重复发送邮件或创建重复记录。

## 常见的 Webhook 错误

### 同步执行过多操作

如果你的 Webhook 处理程序耗时超过服务商的超时时间（通常为 5-30 秒），投递就会被标记为失败并重试，从而造成重复处理。

**解决方法**：立即确认收到 Webhook，然后异步处理：

```javascript
app.post("/webhooks/payments", (req, res) => {
  // 立即确认
  res.status(200).json({ received: true });

  // 异步处理
  processWebhookEvent(req.body).catch(console.error);
});
```

### 未处理事件顺序问题

事件可能会乱序到达。如果出现网络延迟，`subscription.cancelled` 事件可能会先于 `subscription.active` 到达。请将处理程序设计为不依赖顺序，或者将事件加入队列并按顺序处理。

### 未进行身份验证就公开 Webhook 端点

如果不验证签名，你的 Webhook 端点就像一扇敞开的门。攻击者可以发送精心构造的事件来为自己授予访问权限、触发退款或破坏你的数据。处理事件前务必验证签名。

### 忽略失败的投递

大多数服务商都有采用指数退避的重试策略。如果你的端点长时间不可用，可能会永久遗漏事件。在服务商的控制面板中监控 Webhook 投递情况，并为失败设置提醒。

## 在本地测试 Webhook

在开发期间，互联网上无法访问你的 localhost。使用隧道工具公开你的本地服务器：

- **ngrok**：创建一个通向 localhost 的公开 URL
- **Cloudflare Tunnel**：使用 Cloudflare 网络，功能类似于 ngrok
- **localtunnel**：开源替代方案

```bash

# 使用 ngrok

ngrok http 3000

# 为你提供类似 abc123.ngrok.io 的公开 URL

# 将生成的 URL + /webhooks/payments 注册为你的 Webhook URL

```

你还可以在开发期间使用 Dodo Payments [SDK](https://docs.dodopayments.com/developer-resources/dodo-payments-sdks) 的测试模式，向你的端点发送模拟事件。

## 生产环境中的 Webhook 架构

对于处理大量请求的生产系统，可以考虑采用以下架构：

```mermaid
flowchart LR
    A[支付服务商] -->|"Webhook"| B[API 网关]
    B -->|"验证 + 排队"| C[消息队列]
    C -->|"消费"| D[工作进程]
    D -->|"处理"| E[数据库]
    D -->|"通知"| F[邮件服务]
    D -->|"更新"| G[订阅服务]
```

基于队列处理的优势：

- **可靠性**：即使工作进程崩溃，事件仍会保留在队列中
- **可扩展性**：增加更多工作进程来应对流量峰值
- **重试逻辑**：内置重试和死信队列
- **顺序处理**：按客户顺序处理事件

## Webhook 安全检查清单

- [ ] 验证每个请求的 Webhook 签名
- [ ] 为 Webhook 端点使用 HTTPS
- [ ] 使用事件 ID 实现幂等性
- [ ] 设置较短的超时时间并进行异步处理
- [ ] 记录所有收到的事件以便调试
- [ ] 监控投递成功率
- [ ] 妥善处理重试
- [ ] 拒绝签名无效或缺失的事件
- [ ] 安全保存 Webhook 密钥（使用环境变量，而不是写入代码）
- [ ] 上线前使用模拟事件进行测试

## 集成 Dodo Payments Webhook

[Dodo Payments](https://dodopayments.com) 为所有支付生命周期事件提供 Webhook。[Webhook 文档](https://docs.dodopayments.com/developer-resources/webhooks)涵盖：

- 事件类型和负载架构
- 签名验证方法
- 重试策略和超时配置
- 使用沙盒事件进行测试

结合 [Dodo API](https://docs.dodopayments.com/api-reference/introduction) 和 [SDK](https://docs.dodopayments.com/developer-resources/dodo-payments-sdks)，Webhook 可以让你全面了解一次性支付、[订阅](https://docs.dodopayments.com/features/subscription)和[许可证密钥](https://docs.dodopayments.com/features/license-keys)激活过程中的支付状态变化。

如需完整的集成教程，请参阅[开发者体验指南](https://dodopayments.com/blogs/developer-experience-dodo-payments)和[适合开发者的最佳账单 API](https://dodopayments.com/blogs/best-billing-apis-developers)。

## 常见问题

### 如果我的 Webhook 端点宕机会发生什么？

支付服务商会使用指数退避策略重试失败的 Webhook 投递，通常持续 24-72 小时。如果你的端点持续无法访问，事件最终会被丢弃。大多数服务商都会在控制面板中显示失败的投递，因此你可以手动重新播放这些事件。请将通过 API 轮询协调状态作为备用方案，设计你的系统。

### 如何在本地开发期间测试 Webhook？

使用 ngrok 或 Cloudflare Tunnel 等隧道服务，将你的本地服务器公开到互联网。运行 `ngrok http 3000` 启动 ngrok，然后在支付服务商的控制面板中，将生成的公开 URL 注册为 Webhook 端点。大多数服务商也提供测试事件模拟器。

### 我应该同步还是异步处理 Webhook？

始终先通过 200 响应立即确认收到 Webhook，然后使用消息队列或后台任务进行异步处理。同步处理可能导致超时，从而触发重试并造成重复处理。基于队列的架构能够提供可靠性、可扩展性和内置重试逻辑。

### 如何避免重复处理 Webhook？

在数据库中保存已处理的事件 ID，以实现幂等性。处理任何事件前，先检查是否已经处理过具有相同 ID 的事件。如果处理过，就跳过。这样可以确保重试投递不会导致重复操作，例如重复扣款或发送多封确认邮件。

### 什么是 Webhook 签名验证，为什么它很重要？

Webhook 签名验证可以确认传入的 Webhook 确实来自你的支付服务商，且在传输过程中未被篡改。服务商会使用共享密钥为每个请求签名。你的服务器会重新计算签名并进行比对。如果不进行验证，任何发现你的 Webhook URL 的人都可以发送恶意伪造事件。

## 总结

Webhook 是任何支付集成中由事件驱动的核心基础设施。从一开始就正确设置 Webhook----验证签名、实现幂等性、进行异步处理----你就能构建一个可靠且可扩展的系统。

如果你正在寻找具备完善 Webhook 文档和开发者友好 SDK 的支付平台，可以了解 [Dodo Payments](https://dodopayments.com)，并查看[定价](https://dodopayments.com/pricing)。
---
- [More Developer Guide articles](https://dodopayments.com/blogs/category/developer-guide)
- [All articles](https://dodopayments.com/blogs)