> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# FastAPI 模板

> 克隆 FastAPI boilerplate，使用 Python 后端和 Dodo Payments SDK 创建 checkout sessions、验证 webhooks 并打开 Customer Portal。

> **[GitHub Repository](https://github.com/dodopayments/fastapi-boilerplate)**
>
> FastAPI 和 Dodo Payments boilerplate 的源代码。

## 概述

FastAPI boilerplate 是一个已经连接 Dodo Payments 的 Python 后端。它包含用于创建 checkout sessions 和 Customer Portal sessions 的 endpoints、用于验证 signatures 的 webhook endpoint，以及使用 Jinja2 templates 渲染的 pricing page。

> [!NOTE]
>
> 此 boilerplate 使用 FastAPI 和 `async` route handlers、Pydantic 进行 validation 和 settings 管理，以及 `dodopayments` Python SDK。Handlers 调用同步的 `DodoPayments` client。为避免阻塞 event loop，请切换到 `AsyncDodoPayments`，并对其调用使用 `await`。

### 特性

此 boilerplate 包含：

* **快速设置**：从克隆到运行服务器大约只需五分钟。
* **Async Handlers**：Route handlers 是 FastAPI `async def` functions。
* **Checkout Sessions**：预配置的 checkout endpoint，使用 Python SDK。
* **Webhook Handling**：使用 SDK 的 `unwrap` method 验证每个 signature 的 webhook endpoint。
* **Customer Portal**：用于创建 Customer Portal sessions 的 endpoint。
* **Type Safety**：Pydantic models 验证 request bodies，代码使用 type hints。
* **Environment Configuration**：`pydantic-settings` 从 `.env` 加载并验证 configuration。

## 前置条件

开始之前，你需要：

* **Python 3.9 或更高版本**，`dodopayments` SDK 需要此版本。建议使用 Python 3.11 或更高版本。
* 用于 package management 的 **pip** 或 **uv**。
* **Dodo Payments 账户**，用于在 dashboard 中创建 API key 和 webhook signing secret。

## 快速开始

**1. Clone the Repository**

```bash theme={null}
git clone https://github.com/dodopayments/fastapi-boilerplate.git
cd fastapi-boilerplate
```

**2. Create Virtual Environment**

设置隔离的 Python environment：

```bash theme={null}
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate
```

或者使用 uv，以更快地管理 dependencies：

```bash theme={null}
uv venv
source .venv/bin/activate
```

**3. Install Dependencies**

```bash theme={null}
pip install -r requirements.txt
```

或者使用 uv：

```bash theme={null}
uv pip install -r requirements.txt
```

**4. Get API Credentials**

在 [Dodo Payments](https://dodopayments.com/) 注册，然后从 dashboard 获取 credentials：

* **API Key：** 在 [Dashboard → Developer → API Keys](https://app.dodopayments.com/developer/api-keys) 下创建 key。
* **Webhook Key：** 在 [Dashboard → Developer → Webhooks](https://app.dodopayments.com/developer/webhooks) 下添加 endpoint，然后复制其 signing secret。Endpoint URL 必须公开且使用 HTTPS。若要在本机接收 events，请参阅[在本地测试 Webhooks](#testing-webhooks-locally)。

> [!TIP]
>
> 在侧边栏中的 **Live Mode** 开关关闭时创建这两项。Test mode key 只能与 `DODO_PAYMENTS_ENVIRONMENT=test_mode` 一起使用，test mode payments 不会转移真实资金。

**5. Configure Environment Variables**

复制示例文件，在根目录中创建 `.env` 文件：

```bash theme={null}
cp .env.example .env
```

将值设置为你的 Dodo Payments credentials：

```bash .env theme={null}
DODO_PAYMENTS_API_KEY=your_api_key_here
DODO_PAYMENTS_WEBHOOK_KEY=your_webhook_signing_key_here
DODO_PAYMENTS_RETURN_URL=http://localhost:8000
DODO_PAYMENTS_ENVIRONMENT=test_mode
```

四个 variables 都是必需的。`app/core/config.py` 使用 `pydantic-settings` 加载它们；如果其中一项缺失或为空，app 将无法启动。`DODO_PAYMENTS_RETURN_URL` 是 checkout 在付款后将 customer 发送到的位置。

> [!WARNING]
>
> 不要将 `.env` 文件提交到 version control。Repository 的 `.gitignore` 已经将其排除。

**6. Add Your Products**

将 `app/lib/products.py` 中的示例 products 替换为你自己的 products。将每个 `product_id` 设置为 dashboard 中 **Products** 下某个 product 的 ID。Pricing page 会显示这些 products。

**7. Run the Development Server**

```bash theme={null}
uvicorn app.main:app --reload --port 8000
```

打开 [http://localhost:8000/docs](http://localhost:8000/docs) 查看 interactive API documentation。

> [!TIP]
>
> Swagger UI 会列出 `/api/checkout/`、`/api/webhook/` 和 `/api/customer-portal/` endpoints，可直接进行测试。

根 URL `http://localhost:8000` 提供定价页面。

> [!WARNING]
>
> `app/main.py` 调用 `templates.TemplateResponse("index.html", {"request": request, ...})`，而 Starlette 1.x 不再接受此签名，因此在全新安装中，定价页面会返回 `500` 错误。要修复此问题，请将调用更改为 `templates.TemplateResponse(request, "index.html", {"products": products})`。

## 项目结构

```text theme={null}
fastapi-boilerplate/
├── app/
│   ├── main.py             # FastAPI application entry point
│   ├── api/
│   │   ├── checkout.py     # Checkout session endpoint
│   │   ├── portal.py       # Customer portal endpoint
│   │   └── webhook.py      # Webhook handler endpoint
│   ├── core/
│   │   └── config.py       # Environment configuration
│   ├── lib/
│   │   ├── customers.py    # Customer payload helpers
│   │   └── products.py     # Products shown on the pricing page
│   └── templates/
│       ├── base.html       # Base template
│       └── index.html      # Pricing page
├── requirements.txt        # Python dependencies
├── .env.example            # Environment template
└── README.md
```

## API 端点

`app/main.py` 会将每个路由器挂载到 `/api` 前缀下：

| 端点 | 方法 | 描述 |
| - | - | - |
| `/api/checkout/` | POST | 创建新的结账会话 |
| `/api/webhook/` | POST | 处理 Dodo Payments webhooks |
| `/api/customer-portal/` | POST | 生成 Customer Portal URL |

每个路径都以斜杠结尾。FastAPI 会使用 `307` 重定向来响应不带斜杠的路径请求，因此请使用准确的路径，尤其是在 webhook URL 中。

## 代码示例

这些示例整理自 `app/api/` 中的文件。

### 创建结账会话

`app/api/checkout.py` 会创建结账会话并返回其 `checkout_url`。请求正文接受一个 `product_id`、一个可选的 `quantity`，以及一个可选的 `customer` 对象，其中包含 `name` 和 `email`：

```python theme={null}
from typing import Optional

from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from dodopayments import DodoPayments
from app.core.config import settings
from app.lib.customers import CustomerData, to_dodo_customer_payload

router = APIRouter()
client = DodoPayments(
    bearer_token=settings.DODO_PAYMENTS_API_KEY,
    environment=settings.DODO_PAYMENTS_ENVIRONMENT,
)

class CheckoutRequest(BaseModel):
    product_id: str
    quantity: int = 1
    customer: Optional[CustomerData] = None

@router.post("/")
async def create_checkout(request: CheckoutRequest):
    try:
        checkout_session = client.checkout_sessions.create(
            product_cart=[{
                "product_id": request.product_id,
                "quantity": request.quantity,
            }],
            customer=to_dodo_customer_payload(request.customer),
            show_saved_payment_methods=True,
            feature_flags={"allow_discount_code": True},
            return_url=settings.DODO_PAYMENTS_RETURN_URL,
        )
        return {"session_id": checkout_session.session_id, "checkout_url": checkout_session.checkout_url}
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))
```

### 处理 Webhooks

`app/api/webhook.py` 使用 SDK 的 `unwrap` 方法验证签名，然后根据事件类型进行分支处理：

```python theme={null}
import json

from fastapi import APIRouter, Request, HTTPException
from dodopayments import DodoPayments
from app.core.config import settings

router = APIRouter()
client = DodoPayments(
    bearer_token=settings.DODO_PAYMENTS_API_KEY,
    environment=settings.DODO_PAYMENTS_ENVIRONMENT,
    webhook_key=settings.DODO_PAYMENTS_WEBHOOK_KEY,
)

@router.post("/")
async def handle_webhook(request: Request):
    body = await request.body()

    # Dodo Payments follows the Standard Webhooks spec: the signature covers
    # `id.timestamp.body`, so all three webhook-* headers must be passed in.
    try:
        client.webhooks.unwrap(
            body,
            headers={
                "webhook-id": request.headers.get("webhook-id", ""),
                "webhook-signature": request.headers.get("webhook-signature", ""),
                "webhook-timestamp": request.headers.get("webhook-timestamp", ""),
            },
        )
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Webhook verification failed: {e}")

    event_type = json.loads(body).get("type", "")

    if event_type == "subscription.active":
        print("Subscription is active")  # Grant access, update DB, send email
    elif event_type == "payment.succeeded":
        print("Payment succeeded")  # Fulfill order, update DB, send email
    elif event_type == "subscription.cancelled":
        print("Subscription cancelled")  # Revoke access, update user status
    elif event_type == "payment.failed":
        print("Payment failed")  # Notify user, update payment status
    else:
        print(f"Unhandled event type: {event_type}")

    return {"status": "success", "event": event_type}
```

### Customer Portal 集成

`app/api/portal.py` 为客户 ID 创建 Customer Portal 会话，并将门户链接作为 `url` 返回：

```python theme={null}
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from dodopayments import DodoPayments
from app.core.config import settings
from app.lib.customers import sanitize_customer_id

router = APIRouter()
client = DodoPayments(
    bearer_token=settings.DODO_PAYMENTS_API_KEY,
    environment=settings.DODO_PAYMENTS_ENVIRONMENT,
)

class PortalRequest(BaseModel):
    customer_id: str

@router.post("/")
async def create_customer_portal(request: PortalRequest):
    try:
        customer_id = sanitize_customer_id(request.customer_id)
        portal_session = client.customers.customer_portal.create(
            customer_id=customer_id,
        )
        return {"url": portal_session.link}
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))
```

`app/templates/index.html` 中的定价页面会向此端点发送硬编码的客户 ID（`cus_001`），并向结账端点发送硬编码的姓名和电子邮件。请将其替换为已登录用户的信息。

## Webhook 事件

`app/api/webhook.py` 中的处理程序会根据以下事件进行分支处理：

| 事件 | 描述 |
| - | - |
| `payment.succeeded` | 付款成功完成 |
| `payment.failed` | 付款尝试失败 |
| `subscription.active` | 订阅现已激活 |
| `subscription.cancelled` | 订阅已取消 |

要处理其他事件，请为其类型添加分支，例如为处理成功的退款添加 `refund.succeeded`。有关所有事件类型，请参阅 [Webhook 事件指南](/developer-resources/webhooks/intents/webhook-events-guide)。

在 webhook 处理程序中添加业务逻辑，以执行以下操作：

* 更新数据库中的用户权限
* 发送确认电子邮件
* 为数字产品配置访问权限
* 跟踪分析数据和指标

## 在本地测试 Webhooks

Dodo Payments 无法访问 `localhost`。进行本地开发时，请使用 [ngrok](https://ngrok.com/) 等工具公开本地服务器：

```bash theme={null}
ngrok http 8000
```

将 ngrok HTTPS URL 后接 `/api/webhook/`，作为端点添加到 [Dodo Payments Dashboard](https://app.dodopayments.com/developer/webhooks) 中：

```text theme={null}
https://your-ngrok-url.ngrok.io/api/webhook/
```

将端点的签名密钥复制到 `.env` 中的 `DODO_PAYMENTS_WEBHOOK_KEY`，然后重新启动服务器。应用仅在启动时读取 `.env`。

## 部署

### Docker

代码仓库中不包含 `Dockerfile`。要在容器中运行应用，请将此 `Dockerfile` 添加到代码仓库根目录：

```dockerfile theme={null}
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

`COPY . .` 会复制构建上下文中的每个文件，包括 `.env`。要避免将密钥放入镜像中，请添加一个 `.dockerignore` 文件，其中列出 `.env`。然后构建镜像，并使用环境文件运行它：

```bash theme={null}
docker build -t fastapi-dodo .
docker run -p 8000:8000 --env-file .env fastapi-dodo
```

### 生产环境注意事项

> [!WARNING]
>
> 部署到生产环境前：
>
> * 将 `DODO_PAYMENTS_ENVIRONMENT` 切换为 `live_mode`。
> * 使用 Dashboard 中的 live mode API key。
> * 为生产域名添加 webhook 端点，并将 `DODO_PAYMENTS_WEBHOOK_KEY` 设置为其签名密钥。
> * 将 `DODO_PAYMENTS_RETURN_URL` 设置为生产 URL。
> * 为所有端点启用 HTTPS。

## 故障排除

> **Import errors or missing modules**
>
> 确保虚拟环境已激活，并且依赖项已安装：
>
> ```bash theme={null}
> source venv/bin/activate
> pip install -r requirements.txt
> ```

> **Server fails to start with Directory 'app/static' does not exist**
>
> `app/main.py` 从 `app/static` 提供静态文件，但代码仓库中不包含该目录。使用 `mkdir app/static` 创建该目录，然后再次启动服务器。

> **Checkout session creation fails**
>
> 检查以下常见原因：
>
> * 产品 ID 不存在于你的 Dodo Payments Dashboard 中。
> * `.env` 中的 API key 或 `DODO_PAYMENTS_ENVIRONMENT` 不正确。测试模式密钥只能与 `test_mode` 一起使用。
>
> 端点会在 `400` 响应中返回 SDK 错误。请检查 FastAPI 日志以获取详细的错误消息。

> **Webhooks not receiving events**
>
> 进行本地测试时，请使用 [ngrok](https://ngrok.com) 公开服务器：
>
> ```bash theme={null}
> ngrok http 8000
> ```
>
> 在你的 [Dodo dashboard](https://app.dodopayments.com/developer/webhooks) 中，添加一个端点，使用 ngrok URL 后接 `/api/webhook/`，并包含末尾斜杠。将该端点的签名密钥复制到你的 `.env` 文件中的 `DODO_PAYMENTS_WEBHOOK_KEY`。

> **Webhook signature verification fails**
>
> * 确保 `.env` 中的 `DODO_PAYMENTS_WEBHOOK_KEY` 与端点的签名密钥一致。
> * 在将请求解析为 JSON 之前，针对原始请求正文验证签名。
> * 将全部三个 `webhook-id`、`webhook-timestamp` 和 `webhook-signature` headers 传递给 `client.webhooks.unwrap()`。Standard Webhooks 签名涵盖 `id.timestamp.body`，而不仅仅是正文。

## 了解更多

> **[Python SDK](/developer-resources/sdks/python)**
>
> 完整的 Python SDK 文档，支持异步

> **[Webhooks Documentation](/developer-resources/webhooks)**
>
> 了解所有 webhook 事件和最佳实践

> **[Checkout Sessions](/developer-resources/checkout-session)**
>
> 深入了解结账会话配置

> **[API Reference](/api-reference/introduction)**
>
> 完整的 Dodo Payments API 文档

## 支持

有关样板项目的帮助：

* 在 [Discord 社区](https://discord.gg/bYqAp4ayYh) 中提问。
* 在 [GitHub 代码仓库](https://github.com/dodopayments/fastapi-boilerplate) 中报告问题并关注更新。
* 发送电子邮件至 [支持团队](mailto:support@dodopayments.com)。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.