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.
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.
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.
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".
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 go in as decimal strings and come back as arbitrary-precision JSON
numbers — the SDK never converts money through an f64 in either direction.
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