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

# Rust

> 使用 Dodo Payments Rust SDK——一个基于 Tokio 和 reqwest 构建的 async client——来创建 checkout sessions、customers、subscriptions 和 usage events。

Rust SDK 为 async Rust 应用提供了对 Dodo Payments REST API 的类型化访问。它基于 Tokio 和 reqwest 构建，使用类型化的 request 和 response structs，支持流式处理分页结果，并会重试失败的 requests。

## 安装

使用 Cargo 将 SDK 添加到您的项目中：

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

或者手动将其添加到您的 `Cargo.toml` 中：

```toml theme={null}
[dependencies]
dodopayments = "1.119.0"
tokio = { version = "1", features = ["full"] }
serde_json = "1"
futures = "0.3" # required for streaming paginated results
```

<Info>
  SDK 要求 Rust 1.75 或更高版本。
</Info>

## 快速开始

`Client::from_env()` 会从 `DODO_PAYMENTS_API_KEY` 环境变量中读取 API key。创建 client，然后创建 checkout session：

```rust theme={null}
use dodopayments::Client;

#[tokio::main]
async fn main() -> dodopayments::Result<()> {
    let client = Client::from_env()?;

    let result = client
        .checkout_sessions()
        .create()
        .body(dodopayments::models::CheckoutSessionsCreateParams {
            product_cart: Some(vec![dodopayments::models::ProductItemReq {
                product_id: "pdt_123".to_string(),
                quantity: 1,
                addons: None,
                amount: None,
                credit_entitlements: None,
            }]),
            ..Default::default()
        })
        .await?;

    println!("{result:?}");
    Ok(())
}
```

