Skip to main content

iran_pay/
types.rs

1//! Common request / response types used by every [`Gateway`](crate::Gateway).
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::Amount;
8
9// ── start ────────────────────────────────────────────────────────────────────
10
11/// Inputs for [`Gateway::start_payment`](crate::Gateway::start_payment).
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct StartRequest {
14    /// The amount to charge.
15    pub amount: Amount,
16    /// Short human-readable description shown to the user on the gateway page.
17    pub description: String,
18    /// HTTPS URL the gateway will redirect the user to after payment.
19    pub callback_url: String,
20    /// Optional buyer e-mail (used by some providers for receipts).
21    pub email: Option<String>,
22    /// Optional buyer mobile number (Iranian format, used by some providers
23    /// to pre-fill the OTP step).
24    pub mobile: Option<String>,
25    /// Optional merchant-side order ID — echoed back in the verify response
26    /// where supported.  Strongly recommended for reconciliation.
27    pub order_id: Option<String>,
28    /// Free-form provider-specific extras (forwarded as-is to drivers that
29    /// support metadata).
30    #[serde(default)]
31    pub extras: HashMap<String, String>,
32}
33
34impl StartRequest {
35    /// Start a builder.
36    #[must_use]
37    pub fn builder() -> StartRequestBuilder {
38        StartRequestBuilder::default()
39    }
40}
41
42/// Step-builder for [`StartRequest`].
43#[derive(Debug, Default)]
44pub struct StartRequestBuilder {
45    amount: Option<Amount>,
46    description: Option<String>,
47    callback_url: Option<String>,
48    email: Option<String>,
49    mobile: Option<String>,
50    order_id: Option<String>,
51    extras: HashMap<String, String>,
52}
53
54impl StartRequestBuilder {
55    /// Set the amount (required).
56    #[must_use]
57    pub fn amount(mut self, amount: Amount) -> Self {
58        self.amount = Some(amount);
59        self
60    }
61
62    /// Set the description (required).
63    #[must_use]
64    pub fn description(mut self, d: impl Into<String>) -> Self {
65        self.description = Some(d.into());
66        self
67    }
68
69    /// Set the callback URL (required).
70    #[must_use]
71    pub fn callback_url(mut self, url: impl Into<String>) -> Self {
72        self.callback_url = Some(url.into());
73        self
74    }
75
76    /// Set the buyer e-mail (optional).
77    #[must_use]
78    pub fn email(mut self, email: impl Into<String>) -> Self {
79        self.email = Some(email.into());
80        self
81    }
82
83    /// Set the buyer mobile number (optional).
84    #[must_use]
85    pub fn mobile(mut self, mobile: impl Into<String>) -> Self {
86        self.mobile = Some(mobile.into());
87        self
88    }
89
90    /// Set the merchant order ID (optional but recommended).
91    #[must_use]
92    pub fn order_id(mut self, id: impl Into<String>) -> Self {
93        self.order_id = Some(id.into());
94        self
95    }
96
97    /// Add a single provider-specific extra metadata key/value.
98    #[must_use]
99    pub fn extra(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
100        self.extras.insert(key.into(), value.into());
101        self
102    }
103
104    /// Build the request.  Panics if `amount`, `description`, or
105    /// `callback_url` were not set — these are always required by every
106    /// supported gateway.
107    pub fn build(self) -> StartRequest {
108        StartRequest {
109            amount: self.amount.expect("StartRequest: amount is required"),
110            description: self
111                .description
112                .expect("StartRequest: description is required"),
113            callback_url: self
114                .callback_url
115                .expect("StartRequest: callback_url is required"),
116            email: self.email,
117            mobile: self.mobile,
118            order_id: self.order_id,
119            extras: self.extras,
120        }
121    }
122}
123
124/// Output of [`Gateway::start_payment`](crate::Gateway::start_payment).
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct StartResponse {
127    /// Provider-issued token / authority for this payment session.  Save it
128    /// — you'll need it to call [`Gateway::verify_payment`](crate::Gateway::verify_payment).
129    pub authority: String,
130    /// HTTPS URL to redirect the user to.  After they pay, the gateway
131    /// redirects back to your `callback_url`.
132    pub payment_url: String,
133    /// Driver name that produced this response.
134    pub provider: &'static str,
135    /// Full provider response, retained for debugging / logging.
136    #[serde(default)]
137    pub raw: serde_json::Value,
138}
139
140// ── verify ───────────────────────────────────────────────────────────────────
141
142/// Inputs for [`Gateway::verify_payment`](crate::Gateway::verify_payment).
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct VerifyRequest {
145    /// The `authority` you received in [`StartResponse::authority`].
146    pub authority: String,
147    /// The amount you originally charged.  Re-confirming the amount catches
148    /// callback-tampering attacks where an attacker swaps the authority for
149    /// one tied to a smaller transaction.
150    pub amount: Amount,
151}
152
153/// Output of [`Gateway::verify_payment`](crate::Gateway::verify_payment).
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct VerifyResponse {
156    /// Gateway's permanent transaction reference (`RefId` for ZarinPal,
157    /// `track_id` for IDPay, etc.).  Persist this for refunds and audits.
158    pub transaction_id: String,
159    /// The original authority that was just verified.
160    pub authority: String,
161    /// Final settled amount (should match what you sent — the SDK already
162    /// double-checks).
163    pub amount: Amount,
164    /// Masked card PAN if the gateway returned it.
165    pub card_pan: Option<String>,
166    /// Hash of the payer's card (lets you fingerprint repeat customers
167    /// without storing PANs).
168    pub card_hash: Option<String>,
169    /// Gateway fee, when reported.
170    pub fee: Option<Amount>,
171    /// Driver name.
172    pub provider: &'static str,
173    /// Full provider response for debugging / audit logging.
174    #[serde(default)]
175    pub raw: serde_json::Value,
176}
177
178// ── refund ───────────────────────────────────────────────────────────────────
179
180/// Inputs for [`Gateway::refund_payment`](crate::Gateway::refund_payment).
181///
182/// Not every Iranian gateway supports automated refunds; drivers that don't
183/// will return [`Error::Unsupported`](crate::Error::Unsupported).
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct RefundRequest {
186    /// The `transaction_id` from a successful [`VerifyResponse`].
187    pub transaction_id: String,
188    /// Optional partial refund amount.  Omit for a full refund.
189    pub amount: Option<Amount>,
190    /// Optional reason string — surfaced to the merchant dashboard.
191    pub reason: Option<String>,
192}
193
194/// Output of [`Gateway::refund_payment`](crate::Gateway::refund_payment).
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct RefundResponse {
197    /// Gateway's refund reference number.
198    pub refund_id: String,
199    /// The transaction that was refunded.
200    pub transaction_id: String,
201    /// How much was actually refunded.
202    pub amount: Amount,
203    /// Driver name.
204    pub provider: &'static str,
205    /// Full provider response.
206    #[serde(default)]
207    pub raw: serde_json::Value,
208}