> ## 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.

# Express 适配器

> 了解如何使用我们的 Express 适配器将 Dodo Payments 集成到您的 Express App Router 项目中。涵盖结账、客户门户、Webhook 和安全环境设置。

<CardGroup cols={2}>
  <Card title="Checkout Handler" icon="cart-shopping" href="#checkout-route-handler">
    在你的 Express 应用中集成 Dodo Payments 结账。
  </Card>

  <Card title="Customer Portal" icon="user" href="#customer-portal-route-handler">
    允许客户通过 Dodo Payments 客户门户管理订阅和详细信息。
  </Card>

  <Card title="Webhooks" icon="bell" href="#webhook-route-handler">
    接收并处理 Dodo Payments webhook 事件。
  </Card>
</CardGroup>

## 安装

<Steps>
  <Step title="Install the package">
    在项目根目录运行以下命令：

    ```bash theme={null}
    npm install @dodopayments/express
    ```
  </Step>

  <Step title="Set up environment variables">
    在项目根目录创建一个 <code>.env</code> 文件：

    ```env expandable theme={null}
    DODO_PAYMENTS_API_KEY=your-api-key
    DODO_PAYMENTS_WEBHOOK_KEY=your-webhook-secret
    DODO_PAYMENTS_ENVIRONMENT="test_mode" or "live_mode"
    DODO_PAYMENTS_RETURN_URL=your-return-url
    ```

    <Warning>
      切勿将 <code>.env</code> 文件或密钥提交到版本控制。
    </Warning>
  </Step>
</Steps>

## 路由处理器示例