如果未设置 `DODO_PAYMENTS_API_KEY`，`Client::from_env()` 会返回 `Error::Config`。除非选择其他 environment，否则 client 会连接到 live mode，如[Environments](#environments)所示。test mode API key 只能在 test mode 中使用。

<Warning>
  请将 API keys 保存在环境变量或 secrets manager 中。切勿将其硬编码到源代码中。
</Warning>

## 核心功能

<CardGroup cols={2}>
  <Card title="Async First" icon="bolt">
    基于 Tokio 和 reqwest 构建，为每个 request 提供 `async`/`await`。
  </Card>

  <Card title="Strong Typing" icon="shield-check">
    类型化的 request 和 response structs，可在 compile time 进行检查。
  </Card>

  <Card title="Auto-Pagination" icon="layer-group">
    流式处理所有页面中的每个 item，或一次移动一页。
  </Card>

  <Card title="Configurable" icon="sliders">
    为每个 client 设置 environment、base URL、timeout 和 retry count。
  </Card>
</CardGroup>

## 配置

### 环境变量

`Client::from_env()` 会从 `DODO_PAYMENTS_API_KEY` 中读取 API key。除非设置 `DODO_PAYMENTS_BASE_URL`，否则它会使用 live mode URL：

```bash theme={null}
export DODO_PAYMENTS_API_KEY="your_api_key"
export DODO_PAYMENTS_BASE_URL="https://test.dodopayments.com" # optional
```

Rust SDK 不会读取 `DODO_PAYMENTS_WEBHOOK_KEY`，也没有用于验证 webhook signatures 的 method。要验证它们，请参阅 [Webhooks](/developer-resources/webhooks)。

你也可以显式配置 client。`Client::new` 会返回一个 `Result`，因此请在返回 `dodopayments::Result` 的 function 中使用 `?` 对其进行 unwrap：

```rust theme={null}
use dodopayments::{Client, ClientConfig};

#[tokio::main]
async fn main() -> dodopayments::Result<()> {
    let client = Client::new(
        ClientConfig::new("https://live.dodopayments.com").with_api_key("My API Key"),
    )?;
    println!("{}", client.base_url());
    Ok(())
}
```

### Environments

SDK 有两种 environment：

| Name | Base URL |
| - | - |
| `live_mode` | `https://live.dodopayments.com` |
| `test_mode` | `https://test.dodopayments.com` |

默认 base URL 为 `https://live.dodopayments.com`。要选择其他 environment，请使用 `Environment` enum，而不是硬编码 URL：

```rust theme={null}
use dodopayments::{Client, ClientConfig, Environment};

let client = Client::new(
    ClientConfig::from_environment(Environment::TestMode).with_api_key("My API Key"),
)?;
```

如果要继续从 `DODO_PAYMENTS_API_KEY` 中读取 API key，并通过 `from_env()` 指定另一个 environment，请在 config 上覆盖 environment：

```rust theme={null}
use dodopayments::{Client, ClientConfig, Environment};

let client = Client::new(ClientConfig::from_env()?.with_environment(Environment::TestMode))?;
```

### 超时

默认 request timeout 为 30 秒。使用 `with_timeout` 为 client 覆盖该设置：

```rust theme={null}
use std::time::Duration;
use dodopayments::{Client, ClientConfig};

let client = Client::new(
    ClientConfig::new("https://live.dodopayments.com")
        .with_api_key("My API Key")
        .with_timeout(Duration::from_secs(60)),
)?;
```

client 会重试 connection errors，以及 status 为 408、409、429 或 500 及以上的 responses。默认重试两次，并使用 exponential backoff；当 API 发送 `Retry-After` header 时，client 会等待该 header 指定的时间。要更改 retry count，请在 `ClientConfig` 上调用 `with_max_retries`，例如使用 `.with_max_retries(0)` 来关闭重试。

## 常见操作

本节中的示例使用 [Quick Start](#quick-start) 中的 `client`。

### 创建 Checkout Session

使用 return URL 创建 checkout session：

```rust theme={null}
let session = client
    .checkout_sessions()
    .create()
    .body(dodopayments::models::CheckoutSessionsCreateParams {
        product_cart: Some(vec![dodopayments::models::ProductItemReq {
            product_id: "pdt_123".to_string(),
            quantity: 1,
            addons: None,
            amount: None,
            credit_entitlements: None,
        }]),
        return_url: Some("https://yourdomain.com/return".to_string()),
        ..Default::default()
    })
    .await?;

println!("{session:?}");
```

将 customer 重定向到 `session.checkout_url`。每个 checkout URL 只能使用一次，并会在 24 小时后过期。有关每个 session option 的信息，请参阅 [Checkout Sessions](/developer-resources/checkout-session)。

### 管理 Customers

使用 email address 和 name 创建 customer，然后通过 ID 获取它：

```rust theme={null}
// Create a customer
let customer = client
    .customers()
    .create()
    .body(dodopayments::models::CustomersCreateParams {
        email: Some("customer@example.com".to_string()),
        name: Some("John Doe".to_string()),
        ..Default::default()
    })
    .await?;

// Retrieve a customer
let customer = client
    .customers()
    .retrieve()
    .customer_id("cus_123")
    .await?;

println!("{customer:?}");
```

### 处理 Subscriptions

为现有 customer 创建 subscription。

<Warning>
  `POST /subscriptions`（SDK 的 `subscriptions().create()` method）已被 **弃用**。它仍适用于现有 integrations，但新的 integrations 应通过 [Checkout Session](/developer-resources/checkout-session) 创建 subscriptions。
</Warning>

```rust theme={null}
let subscription = client
    .subscriptions()
    .create()
    .body(dodopayments::models::SubscriptionsCreateParams {
        billing: Some(Box::new(dodopayments::models::BillingAddress {
            country: Box::new(dodopayments::models::CountryCode::Us),
            city: Some("San Francisco".to_string()),
            state: Some("CA".to_string()),
            street: Some("1 Market St".to_string()),
            zipcode: Some("94105".to_string()),
        })),
        customer: Some(Box::new(
            dodopayments::models::CustomerRequest::AttachExistingCustomer(Box::new(
                dodopayments::models::AttachExistingCustomer {
                    customer_id: "cus_123".to_string(),
                },
            )),
        )),
        product_id: Some("pdt_456".to_string()),
        quantity: Some(1),
        ..Default::default()
    })
    .await?;

println!("{subscription:?}");
```

<Info>
  `billing` 只需要 `country`，即 `CountryCode::Us` 等 `CountryCode` enum variant。`customer` 是一个 `CustomerRequest` enum：对于现有 customer，传入 `AttachExistingCustomer`；或者传入 `NewCustomer` 来创建 customer。要为 [on-demand subscription](/developer-resources/ondemand-subscriptions) 收费，请使用 `client.subscriptions().charge().subscription_id(...)`，并传入 `SubscriptionsChargeParams` body。金额字段（例如 `product_price`）使用货币的最小单位表示（例如，`2500` 表示 \$25.00）。
</Info>

## 基于用量的计费

### 写入 Usage Events

为 customer 发送 usage events：

```rust theme={null}
let response = client
    .usage_events()
    .ingest()
    .body(dodopayments::models::UsageEventsIngestParams {
        events: Some(vec![dodopayments::models::EventInput {
            customer_id: "cus_abc123".to_string(),
            event_id: "api_call_12345".to_string(),
            event_name: "api_request".to_string(),
            metadata: None,
            timestamp: None,
        }]),
        ..Default::default()
    })
    .await?;

println!("{response:?}");
```

`event_id` 是 idempotency key，因此请为每个 event 提供唯一值。如果 `timestamp` 为 `None`，则 event 会使用当前时间。

### 列出 Usage Events

列出按 customer 和 event name 过滤的 events。过滤条件位于 JSON query object 中：

```rust theme={null}
let events = client
    .usage_events()
    .list()
    .query(serde_json::json!({
        "customer_id": "cus_abc123",
        "event_name": "api_request",
    }))
    .await?;

for event in &events.items {
    println!("{event:?}");
}
```

## 分页

List endpoints 会返回一个类型化 page，其 `items` field 保存当前页面的 results。要流式处理所有页面中的每个 item，请调用 `into_stream`：

```rust theme={null}
use futures::StreamExt;

let mut items = Box::pin(
    client
        .payments()
        .list()
        .query(serde_json::json!({}))
        .await?
        .into_stream(),
);

while let Some(item) = items.next().await {
    let item = item?;
    println!("{item:?}");
}
```

要一次移动一页，请调用 `get_next_page`。最后一页之后，它会返回 `None`：

```rust theme={null}
let mut page = client
    .payments()
    .list()
    .query(serde_json::json!({}))
    .await?;

loop {
    for item in &page.items {
        println!("{item:?}");
    }
    match page.get_next_page().await? {
        Some(next) => page = next,
        None => break,
    }
}
```

## 错误处理

每个 method 都会返回一个 `dodopayments::Result<T>`。失败结果是 `dodopayments::Error` enum 的 variants：`Api` 表示 API 返回的 error status，`Http` 表示 transport errors，`Json` 表示 serialization errors，`Config` 表示 configuration errors，`MissingPathParam` 或 `MissingBody` 表示 requests 不完整。对其进行 match，以便分别处理 API errors 和 transport errors：

```rust theme={null}
let result = client
    .checkout_sessions()
    .create()
    .body(dodopayments::models::CheckoutSessionsCreateParams {
        product_cart: Some(vec![dodopayments::models::ProductItemReq {
            product_id: "pdt_123".to_string(),
            quantity: 1,
            addons: None,
            amount: None,
            credit_entitlements: None,
        }]),
        ..Default::default()
    })
    .await;

match result {
    Ok(value) => println!("{value:?}"),
    Err(dodopayments::Error::Api { status, message }) => {
        eprintln!("API returned {status}: {message}");
    }
    Err(err) => eprintln!("request failed: {err}"),
}
```

## 未文档化的 Endpoints

要调用没有类型化 method 的 endpoint，请使用底层的 `request` builder。它会应用 authentication 和 base URL。要命名 `reqwest::Method`，请将 `reqwest` 0.12 添加到 dependencies 中：

```rust theme={null}
let response = client
    .request(reqwest::Method::GET, "/some/path")
    .send()
    .await?;
```

## 资源

<CardGroup cols={2}>
  <Card title="GitHub Repository" icon="github" href="https://github.com/dodopayments/dodopayments-rust">
    源代码、releases 和完整的 method 列表。
  </Card>

  <Card title="Crates.io" icon="cube" href="https://crates.io/crates/dodopayments">
    已发布的 crate 及其 versions。
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/introduction">
    每个 endpoint、parameter 和 response。
  </Card>

  <Card title="Discord Community" icon="discord" href="https://discord.gg/bYqAp4ayYh">
    提出问题，并与其他 developers 交流。
  </Card>
</CardGroup>

## 支持

如需 Rust SDK 方面的帮助：

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

## 参与贡献

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


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