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}