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

# Ruby

> 使用 Dodo Payments Ruby SDK，从 Ruby 3.2+ 创建 checkout sessions、customers 和 subscriptions，并支持自动分页、重试和 Sorbet 类型。

Ruby SDK 让 Ruby 应用可以访问 Dodo Payments REST API。它使用标准库的 `net/http` 和连接池发送请求，重试失败的请求，为你遍历分页列表，并提供 RBI 和 RBS 类型定义。

## 安装

将 gem 添加到您的 Gemfile：

```ruby Gemfile theme={null}
gem "dodopayments", "~> 2.30"
```

> [!TIP]
>
> SDK 版本会增加对 API 变更的支持。定期运行 `bundle update dodopayments` 以保持最新。

然后安装它：

```bash theme={null}
bundle install
```

> [!NOTE]
>
> SDK 要求 Ruby 3.2.0 或更高版本。

## 快速开始

创建一个 client，然后创建一个 checkout session：

```ruby theme={null}
require "bundler/setup"
require "dodopayments"

dodo_payments = Dodopayments::Client.new(
  bearer_token: ENV["DODO_PAYMENTS_API_KEY"], # This is the default and can be omitted
  environment: "test_mode" # defaults to "live_mode"
)

checkout_session_response = dodo_payments.checkout_sessions.create(
  product_cart: [{product_id: "pdt_123", quantity: 1}]
)

puts(checkout_session_response.session_id)
```

如果省略 `bearer_token`，client 会读取 `DODO_PAYMENTS_API_KEY` 环境变量。如果省略 `environment`，client 会连接到 live mode。test mode API key 只能与 `environment: "test_mode"` 配合使用。

> [!WARNING]
>
> 请将 API keys 保存在环境变量或 secrets manager 中。切勿将它们提交到版本控制系统，或在代码中公开它们。

## 核心功能

> **Ruby Conventions**
>
> 使用 snake\_case 方法和关键字参数，嵌套参数也接受普通 hash。

> **Elegant Syntax**
>
> Responses 是带有 attribute readers 的对象，`obj[:prop]` 还可以读取 SDK 未定义的字段。

> **Auto-Pagination**
>
> `auto_paging_each` 会遍历每个 item，并在需要时获取下一页。

> **Type Safety**
>
> 用于 Sorbet 的 RBI 定义，不依赖 `sorbet-runtime`。

## 配置

`Dodopayments::Client.new` 接受 `bearer_token`、`webhook_key`、`environment`、`base_url`、`max_retries`、`timeout`、`initial_retry_delay` 和 `max_retry_delay`。省略这些参数时，它会从环境中读取 `DODO_PAYMENTS_API_KEY`、`DODO_PAYMENTS_WEBHOOK_KEY`（你的 webhook signing secret）和 `DODO_PAYMENTS_BASE_URL`。client 是 thread-safe 的，并维护自己的连接池，因此请为应用创建一个 client 并重复使用它。

要验证 webhook，请将原始请求 body 和 headers 传递给 `dodo_payments.webhooks.unwrap(payload, headers: headers)`。它会使用你的 webhook key 检查 signature，并返回解析后的 event。`dodo_payments.webhooks.unsafe_unwrap(payload)` 会在不验证的情况下解析 body，因此只能用于测试。请参阅 [Webhooks](/developer-resources/webhooks)。

### Timeout 配置

Requests 默认在 60 秒后超时。在 client 或单个 request 上以秒为单位设置 `timeout`：

```ruby theme={null}
# Configure default for all requests (default is 60 seconds)
dodo_payments = Dodopayments::Client.new(
  timeout: nil # disable timeout
)

# Or, configure per-request
dodo_payments.checkout_sessions.create(
  product_cart: [{product_id: "pdt_123", quantity: 1}],
  request_options: {timeout: 5}
)
```

当 request 超时时，SDK 会抛出 `Dodopayments::Errors::APITimeoutError`。超时的 requests 默认会重试。

### Retry 配置

SDK 会重试 connection errors、timeouts，以及 status 为 408、409、429 或 500 及以上的 responses。默认重试两次，并采用短暂的 exponential backoff。在 client 或单个 request 上设置 `max_retries`：

```ruby theme={null}
# Configure default for all requests (default is 2)
dodo_payments = Dodopayments::Client.new(
  max_retries: 0 # disable retries
)

# Or, configure per-request
dodo_payments.checkout_sessions.create(
  product_cart: [{product_id: "pdt_123", quantity: 1}],
  request_options: {max_retries: 5}
)
```

## 常见操作

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

### 创建 Checkout Session

