yapay_sdk_rust/
checkout.rs

1use reqwest::IntoUrl;
2use serde::Serialize;
3use validator::Validate;
4
5use crate::common_types::AsPaymentMethod;
6use crate::helpers::format_available_payment_method;
7use crate::{InvalidError, SDKError, YapayProduct};
8
9/// Preferences to create your checkout.
10///
11///
12/// [`notification_url`] is optional, but highly recommended way to receive payment updates.
13///
14///
15/// [`available_payment_methods`] is used to restrict payment options, and can be used with the
16/// `set_available_payment_methods` builder.
17#[derive(Validate, Default, Debug, Clone, PartialEq, Serialize)]
18pub struct CheckoutPreferences {
19    order_number: String,
20
21    transaction_products: Vec<YapayProduct>,
22
23    /// URL para redirecionamento caso concluída a transação com sucesso
24    url_success: Option<String>,
25    /// URL para redirecionamento caso pedido esteja aguardando a confirmação de pagamento
26    url_process: Option<String>,
27    /// URL para redirecionamento caso concluída a transação mas ocorreu falha no pagamento
28    url_cancel: Option<String>,
29
30    /// URL para onde deve notificado após mudanças no status de pagamento.
31    notification_url: Option<String>,
32
33    ///
34    available_payment_methods: Option<String>,
35}
36
37impl CheckoutPreferences {
38    pub fn new(order_number: String, products: Vec<YapayProduct>) -> Result<Self, SDKError> {
39        let builder = Self {
40            order_number,
41            transaction_products: products,
42            url_success: None,
43            url_process: None,
44            url_cancel: None,
45            notification_url: None,
46            available_payment_methods: None,
47        };
48
49        if let Err(err) = builder.validate() {
50            return Err(InvalidError::ValidatorLibError(err).into());
51        }
52
53        Ok(builder)
54    }
55
56    /// Restricts payment methods to those used on `methods` parameter.
57    ///
58    ///
59    /// # Usage
60    ///
61    /// ```
62    /// use std::num::NonZeroU8;
63    ///
64    /// use yapay_sdk_rust::checkout::CheckoutPreferences;
65    /// use yapay_sdk_rust::common_types::{AsPaymentMethod, PaymentCreditCard, YapayProduct};
66    ///
67    /// let product = YapayProduct::new(
68    ///     "sample".to_string(),
69    ///     "a sample product".to_string(),
70    ///     NonZeroU8(1).unwrap(),
71    ///     10_f64,
72    /// );
73    ///
74    /// let preferences = CheckoutPreferences::new("order_number".to_string(), vec![product])
75    ///     .unwrap()
76    ///     .set_available_payment_methods(&[PaymentCreditCard::payment_methods_all()]);
77    ///
78    /// // now you can only pay with credit cards
79    /// ```
80    #[must_use]
81    pub fn set_available_payment_methods<PM>(mut self, methods: &[PM]) -> Self
82    where
83        PM: AsPaymentMethod,
84    {
85        self.available_payment_methods = Some(format_available_payment_method(methods));
86        self
87    }
88
89    pub fn set_notification_url<U>(mut self, url: U) -> Result<Self, SDKError>
90    where
91        U: IntoUrl,
92    {
93        let res = url
94            .into_url()
95            .map(|a| a.as_str().to_string())
96            .map_err::<SDKError, _>(|e| InvalidError::URLError(e).into())?;
97
98        self.notification_url = Some(res);
99        Ok(self)
100    }
101
102    /// Sets the `url_process`, which will redirect the user after payment.
103    /// This is the standard way of redirecting, it doesn't matter if the transaction failed, or was
104    /// a success.
105    ///
106    ///
107    /// You can use this to trigger a response to your server that the user has finished a payment,
108    /// and now needs to wait for a definitive response.
109    pub fn set_process_url<U>(mut self, url: U) -> Result<Self, SDKError>
110    where
111        U: IntoUrl,
112    {
113        let res = url
114            .into_url()
115            .map(|a| a.as_str().to_string())
116            .map_err::<SDKError, _>(|e| InvalidError::URLError(e).into())?;
117
118        self.url_process = Some(res);
119        Ok(self)
120    }
121
122    #[must_use]
123    pub fn to_form(self, token: &str) -> String {
124        let mut base_vec = vec![
125            ("token_account", token.to_string()),
126            ("order_number", self.order_number),
127        ];
128
129        for item in self.transaction_products {
130            base_vec.push(("transaction_product[][description]", item.description));
131            base_vec.push(("transaction_product[][quantity]", item.quantity));
132            base_vec.push(("transaction_product[][price_unit]", item.price_unit));
133        }
134
135        if let Some(url) = self.notification_url {
136            base_vec.push(("notification_url", url));
137        }
138
139        if let Some(payment_methods) = self.available_payment_methods {
140            base_vec.push(("available_payment_methods", payment_methods));
141        }
142
143        if let Some(url) = self.url_process {
144            base_vec.push(("url_process", url));
145        }
146
147        let mut querystring = String::new();
148        base_vec
149            .into_iter()
150            .enumerate()
151            .for_each(|(idx, (key, val))| {
152                if idx == 0 {
153                    querystring.push_str(&*format!("{}={}", key, val));
154                } else {
155                    querystring.push_str(&*format!("&{}={}", key, val));
156                }
157            });
158
159        querystring
160    }
161}