iran_pay/lib.rs
1//! # iran-pay
2//!
3//! Unified async SDK for Iranian payment gateways. One [`Gateway`] trait,
4//! **six production drivers** (ZarinPal, IDPay, NextPay, Pay.ir, Zibal,
5//! Vandar), shared strongly-typed request/response/error types, an in-memory
6//! mock gateway, security helpers (HMAC verification, constant-time compare,
7//! amount-mismatch guard), and per-provider API-version pinning. See
8//! [VERSIONING.md](https://github.com/obsernetics/rust-lib/blob/main/crates/iran-pay/VERSIONING.md)
9//! for the upgrade policy.
10//!
11//! ## At a glance
12//!
13//! ```no_run
14//! use iran_pay::{Amount, Gateway, StartRequest, VerifyRequest};
15//! use iran_pay::providers::ZarinPal;
16//!
17//! # async fn run() -> Result<(), iran_pay::Error> {
18//! let gateway = ZarinPal::new("YOUR-MERCHANT-UUID").sandbox();
19//!
20//! // 1. Initiate the payment.
21//! let start = gateway.start_payment(&StartRequest::builder()
22//! .amount(Amount::toman(50_000))
23//! .description("Pro subscription — May 2026")
24//! .callback_url("https://example.com/payment/callback")
25//! .order_id("ORD-12345")
26//! .build()).await?;
27//!
28//! // 2. Redirect the user to `start.payment_url`.
29//! println!("Send user to: {}", start.payment_url);
30//!
31//! // 3. After they return, verify. Pass the same amount back in to
32//! // catch tampering with the callback query string.
33//! let verified = gateway.verify_payment(&VerifyRequest {
34//! authority: start.authority,
35//! amount: Amount::toman(50_000),
36//! }).await?;
37//!
38//! println!("Paid! Transaction ID = {}", verified.transaction_id);
39//! # Ok(()) }
40//! ```
41//!
42//! ## Why a trait?
43//!
44//! Iranian e-commerce apps often switch gateways (or run several in parallel
45//! for redundancy / fee optimisation). Code your checkout against
46//! `dyn Gateway` or `impl Gateway` and you can swap providers with one line
47//! of configuration.
48//!
49//! ```ignore
50//! fn select_gateway(provider: &str) -> Box<dyn Gateway> {
51//! match provider {
52//! "zarinpal" => Box::new(ZarinPal::new(env::var("ZP_ID").unwrap())),
53//! "idpay" => Box::new(IDPay::new(env::var("IDPAY_KEY").unwrap())),
54//! "nextpay" => Box::new(NextPay::new(env::var("NEXTPAY_KEY").unwrap())),
55//! "payir" => Box::new(PayIr::new(env::var("PAYIR_KEY").unwrap())),
56//! _ => unreachable!(),
57//! }
58//! }
59//! ```
60//!
61//! ## Sandbox / test mode
62//!
63//! Every provider exposes `.sandbox()` to flip to its test endpoint. No real
64//! money moves. Use this in CI and for local development.
65//!
66//! ## Mock gateway
67//!
68//! For unit tests of *your* code, use [`mock::MockGateway`] — it implements
69//! [`Gateway`] without any network I/O, and lets you script success / failure
70//! responses programmatically.
71//!
72//! ## Cargo features
73//!
74//! | Feature | Default | What it enables |
75//! |--------------|---------|-----------------------------------------------|
76//! | `zarinpal` | ✓ | The [`providers::ZarinPal`] driver |
77//! | `idpay` | ✓ | The [`providers::IDPay`] driver |
78//! | `nextpay` | ✓ | The [`providers::NextPay`] driver |
79//! | `payir` | ✓ | The [`providers::PayIr`] driver |
80//! | `zibal` | ✓ | The [`providers::Zibal`] driver |
81//! | `vandar` | ✓ | The [`providers::Vandar`] driver |
82//! | `validators` | ✓ | Re-export `parsitext`'s Iranian validators |
83//! | `rustls-tls` | ✓ | Use rustls for HTTPS (no system OpenSSL) |
84//! | `native-tls` | | Use the platform TLS library |
85//!
86//! Disabling all provider features still gives you the trait, types, mock,
87//! and security helpers — useful if you build your own driver against this
88//! abstraction.
89
90#![cfg_attr(docsrs, feature(doc_cfg))]
91#![warn(missing_docs)]
92#![warn(rustdoc::broken_intra_doc_links)]
93
94mod amount;
95mod error;
96mod gateway;
97mod types;
98
99pub mod mock;
100pub mod providers;
101pub mod security;
102
103#[cfg(feature = "validators")]
104#[cfg_attr(docsrs, doc(cfg(feature = "validators")))]
105pub mod validators;
106
107pub use amount::{Amount, Currency};
108pub use error::Error;
109pub use gateway::Gateway;
110pub use types::{
111 RefundRequest, RefundResponse, StartRequest, StartRequestBuilder, StartResponse, VerifyRequest,
112 VerifyResponse,
113};
114
115/// Crate-wide `Result` alias.
116pub type Result<T> = std::result::Result<T, Error>;
117
118/// Re-exports of the most commonly used types.
119///
120/// `use iran_pay::prelude::*;` brings everything you need for a typical
121/// checkout flow into scope.
122pub mod prelude {
123 pub use crate::{
124 Amount, Currency, Error, Gateway, RefundRequest, RefundResponse, Result, StartRequest,
125 StartResponse, VerifyRequest, VerifyResponse,
126 };
127}