创建一个 checkout session，然后将 customer 重定向到返回的 `checkout_url`：

```ruby theme={null}
session = dodo_payments.checkout_sessions.create(
  product_cart: [
    {
      product_id: "pdt_123",
      quantity: 1
    }
  ],
  return_url: "https://yourdomain.com/return"
)

# In a Rails controller, redirect to checkout
redirect_to session.checkout_url, allow_other_host: true
```

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

### 管理 Customers

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

```ruby theme={null}
# Create a customer
customer = dodo_payments.customers.create(
  email: "customer@example.com",
  name: "John Doe",
  metadata: {
    user_id: "12345"
  }
)

# Retrieve customer
customer = dodo_payments.customers.retrieve("cus_123")
puts "Customer: #{customer.name} (#{customer.email})"
```

### 处理 Subscriptions

创建 subscription、为 on-demand subscription 收费，以及更新 subscription 的 metadata。

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

```ruby theme={null}
# Create a subscription
subscription = dodo_payments.subscriptions.create(
  billing: {
    country: "US",
    city: "San Francisco",
    state: "CA",
    street: "1 Market St",
    zipcode: "94105"
  },
  customer: { customer_id: "cus_123" }, # or { email: "...", name: "..." } for 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)
charge = dodo_payments.subscriptions.charge(
  subscription.subscription_id,
  product_price: 2500
)

# Update subscription metadata
updated = dodo_payments.subscriptions.update(
  subscription.subscription_id,
  metadata: { plan_type: "premium" }
)
```

> [!NOTE]
>
> `billing` 只需要 `country`，即两位字母的 ISO country code。`customer` 接受 `{ customer_id: "..." }` 以关联现有 customer，或接受 `{ email: "...", name: "..." }` 以创建 customer。`charge` 用于 [on-demand subscriptions](/developer-resources/ondemand-subscriptions)，而 `product_price` 使用最小货币单位表示。

## 分页

### 自动分页

List methods 会返回一页数据。读取 `items` 获取当前页，或调用 `auto_paging_each` 遍历每个 item。它会在需要时获取下一页：

```ruby theme={null}
page = dodo_payments.payments.list

# Fetch single item from page
payment = page.items[0]
puts(payment.brand_id)

# Automatically fetches more pages as needed
page.auto_paging_each do |payment|
  puts(payment.brand_id)
end
```

### 手动分页

如需逐页移动，请调用 `next_page?` 和 `next_page`：

```ruby theme={null}
page = dodo_payments.payments.list

if page.next_page?
  new_page = page.next_page
  puts(new_page.items[0].brand_id)
end
```

## 错误处理

当 SDK 无法连接到 API，或 API 返回 4xx 或 5xx status 时，SDK 会抛出 `Dodopayments::Errors::APIError` 的子类：

```ruby theme={null}
begin
  checkout_session = dodo_payments.checkout_sessions.create(
    product_cart: [{product_id: "pdt_123", quantity: 1}]
  )
rescue Dodopayments::Errors::APIConnectionError => e
  puts("The server could not be reached")
  puts(e.cause)  # an underlying Exception, likely raised within `net/http`
rescue Dodopayments::Errors::RateLimitError => e
  puts("A 429 status code was received; we should back off a bit.")
rescue Dodopayments::Errors::APIStatusError => e
  puts("Another non-200-range status code was received")
  puts(e.status)
end
```

错误类取决于具体原因。每个 error 都有 `status`、`headers` 和 `body` attributes：

| 原因 | Error class |
| - | - |
| HTTP 400 | `BadRequestError` |
| HTTP 401 | `AuthenticationError` |
| HTTP 403 | `PermissionDeniedError` |
| HTTP 404 | `NotFoundError` |
| HTTP 409 | `ConflictError` |
| HTTP 422 | `UnprocessableEntityError` |
| HTTP 429 | `RateLimitError` |
| HTTP 500 及以上 | `InternalServerError` |
| 其他 HTTP error | `APIStatusError` |
| Timeout | `APITimeoutError` |
| Network error | `APIConnectionError` |

> [!TIP]
>
> SDK 已经使用 exponential backoff 重试 429 responses。`RateLimitError` 表示这些重试也失败了，因此请等待更长时间后再重新发送 request。

## 使用 Sorbet 确保类型安全

SDK 提供 RBI 定义，并不依赖 `sorbet-runtime`。要对 request parameters 进行类型检查，请传递 model classes，而不是 hashes：

