Skip to content

Rust SDK for working with the Paddle API in server-side apps. (Unofficial)

License

Notifications You must be signed in to change notification settings

caido/dependency-paddle

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Paddle Rust SDK

crates.io Docs License

Rust SDK for working with the Paddle API in server-side apps. (Unofficial)

Installation and Usage

To install the Paddle Rust SDK, run the following Cargo command in your project directory:

cargo add paddle-rust-sdk

To authenticate, you'll need an API key. You can create and manage API keys in Paddle > Developer tools > Authentication.

Pass your API key while initializing a new Paddle client.

use paddle_rust_sdk::Paddle;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Paddle::new(std::env::var("PADDLE_API_KEY")?, Paddle::SANDBOX)?;

    Ok(())
}

Fetching Entities

You can list supported entities with the *-list() builders on the Paddle client. It returns an iterator-like struct to help when working with multiple pages.

Example for customers:

use paddle_rust_sdk::Paddle;

#[tokio::main]
async fn main() {
    let client = Paddle::new(std::env::var("PADDLE_API_KEY").unwrap(), Paddle::SANDBOX).unwrap();

    let mut list = client.customers_list();
    let mut paginated = list.per_page(2).send();

    while let Some(page) = paginated.next().await.unwrap() {
        dbg!(page.data);
    }
}

Additionally all entities can be fetched via the .all() method.

use paddle_rust_sdk::Paddle;

#[tokio::main]
async fn main() {
    let client = Paddle::new(std::env::var("PADDLE_API_KEY").unwrap(), Paddle::SANDBOX).unwrap();

    let mut list = client.customers_list();
    let mut paginated = list.per_page(1).send();
    let customers = paginated.all().await.unwrap();

    dbg!(customers);
}

Webhook signature verification

Use the Paddle::unmarshal method to verify that received events are genuinely sent from Paddle. Additionally, this method returns the deserialized event struct.

Example handling webhook delivery with Actix:

use actix_web::{post, App, HttpRequest, HttpResponse, HttpServer, Responder};
use paddle_rust_sdk::{webhooks::MaximumVariance, Paddle};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    // let client = Paddle::new(std::env::var("PADDLE_API_KEY").unwrap(), Paddle::SANDBOX).unwrap();

    HttpServer::new(|| App::new().service(paddle_callback))
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
}

/// http://127.0.0.1:8080/paddle-callback
#[post("/paddle-callback")]
async fn paddle_callback(request_body: String, req: HttpRequest) -> impl Responder {
    let maybe_signature = req
        .headers()
        .get("paddle-signature")
        .and_then(|h| h.to_str().ok());

    let Some(signature) = maybe_signature else {
        return HttpResponse::BadRequest();
    };

    let key = "pdl_ntfset_01jw5t7njm3zfttyc8svst87rm_8ez0Wfm7VaeV+2IT3MpLGxwiQpDHWbYC";

    match Paddle::unmarshal(request_body, key, signature, MaximumVariance::default()) {
        Ok(event) => {
            // Proccess the request asynchronously
            actix_web::rt::spawn(async { dbg!(event) });
        }
        Err(e) => {
            println!("{:?}", e);
            return HttpResponse::BadRequest();
        }
    };

    // Respond as soon as possible
    HttpResponse::Ok()
}

This lib also provides the list live and sandbox IPs that webhook requests originate from.

Use the Paddle::ALLOWED_WEBHOOK_IPS_PRODUCTION and Paddle::ALLOWED_WEBHOOK_IPS_SANDBOX constants to check that a requests is made from a Paddle server. Actix example:

#[post("/webhook")]
async fn webhook(req: HttpRequest, post: String) -> Result<impl Responder> {
    // SECURITY: Do not use realip_remote_addr unless you can be sure that the Forwarded and X-Forwarded-For headers cannot be spoofed by the client. If you are running without a proxy then obtaining the peer address [ConnectionInfo::peer_addr] would be more appropriate.
    let maybe_remote_addr = req.connection_info().realip_remote_addr().map(|s| s.to_string());

    let Some(remote_addr) = maybe_remote_addr else {
        return Ok("");
    };

    if !Paddle::ALLOWED_WEBHOOK_IPS_PRODUCTION.contains(&remote_addr.as_str()) {
        return Ok("");
    }

    //...snip
}

Running examples

<YOUR_API_KEY> must be generated in the sandbox environment. All examples call the sandbox endpoints.

PADDLE_API_KEY=<YOUR_API_KEY> cargo run --example products-list

Paddle API Coverage

The following list outlines the current coverage of the Paddle API in this crate.

  • ✅ Products
  • ✅ Prices
  • ✅ Discounts
  • ✅ Customers
  • ✅ Addresses
  • ✅ Businesses
  • ✅ Payment methods
  • ✅ Customer portal sessions
  • ✅ Transactions
  • ✅ Subscriptions
  • ✅ Adjustments
  • ✅ Pricing preview
  • ✅ Reports
  • ✅ Events
  • 🚧 Notifications
  • 🚧 Simulations

License

About

Rust SDK for working with the Paddle API in server-side apps. (Unofficial)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 100.0%