Async Stripe

Request Strategies

Handle network failures gracefully with automatic retries and idempotency keys to prevent duplicate charges and ensure reliable payment processing.

Network requests fail. async-stripe provides a robust RequestStrategy API to handle idempotency keys and retries automatically, ensuring your application handles transient failures gracefully without double-charging customers.

Strategy Options

The library implements the "Full Jitter" exponential backoff algorithm recommended by Stripe engineering to prevent thundering herd problems.

StrategyDescriptionUse Case
OnceFire and forget. No retries.You are handling retries manually
Idempotent(key)Fire once with a specific, user-provided Idempotency KeyCritical payment creation flows where you need control over the idempotency key
Retry(n)Retry n times with a random UUID idempotency keyGeneral retry logic without backoff
ExponentialBackoff(n)Retry n times with exponential backoff (0.5s base, up to 8s max) and randomized jitterRecommended for production to handle transient failures gracefully

Usage

You can apply a strategy globally to the client (for all requests) or override it on a per-request basis.

Global Client Strategy

pub async fn run_strategy_example() -> Result<(), StripeError> {
    let secret_key = std::env::var("STRIPE_SECRET_KEY").expect("Missing STRIPE_SECRET_KEY in env");
    let client = ClientBuilder::new(secret_key)
        .request_strategy(RequestStrategy::idempotent_with_uuid())
        .build()?;
    let first_page = ListCustomer::new().send(&client).await?;

    println!(
        "first page of customers: {:#?}",
        first_page.data.iter().map(|c| c.name.as_ref().unwrap()).collect::<Vec<_>>()
    );
    Ok(())
}

Per-Request Strategy

    );
    Ok(())
}

pub async fn per_request_strategy_example(client: &Client) -> Result<(), StripeError> {
    let params = CreateCustomer::new();
    let customer = params
        .customize() // Enter builder mode
        .request_strategy(RequestStrategy::Retry(5)) // Override strategy for this call only
        .send(client)
        .await?;

Custom Idempotency Keys

For critical operations where you need precise control over deduplication (e.g., linking payment creation to your own order IDs), you can provide your own idempotency key instead of using an auto-generated UUID.

The IdempotencyKey type validates that your key:

  • Is not empty
  • Does not exceed 255 characters (Stripe's limit)
use stripe::{Client, CreatePaymentIntent, Currency, RequestStrategy, IdempotencyKey};

let client = Client::new(secret_key);

// Use your own unique identifier (e.g., from your database)
let key = IdempotencyKey::new("order_12345_attempt_1").unwrap();

CreatePaymentIntent::new(1000, Currency::USD)
    .request_strategy(RequestStrategy::Idempotent(key))
    .send(&client)
    .await?;

This ensures that even if your application retries the request due to a network failure, Stripe will recognize it as the same operation and not create duplicate charges.

Idempotency keys are valid for 24 hours. After that, Stripe will treat a request with the same key as a new operation.

When to Use Custom Keys

  • Order processing: Use order_{id}_payment to tie payment creation to order IDs
  • Subscription operations: Use subscription_{id}_cancel_{timestamp} for cancellations
  • Multi-step flows: Maintain idempotency across application restarts by persisting keys

When to Use Auto-Generated Keys

For most operations, the auto-generated UUID keys from RequestStrategy::Retry(n) or RequestStrategy::ExponentialBackoff(n) are sufficient and more convenient.

Have feedback? Let us know here