# 如何在 Remix 中设置支付

> 在 Remix 应用中集成 Dodo Payments 的完整指南，涵盖用于 Webhook 的 loader、action 和资源路由，以及结账组件。
- **Author**: Ayush Agarwal
- **Published**: 2026-03-29
- **Category**: Payments, Developer Tools, How-To
- **URL**: https://dodopayments.com/blogs/zh/set-up-payments-remix

---

Remix 对 Web 标准和渐进式增强的理念，使其非常适合构建具备韧性的 Web 应用。使用 Remix 构建应用时，你是在顺应 Web 的运行方式：使用 loader 获取数据，使用 action 修改数据。在这种架构中添加支付功能，也应该同样自然。借助 Remix 内置的基础能力，你可以创建兼具可靠性和可维护性的结账流程。

在本指南中，我们将介绍如何使用 Dodo Payments 在 Remix 应用中设置支付功能。内容涵盖从初始安装到处理 Webhook 以及管理客户订阅的完整流程。读完本指南后，你将拥有一个功能完整的支付系统，并且能够完美契合 Remix 的开发方式。

## 为什么选择 Remix 和 Dodo Payments？

Remix 通过依赖浏览器标准，旨在简化 Web 开发中复杂的部分。Dodo Payments 也秉持类似理念，致力于简化全球商业中的复杂环节。将两者结合后，你将获得一套强大的技术栈，同时处理前端架构和后端合规中的繁重工作。

> 支付基础设施应该隐退到幕后。如果你的工程团队每月在计费问题上花费超过一天，说明你的支付技术栈正在拖累你。
>
> \- Rishabh Goel，Dodo Payments 联合创始人兼 CEO

Dodo Payments 充当 MoR（名义商户），这意味着我们负责在全球销售软件时那些繁琐的环节。这包括在 220 多个国家和地区自动计算并收取税费、遵守当地法规，以及管理拒付。对于 Remix 开发者来说，这意味着你可以专注于构建产品，而不必担心 VAT OSS 或美国不同州的经济关联规则。

由于 Dodo 提供专用的 Remix 无缝接入方案，整个集成过程尤其顺畅。该无缝接入方案专为 Remix 基于路由的架构打造，因此你可以轻松地在 action 中设置结账触发器，并在资源路由中设置 Webhook 处理器。你不需要编写样板代码来解析请求正文或手动验证签名。

## 前置条件

在开始编写代码前，请确保你具备以下条件：

