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

# TypeScript

> 使用 Dodo Payments TypeScript SDK 从 Node.js 和其他 JavaScript 运行时创建结账会话、客户、订阅和用量事件。

TypeScript SDK 为服务器端 TypeScript 和 JavaScript 代码提供对 Dodo Payments REST API 的类型化访问。它包含每个请求和响应的类型定义、类型化错误、自动重试、超时和自动分页。

## 安装

使用包管理器安装 `dodopayments` 包：

```bash npm theme={null}
npm install dodopayments
```

```bash yarn theme={null}
yarn add dodopayments
```

```bash pnpm theme={null}
pnpm add dodopayments
```

## 快速开始

创建客户端，然后创建结账会话：

```javascript theme={null}
import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: process.env['DODO_PAYMENTS_API_KEY'], // This is the default and can be omitted
  environment: 'test_mode', // defaults to 'live_mode'
});

const checkoutSessionResponse = await client.checkoutSessions.create({
  product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
});

console.log(checkoutSessionResponse.session_id);
```

如果省略 `bearerToken`，客户端会读取 `DODO_PAYMENTS_API_KEY` 环境变量。如果省略 `environment`，客户端会连接到 live mode。test mode API key 仅适用于 `environment: 'test_mode'`。

> [!WARNING]
>
> 将 API key 保存在环境变量或 secrets manager 中。切勿将其提交到版本控制系统，或在客户端代码中公开。

## 核心功能

> **TypeScript First**
>
> 编辑器中会显示每个请求参数和响应字段的类型定义。

> **Auto-Pagination**
>
> 使用 `for await...of` 进行迭代时，List 方法会为你获取下一页。

> **Error Handling**
>
> 每个 HTTP error status 都有对应的类型化错误类，其中包含 status、headers 和 response body。

> **Smart Retries**
>
> 对于 connection errors 和可重试的 status codes，默认重试两次，并使用 exponential backoff。

## 配置

### 环境变量

将 API key 存储在环境变量中：

```bash .env theme={null}
DODO_PAYMENTS_API_KEY=your_api_key_here
```

如果未传入相应选项，客户端会读取这些变量：