```ruby theme={null}
# Type-safe using Sorbet RBI definitions
dodo_payments.checkout_sessions.create(
  product_cart: [
    Dodopayments::ProductItemReq.new(
      product_id: "pdt_123",
      quantity: 1
    )
  ]
)

# Hashes work, but are not typesafe
dodo_payments.checkout_sessions.create(
  product_cart: [{product_id: "pdt_123", quantity: 1}]
)

# You can also splat a full Params class
params = Dodopayments::CheckoutSessionCreateParams.new(
  product_cart: [
    Dodopayments::ProductItemReq.new(
      product_id: "pdt_123",
      quantity: 1
    )
  ]
)
dodo_payments.checkout_sessions.create(**params)
```

## 高级用法

### 未文档化的 Endpoints

要调用没有 SDK method 的 endpoint，请使用 `request`。它会应用与 SDK methods 相同的 authentication 和 retries：

```ruby theme={null}
response = dodo_payments.request(
  method: :post,
  path: '/undocumented/endpoint',
  query: {"dog": "woof"},
  headers: {"useful-header": "interesting-value"},
  body: {"hello": "world"}
)
```

### 未文档化的 Parameters

要发送 SDK 未定义的 parameters，请将它们传入 `request_options`。名称与已文档化 parameter 相同的 `extra_*` parameter 会覆盖该 parameter：

```ruby theme={null}
checkout_session_response = dodo_payments.checkout_sessions.create(
  product_cart: [{product_id: "pdt_123", quantity: 1}],
  request_options: {
    extra_query: {my_query_parameter: "value"},
    extra_body: {my_body_parameter: "value"},
    extra_headers: {"my-header": "value"}
  }
)

# Access undocumented response properties
puts(checkout_session_response[:my_undocumented_property])
```

## Rails 集成

### 创建 Initializer

在 Rails 启动时，于 `config/initializers/dodo_payments.rb` 中创建一个 client：

```ruby theme={null}
require "dodopayments"

DODO_CLIENT = Dodopayments::Client.new(
  bearer_token: Rails.application.credentials.dodo_api_key,
  environment: Rails.env.production? ? "live_mode" : "test_mode"
)
```

### Service Object 模式

将 client 封装在 service object 中：

```ruby theme={null}
# app/services/payment_service.rb
class PaymentService
  def initialize
    @client = DODO_CLIENT
  end

  def create_checkout(items)
    @client.checkout_sessions.create(
      product_cart: items,
      return_url: Rails.application.routes.url_helpers.checkout_return_url
    )
  end

  def process_payment(product_id:, customer_id:, billing:)
    # Note: POST /payments is deprecated — prefer checkout sessions for new integrations
    @client.payments.create(
      billing: billing,
      customer: { customer_id: customer_id },
      product_cart: [{ product_id: product_id, quantity: 1 }]
    )
  end
end
```

### Controller 集成

从 controller 调用 service，并重定向到 checkout page：

```ruby theme={null}
# app/controllers/checkouts_controller.rb
class CheckoutsController < ApplicationController
  def create
    service = PaymentService.new
    session = service.create_checkout(checkout_params[:items])
    
    redirect_to session.checkout_url, allow_other_host: true
  rescue Dodopayments::Errors::APIError => e
    flash[:error] = "Payment error: #{e.message}"
    redirect_to cart_path
  end

  private

  def checkout_params
    params.require(:checkout).permit(items: [:product_id, :quantity])
  end
end
```

## Sinatra 集成

在 `configure` block 中创建一次 client，并在 routes 中使用它：

```ruby theme={null}
require "sinatra"
require "dodopayments"

configure do
  set :dodo_client, Dodopayments::Client.new(
    bearer_token: ENV["DODO_PAYMENTS_API_KEY"]
  )
end

post "/create-checkout" do
  content_type :json
  
  begin
    session = settings.dodo_client.checkout_sessions.create(
      product_cart: JSON.parse(request.body.read)["items"],
      return_url: "#{request.base_url}/return"
    )
    
    { checkout_url: session.checkout_url }.to_json
  rescue Dodopayments::Errors::APIError => e
    status 400
    { error: e.message }.to_json
  end
end
```

## 资源

> **[GitHub Repository](https://github.com/dodopayments/dodopayments-ruby)**
>
> 源代码、releases 和完整的 method 列表。

> **[API Reference](/api-reference/introduction)**
>
> 每个 endpoint、parameter 和 response。

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

> **[Report Issues](https://github.com/dodopayments/dodopayments-ruby/issues)**
>
> 报告 bugs 或请求 features。

## 支持

如需 Ruby SDK 帮助：

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

## 贡献

如需贡献，请阅读 [contributing guidelines](https://github.com/dodopayments/dodopayments-ruby/blob/main/CONTRIBUTING.md)。


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