iran-pay 0.1.1

Unified async SDK for Iranian payment gateways — ZarinPal, IDPay, NextPay, Pay.ir behind one strongly-typed Rust trait.
Documentation

= iran-pay

image:https://img.shields.io/crates/v/iran-pay.svg[crates.io,link=https://crates.io/crates/iran-pay] image:https://docs.rs/iran-pay/badge.svg[docs.rs,link=https://docs.rs/iran-pay] image:https://github.com/obsernetics/rust-lib/actions/workflows/ci.yml/badge.svg[CI,link=https://github.com/obsernetics/rust-lib/actions/workflows/ci.yml] image:https://img.shields.io/badge/license-Apache--2.0-blue.svg[License] image:https://img.shields.io/badge/MSRV-1.88-orange.svg[MSRV] image:https://deps.rs/repo/github/obsernetics/rust-lib/status.svg[Dependencies,link=https://deps.rs/repo/github/obsernetics/rust-lib]

Unified async SDK for Iranian payment gateways.

== Features

  • ZarinPal driver — production v4 JSON API + sandbox.
  • IDPay driver — X-API-KEY JSON API + sandbox header.
  • NextPay driver — form-encoded API with code: -1 semantics.
  • Pay.ir driver — form-encoded API with the magic "test" sandbox key.
  • Single dyn-safe Gateway trait — swap providers with one line at runtime.
  • Amount type with explicit Toman / Rial unit constructors — eliminates the most common Iranian-payment-integration bug at the type level.
  • Built-in MockGateway for fast, deterministic, network-free unit tests of your checkout code.
  • Re-export of the parsitext Iranian validators (national ID, IBAN, bank-card, mobile, postal code, car plate) — pre-flight buyer input before you call the gateway.
  • tracing instrumentation on every driver method (provider name, amount, authority surfaced as fields).
  • Strongly-typed thiserror error enum: transport, gateway-business, amount mismatch, configuration, decode, and unsupported-operation variants are all separate.
  • Async-first (async-trait), Tokio-friendly, no blocking calls.

=== Optional features

[cols="1,1,3", options="header"] |=== | Feature | Default | What it enables

| zarinpal | ✓ | Compile the providers::ZarinPal driver | idpay | ✓ | Compile the providers::IDPay driver | nextpay | ✓ | Compile the providers::NextPay driver | payir | ✓ | Compile the providers::PayIr driver | validators | ✓ | Re-export parsitext's Iranian validators | rustls-tls | ✓ | Use rustls for HTTPS (no system OpenSSL needed) | native-tls | | Use the platform TLS library instead of rustls |===

Disabling all four provider features still builds the trait, types, and mock gateway — useful when you bring your own driver against this abstraction.

== Quick start

[source,toml]

[dependencies] iran-pay = "0.1.1" tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

Or pick only the providers you need:

iran-pay = { version = "0.1.1", default-features = false, features = ["zarinpal", "rustls-tls"] }

[source,rust]

use iran_pay::providers::ZarinPal; use iran_pay::{Amount, Gateway, StartRequest, VerifyRequest};

#[tokio::main] async fn main() -> Result<(), iran_pay::Error> { let gateway = ZarinPal::new("YOUR-MERCHANT-UUID").sandbox();

// 1. Initiate the payment.
let start = gateway.start_payment(&StartRequest::builder()
    .amount(Amount::toman(50_000))
    .description("Pro subscription — May 2026")
    .callback_url("https://example.com/payment/callback")
    .order_id("ORD-12345")
    .build()).await?;

// 2. Redirect the user to `start.payment_url`.
println!("Send user to: {}", start.payment_url);

// 3. After they return, verify.  Pass the same amount back in to
//    catch tampering with the callback query string.
let verified = gateway.verify_payment(&VerifyRequest {
    authority: start.authority,
    amount: Amount::toman(50_000),
}).await?;

println!("Paid! Transaction ID = {}", verified.transaction_id);
Ok(())

}

== Examples

Each runnable example lives in examples/:

[cols="1,2", options="header"] |=== | Example | What it demonstrates

| zarinpal_basic | Minimal start-payment flow against the ZarinPal sandbox. | multi_gateway | Box<dyn Gateway> polymorphism — register all four providers and look them up by key. | with_validators | Use the validators re-export to check bank cards / mobiles / operators before calling the gateway. | mock_in_test | Drop MockGateway into your own service tests; assert call counts and script failures. |===

Run with:

[source,bash]

cargo run --example -p iran-pay

== Comparison

[cols="1,1,1,1", options="header"] |=== | Capability | iran-pay | JS iranianbank / iranian-bank-gateways | Hand-rolled reqwest

| Unified Gateway trait | ✓ | partial (per-provider classes) | ✗ | Typed Amount (Toman / Rial) | ✓ (compile-time) | ✗ (raw numbers) | ✗ | Async / Tokio-native | ✓ | ✓ (Node) | yes (manual) | tracing instrumentation | ✓ (built-in spans) | ✗ | manual | Mock driver shipped with the SDK | ✓ | ✗ | ✗ | Strong error taxonomy | ✓ (thiserror) | strings / Error | ad-hoc | Iranian validators bundled | ✓ (via parsitext) | separate package | ✗ |===

== Documentation

[source,bash]

cargo doc -p iran-pay --all-features

Hosted docs: link:https://docs.rs/iran-pay[docs.rs/iran-pay].

== License

Apache-2.0.