| 变量 | 客户端选项 | 用途 |
| - | - | - |
| `DODO_PAYMENTS_API_KEY` | `bearerToken` | 你的 API key。如果两者都未设置，客户端会抛出错误。 |
| `DODO_PAYMENTS_WEBHOOK_KEY` | `webhookKey` | 你的 webhook signing secret，用于验证 webhooks。 |
| `DODO_PAYMENTS_BASE_URL` | `baseURL` | 自定义 API URL，用于替换 `environment` URL。 |
| `DODO_PAYMENTS_LOG` | `logLevel` | 日志级别。请参阅 [日志记录](#logging)。 |

如果设置了 base URL，同时又传入 `environment`，构造函数会抛出 "Ambiguous URL" 错误。在这种情况下要使用 `environment`，请传入 `baseURL: null`。

要验证 webhook，请将原始请求 body 和 headers 传入 `client.webhooks.unwrap(rawBody, { headers })`。它会使用你的 webhook key 检查签名，并返回解析后的 event。`client.webhooks.unsafeUnwrap(rawBody)` 会在不验证的情况下解析 body，因此只能用于测试。请参阅 [Webhooks](https://docs.dodopayments.com/developer-resources/webhooks.md)。

### 超时配置

请求默认在 1 分钟后超时。在客户端或单个请求上以毫秒为单位设置 `timeout`：

```typescript theme={null}
// Configure default timeout for all requests (default is 1 minute)
const client = new DodoPayments({
  timeout: 20 * 1000, // 20 seconds
});

// Override per-request
await client.checkoutSessions.create(
  { product_cart: [{ product_id: 'pdt_123', quantity: 1 }] },
  { timeout: 5 * 1000 },
);
```

请求超时时，SDK 会抛出 `APIConnectionTimeoutError`。超时的请求会重试，因此调用失败前可能需要比 `timeout` 更长的时间。

### 重试配置

在客户端或单个请求上设置 `maxRetries`：

```javascript theme={null}
// Configure default for all requests (default is 2 retries)
const client = new DodoPayments({
  maxRetries: 0, // disable retries
});

// Override per-request
await client.checkoutSessions.create(
  { product_cart: [{ product_id: 'pdt_123', quantity: 1 }] },
  { maxRetries: 5 },
);
```

> [!TIP]
>
> SDK 会重试 connection errors，以及 status 为 408、409、429 或 500 及以上的响应。默认重试两次，并使用 exponential backoff。

请求仍然失败时，SDK 会抛出 `DodoPayments.APIError` 的子类。每个错误都有 `status`、`headers` 和 `error` 属性（后者为 response body）。使用 `instanceof` 检查特定类，例如 `err instanceof DodoPayments.RateLimitError`：

| Status | 错误类 |
| - | - |
| 400 | `BadRequestError` |
| 401 | `AuthenticationError` |
| 403 | `PermissionDeniedError` |
| 404 | `NotFoundError` |
| 409 | `ConflictError` |
| 422 | `UnprocessableEntityError` |
| 429 | `RateLimitError` |
| 500 及以上 | `InternalServerError` |
| 无响应 | `APIConnectionError` |

## 常见操作

本节中的示例使用 [快速开始](#quick-start) 中的 `client`。

### 创建结账会话

创建结账会话，然后将客户重定向到返回的 `checkout_url`：

```typescript theme={null}
const session = await client.checkoutSessions.create({
  product_cart: [
    {
      product_id: 'pdt_123',
      quantity: 1
    }
  ],
  return_url: 'https://yourdomain.com/return'
});

console.log('Redirect to:', session.checkout_url);
```

每个 `checkout_url` 只能使用一次，并会在 24 小时后过期。有关每个会话选项，请参阅 [Checkout Sessions](https://docs.dodopayments.com/developer-resources/checkout-session.md)。

### 管理客户

使用 email address 和 name 创建客户，然后通过 ID 获取该客户：

```typescript theme={null}
// Create a customer
const customer = await client.customers.create({
  email: 'customer@example.com',
  name: 'John Doe',
  metadata: {
    user_id: '12345'
  }
});

// Retrieve customer
const retrieved = await client.customers.retrieve('cus_123');
console.log(`Customer: ${retrieved.name} (${retrieved.email})`);
```

### 处理订阅

创建订阅、为按需订阅收费，并读取订阅的用量历史记录。

> [!WARNING]
>
> `POST /subscriptions`（SDK 的 `subscriptions.create` method）已弃用。它仍可用于现有集成，但新集成应通过 [Checkout Session](https://docs.dodopayments.com/developer-resources/checkout-session.md) 创建订阅。

```typescript expandable theme={null}
// Create a subscription
const subscription = await client.subscriptions.create({
  billing: {
    country: 'US',
    city: 'San Francisco',
    state: 'CA',
    street: '1 Market St',
    zipcode: '94105',
  },
  customer: {
    customer_id: 'cus_123', // or pass { email, name } to create a new customer
  },
  product_id: 'pdt_456',
  quantity: 1,
});

// Charge an on-demand subscription
// product_price is in the lowest currency denomination (e.g., 2500 = $25.00 USD)
const chargeResponse = await client.subscriptions.charge(subscription.subscription_id, {
  product_price: 2500,
});

// Retrieve subscription usage history (for metered subscriptions)
const usageHistory = await client.subscriptions.retrieveUsageHistory(subscription.subscription_id, {
  start_date: '2024-01-01T00:00:00Z',
  end_date: '2024-03-31T23:59:59Z',
});
```

> [!NOTE]
>
> `billing` 只需要 `country`，即两位 ISO 国家代码。`customer` 接受 `{ customer_id }` 以关联现有客户，或接受 `{ email, name? }` 以创建客户。`charge` 用于[按需订阅](https://docs.dodopayments.com/developer-resources/ondemand-subscriptions.md)，`product_price` 使用最小货币单位。`retrieveUsageHistory` 返回分页列表，你可以按照[自动分页](#auto-pagination)中的示例进行迭代。

## 基于用量的计费

### 摄取用量事件

为客户发送用量事件：

```typescript theme={null}
await client.usageEvents.ingest({
  events: [
    {
      event_id: 'api_call_12345',
      customer_id: 'cus_abc123',
      event_name: 'api_request',
      timestamp: new Date().toISOString(),
      metadata: {
        endpoint: '/api/v1/users',
        method: 'GET',
        tokens_used: '150'
      }
    }
  ]
});
```

> [!NOTE]
>
> `event_id` 是幂等键，因此应为每个事件提供唯一值。如果同一个 `event_id` 在单个请求中出现两次，整个请求都会被拒绝。如果某个 `event_id` 已经被摄取，则会忽略新事件。单个请求最多接受 1,000 个事件。`timestamp` 默认为当前时间；如果其早于当前时间超过 1 小时，或晚于当前时间超过 5 分钟，则会被拒绝。

### 获取用量事件

通过 `event_id` 获取单个事件，或按 customer、event name 和 time range 筛选事件列表：

```typescript theme={null}
// Get a specific event
const event = await client.usageEvents.retrieve('api_call_12345');

// List events with filtering
const events = await client.usageEvents.list({
  customer_id: 'cus_abc123',
  event_name: 'api_request',
  start: '2024-01-14T10:30:00Z',
  end: '2024-01-15T10:30:00Z'
});
```

`usageEvents.list` 还接受 `meter_id`，并返回分页列表。

## Proxy 配置

要通过 proxy 发送请求，请将运行时的 proxy settings 传入 `fetchOptions`。

### Node.js（使用 Undici）

传入 undici `ProxyAgent` 作为 `dispatcher`：

```typescript theme={null}
import DodoPayments from 'dodopayments';
import * as undici from 'undici';

const proxyAgent = new undici.ProxyAgent('http://localhost:8888');
const client = new DodoPayments({
  fetchOptions: {
    dispatcher: proxyAgent,
  },
});
```

### Bun

设置 `proxy` 选项：

```typescript theme={null}
import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  fetchOptions: {
    proxy: 'http://localhost:8888',
  },
});
```

### Deno

使用 `Deno.createHttpClient` 创建 HTTP client，并将其作为 `client` 传入：

```typescript theme={null}
import DodoPayments from 'npm:dodopayments';

const httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });
const client = new DodoPayments({
  fetchOptions: {
    client: httpClient,
  },
});
```

## 日志记录

使用 `logLevel` client option 或 `DODO_PAYMENTS_LOG` environment variable 设置 log level。client option 会覆盖 environment variable。

> [!WARNING]
>
> 在 `debug` level 下，SDK 会记录每个 HTTP request 和 response，包括 headers 和 bodies。某些 authentication headers 会被隐藏，但 bodies 中的敏感数据仍可能可见。

```typescript theme={null}
// Via client option
const client = new DodoPayments({
  logLevel: 'debug', // Show all log messages
});
```

```bash theme={null}
# Via environment variable
export DODO_PAYMENTS_LOG=debug
```

日志级别按详细程度从高到低依次为：

* `'debug'`：Debug messages、info、warnings 和 errors。
* `'info'`：Info messages、warnings 和 errors。
* `'warn'`：Warnings 和 errors。这是默认级别。
* `'error'`：仅 errors。
* `'off'`：不记录日志。

SDK 默认将日志记录到 `console`。要使用 `pino`、`winston` 或其他 logging library，请将 logger 作为 `logger` option 传入；`logLevel` 仍控制哪些 messages 会传递给它。日志 messages 仅用于调试，其格式可能在不同版本之间发生变化。

## 从 Node.js SDK 迁移

如果你使用旧版 Node.js SDK，请按照迁移指南进行升级。当前 SDK 使用内置的 `fetch` API，而不是 `node-fetch`；它需要 Node.js 20、TypeScript 4.9 和 Jest 28 或更高版本，并包含一个可更新大部分代码的迁移工具。

> **[View Migration Guide](https://github.com/dodopayments/dodopayments-typescript/blob/main/MIGRATION.md)**
>
> 了解如何从 Node.js SDK 迁移到 TypeScript SDK

## 自动分页

List methods 返回分页结果。使用 `for await...of` 进行迭代，以获取每一页中的项目。SDK 会在需要时请求下一页：

```typescript theme={null}
async function fetchAllPayments() {
  const allPayments = [];
  // Automatically fetches more pages as needed.
  for await (const paymentListResponse of client.payments.list()) {
    allPayments.push(paymentListResponse);
  }
  return allPayments;
}
```

如需一次处理一页，请读取 `page.items`，并调用 `hasNextPage()` 和 `getNextPage()`：

```typescript theme={null}
let page = await client.payments.list();
for (const paymentListResponse of page.items) {
  console.log(paymentListResponse);
}

// Convenience methods are provided for manually paginating:
while (page.hasNextPage()) {
  page = await page.getNextPage();
  // Process page.items here
}
```

要设置页面大小，请将 `page_size` 传入 list method，例如 `client.payments.list({ page_size: 50 })`。

## 要求

SDK 支持 TypeScript 4.9 或更高版本，以及以下运行时：

* Web browsers（最新版本的 Chrome、Firefox、Safari、Edge 及其他浏览器）
* Node.js 20 LTS 或更高版本（[non-EOL](https://endoflife.date/nodejs)）
* Deno 1.28.0 或更高版本
* Bun 1.0 或更高版本
* Cloudflare Workers
* Vercel Edge Runtime
* Jest 28 或更高版本，并使用 `"node"` environment（不支持 `"jsdom"` environment）
* Nitro 2.6 或更高版本

不支持 React Native。

## 资源

> **[GitHub Repository](https://github.com/dodopayments/dodopayments-typescript)**
>
> 源代码、发行版本和完整的方法列表。

> **[API Reference](https://docs.dodopayments.com/api-reference/introduction.md)**
>
> 每个 endpoint、parameter 和 response。

> **[Discord Community](https://discord.gg/bYqAp4ayYh)**
>
> 提出问题并与其他开发者交流。

> **[Report Issues](https://github.com/dodopayments/dodopayments-typescript/issues)**
>
> 报告 bug 或请求功能。

## 支持

如需 TypeScript SDK 方面的帮助：

* **Discord**：加入[社区服务器](https://discord.gg/bYqAp4ayYh)获取实时帮助。
* **Email**：联系 [support@dodopayments.com](mailto:support@dodopayments.com)。
* **GitHub**：在[代码仓库](https://github.com/dodopayments/dodopayments-typescript)中提交 issue。

## 贡献

如需参与贡献，请阅读[贡献指南](https://github.com/dodopayments/dodopayments-typescript/blob/main/CONTRIBUTING.md)。


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