Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

payzum — official Rust SDK

Accept crypto and stablecoin payments (USDC/USDT, multi-chain) with Payzum. Non-custodial — funds settle to your own wallet.

[dependencies]
payzum = "0.1"

You need two values from your Payzum dashboard: an API key and a webhook secret.

Charge a customer

use payzum::{CreatePaymentParams, Payzum};

let payzum = Payzum::new("your-api-key")?;        // Payzum::sandbox(...) for staging

let invoice = payzum.payments().create(
    "49.99",                                      // a decimal string — never a float
    "usd",
    "all",                                        // the buyer picks the coin on the checkout page
    CreatePaymentParams {
        order_id: Some("ORDER-12345"),
        ipn_callback_url: Some("https://your-shop.example/payzum/ipn"),
        idempotency_key: Some("ORDER-12345"),     // lets the SDK retry safely on network hiccups
        ..Default::default()
    },
)?;
let checkout_url = invoice["invoice_url"].as_str().unwrap_or_default();
# Ok::<(), payzum::PayzumError>(())

That's the whole flow: create, redirect, and wait for the webhook. Which coins the buyer can pick is configured in your dashboard, not in code.

Get notified when it's paid

Payzum POSTs a signed webhook (IPN) to your ipn_callback_url. Hand the SDK the raw request body and the headers — it finds the right header, checks the signature and rejects replays, all by itself:

use payzum::{PaymentStatus, Verifier};

# let (raw_body, headers, secret): (&[u8], Vec<(String, String)>, &str) = unimplemented!();
let verifier = Verifier::new(secret)?;
let payload = verifier.verify_payment_ipn(raw_body, &headers, None)?;   // RAW bytes!

let status = PaymentStatus::from_merchant(
    payload["payment_status"].as_str().unwrap_or_default(),
)?;
if status.is_paid() {
    // only fulfil on is_paid()
}
# Ok::<(), payzum::PayzumError>(())

Deliveries can arrive more than once — deduplicate on verifier.event_id(&headers) if a repeat must be a no-op on your side.

Statuses

A payment is always in one of five states: waiting, partially_paid, finished, expired, failed. Everything you need is on the enum:

  • status.is_paid() — safe to fulfil (covers overpayment too).
  • status.is_terminal() — nothing further will happen.
  • PaymentStatus::from_merchant(...) errors on anything unexpected, so a surprise value can never be mistaken for "paid".

Errors

Everything returns Result<_, PayzumError>: Api (with .error_code, e.g. AmountBelowMinimum, to branch on), Signature for webhooks, Transport for network failures, Validation for local checks.

Transient failures are retried for you, honouring the server's back-off hints. A create is only retried when you pass an idempotency_key, and then in a way that cannot double-charge.

Amounts are exact

Amounts go in as decimal strings and come back as arbitrary-precision JSON numbers — the SDK never converts money through an f64 in either direction.

Testing your integration

The Transport trait lets you swap the HTTP stack or script responses without touching the network. The SDK's own suite runs with cargo test.

Docs: https://merchant.payzum.com/docs · llms: https://merchant.payzum.com/llms.txt

Releases

Packages

Contributors

Languages