use std::future::{Future, IntoFuture};
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
use crate::{error::ZarinResult, results::request::Request, ZarinpalClient};
use super::ApiMethod;
#[derive(Debug, Clone, Serialize, Default)]
pub enum Currency {
#[default]
IRR,
IRT,
}
#[derive(Debug, Clone, Serialize, TypedBuilder, Default)]
pub struct Metadata {
#[builder(default, setter(strip_option, into))]
#[serde(skip_serializing_if = "Option::is_none")]
mobile: Option<String>,
#[builder(default, setter(strip_option, into))]
#[serde(skip_serializing_if = "Option::is_none")]
email: Option<String>,
#[builder(default, setter(strip_option, into))]
#[serde(skip_serializing_if = "Option::is_none")]
order_id: Option<String>,
#[builder(default, setter(strip_option, into))]
#[serde(skip_serializing_if = "Option::is_none")]
card_pan: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
pub struct Wage {
#[builder(setter(into))]
iban: String,
amount: u64,
#[builder(setter(into))]
description: String,
}
#[derive(Debug, Clone, Serialize, TypedBuilder)]
pub struct RequestPayment<'z, Z: ZarinpalClient> {
#[builder(default, setter(strip_option, into))]
merchant_id: Option<String>,
#[builder(default, setter(strip_option))]
#[serde(skip_serializing_if = "Option::is_none")]
currency: Option<Currency>,
amount: u64,
#[builder(setter(into))]
callback_url: String,
#[builder(setter(into))]
description: String,
#[builder(default)]
metadata: Metadata,
#[builder(default, setter(strip_option, into))]
#[serde(skip_serializing_if = "Option::is_none")]
wages: Option<Vec<Wage>>,
#[serde(skip_serializing)]
#[builder(setter(strip_option))]
zarinpal: Option<&'z Z>,
}
impl<'z, Z: ZarinpalClient + Sync + Send> IntoFuture for RequestPayment<'z, Z> {
type Output = ZarinResult<Request>;
type IntoFuture = ::core::pin::Pin<Box<dyn Future<Output = Self::Output> + Send + 'z>>;
fn into_future(mut self) -> Self::IntoFuture {
let zarinpal = std::mem::take(&mut self.zarinpal).unwrap(); Box::pin(zarinpal.send(self))
}
}
impl<'z, Z: ZarinpalClient> ApiMethod for RequestPayment<'z, Z> {
const PATH: &'static str = "pg/v4/payment/request.json";
type Result = Request;
fn set_merchant_id_if_needed(&mut self, merchant_id: impl Into<String>) {
match self.merchant_id {
None => self.merchant_id = Some(merchant_id.into()),
_ => (),
}
}
}
#[cfg(test)]
mod tests {
use crate::Zarinpal;
use super::*;
#[test]
fn test_serialization() {
let zarinpal = Zarinpal::new(uuid::Uuid::new_v4().to_string().as_str()).unwrap();
let raw_json = serde_json::json!({
"merchant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"amount": 1000,
"callback_url": "http://alireza.work/verify",
"description": "Transaction description.",
"metadata": {
"mobile": "09106869409",
"email": "info.test@gmail.com"
}
});
let from_model = serde_json::to_value(
&RequestPayment::builder()
.merchant_id("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
.amount(1000)
.callback_url("http://alireza.work/verify")
.description("Transaction description.")
.metadata(
Metadata::builder()
.mobile("09106869409")
.email("info.test@gmail.com")
.build(),
)
.zarinpal(&zarinpal)
.build(),
)
.unwrap();
assert_eq!(raw_json, from_model)
}
#[test]
fn test_serialization_with_currency() {
let zarinpal = Zarinpal::new(uuid::Uuid::new_v4().to_string().as_str()).unwrap();
let raw_json = serde_json::json!({
"merchant_id": "1344b5d4-0048-11e8-94db-005056a205be",
"currency": "IRT",
"amount": 10000,
"callback_url": "http://yoursite.com/verify",
"description": "افزایش اعتبار کاربر شماره ۱۱۳۴۶۲۹",
"metadata": {"mobile": "09121234567","email": "info.test@gmail.com"}
});
let from_model = serde_json::to_value(
&RequestPayment::builder()
.merchant_id("1344b5d4-0048-11e8-94db-005056a205be")
.amount(10000)
.callback_url("http://yoursite.com/verify")
.currency(Currency::IRT)
.description("افزایش اعتبار کاربر شماره ۱۱۳۴۶۲۹")
.metadata(
Metadata::builder()
.mobile("09121234567")
.email("info.test@gmail.com")
.build(),
)
.zarinpal(&zarinpal)
.build(),
)
.unwrap();
assert_eq!(raw_json, from_model)
}
#[test]
fn test_serialization_with_wages() {
let zarinpal = Zarinpal::new(uuid::Uuid::new_v4().to_string().as_str()).unwrap();
let raw_json = serde_json::json!({
"merchant_id": "1344b5d4-0048-11e8-94db-005056a205be",
"amount": 20000,
"callback_url": "http://yoursite.com/verify",
"description": "Transaction description.",
"metadata": {
"mobile": "091212334567",
"email": "info.test@gmail.com"
},
"wages": [
{
"iban": "IR130570028780010957775103",
"amount": 1000,
"description": "تسهیم سود فروش از محصول به مسعود امینی"
},
{
"iban": "IR670170000000352965862009",
"amount": 5000,
"description": "تسهیم سود فروش از محصول به یوسفی"
}
]
});
let from_model = serde_json::to_value(
&RequestPayment::builder()
.merchant_id("1344b5d4-0048-11e8-94db-005056a205be")
.amount(20000)
.callback_url("http://yoursite.com/verify")
.description("Transaction description.")
.metadata(
Metadata::builder()
.mobile("091212334567")
.email("info.test@gmail.com")
.build(),
)
.wages([
Wage::builder()
.iban("IR130570028780010957775103")
.amount(1000)
.description("تسهیم سود فروش از محصول به مسعود امینی")
.build(),
Wage::builder()
.iban("IR670170000000352965862009")
.amount(5000)
.description("تسهیم سود فروش از محصول به یوسفی")
.build(),
])
.zarinpal(&zarinpal)
.build(),
)
.unwrap();
assert_eq!(raw_json, from_model)
}
#[test]
fn test_serialization_with_card_pan() {
let zarinpal = Zarinpal::new(uuid::Uuid::new_v4().to_string().as_str()).unwrap();
let raw_json = serde_json::json!({
"merchant_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"amount": 10000,
"callback_url": "http://yoursite.com/verify",
"description": "پرداخت تست ۱۱۰",
"metadata": {
"mobile": "09121234567",
"email": "info.test@gmail.com",
"card_pan":"5022291083818920",
}
});
let from_model = serde_json::to_value(
&RequestPayment::builder()
.merchant_id("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
.amount(10000)
.callback_url("http://yoursite.com/verify")
.description("پرداخت تست ۱۱۰")
.metadata(
Metadata::builder()
.mobile("09121234567")
.email("info.test@gmail.com")
.card_pan("5022291083818920")
.build(),
)
.zarinpal(&zarinpal)
.build(),
)
.unwrap();
assert_eq!(raw_json, from_model)
}
}