The Dodo Payments Rust library provides convenient access to the Dodo Payments REST API from applications written in Rust.
The REST API documentation can be found on docs.dodopayments.com. The full API of this library can be found in api.md.
cargo add dodopaymentsOr add it to your Cargo.toml manually:
[dependencies]
dodopayments = "1.126.0"
tokio = { version = "1", features = ["full"] }
serde_json = "1"Rust 1.75 or later.
The client reads your API key from the DODO_PAYMENTS_API_KEY environment variable by default:
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: "product_id".to_string(),
quantity: 0,
addons: None,
amount: None,
credit_entitlements: None,
}]),
..Default::default()
})
.await?;
println!("{result:?}");
Ok(())
}You can also configure the client explicitly. Client::new returns a Result, so unwrap it with ? inside a function that returns dodopayments::Result:
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(())
}List endpoints return a typed page whose items field holds the current page of results. Fetch the next page with get_next_page, or stream every item across all pages with into_stream:
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:?}");
}Or advance one page at a time:
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,
}
}Every method returns a dodopayments::Result<T>. Failures are represented by the dodopayments::Error enum:
let result = client
.checkout_sessions()
.create()
.body(dodopayments::models::CheckoutSessionsCreateParams {
product_cart: Some(vec![dodopayments::models::ProductItemReq {
product_id: "product_id".to_string(),
quantity: 0,
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}"),
}The default request timeout is 30 seconds. Override it per client:
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)),
)?;| Name | Base URL |
|---|---|
live_mode |
https://live.dodopayments.com |
test_mode |
https://test.dodopayments.com |
The default base URL is https://live.dodopayments.com. Select another environment with the Environment enum instead of hard-coding URLs:
use dodopayments::{Client, ClientConfig, Environment};
let client = Client::new(
ClientConfig::from_environment(Environment::TestMode).with_api_key("My API Key"),
)?;To keep reading the API key from DODO_PAYMENTS_API_KEY via from_env() while targeting a non-default environment, override it on the config:
use dodopayments::{Client, ClientConfig, Environment};
let client = Client::new(ClientConfig::from_env()?.with_environment(Environment::TestMode))?;Alternatively, Client::from_env() uses the default base URL unless you set the DODO_PAYMENTS_BASE_URL environment variable, e.g. DODO_PAYMENTS_BASE_URL=https://test.dodopayments.com to target test_mode.
The client can exchange an OIDC token from the platform that runs your code for a short-lived access token, in place of an API key:
use dodopayments::workload_identity;
use dodopayments::{Client, ClientConfig, WorkloadIdentity};
let client = Client::new(
ClientConfig::new("https://live.dodopayments.com").with_workload_identity(
WorkloadIdentity::new(workload_identity::github_actions(None))
.with_identity_provider_id("wip_..."),
),
)?;- Subject token sources in
workload_identity:github_actions,gitlab,terraform_cloud,gcp,vercel,vercel_with_audience,kubernetes,file,aws(your function),static_token, andauto.SubjectTokenSource::from_fnaccepts any async function. vercelreadsVERCEL_OIDC_TOKEN_FILEorVERCEL_OIDC_TOKEN. It exchanges that token at Vercel for a token with the Dodo Payments audience. The Dodo Payments API accepts only that audience.- Without
with_identity_provider_id, the client readsDODO_PAYMENTS_IDENTITY_PROVIDER_ID. IfDODO_PAYMENTS_API_KEYis not set andDODO_PAYMENTS_IDENTITY_PROVIDER_IDis set,Client::from_env()uses workload identity withauto(). - The client gets a new access token when 5 minutes or less remain. Concurrent requests share one exchange. After a
401, the client gets a new token and retries the request once. client.revoke_workload_identity().await?revokes the cached access token and clears the cache.- Do not set
with_api_keyandwith_workload_identitytogether.Client::newreturnsError::Config. client.request(...)does not add the access token. Send the request withclient.send(request).await?.
To call an endpoint not yet exposed as a typed method, use the low-level request builder, which applies authentication and the base URL:
let response = client
.request(reqwest::Method::GET, "/some/path")
.send()
.await?;This package follows SemVer conventions. Breaking changes are released in major versions; minor and patch versions remain backwards compatible.
We take backwards compatibility seriously and work hard to ensure that breaking changes are clearly communicated in the changelog.