- 已经设置好的 Remix 项目。如果还没有，可以使用 `npx create-remix@latest` 创建。
- Dodo Payments 账户。你可以在 [dodopayments.com](https://dodopayments.com) 注册。
- 从控制面板获取的 Dodo Payments API 密钥和 Webhook 密钥。

## 第 1 步：安装

第一步是安装 Dodo Payments 的 Remix 无缝接入方案。该软件包包含用于管理结账、Webhook 和客户门户的处理器。

```bash
npm install @dodopayments/remix
```

该无缝接入方案是 Dodo Payments SDK 的轻量级封装，针对 Remix 环境进行了优化。它负责在标准 Web Fetch API 请求和 Dodo API 之间进行转换，这正是 Remix 处理自身 loader 和 action 的方式。

## 第 2 步：环境变量

处理支付时，安全性至关重要。你不应将 API 密钥或密钥直接写入源代码，而应使用环境变量。如果项目根目录中还没有 `.env` 文件，请创建一个。

```env
DODO_PAYMENTS_API_KEY=your_api_key_here
DODO_PAYMENTS_WEBHOOK_SECRET=your_webhook_secret_here
DODO_PAYMENTS_RETURN_URL=https://dodopayments.com/checkout/success
DODO_PAYMENTS_ENVIRONMENT=test_mode
```

在生产环境中，你应在托管服务商的控制面板中设置这些变量，例如 Vercel、Netlify 或 Fly.io。Remix 会在开发期间自动从 `.env` 文件加载这些变量。

## 第 3 步：Remix 支付架构

在编写更多代码前，我们先看看 Remix 应用的不同部分将如何与 Dodo Payments 交互。通常，流程包括：使用 loader 检查用户当前状态，使用 action 触发结账，以及使用资源路由处理来自 Dodo 的异步更新。

```mermaid
flowchart TD
    A[User visits Pricing Page] --> B[Loader checks Subscription Status]
    B --> C[Component renders Buy Button]
    C --> D[User clicks Buy]
    D --> E[Action triggers Checkout]
    E --> F[Redirect to Dodo Checkout]
    F --> G[User pays]
    G --> H[Dodo sends Webhook]
    H --> I[Resource Route updates DB]
    I --> J[User redirected back to App]
```

这种架构确保你的应用始终是用户权益的唯一事实来源，而 Dodo 负责实际交易。

## 第 4 步：创建结账路由

在 Remix 中，我们可以创建专用路由来处理结账请求。这通常是一个不渲染 UI，而是执行重定向或返回 JSON 的"资源路由"。在 `app/routes/api.checkout.tsx` 创建文件。

```typescript
import { Checkout } from "@dodopayments/remix";
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";

const checkoutHandler = Checkout({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT as any,
  type: "dynamic",
});

export const loader = ({ request }: LoaderFunctionArgs) => {
  // You can use the loader for static payment links if needed
  return checkoutHandler(request);
};

export const action = async ({ request }: ActionFunctionArgs) => {
  // The action handles POST requests, perfect for dynamic checkouts
  return checkoutHandler(request);
};
```

无缝接入方案中的 `Checkout` 处理器非常灵活。它可以通过 loader 中的 GET 请求处理静态链接，也可以通过 action 中的 POST 请求处理动态结账。对于大多数 SaaS 应用，你会使用 action 传递特定的客户信息或产品配置。

## 第 5 步：安全处理 Webhook

Webhook 对于让数据库与支付事件保持同步至关重要。当订阅续期或支付失败时，Dodo 会向你的 Webhook URL 发送 POST 请求。我们需要一个资源路由来接收并验证这些事件。在 `app/routes/api.webhook.tsx` 创建文件。

```typescript
import { Webhooks } from "@dodopayments/remix";
import type { ActionFunctionArgs } from "@remix-run/node";

const webhookHandler = Webhooks({
  webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET!,
  onPaymentSucceeded: async (payload) => {
    const { customer, total_amount, metadata } = payload.data;
    console.log(`Payment of ${total_amount} succeeded for ${customer.email}`);
    // Update your database here
  },
  onSubscriptionActive: async (payload) => {
    const { customer_id, subscription_id } = payload.data;
    // Grant access to your service
  },
  onSubscriptionCancelled: async (payload) => {
    // Revoke access
  },
});

export const action = ({ request }: ActionFunctionArgs) => {
  return webhookHandler(request);
};
```

`Webhooks` 处理器会使用你的 `webhookKey` 自动验证传入请求的签名。如果签名无效，它会返回 401 Unauthorized 响应，保护你的端点免受伪造攻击。它还为不同事件类型提供类型化回调，让你的逻辑保持简洁易读。

## 第 6 步：从 UI 触发结账

现在后端路由已经准备就绪，我们可以在前端添加结账按钮。我们将使用 Remix 的 `<Form>` 组件，向结账路由提交 POST 请求。

```tsx
import { Form } from "@remix-run/react";

export default function PricingPage() {
  return (
    <div className="pricing-container">
      <h2>Pro Plan</h2>
      <p>$29/month</p>
      <Form action="/api/checkout" method="post">
        <input type="hidden" name="productId" value="pdt_your_product_id" />
        <input type="hidden" name="quantity" value="1" />
        <button type="submit" className="buy-button">
          Upgrade Now
        </button>
      </Form>
    </div>
  );
}
```

用户点击按钮后，Remix 会将表单提交到 `/api/checkout`。该路由中的 action 随后调用 Dodo API，并将用户重定向到托管结账页面。即使 JavaScript 被禁用，这种方式仍然有效，实现了渐进式增强。

## 第 7 步：在 Loader 中检查订阅状态

为了保护高级路由，你应在 loader 中检查用户的订阅状态。你可以从自己的数据库中获取此信息（通过 Webhook 更新），也可以直接查询 Dodo API。

```typescript
import { json, redirect } from "@remix-run/node";
import type { LoaderFunctionArgs } from "@remix-run/node";
import { getSession } from "~/sessions.server";
import { db } from "~/db.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const session = await getSession(request.headers.get("Cookie"));
  const userId = session.get("userId");

  if (!userId) {
    return redirect("/login");
  }

  const user = await db.user.findUnique({ where: { id: userId } });

  if (!user?.isSubscribed) {
    return redirect("/pricing");
  }

  return json({ data: "Premium Content" });
};
```

在 loader 中执行检查，可以确保只有拥有有效订阅的用户才能看到高级内容。Remix 会在服务器端处理重定向，为用户提供无缝体验。

## 第 8 步：客户门户

允许用户自行管理订阅是任何 SaaS 的标准需求。Dodo Payments 提供托管客户门户，用户可以在其中更新支付方式、查看发票和取消订阅。我们可以设置一个路由，将用户重定向到该门户。创建 `app/routes/api.customer-portal.tsx`。

```typescript
import { CustomerPortal } from "@dodopayments/remix";
import type { LoaderFunctionArgs } from "@remix-run/node";
import { getSession } from "~/sessions.server";

const portalHandler = CustomerPortal({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT as any,
});

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const session = await getSession(request.headers.get("Cookie"));
  const customerId = session.get("dodoCustomerId");

  if (!customerId) {
    return new Response("Customer ID not found", { status: 400 });
  }

  // Append the customer_id to the request URL for the handler
  const url = new URL(request.url);
  url.searchParams.set("customer_id", customerId);
  const modifiedRequest = new Request(url.toString(), request);

  return portalHandler(modifiedRequest);
};
```

现在，你只需在应用的设置页面中链接到 `/api/customer-portal`，剩下的工作交给 Dodo 处理。

## Remix 支付进阶技巧

### 使用错误边界

支付失败是电子商务中不可避免的情况。Remix 的错误边界非常适合优雅地处理这类问题。如果你的结账 action 失败，最近的错误边界会捕获该错误，让你可以向用户显示有帮助的消息，而不会导致整个页面崩溃。

```tsx
export function ErrorBoundary() {
  return (
    <div className="error-container">
      <h3>Something went wrong with your checkout.</h3>
      <p>Please try again or contact support if the issue persists.</p>
    </div>
  );
}
```

### 使用支付链接实现渐进式增强

动态结账功能强大，但有时你只需要一个简单的静态链接。你可以将 Dodo 的静态支付链接用作备用方案。如果 action 因某种原因失败，可以在 UI 中提供一个指向结账页面的直接链接。这样可以确保你不会因为临时技术故障而错失订单。

### 部署到边缘环境

Remix 旨在支持在任何环境中运行，包括 Cloudflare Workers 或 Vercel Edge Functions 等边缘环境。Dodo Payments 的 Remix 无缝接入方案基于标准 Web API 构建，因此与这些环境完全兼容。无论用户身处何地，你都可以以极低延迟处理支付和 Webhook。

## 总结

在 Remix 中设置支付不必是一项艰巨的任务。借助 `@dodopayments/remix` 无缝接入方案，以及 Remix 的 action、loader 和资源路由等原生功能，你可以构建安全、可扩展且易于维护的支付系统。

Dodo Payments 负责处理全球合规和税务难题，而 Remix 则为出色的用户体验提供架构基础。两者结合，让你能够专注于最重要的事情：为客户打造优秀的产品。

无论你是在构建简单的数字产品商店，还是采用按使用量计费模式的复杂 SaaS，这套方案都能提供你实现增长所需的灵活性。你可以先集成基础结账流程，然后随着需求发展，逐步扩展到客户门户和详细的 Webhook 处理等高级功能。

如需了解更多使用 Dodo Payments 构建应用的信息，请查看我们的[集成指南](https://docs.dodopayments.com/developer-resources/integration-guide)，或浏览 [API 参考](https://docs.dodopayments.com/api-reference/introduction)。

## 常见问题

### Dodo Payments 支持 Remix 全新的 Vite 插件吗？

支持，Dodo Payments Remix 无缝接入方案完全兼容 Remix Vite 插件。由于该无缝接入方案使用标准的 Remix 路由处理器，因此无论你使用哪种构建工具，都能顺畅运行。你可以继续使用现有的 Vite 配置，无需针对支付集成进行特殊调整。

### 如何在本地开发环境中处理 Webhook？

要在本地测试 Webhook，你可以使用 Ngrok 或 Dodo CLI 等工具，将来自互联网的请求通过隧道转发到本地计算机。获得公开 URL 后，可以在 Dodo 控制面板中将其设置为 Webhook 端点。请确保更新本地 `.env` 文件中的 `DODO_PAYMENTS_WEBHOOK_SECRET`，使其与该端点对应的密钥一致。

### 我可以在 Cloudflare Workers 上将 Dodo Payments 与 Remix 结合使用吗？

当然可以。该无缝接入方案基于标准 Web Fetch API 构建，而这正是 Cloudflare Workers 的原生语言。你不会遇到 Node.js 特定依赖带来的问题。对于希望在边缘环境中构建高性能、全球分布式应用的开发者来说，这是一个非常理想的选择。

### 如果用户在结账过程中取消，会发生什么？

如果用户取消操作，他们会根据你提供的 `returnUrl` 被重定向回应用。若需要在数据库中执行特定清理操作，你还可以在 Webhook 处理器中监听 `onPaymentCancelled` 事件。通常，最好将用户状态保持为"待处理"，直到收到支付成功事件。

### 可以自定义结账页面的外观吗？

可以。Dodo Payments 允许你通过控制面板自定义托管结账页面的外观和风格。你可以添加徽标、更改品牌颜色，并自定义文案，从而确保用户获得一致的体验。这些更改会自动应用于 Remix 应用生成的所有结账会话和支付链接。

## 内部链接

- [如何接受在线支付](https://dodopayments.com/blogs/how-to-accept-online-payments)
- [面向 SaaS 的嵌入式支付](https://dodopayments.com/blogs/embedded-payments-saas)
- [SaaS 支付架构](https://dodopayments.com/blogs/payments-architecture-saas)
- [面向 SaaS 的 MoR（名义商户）](https://dodopayments.com/blogs/merchant-of-record-for-saas)
- [如何在线销售软件](https://dodopayments.com/blogs/how-to-sell-software-online)
- [订阅定价模式](https://dodopayments.com/blogs/subscription-pricing-models)
- [销售数字产品的最佳平台](https://dodopayments.com/blogs/best-platform-sell-digital-products)
- [使用 Dodo Payments 进行 Vibe Coding](https://dodopayments.com/blogs/vibe-coding)
- [Dodo Payments 首页](https://dodopayments.com)
- [Dodo Payments 定价](https://dodopayments.com/pricing)

## 文档链接

- [Overlay 结账指南](https://docs.dodopayments.com/developer-resources/overlay-checkout)
- [Webhook 集成](https://docs.dodopayments.com/developer-resources/webhooks)
- [Dodo Payments SDK](https://docs.dodopayments.com/developer-resources/dodo-payments-sdks)
- [集成指南](https://docs.dodopayments.com/developer-resources/integration-guide)
- [Inline 结账指南](https://docs.dodopayments.com/developer-resources/inline-checkout)
---
- [More Payments articles](https://dodopayments.com/blogs/category/payments)
- [All articles](https://dodopayments.com/blogs)