# 2026 年如何为 Next.js 应用添加支付

> 使用 Dodo Payments 在 Next.js 中集成支付的完整指南，涵盖 App Router、服务器组件、覆盖式结账、webhook 和订阅计费。
- **Author**: Ayush Agarwal
- **Published**: 2026-03-23
- **Category**: Payments, Developer Tools, How-To
- **URL**: https://dodopayments.com/blogs/zh/add-payments-nextjs-app

---

要在 Next.js App Router 应用中添加支付，你需要三个部分：一个用于打开结账页面的客户端组件、一个位于 `app/api/webhooks/...` 的路由处理程序，用于验证 webhook 签名并写入订阅状态，以及一个服务器组件或中间件检查，用于控制付费功能的访问权限。`@dodopayments/nextjs` 无缝接入提供了前两部分，因此大多数工作都集中在你的访问控制层。

Next.js 为大量新 SaaS 应用提供支持，但支付设置仍然是开发者最容易卡住的环节。App Router 的细节、服务器组件与客户端组件之间的边界，以及全球税务合规，都可能带来问题。先了解[支付网关的工作原理](https://dodopayments.com/blogs/how-payment-gateway-works)，可以大幅降低集成出错的概率。

过去，集成支付网关意味着要管理复杂状态、处理重定向，并从头构建自定义 webhook 监听器。Dodo Payments 采用 MoR（名义商户）模式，负责税务和合规等繁重工作，同时为 Next.js 提供以开发者为先的集成体验。

本指南将全面介绍如何为 Next.js App Router 应用添加支付。我们会从 SDK 的初始设置开始，一直讲到复杂订阅生命周期的处理，以及如何根据支付状态保护路由。

## 为什么选择 Next.js 和 Dodo Payments？

随着服务器组件和路由处理程序的引入，Next.js 已经发生了显著变化。传统支付集成往往难以适应这些边界，需要通过混乱的变通方案在服务器和客户端之间传递数据。Dodo Payments 在设计时就考虑了这些现代模式。这意味着你可以充分利用 App Router 的能力，同时不牺牲支付流程的用户体验或安全性。

> 税务合规不是一次性设置。它会不断变化：税率会变，门槛会变，每年还会有新的司法管辖区征收数字服务税。如果你面向全球销售，自动化处理并不是可选项。
>
> \- Rishabh Goel，Dodo Payments 联合创始人兼 CEO

[覆盖式结账](https://docs.dodopayments.com/developer-resources/overlay-checkout)是一个可在 App Router 中运行的客户端组件。它可以触发结账流程，而无需强制整页重定向。这一点对于控制面板和单页应用尤为重要，因为页面重新加载可能会丢失进行中的状态。

在服务器端，[Next.js 无缝接入](https://docs.dodopayments.com/developer-resources/nextjs-adaptor)为 webhook 和结账会话提供预构建的路由处理程序。这种分离让前端保持响应迅速，同时由后端负责签名验证和数据库写入。

除了技术集成之外，选择面向 SaaS 的[名义商户](https://dodopayments.com/blogs/merchant-of-record-for-saas)也是一种战略决策。Dodo Payments 不只是支付网关，还会作为法律意义上的销售方。这意味着它会代表你处理全球销售税（VAT、GST 等）、合规，甚至拒付。对于 Next.js 创始人来说，这意味着你可以专注于构建功能，而不是研究国际税法。

## 架构概览

在深入代码之前，先了解支付如何融入现代 Next.js 架构非常重要。典型设置包含三个主要部分：客户端 UI、服务器端 webhook 处理程序，以及访问控制层。这种架构兼顾安全性和可扩展性，让你能够处理成千上万笔交易而无需费心。

客户端 UI 使用 Dodo Payments SDK 初始化并打开覆盖式结账页面。当用户完成支付后，Dodo 的服务器会向你的 webhook 路由处理程序发送 POST 请求。该处理程序是支付平台与数据库之间的桥梁。

它会验证传入请求的签名，然后更新用户的订阅状态。最后，你的应用使用中间件或服务器组件检查来授予或拒绝付费功能的访问权限，因此客户端过期的标记永远无法单独解锁高级内容。

```mermaid
flowchart TD
    subgraph Client ["客户端（Next.js 应用）"]
        A[结账按钮] -->|初始化 SDK| B[覆盖式结账]
        B -->|用户支付| C[成功/失败 UI]
    end
    subgraph Dodo ["Dodo Payments"]
        B -->|处理支付| D[支付引擎]
        D -->|发送 Webhook| E[Webhook 事件]
    end
    subgraph Server ["服务器（Next.js API）"]
        E -->|POST| F[路由处理程序]
        F -->|验证并处理| G[数据库]
        H[中间件/服务器检查] -->|查询| G
        H -->|授予访问权限| I[付费功能]
    end
```

## 第一步：安装 SDK 和无缝接入

第一步是安装所需的软件包。你需要客户端结账 SDK，以及用于服务器端功能的 Next.js 无缝接入。

```bash
npm install dodopayments-checkout @dodopayments/nextjs
```

`dodopayments-checkout` 软件包是一个用于覆盖式结账的轻量级库。`@dodopayments/nextjs` 软件包则提供专为 App Router 设计的路由处理程序和工具。

## 第二步：设置环境

你需要配置环境变量，以安全存储 API 密钥。创建或更新 `.env.local` 文件，并加入以下值。

```env
DODO_PAYMENTS_API_KEY=your_api_key_here
DODO_WEBHOOK_SECRET=your_webhook_secret_here
DODO_PAYMENTS_ENVIRONMENT=test_mode
NEXT_PUBLIC_DODO_PAYMENTS_MODE=test
```

`DODO_PAYMENTS_API_KEY` 用于创建结账会话等服务器端请求。`DODO_WEBHOOK_SECRET` 对验证传入 webhook 确实来自 Dodo Payments 至关重要。我们还设置了一个公开变量，用于指定客户端 SDK 的模式。

## 第三步：创建结账按钮组件

在 Next.js 中，结账 UI 必须是客户端组件，因为它需要与浏览器的 window 对象和 Dodo Payments SDK 交互。我们将创建一个 `CheckoutButton` 组件，用于处理初始化并打开覆盖式结账页面。

```tsx
// components/CheckoutButton.tsx
"use client";

import { DodoPayments } from "dodopayments-checkout";
import { useEffect, useState } from "react";

interface CheckoutButtonProps {
  productId: string;
  className?: string;
}

export default function CheckoutButton({
  productId,
  className,
}: CheckoutButtonProps) {
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    // 组件挂载时初始化一次 SDK
    DodoPayments.Initialize({
      mode: process.env.NEXT_PUBLIC_DODO_PAYMENTS_MODE as "test" | "live",
      onEvent: (event) => {
        if (event.event_type === "checkout.opened") {
          setIsLoading(false);
        } else if (event.event_type === "checkout.error") {
          setIsLoading(false);
          console.error("结账错误：", event.data?.message);
        }
      },
    });
  }, []);

  const handleCheckout = async () => {
    setIsLoading(true);
    try {
      // 在实际应用中，你可能会在这里从 API 获取会话 URL
      // 为简单起见，我们假设你已有结账 URL，或使用一个静态 URL
      const response = await fetch("/api/checkout", {
        method: "POST",
        body: JSON.stringify({ productId }),
      });
      const { checkout_url } = await response.json();

      await DodoPayments.Checkout.open({
        checkoutUrl: checkout_url,
      });
    } catch (error) {
      console.error("无法打开结账页面：", error);
      setIsLoading(false);
    }
  };

  return (
    <button onClick={handleCheckout} disabled={isLoading} className={className}>
      {isLoading ? "处理中..." : "立即升级"}
    </button>
  );
}
```

该组件采用了[集成指南](https://docs.dodopayments.com/developer-resources/integration-guide)中的模式。它会在 `useEffect` hook 中初始化 SDK，并提供一个简洁的接口来触发支付。

## 第四步：创建 Webhook 路由处理程序

Webhook 是任何支付集成中最关键的部分。即使用户在重定向发生前关闭浏览器，它也能确保数据库与实际支付状态保持同步。

借助 `@dodopayments/nextjs` 无缝接入，设置 webhook 处理程序非常简单。在 `app/api/webhooks/dodo/route.ts` 创建文件。

```typescript
// app/api/webhooks/dodo/route.ts
import { Webhooks } from "@dodopayments/nextjs";
import { updateSubscriptionStatus } from "@/lib/db";

export const POST = Webhooks({
  webhookKey: process.env.DODO_WEBHOOK_SECRET!,
  onSubscriptionActive: async (payload) => {
    const { customer, subscription_id, metadata } = payload.data;
    const userId = metadata?.userId;

    if (userId) {
      await updateSubscriptionStatus(userId, {
        subscriptionId: subscription_id,
        status: "active",
        email: customer.email,
      });
    }
  },
  onSubscriptionCancelled: async (payload) => {
    const { metadata } = payload.data;
    const userId = metadata?.userId;

    if (userId) {
      await updateSubscriptionStatus(userId, {
        status: "cancelled",
      });
    }
  },
  onPaymentSucceeded: async (payload) => {
    // 处理一次性支付
    console.log("支付成功：", payload.data.payment_id);
  },
});
```

该处理程序会自动验证 webhook 签名。如果签名无效，它会返回 401 Unauthorized 响应，保护你的端点免受恶意请求攻击。更多详情请参阅 [webhook 文档](https://docs.dodopayments.com/developer-resources/webhooks)。

## 第五步：创建结账会话 API

为了保护 API 密钥安全，你不应直接从客户端创建结账会话。相反，应创建一个调用 Dodo Payments API 的简单路由处理程序。该处理程序充当安全代理，确保敏感凭据不会离开服务器。它还提供了一个集中位置，用于添加自定义逻辑，例如应用折扣或根据用户所在地计算税费。

```typescript
// app/api/checkout/route.ts
import { Checkout } from "@dodopayments/nextjs";
import { auth } from "@/lib/auth";

export const POST = Checkout({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT as
    | "test_mode"
    | "live_mode",
  type: "session",
});
```

当客户端调用此端点时，无缝接入会处理与 Dodo 服务器的通信，并返回一个 `checkout_url`。你可以向此端点传递 `userId` 等额外元数据，稍后这些信息会包含在 webhook 负载中。

这些元数据让你能够将支付关联回特定用户账户、营销活动或特定产品版本，而无需进行第二次查询。应在创建会话时设置元数据；你无法仅凭 webhook 可靠地还原这些信息。

## 第六步：根据支付状态保护路由

支付流程运行后，你需要限制用户对应用高级功能的访问。在 Next.js 中，你可以在页面级别使用服务器组件完成，也可以使用中间件进行全局控制。

### 服务器组件检查

这是保护特定页面最常见的方法。由于服务器组件可以直接获取数据，你可以在渲染页面之前检查用户的订阅状态。

```tsx
// app/dashboard/premium-feature/page.tsx
import { auth } from "@/lib/auth";
import { getUserSubscription } from "@/lib/db";
import { redirect } from "next/navigation";

export default async function PremiumPage() {
  const session = await auth();
  if (!session) redirect("/login");

  const subscription = await getUserSubscription(session.user.id);

  if (subscription?.status !== "active") {
    redirect("/upgrade");
  }

  return (
    <div>
      <h1>欢迎使用高级功能</h1>
      <p>只有活跃订阅用户可以查看此内容。</p>
    </div>
  );
}
```

### 中间件检查

如果你想保护整个目录（例如 `/dashboard/*`），中间件是更高效的选择。

```typescript
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { auth } from "@/lib/auth";

export async function middleware(request: NextRequest) {
  const session = await auth();

  if (request.nextUrl.pathname.startsWith("/dashboard")) {
    if (!session) {
      return NextResponse.redirect(new URL("/login", request.url));
    }

    // 通常你会在这里检查 cookie 或快速访问缓存
    // 以避免每次请求都访问主数据库。
  }

  return NextResponse.next();
}
```

## 处理订阅升级和降级

任何订阅型业务中最复杂的部分之一，就是处理升级和降级。随着需求变化，用户通常希望更改套餐，而你的应用需要平稳地处理这些转换。Dodo Payments 提供统一的订阅管理接口，让这一过程变得简单。

当用户决定升级套餐时，你只需为新产品创建一个新的结账会话。Dodo Payments 会自动处理转换，包括按比例计算旧套餐的剩余余额，并将其应用到新套餐中。这样可以确保用户始终支付正确金额，服务也不会出现空档。

在后端，你的 webhook 处理程序会收到 `subscription.plan_changed` 事件。你可以使用该事件更新数据库，并授予用户新功能的访问权限。同样，如果用户决定降级套餐，也可以用相同方式处理转换。关键是确保应用 UI 始终反映用户当前的订阅状态。

## 全球税务合规与 MoR 模式

对于许多开发者来说，在线销售软件最令人头疼的部分就是处理全球销售税。每个国家都有自己的规则和法规，跟踪所有变化本身就可能成为一份全职工作。这正是 MoR（名义商户）模式发挥作用的地方。通过使用[面向 SaaS 的名义商户](https://dodopayments.com/blogs/merchant-of-record-for-saas)，你实际上是将整个税务和合规流程外包给第三方。

Dodo Payments 会作为你产品的法律销售方。这意味着它负责在每个拥有客户的司法管辖区计算、收取并缴纳销售税。它还会处理当地法律合规，例如欧洲的 GDPR 或加州的 CCPA。这不仅能为你节省大量时间和精力，也能降低法律风险。你可以专注于打造产品，并放心让专家处理全球销售。

除了税务合规之外，MoR 模式还提供针对风控和拒付的保护。如果发生拒付，Dodo 会代表你处理争议流程，因此你无需直接与发卡银行沟通。

## 高级功能：订阅与按用量计费

许多 SaaS 应用正在从简单的固定费率订阅转向按量计费和混合模式。Dodo Payments 原生支持可随用户规模扩展的[按用量计费](https://dodopayments.com/blogs/implement-usage-based-billing)和[订阅定价模式](https://dodopayments.com/blogs/subscription-pricing-models)。如果你正在评估专用计量层，[Orb 计费替代方案](https://dodopayments.com/blogs/orb-billing-alternatives)综述介绍了独立计费引擎何时真正值得额外的集成成本。

要实现按用量计费，你可以使用 Dodo Payments SDK 接收用量事件。例如，如果你正在构建 AI 写作工具，可以跟踪生成的字数。

```typescript
import DodoPayments from "dodopayments";

const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
});

await client.usageEvents.ingest({
  events: [
    {
      event_id: "evt_unique_id",
      customer_id: "cus_abc123",
      event_name: "word_count",
      metadata: { words: 500 },
    },
  ],
});
```

这样，你就可以根据用户的实际用量收费，通常比单一固定费用更加公平，也更有利可图。你可以在[如何在线销售软件](https://dodopayments.com/blogs/how-to-sell-software-online)指南中了解更多信息。

## 测试你的集成

测试支付可能比较棘手，因为 webhook 需要一个公开可访问的 URL。在本地开发期间，你的 Next.js 应用运行在 `localhost` 上，而 Dodo 的服务器无法访问它。

### 使用 ngrok 测试 Webhook

在本地测试 webhook 的最佳方式，是使用 ngrok 等隧道服务。

1. 启动 Next.js 应用：`npm run dev`
2. 启动 ngrok：`ngrok http 3000`
3. 复制 ngrok 输出的 HTTPS 转发 URL
4. 在 Dodo Payments 控制面板中，将 webhook URL 设置为该转发主机，并加上路径 `/api/webhooks/dodo`

现在，当你执行测试支付时，webhook 会被转发到本地计算机，让你能够实时调试处理程序。

### Vercel 预览部署

如果你使用 Vercel，每个拉取请求都会获得唯一的预览 URL。这是在合并前、以接近生产环境的方式测试集成的好方法。只需记住，要在 Dodo 控制面板中更新 webhook URL，使其匹配该次测试对应的预览 URL。

## Next.js 支付最佳实践

- **使用服务器组件获取数据**：始终在服务器上检查订阅状态，以避免布局偏移并提升安全性。
- **处理加载状态**：支付可能需要几秒钟。使用 React 状态在结账按钮上显示加载指示器。
- **善用元数据**：创建结账会话时，将内部 `userId` 或 `orderId` 作为元数据传递。这样可以更轻松地在 webhook 处理程序中将支付关联回用户。
- **幂等性**：确保 webhook 处理程序具有幂等性。Webhook 有时会重试，你不希望重复授予同一个订阅或创建重复记录。
- **保护 Webhook**：永远不要跳过签名验证。这是确保请求合法的唯一方式。

集成支付不必成为持续数月的项目。使用合适的工具并遵循现代模式，你可以在几小时内完成[如何接受在线支付](https://dodopayments.com/blogs/how-to-accept-online-payments)。无论你是在构建简单的微型 SaaS，还是复杂的企业平台，Dodo Payments 都能提供所需基础设施，让你在全球扩展业务而无需承受合规负担。

如果你刚刚起步，可以查看我们的[销售数字产品的最佳平台](https://dodopayments.com/blogs/best-platform-sell-digital-products)指南，进一步了解如何选择合适的技术栈。如果你对[vibe coding](https://dodopayments.com/blogs/vibe-coding)等最新趋势感兴趣，会发现 Dodo 以开发者为先的方法非常适合这种工作流。

如需更多技术细节，请参阅 [SDK](https://docs.dodopayments.com/developer-resources/dodo-payments-sdks) 和[内嵌式结账](https://docs.dodopayments.com/developer-resources/inline-checkout)文档。准备好开始了吗？查看我们的[定价](https://dodopayments.com/pricing)，立即注册测试账户。

## 常见问题

### Dodo Payments 支持 Next.js App Router 吗？

支持。Dodo Payments 完全兼容 Next.js App Router。我们提供专用的 `@dodopayments/nextjs` 无缝接入，其中包含用于结账、客户门户和 webhook 的路由处理程序，让现代 Next.js 应用能够顺畅集成。

### 如何在 Next.js 中处理 Webhook？

你可以在 `app/api` 目录中使用路由处理程序来处理 webhook。借助 Next.js 无缝接入中的 `Webhooks` 工具，你可以轻松验证签名，并将不同事件类型路由到特定的异步函数进行处理。

### 可以将 Dodo Payments 与服务器组件结合使用吗？

虽然覆盖式结账本身是客户端组件，但你应使用服务器组件获取订阅数据并保护路由。这样可以确保敏感的支付检查在服务器上完成，从而提供更好的安全性和性能。

### 如何在 Next.js 中根据支付状态保护路由？

你可以在服务器组件或中间件中检查用户的订阅状态来保护路由。如果用户没有活跃订阅，可以使用 `next/navigation` 中的 `redirect` 函数，将其发送到升级页面或定价页面。

### Dodo Payments 是否同时支持 Next.js 中的一次性支付和订阅计费？

支持。Dodo Payments 支持一次性支付、周期性订阅，甚至复杂的按用量计费模式。所有这些都可以通过同一个 Next.js 集成进行管理，使用统一的结账会话和 webhook 处理程序。

## 结语

在 2026 年为 Next.js 应用添加支付，远不只是放置一个"购买"按钮。它关乎打造一个安全、可扩展且合规的收入引擎。借助 App Router 的优势，并使用 Dodo Payments 这样的 MoR（名义商户），你可以绕过全球商业中最棘手的部分，更快发布产品、更高效地扩展，并专注于真正重要的事情：为用户打造出色的产品。

Next.js 与 Dodo Payments 的组合为任何 SaaS 业务提供了强大基础。无论你刚刚起步，还是计划扩展到数百万用户，这套技术栈都能提供成功所需的灵活性和可靠性。按照本指南中的步骤操作，你可以在几小时内让功能完整的支付集成上线。那么还等什么？今天就开始构建你的下一个伟大创意吧！

无论是处理[面向 SaaS 的嵌入式支付](https://dodopayments.com/blogs/embedded-payments-saas)，还是设计可靠的[SaaS 支付架构](https://dodopayments.com/blogs/payments-architecture-saas)，现在都有工具帮助你以前所未有的速度完成这一过程。专注于产品，让 Dodo 处理支付，今天就开始扩展你的 Next.js 应用。
---
- [More Payments articles](https://dodopayments.com/blogs/category/payments)
- [All articles](https://dodopayments.com/blogs)