paystack/endpoints/
dedicated_virtual_account.rs

1//! Dedicated Virtual Account
2//! =========================
3//! The Dedicated Virtual Account API enables Nigerian and Ghanaian merchants to manage unique payment accounts of their customers.
4
5use super::PAYSTACK_BASE_URL;
6use crate::{
7    BankProviderData, DedicatedVirtualAccountRequest, DedicatedVirtualAccountResponseData,
8    HttpClient, ListDedicatedAccountFilter, PaystackAPIError, PaystackResult, Response,
9    SplitDedicatedAccountTransactionRequest,
10};
11use serde_json::json;
12use std::{marker::PhantomData, sync::Arc};
13
14#[derive(Debug, Clone)]
15pub struct DedicatedVirtualAccountEndpoints<T: HttpClient + Default> {
16    key: String,
17    base_url: String,
18    http: Arc<T>,
19}
20
21/// Handles operations related to dedicated virtual accounts in the Paystack API
22impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
23    /// Creates a new DedicatedVirtualAccountEndpoints instance
24    ///
25    /// # Arguments
26    /// * `key` - The Paystack API key
27    /// * `http` - The HTTP client implementation to use for API requests
28    ///
29    /// # Returns
30    /// A new DedicatedVirtualAccountEndpoints instance
31    pub fn new(key: Arc<String>, http: Arc<T>) -> DedicatedVirtualAccountEndpoints<T> {
32        let base_url = format!("{PAYSTACK_BASE_URL}/dedicated_account");
33        DedicatedVirtualAccountEndpoints {
34            key: key.to_string(),
35            base_url,
36            http,
37        }
38    }
39
40    /// Create a dedicated virtual account for an existing customer.
41    ///
42    /// # Arguments
43    /// * `create_dedicated_virtual_account_request` - The request data to create the dedicated virtual account for the customer.
44    ///   It should be created with the `DedicatedVirtualAccountRequstBuilder` struct.
45    ///
46    /// # Returns
47    /// A Result containing the dedicated virtual account response data or an error
48    pub async fn create_dedicated_virtual_account(
49        &self,
50        create_dedicated_virtual_account_request: DedicatedVirtualAccountRequest,
51    ) -> PaystackResult<DedicatedVirtualAccountResponseData> {
52        let url = &self.base_url;
53        let body = serde_json::to_value(create_dedicated_virtual_account_request)
54            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
55
56        let response = self
57            .http
58            .post(url, &self.key, &body)
59            .await
60            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
61
62        let parsed_response: Response<DedicatedVirtualAccountResponseData> =
63            serde_json::from_str(&response)
64                .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
65
66        Ok(parsed_response)
67    }
68
69    /// Creates a customer, validates them and assigns a dedicated virtual account.
70    ///
71    /// # Arguments
72    /// * `assign_dedicated_virtual_account_request` - The request data to assign the dedicated virtual account.
73    ///   It should be created with the `DedicatedVirtualAccountRequestBuilder`
74    ///
75    /// # Returns
76    /// A Result containing the response or an error
77    pub async fn assign_dedicated_virtual_account(
78        &self,
79        assign_dedicated_virtual_account_request: DedicatedVirtualAccountRequest,
80    ) -> PaystackResult<PhantomData<String>> {
81        let url = &self.base_url;
82        let body = serde_json::to_value(assign_dedicated_virtual_account_request)
83            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
84
85        let response = self
86            .http
87            .post(url, &self.key, &body)
88            .await
89            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
90
91        let parsed_response: Response<PhantomData<String>> = serde_json::from_str(&response)
92            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
93
94        Ok(parsed_response)
95    }
96
97    /// Lists dedicated virtual accounts available on your integration.
98    ///
99    /// # Arguments
100    /// * `filter` - Optional set of parameters to filter the dedicated accounts returned.
101    ///   It should be created with the `ListDedicatedAccountFilterBuilder` struct.
102    ///
103    /// # Returns
104    /// A Result containing a vector of dedicated virtual account response data or an error
105    pub async fn list_dedicated_accounts(
106        &self,
107        filter: Option<ListDedicatedAccountFilter>,
108    ) -> PaystackResult<Vec<DedicatedVirtualAccountResponseData>> {
109        let url = &self.base_url;
110        let mut query = vec![];
111        // Build the query vec with the value in the filter struct
112        if let Some(filter) = filter {
113            if let Some(active) = filter.active {
114                query.push(("active", active.to_string()));
115            }
116            if let Some(currency) = filter.currency {
117                query.push(("currency", currency.to_string()));
118            }
119            if let Some(provider_slug) = filter.provider_slug {
120                query.push(("provider_slug", provider_slug));
121            }
122            if let Some(bank_id) = filter.bank_id {
123                query.push(("bank_id", bank_id));
124            }
125            if let Some(customer) = filter.customer {
126                query.push(("customer", customer));
127            }
128        }
129
130        // Transform String to &str using iter
131        let query: Vec<(&str, &str)> = query.iter().map(|(k, v)| (*k, v.as_str())).collect();
132        let response = self
133            .http
134            .get(url, &self.key, Some(&query))
135            .await
136            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
137
138        let parsed_response: Response<Vec<DedicatedVirtualAccountResponseData>> =
139            serde_json::from_str(&response)
140                .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
141
142        Ok(parsed_response)
143    }
144
145    /// Gets details of a dedicated virtual account on your integration
146    ///
147    /// # Arguments
148    /// * `dedicated_account_id` - ID of dedicated virtual account to fetch
149    ///
150    /// # Returns
151    /// A Result containing the dedicated virtual account response data or an error
152    pub async fn fetch_dedicated_virtual_account(
153        &self,
154        dedicated_account_id: u64,
155    ) -> PaystackResult<DedicatedVirtualAccountResponseData> {
156        let url = format!("{}/{}", self.base_url, dedicated_account_id);
157
158        let response = self
159            .http
160            .get(&url, &self.key, None)
161            .await
162            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
163
164        let parsed_response: Response<DedicatedVirtualAccountResponseData> =
165            serde_json::from_str(&response)
166                .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
167
168        Ok(parsed_response)
169    }
170
171    /// Requery Dedicated Virtual Account for new transactions
172    ///
173    /// # Arguments
174    /// * `account_number` - Virtual account number to requery
175    /// * `provider_slug` - The bank's slug in lowercase, without spaces
176    /// * `date` - Optional day the transfer was made in YYYY-MM-DD format
177    ///
178    /// # Returns
179    /// A Result containing the response or an error
180    pub async fn requery_dedicated_account(
181        &self,
182        account_number: String,
183        provider_slug: String,
184        date: Option<String>,
185    ) -> PaystackResult<PhantomData<String>> {
186        let url = format!("{}/requery", self.base_url);
187        let mut query = vec![
188            ("account_number", account_number),
189            ("provider_slug", provider_slug),
190        ];
191        if let Some(value) = date {
192            query.push(("date", value));
193        }
194
195        // convert Vec<(&str, String)> to Vec<(&str, &str)>
196        let query: Vec<(&str, &str)> = query.iter().map(|(k, v)| (*k, v.as_str())).collect();
197
198        let response = self
199            .http
200            .get(&url, &self.key, Some(&query))
201            .await
202            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
203
204        let parsed_response: Response<PhantomData<String>> = serde_json::from_str(&response)
205            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
206
207        Ok(parsed_response)
208    }
209
210    /// Deactivate a dedicated virtual account on your integration
211    ///
212    /// # Arguments
213    /// * `dedicated_account_id` - ID of dedicated virtual account to deactivate
214    ///
215    /// # Returns
216    /// A Result containing the dedicated virtual account response data or an error
217    pub async fn deactivate_dedicated_account(
218        &self,
219        dedicated_account_id: u64,
220    ) -> PaystackResult<DedicatedVirtualAccountResponseData> {
221        let url = format!("{}/{}", self.base_url, dedicated_account_id);
222        let body = json!({}); // empty body since the route takes none.
223
224        let response = self
225            .http
226            .delete(&url, &self.key, &body)
227            .await
228            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
229
230        let parsed_response: Response<DedicatedVirtualAccountResponseData> =
231            serde_json::from_str(&response)
232                .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
233
234        Ok(parsed_response)
235    }
236
237    /// Split a dedicated virtual account transaction with one or more accounts.
238    ///
239    /// # Arguments
240    /// * `split_dedocated_account_transaction_request` - The request data to split a transaction.
241    ///   It should be created with the `SplitDedicatedAccountTransactionRequestBuilder` struct.
242    ///
243    /// # Returns
244    /// A Result containing the dedicated virtual account response data or an error
245    pub async fn split_dedicated_account_transaction(
246        &self,
247        split_dedocated_account_transaction_request: SplitDedicatedAccountTransactionRequest,
248    ) -> PaystackResult<DedicatedVirtualAccountResponseData> {
249        let url = &self.base_url;
250        let body = serde_json::to_value(split_dedocated_account_transaction_request)
251            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
252
253        let response = self
254            .http
255            .post(url, &self.key, &body)
256            .await
257            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
258
259        let parsed_response: Response<DedicatedVirtualAccountResponseData> =
260            serde_json::from_str(&response)
261                .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
262
263        Ok(parsed_response)
264    }
265
266    /// If you've previously set up split payment for transactions on a dedicated virtual account, you can remove it with this endpoint
267    ///
268    /// # Arguments
269    /// * `account_number` - The account number of the dedicated virtual account to remove split from
270    ///
271    /// # Returns
272    /// A Result containing the dedicated virtual account response data or an error
273    pub async fn remove_split_from_dedicated_account(
274        &self,
275        account_number: String,
276    ) -> PaystackResult<DedicatedVirtualAccountResponseData> {
277        let url = &self.base_url;
278        let body = json!({
279            "account_number": account_number
280        });
281
282        let response = self
283            .http
284            .delete(url, &self.key, &body)
285            .await
286            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
287
288        let parsed_response: Response<DedicatedVirtualAccountResponseData> =
289            serde_json::from_str(&response)
290                .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
291
292        Ok(parsed_response)
293    }
294
295    /// Get available bank providers for a dedicated virtual account
296    ///
297    /// # Arguments
298    /// None
299    ///
300    /// # Returns
301    /// A Result containing a vector of bank provider data or an error
302    pub async fn fetch_bank_providers(&self) -> PaystackResult<Vec<BankProviderData>> {
303        let url = format!("{}/available_providers", self.base_url);
304
305        let response = self
306            .http
307            .get(&url, &self.key, None)
308            .await
309            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
310
311        let parsed_response: Response<Vec<BankProviderData>> = serde_json::from_str(&response)
312            .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
313
314        Ok(parsed_response)
315    }
316}