<Tabs>
  <Tab title="Checkout Handler">
    <Info>
      使用此处理器将 Dodo Payments 结账集成到你的 Express 应用中。支持静态（GET）、动态（POST）和会话（POST）支付流程。
    </Info>

    <CodeGroup>
      ```typescript Express Route Handler expandable theme={null}
      import { checkoutHandler } from '@dodopayments/express';

      app.get('/api/checkout', checkoutHandler({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "static"
      }))

      app.post('/api/checkout', checkoutHandler({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "dynamic"
      }))

      app.post('/api/checkout', checkoutHandler({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
          type: "session"
      }))
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Static Checkout Curl example expandable theme={null}
      curl --request GET \
      --url 'https://example.com/api/checkout?productId=pdt_fqJhl7pxKWiLhwQR042rh' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Dynamic Checkout Curl example expandable theme={null}
      curl --request POST \
      --url https://example.com/api/checkout \
      --header 'Content-Type: application/json' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test \
      --data '{
      "billing": {
        "city": "Texas",
        "country": "US",
        "state": "Texas",
        "street": "56, hhh",
        "zipcode": "560000"
      },
      "customer": {
        "email": "test@example.com",
        	"name": "test"
      },
      "metadata": {},
      "payment_link": true,
        "product_id": "pdt_QMDuvLkbVzCRWRQjLNcs",
        "quantity": 1,
        "billing_currency": "USD",
        "discount_codes": ["IKHZ23M9GQ"],
        "return_url": "https://example.com",
        "trial_period_days": 10
      }'
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Checkout Session Curl example expandable theme={null}
      curl --request POST \
      --url https://example.com/api/checkout \
      --header 'Content-Type: application/json' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test \
      --data '{
      "product_cart": [
        {
          "product_id": "pdt_QMDuvLkbVzCRWRQjLNcs",
          "quantity": 1
        }
      ],
      "customer": {
        "email": "test@example.com",
        "name": "test"
      },
      "return_url": "https://example.com/success"
      }'
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Customer Portal Handler">
    <Info>
      使用此处理器允许客户通过 Dodo Payments 客户门户管理订阅和详细信息。
    </Info>

    <CodeGroup>
      ```typescript Express Route Handler expandable theme={null}
      import { CustomerPortal } from "@dodopayments/express";

      app.get('/api/customer-portal', CustomerPortal({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
      }))
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Customer Portal Curl example expandable theme={null}
      curl --request GET \
      --url 'https://example.com/api/customer-portal?customer_id=cus_9VuW4K7O3GHwasENg31m&send_email=true' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Webhook Handler">
    <Info>
      使用此处理器在 Express 应用中安全接收并处理 Dodo Payments webhook 事件。
    </Info>

    <CodeGroup>
      ```typescript Express Route Handler expandable theme={null}
      import { Webhooks } from "@dodopayments/express";

      app.post('/api/webhook',Webhooks({
          webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
          onPayload: async (payload) => {
              // handle the payload
          },
          // ... other event handlers for granular control
      }))
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## 结账路由处理器

<Info>
  Dodo Payments 支持三种类型的支付流程用于将支付集成到你的网站，本适配器支持所有类型的支付流程。
</Info>

* **静态支付链接：** 可即时分享的 URL，用于快速、无代码的支付收集。
* **动态支付链接：** 使用 API 或 SDK 程序化生成带有自定义详细信息的支付链接。
* **结账会话：** 创建安全、可自定义的结账体验，预配置产品购物车和客户详细信息。

<AccordionGroup>
  <Accordion title="Static Checkout (GET)">
    ### 支持的查询参数

    <ParamField query="productId" type="string" required>
      产品标识符（例如 <code>?productId=pdt\_nZuwz45WAs64n3l07zpQR</code>）。
    </ParamField>

    <ParamField query="quantity" type="integer">
      商品数量。
    </ParamField>

    <ParamField query="fullName" type="string">
      客户全名。
    </ParamField>

    <ParamField query="firstName" type="string">
      客户名字。
    </ParamField>

    <ParamField query="lastName" type="string">
      客户姓氏。
    </ParamField>

    <ParamField query="email" type="string">
      客户电子邮件地址。
    </ParamField>

    <ParamField query="country" type="string">
      客户所在国家。
    </ParamField>

    <ParamField query="addressLine" type="string">
      客户地址行。
    </ParamField>

    <ParamField query="city" type="string">
      客户所在城市。
    </ParamField>

    <ParamField query="state" type="string">
      客户所在州/省。
    </ParamField>

    <ParamField query="zipCode" type="string">
      客户邮政编码。
    </ParamField>

    <ParamField query="disableFullName" type="boolean">
      禁用全名字段。
    </ParamField>

    <ParamField query="disableFirstName" type="boolean">
      禁用名字字段。
    </ParamField>

    <ParamField query="disableLastName" type="boolean">
      禁用姓氏字段。
    </ParamField>

    <ParamField query="disableEmail" type="boolean">
      禁用电子邮件字段。
    </ParamField>

    <ParamField query="disableCountry" type="boolean">
      禁用国家字段。
    </ParamField>

    <ParamField query="disableAddressLine" type="boolean">
      禁用地址行字段。
    </ParamField>

    <ParamField query="disableCity" type="boolean">
      禁用城市字段。
    </ParamField>

    <ParamField query="disableState" type="boolean">
      禁用州/省字段。
    </ParamField>

    <ParamField query="disableZipCode" type="boolean">
      禁用邮政编码字段。
    </ParamField>

    <ParamField query="paymentCurrency" type="string">
      指定支付货币（例如 <code>USD</code>）。
    </ParamField>

    <ParamField query="showCurrencySelector" type="boolean">
      显示货币选择器。
    </ParamField>

    <ParamField query="paymentAmount" type="integer">
      指定支付金额（例如 <code>1000</code> 表示 10.00 美元）。
    </ParamField>

    <ParamField query="showDiscounts" type="boolean">
      显示折扣字段。
    </ParamField>

    <ParamField query="metadata_*" type="string">
      任何以 <code>metadata\_</code> 开头的查询参数将作为元数据传递。
    </ParamField>

    <Warning>
      如果 <code>productId</code> 缺失，处理器将返回 400 响应。无效的查询参数也会导致 400 响应。
    </Warning>

    ### 响应格式

    静态结账返回一个包含结账 URL 的 JSON 响应：

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/..."
    }
    ```
  </Accordion>

  <Accordion title="Dynamic Checkout (POST)">
    * 在 POST 请求中以 JSON 正文发送参数。
    * 支持一次性和定期支付。
    * 有关受支持 POST 请求正文字段的完整列表，请参考：
      * [一次性支付产品的请求正文](https://docs.dodopayments.com/api-reference/payments/post-payments)
      * [订阅产品的请求正文](https://docs.dodopayments.com/api-reference/subscriptions/post-subscriptions)

    ### 响应格式

    动态结账返回一个包含结账 URL 的 JSON 响应：

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/..."
    }
    ```
  </Accordion>

  <Accordion title="Checkout Sessions (POST)">
    Checkout 会话提供更安全的托管结账体验，处理一次性购买和订阅的完整支付流程，并提供完全的自定义控制。

    有关更多详细信息和支持字段的完整列表，请参阅 [结账会话集成指南](https://docs.dodopayments.com/developer-resources/checkout-session)。

    ### 响应格式

    结账会话返回一个包含结账 URL 的 JSON 响应：

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/session/..."
    }
    ```
  </Accordion>
</AccordionGroup>

## 客户门户路由处理器

客户门户路由处理器使您能够将 Dodo Payments 客户门户无缝集成到您的 Express 应用中。

### 查询参数

<ParamField query="customer_id" type="string" required>
  客户门户会话的客户 ID（例如 <code>?customer\_id=cus\_123</code>）。
</ParamField>

<ParamField query="send_email" type="boolean">
  如果设置为 <code>true</code>，则会向客户发送包含门户链接的电子邮件。
</ParamField>

<Warning>
  如果 <code>customer\_id</code> 缺失，则返回 400。
</Warning>

## Webhook 路由处理器

* **方法：** 仅支持 POST 请求。其他方法返回 405。
* **签名验证：** 使用 <code>webhookKey</code> 验证 Webhook 签名。如果验证失败，返回 401。
* **有效负载验证：** 使用 Zod 进行验证。无效的有效负载返回 400。
* **错误处理：**
  * 401：无效签名
  * 400：无效有效负载
  * 500：验证期间的内部错误
* **事件路由：** 根据有效负载类型调用适当的事件处理器。

### 支持的 Webhook 事件处理器

<CodeGroup>
  ```typescript Typescript expandable theme={null}
  onPayload?: (payload: WebhookPayload) => Promise<void>;
  onPaymentSucceeded?: (payload: WebhookPayload) => Promise<void>;
  onPaymentFailed?: (payload: WebhookPayload) => Promise<void>;
  onPaymentProcessing?: (payload: WebhookPayload) => Promise<void>;
  onPaymentCancelled?: (payload: WebhookPayload) => Promise<void>;
  onRefundSucceeded?: (payload: WebhookPayload) => Promise<void>;
  onRefundFailed?: (payload: WebhookPayload) => Promise<void>;
  onDisputeOpened?: (payload: WebhookPayload) => Promise<void>;
  onDisputeExpired?: (payload: WebhookPayload) => Promise<void>;
  onDisputeAccepted?: (payload: WebhookPayload) => Promise<void>;
  onDisputeCancelled?: (payload: WebhookPayload) => Promise<void>;
  onDisputeChallenged?: (payload: WebhookPayload) => Promise<void>;
  onDisputeWon?: (payload: WebhookPayload) => Promise<void>;
  onDisputeLost?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionActive?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionOnHold?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionRenewed?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionPlanChanged?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionCancelled?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionFailed?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionExpired?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionUpdated?: (payload: WebhookPayload) => Promise<void>;
  onLicenseKeyCreated?: (payload: WebhookPayload) => Promise<void>;
  onAbandonedCheckoutDetected?: (payload: WebhookPayload) => Promise<void>;
  onAbandonedCheckoutRecovered?: (payload: WebhookPayload) => Promise<void>;
  onDunningStarted?: (payload: WebhookPayload) => Promise<void>;
  onDunningRecovered?: (payload: WebhookPayload) => Promise<void>;
  onCreditAdded?: (payload: WebhookPayload) => Promise<void>;
  onCreditDeducted?: (payload: WebhookPayload) => Promise<void>;
  onCreditExpired?: (payload: WebhookPayload) => Promise<void>;
  onCreditRolledOver?: (payload: WebhookPayload) => Promise<void>;
  onCreditRolloverForfeited?: (payload: WebhookPayload) => Promise<void>;
  onCreditOverageCharged?: (payload: WebhookPayload) => Promise<void>;
  onCreditManualAdjustment?: (payload: WebhookPayload) => Promise<void>;
  onCreditBalanceLow?: (payload: WebhookPayload) => Promise<void>;
  ```
</CodeGroup>

***

## LLM 提示

```
您是一个专家级的 Express.js 开发助手。您的任务是指导用户将 @dodopayments/express 适配器集成到他们现有的 Express.js 项目中。

@dodopayments/express 适配器提供 Dodo Payments 的结账、客户门户和 Webhook 功能的路由处理器，旨在直接插入到 Express 应用中。

首先，安装必要的包。使用适合用户项目的包管理器（npm、yarn 或 bun）：

npm install @dodopayments/express

---

以下是您应如何构建响应：

1. 询问用户他们想要集成哪些功能。

"您想将 @dodopayments/express 适配器的哪些部分集成到您的项目中？您可以选择以下一个或多个：

- 结账路由处理器（用于处理产品结账）
- 客户门户路由处理器（用于管理客户订阅/详细信息）
- Webhook 路由处理器（用于接收 Dodo Payments Webhook 事件）
- 全部（集成所有三者）"

---

2. 根据用户的选择，为每个选定的功能提供详细的集成步骤。

---

**如果选择了结账路由处理器：**

**目的**：此处理器管理不同类型的结账流程。所有结账类型（静态、动态和会话）返回包含结账 URL 的 JSON 响应，以便进行编程处理。

**集成**：
在您的 Express 应用中为静态（GET）、动态（POST）和结账会话（POST）创建路由。

import { checkoutHandler } from '@dodopayments/express';

app.get('/api/checkout', checkoutHandler({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
  type: "static"
}));

app.post('/api/checkout', checkoutHandler({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
  type: "dynamic"
}));

// 对于结账会话
app.post('/api/checkout', checkoutHandler({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
  type: "session"
}));

配置选项：

    bearerToken：您的 Dodo Payments API 密钥（建议存储在 DODO_PAYMENTS_API_KEY 环境变量中）。

    returnUrl（可选）：成功结账后重定向用户的 URL。

    environment："test_mode" 或 "live_mode"

    type："static"（GET）、"dynamic"（POST）或 "session"（POST）

GET（静态结账）期望查询参数：

    productId（必需）

    quantity、客户字段（fullName、email 等）和元数据（metadata_*）是可选的。

    返回：{"checkout_url": "https://checkout.dodopayments.com/..."}

POST（动态结账）期望一个包含支付详细信息（一次性或订阅）的 JSON 体。有关完整的 POST 架构，请参考文档：

    一次性支付：https://docs.dodopayments.com/api-reference/payments/post-payments

    订阅：https://docs.dodopayments.com/api-reference/subscriptions/post-subscriptions

    返回：{"checkout_url": "https://checkout.dodopayments.com/..."}

POST（结账会话） - （推荐）更可自定义的结账体验。返回 JSON，包含 checkout_url：参数作为 JSON 体发送。支持一次性和定期支付。返回：{"checkout_url": "https://checkout.dodopayments.com/session/..."}。有关支持字段的完整列表，请参阅：

  结账会话集成指南：https://docs.dodopayments.com/developer-resources/checkout-session

如果选择了客户门户路由处理器：

目的：此路由允许客户通过 Dodo Payments 门户管理他们的订阅。

集成：

import { CustomerPortal } from "@dodopayments/express";

app.get('/api/customer-portal', CustomerPortal({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
}));

查询参数：

    customer_id（必需）：例如，?customer_id=cus_123

    send_email（可选）：如果为 true，客户将收到包含门户链接的电子邮件

如果缺少 customer_id，将返回 400。

如果选择了 Webhook 路由处理器：

目的：处理来自 Dodo Payments 的传入 Webhook 事件，以触发您应用中的事件。

集成：

import { Webhooks } from "@dodopayments/express";

app.post('/api/webhook', Webhooks({
  webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
  onPayload: async (payload) => {
    // 处理通用有效负载
  },
  // 您还可以为每种事件类型提供细粒度的处理器
}));

功能：

    仅允许 POST 方法 - 其他返回 405

    使用 webhookKey 进行签名验证。如果无效，返回 401。

    基于 Zod 的有效负载验证。如果无效架构，返回 400。

    所有处理器都是异步函数。

支持的 Webhook 事件处理器：

您可以传入以下任何处理器：

    onPayload

    onPaymentSucceeded

    onPaymentFailed

    onPaymentProcessing

    onPaymentCancelled

    onRefundSucceeded

    onRefundFailed

    onDisputeOpened, onDisputeExpired, onDisputeAccepted, onDisputeCancelled, onDisputeChallenged, onDisputeWon, onDisputeLost

    onSubscriptionActive, onSubscriptionOnHold, onSubscriptionRenewed, onSubscriptionPlanChanged, onSubscriptionCancelled, onSubscriptionFailed, onSubscriptionExpired, onSubscriptionUpdated

    onLicenseKeyCreated

    onAbandonedCheckoutDetected, onAbandonedCheckoutRecovered

    onDunningStarted, onDunningRecovered

    onCreditAdded, onCreditDeducted, onCreditExpired, onCreditRolledOver, onCreditRolloverForfeited, onCreditOverageCharged, onCreditManualAdjustment, onCreditBalanceLow

环境变量设置：

确保在你的项目中定义这些环境变量：

DODO_PAYMENTS_API_KEY=your-api-key
DODO_PAYMENTS_WEBHOOK_KEY=your-webhook-secret
DODO_PAYMENTS_ENVIRONMENT="test_mode" 或 "live_mode"
DODO_PAYMENTS_RETURN_URL=your-return-url

在代码中这样使用：

process.env.DODO_PAYMENTS_API_KEY
process.env.DODO_PAYMENTS_WEBHOOK_SECRET

安全注意：不要将密钥提交到版本控制。请在本地使用.env文件，并在部署环境中使用密钥管理器（例如，AWS、Vercel、Heroku等）。
```
