async-stripe

repository·master·Indexed 20 days ago

https://github.com/arlyon/async-stripe

A high-performance, strongly-typed Rust library for the Stripe HTTP API, generated from the official Stripe OpenAPI specification. It features a modular crate structure to optimize compilation times, supporting various async runtimes (tokio, async-std) and TLS backends. The library provides type-safe request builders, asynchronous streaming for pagination, and dedicated tools for verifying and deserializing Stripe webhooks.

Tokens
26.4K
Snippets
84
Records
110
Agent score
72%

What's inside async-stripe

  1. Explore async-stripe usage examples

    master

    The examples/ directory contains standalone crates that demonstrate various ways to integrate async-stripe into your project. Each example is a complete crate, making it easy to see the specific dependencies required for different use cases.

    Available examples include:

    • endpoints: Demonstrates general usage, including API calls for common resources and different request strategies.
    • pagination: Shows how to perform asynchronous streaming of Stripe API calls to handle Stripe Pagination.
    • webhook-actix: Demonstrates how to receive and process Stripe webhooks using the actix-web framework.
    • webhook-rocket: Demonstrates how to receive and process Stripe webhooks using the rocket framework.
    • webhook-axum: Demonstrates how to receive and process Stripe webhooks using the axum framework.
  2. How to extend generated code without losing changes

    master

    Because the library is procedurally generated, all code in generated/* is overwritten during updates and must never be edited manually.

    To add custom logic (such as convenience methods or custom serialization) without conflicts, use the following pattern:

    • Generated Code: Stays in generated/*.
    • Extensions: Place hand-written logic in _ext.rs files located inside the resource crates or async-stripe-types. This ensures your manual code persists across regenerations.
  3. Working with Expandable Fields using Expandable<T>

    master

    Stripe API responses often return related objects as IDs by default. To retrieve the full object, you must pass the field name in the expand parameter during the request. The library handles these fields using the Expandable<T> type.

    Accessing Data

    • Always get the ID: You can always call .id() on an Expandable<T> field to get the ID string, whether the field was expanded or not.
    • Access the full object: If the field was expanded, use .as_object() to get a reference to the full object or .into_object() to take ownership.

    Available Methods on Expandable<T>

    • .id(): Returns the ID (works regardless of expansion status).
    • .is_object(): Returns true if the field contains the full object.
    • .as_object(): Returns a reference to the object if available.
    • .into_object(): Takes ownership of the object if available.
    • .into_id(): Takes ownership of the ID.
    use stripe::{Client, Charge};
    
    let client = Client::new(secret_key);
    
    // 1. Always safe to get ID
    let charge = Charge::retrieve(&client, &charge_id, &[]).await?;
    let customer_id = charge.customer.id();
    
    // 2. Requesting expansion
    let charge = Charge::retrieve(&client, &charge_id, &["customer"]).await?;
    
    // 3. Accessing expanded object
    if let Some(customer) = charge.customer.as_object() {
        println!("Customer email: {:?}", customer.email);
    } else {
        println!("Customer was not expanded");
    }
  4. How the Modular Crate Structure works

    master

    To keep compile times low and binary sizes small, async-stripe is split into specialized crates. You must include the core client and then add the specific crates for the Stripe API areas you intend to use.

    • async-stripe: The core client. Always required.
    • stripe-core: Core resources like Customers, Charges, Payment Intents, and Refunds.
    • stripe-payment: Payment Methods, Payment Links, and Sources.
    • stripe-billing: Invoices, Subscriptions, Plans, and Quotes.
    • stripe-connect: Accounts, Account Links, and Transfers.
    • stripe-fraud: Radar and Reviews.
    • stripe-checkout: Checkout Sessions.
    • stripe-webhook: Securely receive and deserialize webhook events.
    • stripe-product: Products, Prices, Coupons, and Tax Rates.
    • stripe-issuing: Card creation and management.
    • stripe-terminal: In-person payments.
    • stripe-treasury: Financial accounts and money movement.
    • stripe-misc: Tax, Identity, Reporting, Sigma, etc.

    To use multiple resources from one crate, enable them via features, e.g., stripe-core = { version = "...", features = ["customer", "charge"] }.

  5. Performance: Using `miniserde` for deserialization

    master

    To optimize for performance and binary size, async-stripe uses a hybrid approach for JSON handling:

    1. serde is used for serializing request parameters (sending data to Stripe).
    2. miniserde is used for deserializing API responses (receiving data from Stripe).

    If your application needs to use serde::Deserialize on Stripe response types, you must enable the deserialize feature on the relevant stripe-* crate.

  6. How the code generation workflow works

    master

    The generator follows a multi-step optimization pipeline to manage the complexity of the Stripe API:

    1. Fetching & Parsing: Downloads spec3.sdk.json and parses schema definitions.
    2. Dependency Analysis: Builds a directed graph of Stripe objects to identify resource dependencies.
    3. Crate Splitting: Uses gen_crates.toml to group resources into modular crates (e.g., stripe-billing, stripe-connect).
    4. Cycle Breaking: Identifies cyclic dependencies (e.g., Customer $\leftrightarrow$ Subscription) and extracts shared types into the async-stripe-types crate to resolve them.
    5. Rendering: Outputs strongly-typed Rust structs, enums, and builder methods using miniserde for fast compilation.
  7. Understand the async-stripe API Versioning

    master

    The async-stripe library is pinned to a specific Stripe API version. This ensures that all API requests use a consistent version regardless of your Stripe account's default settings, preventing unexpected breaking changes.

    Important: Because the library is pinned, you must ensure your understanding of the version matches the library version. It is recommended to regularly update both the async-stripe library and your Stripe account's API version to stay in sync. Webhooks that do not match the library's pinned version will trigger a warning via the tracing crate.

  8. Best practices for handling Stripe webhooks

    master

    To ensure reliable and secure webhook processing, follow these patterns:

    1. Return 200 OK quickly

    Stripe expects a response within a few seconds. If your processing logic is heavy, spawn a background task (e.g., using tokio::spawn) and return StatusCode::OK immediately.

    2. Handle Idempotency

    Stripe may send the same event multiple times. Use the event.id to track processed events in your database to avoid duplicate processing.

    3. Security

    • Use HTTPS in production.
    • Store your webhook signing secret in environment variables.
    • Always verify signatures using construct_event() in production.
    // Pattern: Return 200 quickly by spawning a task
    async fn handle_webhook(StripeEvent(event): StripeEvent) -> StatusCode {
        tokio::spawn(async move {
            process_event(event).await;
        });
    
        StatusCode::OK
    }
    
    // Pattern: Handle idempotency using event.id
    async fn handle_webhook(StripeEvent(event): StripeEvent) {
        if already_processed(&event.id).await {
            return; // Skip duplicate
        }
    
        process_event(event).await;
        mark_as_processed(&event.id).await;
    }
  9. Understand the async-stripe modular crate architecture

    master

    The async-stripe library uses a modular workspace architecture instead of a single monolithic crate. This design is intended to improve compilation times and reduce binary size by allowing you to only include the Stripe domains you actually use. The architecture is divided into three layers:

    1. Client Layer: Manages the HTTP runtime and configuration.
    2. Shared Layer: Contains common types used across all resources (e.g., IDs, Currency, Errors).
    3. Resource Layer: Contains specific Stripe domains (e.g., Billing, Connect, Payments).

    To use the library, you must include the core client and then add the specific resource crates required for your integration.

  10. Configure Request Strategies

    master

    The RequestStrategy API allows you to handle network failures and prevent duplicate charges using idempotency keys and retries. The library automatically respects the Stripe-Should-Retry response header or falls back to retrying transient status codes like 409, 424, 429, and 5xx.

    Available strategies:

    • Once: No retries. Use this if you are managing retries manually.
    • Idempotent(key): Executes the request once using a specific, user-provided IdempotencyKey. Use this for critical flows where you need control over the key.
    • Retry(n): Retries the request up to n total times using a random UUID as the idempotency key. Best for general retry logic without backoff.
    • ExponentialBackoff(n): Retries the request up to n total times using exponential backoff. Recommended for production to handle transient failures gracefully.
    // Example of strategy options
    let strategy = RequestStrategy::ExponentialBackoff(3);
  11. How async-stripe handles serialization and deserialization

    master

    To balance performance and compile times, async-stripe uses a hybrid serialization strategy. It uses serde for outgoing requests to provide a rich feature set for complex parameters, and miniserde for incoming responses to ensure fast compile times and small binary sizes.

    OperationLibraryWhy
    Serialization (requests → Stripe)serdeRich feature set for complex request parameters.
    Deserialization (Stripe → responses)miniserdeMinimal, high-performance library that reduces compile times and binary size.

    Note on Error Reporting: miniserde provides minimal error messages during deserialization. If you need detailed diagnostics (e.g., which specific field failed), you should enable the deserialize feature to switch to serde for responses.