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