# 如何在 FastAPI 后端中接受付款

> 了解如何将 Dodo Payments 集成到 FastAPI 后端，包括异步 Webhook 处理程序、Pydantic 验证和安全的签名验证。
- **Author**: Ayush Agarwal
- **Published**: 2026-03-29
- **Category**: Payments, Developer Tools, How-To
- **URL**: https://dodopayments.com/blogs/zh/accept-payments-fastapi

---

要在 FastAPI 后端中接受付款，请使用异步 Dodo Payments 客户端创建结账会话，公开一个 `POST /webhook` 路由，并在信任负载之前通过 `client.webhooks.unwrap` 验证签名。请根据已验证的事件更新数据库，而不是依据浏览器重定向来更新。Pydantic 会在请求进入时处理验证；对于付款状态，Webhook 才是唯一可信来源。

FastAPI 已成为追求高性能以及类型提示、异步支持等现代功能的 Python 开发者的首选框架。当你构建 SaaS 或数字产品时，后端最关键的部分就是付款集成。你需要的不仅是快速，而且安全、易维护的系统，而这首先要求你全面了解[支付网关的工作原理](https://dodopayments.com/blogs/how-payment-gateway-works)。

在本指南中，我们将逐步介绍如何将 Dodo Payments 集成到 FastAPI 应用中。内容涵盖从环境设置，到处理异步 Webhook，再到使用 Pydantic 验证负载的完整流程。读完本文，你将拥有一个可用于生产环境的支付系统，能够自动处理全球税务和合规事务。

## 为什么选择 FastAPI 处理付款？

FastAPI 构建于 Starlette 和 Pydantic 之上，因此速度极快且可靠。它原生支持异步编程，非常适合支付处理，因为你经常需要等待外部 API 调用或数据库更新。借助 Swagger UI 自动生成文档，测试支付端点也变得非常轻松。

> 付款集成应该耗时几小时，而不是几周。如果开发者需要花几天阅读文档，才能写下第一行代码，那么这个平台就不是开发者优先，而是对开发者不友好。
>
> \- Ayush Agarwal，Dodo Payments 联合创始人兼 CPTO

将 FastAPI 与 Dodo Payments 结合，你就能获得一套强大的技术栈。Dodo Payments 充当 MoR（名义商户），这意味着它会处理与 Python 无关的全球商业事务：征收增值税、缴纳税款以及合规。如果你还不熟悉 MoR 模式，可以阅读[什么是名义商户](https://dodopayments.com/blogs/what-is-a-merchant-of-record)，了解究竟哪些责任会发生转移。

## 设置 FastAPI 项目

在开始编写代码之前，先设置一个整洁的开发环境。我们建议使用 Python 3.9 或更高版本。你可以使用 `pip` 或 `uv` 管理依赖项。本项目需要 `fastapi`、`uvicorn` 和 `dodopayments` Python SDK。

```bash
pip install fastapi uvicorn dodopayments python-dotenv
```

在项目根目录创建 `.env` 文件，用于存储 API 密钥。你可以在 Dodo Payments 控制面板中找到这些密钥。切勿将此文件提交到版本控制系统。

```env
DODO_PAYMENTS_API_KEY=your_api_key_here
DODO_PAYMENTS_WEBHOOK_KEY=your_webhook_key_here
DODO_PAYMENTS_ENVIRONMENT=test_mode
```

## 初始化 Dodo Payments 客户端

Dodo Payments Python SDK 同时提供同步和异步客户端。由于我们使用 FastAPI，因此将使用 `AsyncDodoPayments` 客户端，让应用保持非阻塞。

```python
import os
from dotenv import load_dotenv
from dodopayments import AsyncDodoPayments

load_dotenv()

client = AsyncDodoPayments(
    bearer_token=os.getenv("DODO_PAYMENTS_API_KEY"),
    environment=os.getenv("DODO_PAYMENTS_ENVIRONMENT")
)
```

该客户端将用于创建结账会话并与 Dodo Payments API 交互。使用异步客户端可以确保服务器在等待支付网关响应时，仍能处理其他请求。

## 创建结账会话

任何支付流程的第一步都是创建结账会话。该会话会生成一个安全 URL，让客户输入付款信息。在 FastAPI 中，我们可以创建一个简单的 POST 端点，接收产品 ID 并返回结账 URL。

```python
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel

router = APIRouter()

class CheckoutRequest(BaseModel):
    product_id: str
    quantity: int = 1
    customer_email: str
    customer_name: str

@router.post("/checkout")
async def create_checkout(request: CheckoutRequest):
    try:
        session = await client.checkout_sessions.create(
            product_cart=[{
                "product_id": request.product_id,
                "quantity": request.quantity
            }],
            customer={
                "email": request.customer_email,
                "name": request.customer_name
            },
            return_url="https://dodopayments.com/success"
        )
        return {"checkout_url": session.checkout_url, "session_id": session.session_id}
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))
```

该端点使用 Pydantic 验证请求，确保传入的数据格式正确。`checkout_sessions.create` 方法会返回一个会话对象，其中包含你应将用户重定向到的 URL。

## 深入了解 Pydantic 验证

FastAPI 的一大亮点是它与 Pydantic 的集成。当你定义 `CheckoutRequest` 模型时，FastAPI 会自动根据该模型验证传入的 JSON 负载。这意味着你不必为每个字段编写手动验证逻辑。

```python
from pydantic import EmailStr, Field

class CheckoutRequest(BaseModel):
    product_id: str = Field(..., description="The unique ID of the product")
    quantity: int = Field(1, ge=1, le=100, description="Quantity must be between 1 and 100")
    customer_email: EmailStr = Field(..., description="A valid email address")
    customer_name: str = Field(..., min_length=2, max_length=100)
```

通过使用 `EmailStr`，Pydantic 可以确保 `customer_email` 字段符合有效的电子邮件格式。`Field` 函数允许你添加 `ge`（大于或等于）和 `le`（小于或等于）等约束，还可以添加会显示在自动生成的 API 文档中的描述。

如果用户发送了无效请求，FastAPI 会自动返回 422 Unprocessable Entity 错误，并详细说明问题所在。这能为你节省大量时间，并减少支付流程中的错误隐患。

## 与数据库集成

在真实应用中，你需要将付款信息存储在数据库中。无论你使用 SQLAlchemy、SQLModel 还是 Tortoise ORM，模式都一样：在 Webhook 确认付款成功后更新记录。

下面来看一个使用 SQLModel 的示例。SQLModel 构建于 SQLAlchemy 和 Pydantic 之上，专为 FastAPI 设计。

```python
from sqlmodel import SQLModel, Field, create_engine, Session, select

class User(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    email: str = Field(index=True)
    is_premium: bool = Field(default=False)
    subscription_id: str | None = None

sqlite_url = "sqlite:///database.db"
engine = create_engine(sqlite_url)

def update_user_subscription(email: str, subscription_id: str):
    with Session(engine) as session:
        statement = select(User).where(User.email == email)
        user = session.exec(statement).first()
        if user:
            user.is_premium = True
            user.subscription_id = subscription_id
            session.add(user)
            session.commit()
```

在 Webhook 处理程序中，当收到 `subscription.active` 事件时调用 `update_user_subscription`。这样可以确保应用状态始终与实际付款状态保持同步。

## 稳健的错误处理与日志记录

支付处理事关重大。如果出现问题，你需要准确了解发生了什么以及原因。这正是结构化日志和稳健错误处理发挥作用的地方。

FastAPI 提供了强大的异常处理系统。你可以定义自定义异常处理程序，捕获特定错误并返回格式化的 JSON 响应。

```python
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@router.post("/webhook")
async def handle_webhook(request: Request):
    # ... verification logic ...
    try:
        # ... processing logic ...
        logger.info(f"Successfully processed event: {event.type}")
    except Exception as e:
        logger.error(f"Error processing webhook: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail="Internal server error")
```

记录事件类型和发生的任何错误，可以创建用于排查问题的审计轨迹。在日志记录器中使用 `exc_info=True` 会包含完整堆栈跟踪，让你更容易找到失败的根本原因。

## 使用 Pytest 和 HTTPX 进行测试

测试支付端点是必不可少的。你应该为验证逻辑编写单元测试，并为 API 端点编写集成测试。FastAPI 的 `TestClient`（底层使用 `httpx`）让这一过程变得简单直接。

```python
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_create_checkout_invalid_email():
    response = client.post(
        "/checkout",
        json={
            "product_id": "pdt_123",
            "quantity": 1,
            "customer_email": "not-an-email",
            "customer_name": "John Doe"
        }
    )
    assert response.status_code == 422
    assert "customer_email" in response.json()["detail"][0]["loc"]
```

测试 Webhook 时，你可以模拟签名验证，或使用测试 Webhook 密钥在测试套件中生成有效签名。这样无需从 Dodo Payments 发送真实请求，也能验证事件路由逻辑是否正常工作。

## 安全地处理 Webhook

Webhook 对于让数据库与支付事件保持同步至关重要。付款成功后，Dodo Payments 会向你的服务器发送 POST 请求。验证这些请求的签名至关重要，这样才能确认它们确实来自 Dodo。

FastAPI 可以轻松处理原始请求正文，而签名验证正需要使用原始请求正文。我们可以使用 SDK 提供的 `client.webhooks.unwrap` 方法验证并解析传入事件。

```python
from fastapi import Request, Header

@router.post("/webhook")
async def handle_webhook(
    request: Request,
    webhook_id: str = Header(None),
    webhook_signature: str = Header(None),
    webhook_timestamp: str = Header(None)
):
    payload = await request.body()

    try:
        event = client.webhooks.unwrap(
            payload,
            headers={
                "webhook-id": webhook_id,
                "webhook-signature": webhook_signature,
                "webhook-timestamp": webhook_timestamp,
            }
        )
    except Exception:
        raise HTTPException(status_code=401, detail="Invalid signature")

    # Process the event
    await process_event(event)
    return {"status": "received"}
```

借助 FastAPI 的 `Header` 依赖项，我们可以简洁地提取验证所需的元数据。如果签名无效，就返回 401 Unauthorized 状态码。

## 处理 Webhook 事件

验证事件后，你需要处理具体的事件类型。常见事件包括 `payment.succeeded`、`subscription.active` 和 `refund.succeeded`。你可以使用 Python 的 `match` 语句清晰地路由事件。

```python
async def process_event(event):
    event_type = event.type
    data = event.data

    match event_type:
        case "payment.succeeded":
            payment_id = data.payment_id
            customer_email = data.customer.email
            # Update your database, grant access, etc.
            print(f"Payment {payment_id} succeeded for {customer_email}")

        case "subscription.active":
            subscription_id = data.subscription_id
            # Activate user subscription
            print(f"Subscription {subscription_id} is now active")

        case "payment.failed":
            # Handle failed payment, maybe send an email
            print(f"Payment failed for {data.customer.email}")
```

这种模块化方式能让 Webhook 处理程序保持整洁并易于测试。随着应用发展，你还可以添加更多分支，例如处理争议或创建许可证密钥。

## 支付架构概览

要成功完成集成，关键是理解 FastAPI 后端、用户浏览器与 Dodo Payments 之间的数据流。下面的图示展示了典型的支付生命周期。

```mermaid
flowchart TD
    A[User clicks Buy] --> B[FastAPI: Create Checkout Session]
    B --> C[Redirect User to Dodo Checkout]
    C --> D[User Completes Payment]
    D --> E[Dodo: Process Transaction & Tax]
    E --> F[Dodo: Send Webhook to FastAPI]
    F --> G[FastAPI: Verify Signature & Update DB]
    G --> H[User Redirected to Success Page]
```

这种架构确保后端始终是付款状态的可信来源，同时由 Dodo Payments 负责繁重的交易处理和合规工作。

## 高级集成：依赖注入

FastAPI 的依赖注入系统是其最强大的功能之一。你可以使用它创建一个可复用的依赖项，在允许用户访问特定路由之前检查其是否拥有有效订阅。

```python
from fastapi import Depends

async def get_current_user_subscription(user_id: str):
    # Fetch subscription status from your database
    # This status was updated by your webhook handler
    subscription = await db.get_subscription(user_id)
    if not subscription or subscription.status != "active":
        raise HTTPException(status_code=403, detail="Active subscription required")
    return subscription

@router.get("/premium-content")
async def get_premium_data(subscription = Depends(get_current_user_subscription)):
    return {"data": "This is premium content"}
```

这种模式能让路由处理程序保持简洁，并确保整个 API 的访问控制一致。

## 使用 ngrok 在本地测试 Webhook

单元测试可以覆盖处理程序逻辑，但无法证明 Dodo 确实能访问你的机器。Dodo Payments 提供测试模式，可以模拟成功和失败的付款而不转移真实资金；要让这些事件抵达，你还需要一个公开 URL。

使用 `ngrok` 将本地服务器暴露到互联网，让 Webhook 请求能够到达你的开发机器。

```bash
ngrok http 8000
```

ngrok 运行后，在 Dodo Payments 控制面板中更新 Webhook URL，将其指向你的 ngrok URL。然后，你可以使用控制面板中的"Test Webhook"功能，向 FastAPI 后端发送示例事件。

## 部署注意事项

准备部署 FastAPI 应用时，请注意以下几点：

- **使用生产服务器**：在生产环境中使用 `gunicorn` 搭配 `uvicorn` worker 类，以获得更好的性能和稳定性。
- **必须使用 HTTPS**：切勿通过未加密的 HTTP 处理付款。请确保生产环境使用 SSL/TLS。
- **管理密钥**：使用 AWS Secrets Manager、[Infisical Secrets Manager](https://infisical.com/blog/secrets-management-complete-guide)、HashiCorp Vault，或平台内置的密钥管理功能（如 GitHub Actions secrets 或 Vercel 环境变量）存储 Dodo Payments 密钥。
- **监控**：实施运行状况检查和监控，确保支付端点始终可用。

## FastAPI 支付最佳实践

使用 FastAPI 构建支付系统时，请牢记以下最佳实践：

- **全面使用异步**：确保数据库驱动程序和外部 API 调用全部采用异步方式，避免阻塞事件循环。
- **验证所有内容**：对传入请求和传出响应都使用 Pydantic 模型。这能提供自动文档和运行时验证。
- **记录所有内容**：详细记录所有 Webhook 事件和 API 响应。这对于排查支付问题非常有价值。
- **幂等性**：确保 Webhook 处理程序具备幂等性。如果 Dodo Payments 两次发送同一事件，你的系统应能妥善处理，而不会重复执行操作。
- **保护密钥**：使用环境变量或密钥管理器存储 API 密钥。切勿将密钥硬编码到源代码中。

## 总结

将付款集成到 FastAPI 后端不必复杂。通过让 Dodo Payments 充当 MoR（名义商户），你可以将全球税务和合规的复杂工作交给 Dodo，同时完全掌控用户体验。借助 FastAPI 的现代功能和 Dodo Payments Python SDK，你可以在极短时间内构建稳健、高性能的支付系统。

无论你是在构建简单的数字产品，还是复杂的 B2B SaaS，FastAPI 与 Dodo Payments 的组合都能为业务全球化增长提供所需的可扩展性和可靠性。

想进一步了解如何使用 Dodo Payments 构建产品，可以查看我们的[如何接受在线付款](https://dodopayments.com/blogs/how-to-accept-online-payments)和 [API 变现](https://dodopayments.com/blogs/api-monetization)指南。你还可以探索我们的 [SaaS 支付架构](https://dodopayments.com/blogs/payments-architecture-saas)，进一步了解如何构建可扩展的计费系统。

如果你想了解更多技术细节，[Dodo Payments 文档](https://docs.dodopayments.com)是很好的起点。你可以查看关于 [Webhook](https://docs.dodopayments.com/developer-resources/webhooks)、[Python SDK](https://docs.dodopayments.com/developer-resources/dodo-payments-sdks)和[集成指南](https://docs.dodopayments.com/developer-resources/integration-guide)的详细信息。

## 常见问题

### 如何在 FastAPI 中处理周期性付款？

你可以监听 `subscription.active` 和 `subscription.renewed` Webhook 事件来处理周期性付款。发生这些事件时，更新数据库中的用户订阅状态，确保用户能够继续访问你的服务。

### Dodo Payments Python SDK 是否支持异步？

支持。Dodo Payments Python SDK 提供了专为 FastAPI 等异步框架设计的 `AsyncDodoPayments` 客户端。你可以使用它发起非阻塞 API 调用并保持高性能。

### 如何在 FastAPI 中验证 Webhook 签名？

你可以使用 `client.webhooks.unwrap` 方法验证 Webhook 签名。该方法接收原始请求正文以及 Dodo Payments 提供的请求标头，以确保请求真实可信且未被篡改。

### 我可以将 FastAPI 与 Dodo Payments 搭配用于全球销售吗？

当然可以。Dodo Payments 充当 MoR（名义商户），为你处理全球税款征收和合规事务。这意味着你可以向 220 多个国家和地区的客户销售产品，而不必担心当地税法或法规。

### 如果付款失败，会发生什么？

如果付款失败，Dodo Payments 会向你的服务器发送 `payment.failed` Webhook 事件。你可以使用此事件通知用户，例如发送一封包含更新付款方式链接的自动邮件。

## 最后总结

构建支付系统是一段持续的旅程，而不是一个终点。随着业务增长，你可能需要实现更复杂的功能，例如[按用量计费](https://dodopayments.com/blogs/implement-usage-based-billing)或 [SaaS 的名义商户](https://dodopayments.com/blogs/merchant-of-record-for-saas)。从 FastAPI 和 Dodo Payments 这样的坚实基础开始，你就能更好地应对未来的各种挑战。

Python 后端也是代理驱动购买往往最先落地的地方，因为买方是另一个服务，而不是浏览器。如果你的 API 将由自主代理调用，[x402 协议](https://dodopayments.com/blogs/x402-protocol)介绍了如何协商付费 HTTP 请求，而[代理商务](https://dodopayments.com/blogs/agentic-commerce)则介绍了这一转变的商业层面。

准备好开始接受付款了吗？立即[注册 Dodo Payments](https://dodopayments.com)，几分钟内即可让你的 FastAPI 后端运行起来。
---
- [More Payments articles](https://dodopayments.com/blogs/category/payments)
- [All articles](https://dodopayments.com/blogs)