From f160d7f265392f87572d0d1106a4daceed4393b3 Mon Sep 17 00:00:00 2001 From: Oghenemarho Orukele Date: Tue, 26 Aug 2025 18:28:14 +0200 Subject: [PATCH 1/3] chore: added better error handling to all the routes in the create - goal is to reduce use of match statements --- README.md | 2 +- src/endpoints/apple_pay.rs | 63 +++---- src/endpoints/customers.rs | 126 ++++++------- src/endpoints/dedicated_virtual_account.rs | 168 ++++++++---------- src/endpoints/subaccount.rs | 18 +- src/endpoints/terminal.rs | 144 +++++++-------- src/endpoints/transaction.rs | 165 ++++++++--------- src/endpoints/transaction_split.rs | 114 ++++++------ src/endpoints/virtual_terminal.rs | 167 ++++++++--------- .../{apple_pay.rs => apple_pay_models.rs} | 0 ...thorization.rs => authorization_models.rs} | 0 src/models/{bearer.rs => bearer_models.rs} | 0 src/models/{channel.rs => channel_models.rs} | 0 src/models/{charge.rs => charge_models.rs} | 0 .../{currency.rs => currency_models.rs} | 0 .../{customer.rs => customer_models.rs} | 0 ...rs => dedicated_virtual_account_models.rs} | 0 src/models/mod.rs | 68 +++---- .../{response.rs => response_models.rs} | 0 src/models/{split.rs => split_models.rs} | 0 src/models/{status.rs => status_models.rs} | 0 .../{subaccount.rs => subaccount_models.rs} | 0 ...subscription.rs => subscription_models.rs} | 0 .../{terminal.rs => terminal_models.rs} | 0 .../{transaction.rs => transaction_models.rs} | 0 ...n_split.rs => transaction_split_models.rs} | 0 ...terminal.rs => virtual_terminal_models.rs} | 0 27 files changed, 479 insertions(+), 556 deletions(-) rename src/models/{apple_pay.rs => apple_pay_models.rs} (100%) rename src/models/{authorization.rs => authorization_models.rs} (100%) rename src/models/{bearer.rs => bearer_models.rs} (100%) rename src/models/{channel.rs => channel_models.rs} (100%) rename src/models/{charge.rs => charge_models.rs} (100%) rename src/models/{currency.rs => currency_models.rs} (100%) rename src/models/{customer.rs => customer_models.rs} (100%) rename src/models/{dedicated_virtual_account.rs => dedicated_virtual_account_models.rs} (100%) rename src/models/{response.rs => response_models.rs} (100%) rename src/models/{split.rs => split_models.rs} (100%) rename src/models/{status.rs => status_models.rs} (100%) rename src/models/{subaccount.rs => subaccount_models.rs} (100%) rename src/models/{subscription.rs => subscription_models.rs} (100%) rename src/models/{terminal.rs => terminal_models.rs} (100%) rename src/models/{transaction.rs => transaction_models.rs} (100%) rename src/models/{transaction_split.rs => transaction_split_models.rs} (100%) rename src/models/{virtual_terminal.rs => virtual_terminal_models.rs} (100%) diff --git a/README.md b/README.md index b04f2b8..e7e23d1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The client currently covers the following section of the API, and the sections t - [x] Virtual Terminal - [x] Customers - [x] Dedicated Virtual Account -- [ ] Apple Pay +- [x] Apple Pay - [ ] Subaccounts - [ ] Plans - [ ] Subscriptions diff --git a/src/endpoints/apple_pay.rs b/src/endpoints/apple_pay.rs index fee6e6b..370bddc 100644 --- a/src/endpoints/apple_pay.rs +++ b/src/endpoints/apple_pay.rs @@ -5,7 +5,7 @@ use std::{marker::PhantomData, sync::Arc}; use serde_json::json; -use crate::{ApplePayResponseData, HttpClient, PaystackAPIError, PaystackResult, Response}; +use crate::{ApplePayResponseData, HttpClient, PaystackAPIError, PaystackResult}; #[derive(Debug, Clone)] pub struct ApplePayEndpoints { @@ -52,18 +52,16 @@ impl ApplePayEndpoints { "domainName": domain_name }); - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; + let parsed_response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::ApplePay(e.to_string())), - } + Ok(parsed_response) } /// Lists all domains registered on your integration @@ -73,20 +71,25 @@ impl ApplePayEndpoints { pub async fn list_domains(&self) -> PaystackResult { let url = format!("{}", self.base_url); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; + let parsed_response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::ApplePay(e.to_string())), - } + Ok(parsed_response) } + /// Unregister a top-level domain or subdomain previously used for your Apple Pay integration. + /// + /// # Arguments + /// * `domain_name` - The name of the domain to unregister + /// + /// # Returns + /// A result containing a success message without data. pub async fn unregister_domain( &self, domain_name: String, @@ -96,17 +99,15 @@ impl ApplePayEndpoints { "domainName": domain_name }); - let response = self.http.delete(&url, &self.key, &body).await; + let response = self + .http + .delete(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; + let parsed_response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::ApplePay(e.to_string())), - } + Ok(parsed_response) } } diff --git a/src/endpoints/customers.rs b/src/endpoints/customers.rs index c6174e0..6eee8d5 100644 --- a/src/endpoints/customers.rs +++ b/src/endpoints/customers.rs @@ -56,18 +56,16 @@ impl CustomersEndpoints { let body = serde_json::to_value(create_customer_request) .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Customer(e.to_string())), - } + Ok(parsed_response) } /// Lists customers available on your integration @@ -89,18 +87,16 @@ impl CustomersEndpoints { let page = page.unwrap_or(1).to_string(); let query = vec![("perPage", per_page.as_str()), ("page", page.as_str())]; - let response = self.http.get(&url, &self.key, Some(&query)).await; + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Customer(e.to_string())), - } + Ok(parsed_response) } /// Gets details of a customer on your integration @@ -116,18 +112,16 @@ impl CustomersEndpoints { ) -> PaystackResult { let url = format!("{}/{}", self.base_url, email_or_code); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Customer(e.to_string())), - } + Ok(parsed_response) } /// Updates a customer's details on your integration @@ -148,18 +142,16 @@ impl CustomersEndpoints { let body = serde_json::to_value(update_customer_request) .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - let response = self.http.put(&url, &self.key, &body).await; + let response = self + .http + .put(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Customer(e.to_string())), - } + Ok(parsed_response) } /// Validates a customer's identity @@ -180,18 +172,16 @@ impl CustomersEndpoints { let body = serde_json::to_value(customer_validation_request) .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Customer(e.to_string())), - } + Ok(parsed_response) } /// Whitelists or blacklists a customer on your integration @@ -213,18 +203,16 @@ impl CustomersEndpoints { "risk_action": risk_action }); - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Customer(e.to_string())), - } + Ok(parsed_response) } /// Deactivates an authorization when the card needs to be forgotten @@ -243,17 +231,15 @@ impl CustomersEndpoints { "authorization_code": authorization_code }); - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Customer(e.to_string())), - } + Ok(parsed_response) } } diff --git a/src/endpoints/dedicated_virtual_account.rs b/src/endpoints/dedicated_virtual_account.rs index ba5aade..c114376 100644 --- a/src/endpoints/dedicated_virtual_account.rs +++ b/src/endpoints/dedicated_virtual_account.rs @@ -54,18 +54,17 @@ impl DedicatedVirtualAccountEndpoints { let body = serde_json::to_value(create_dedicated_virtual_account_request) .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } /// Creates a customer, validates them and assigns a dedicated virtual account. @@ -84,18 +83,16 @@ impl DedicatedVirtualAccountEndpoints { let body = serde_json::to_value(assign_dedicated_virtual_account_request) .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } /// Lists dedicated virtual accounts available on your integration. @@ -133,18 +130,17 @@ impl DedicatedVirtualAccountEndpoints { // Transform String to &str using iter let query: Vec<(&str, &str)> = query.iter().map(|(k, v)| (*k, v.as_str())).collect(); - let response = self.http.get(&url, &self.key, Some(&query)).await; + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response> = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } /// Gets details of a dedicated virtual account on your integration @@ -160,18 +156,17 @@ impl DedicatedVirtualAccountEndpoints { ) -> PaystackResult { let url = format!("{}/{}", self.base_url, dedicated_account_id); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } /// Requery Dedicated Virtual Account for new transactions @@ -201,18 +196,16 @@ impl DedicatedVirtualAccountEndpoints { // convert Vec<(&str, String)> to Vec<(&str, &str)> let query: Vec<(&str, &str)> = query.iter().map(|(k, v)| (*k, v.as_str())).collect(); - let response = self.http.get(&url, &self.key, Some(&query)).await; + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } /// Deactivate a dedicated virtual account on your integration @@ -229,18 +222,17 @@ impl DedicatedVirtualAccountEndpoints { let url = format!("{}/{}", self.base_url, dedicated_account_id); let body = json!({}); // empty body since the route takes none. - let response = self.http.delete(&url, &self.key, &body).await; + let response = self + .http + .delete(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } /// Split a dedicated virtual account transaction with one or more accounts. @@ -260,18 +252,17 @@ impl DedicatedVirtualAccountEndpoints { let body = serde_json::to_value(split_dedocated_account_transaction_request) .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } /// If you've previously set up split payment for transactions on a dedicated virtual account, you can remove it with this endpoint @@ -290,18 +281,17 @@ impl DedicatedVirtualAccountEndpoints { "account_number": account_number }); - let response = self.http.delete(&url, &self.key, &body).await; + let response = self + .http + .delete(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } /// Get available bank providers for a dedicated virtual account @@ -314,17 +304,15 @@ impl DedicatedVirtualAccountEndpoints { pub async fn fetch_bank_providers(&self) -> PaystackResult> { let url = format!("{}/available_providers", self.base_url); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::DedicatedVirtualAccount(e.to_string())), - } + Ok(parsed_response) } } diff --git a/src/endpoints/subaccount.rs b/src/endpoints/subaccount.rs index e255b51..e21cd21 100644 --- a/src/endpoints/subaccount.rs +++ b/src/endpoints/subaccount.rs @@ -54,16 +54,14 @@ impl SubaccountEndpoints { let body = serde_json::to_value(subaccount_request) .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Subaccount(e.to_string())), - } + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; + Ok(parsed_response) } } diff --git a/src/endpoints/terminal.rs b/src/endpoints/terminal.rs index dc53ff1..92bd185 100644 --- a/src/endpoints/terminal.rs +++ b/src/endpoints/terminal.rs @@ -56,18 +56,16 @@ impl TerminalEndpoints { let body = serde_json::to_value(event_request) .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Terminal(e.to_string())), - } + Ok(parsed_response) } /// Check the status of an event sent to the Paystack Terminal @@ -85,18 +83,17 @@ impl TerminalEndpoints { ) -> PaystackResult { let url = format!("{}/{}/event/{}", self.base_url, terminal_id, event_id); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Terminal(e.to_string())), - } + Ok(parsed_response) } /// Check the availiability of a Terminal before sending an event to it @@ -112,18 +109,17 @@ impl TerminalEndpoints { ) -> PaystackResult { let url = format!("{}/{}/presence", self.base_url, terminal_id); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Terminal(e.to_string())), - } + Ok(parsed_response) } /// List the Terminals available on your integration @@ -138,17 +134,16 @@ impl TerminalEndpoints { let per_page = per_page.unwrap_or(50).to_string(); let query = vec![("perPage", per_page.as_str())]; - let response = self.http.get(&url, &self.key, Some(&query)).await; + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Terminal(e.to_string())), - } + Ok(parsed_response) } /// Get the details of a Terminal @@ -161,17 +156,16 @@ impl TerminalEndpoints { pub async fn fetch_terminal(&self, terminal_id: String) -> PaystackResult { let url = format!("{}/{}", self.base_url, terminal_id); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Terminal(e.to_string())), - } + Ok(parsed_response) } /// Update the details of a Terminal @@ -191,18 +185,16 @@ impl TerminalEndpoints { let body = serde_json::to_value(update_request) .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - let response = self.http.put(&url, &self.key, &body).await; + let response = self + .http + .put(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Terminal(e.to_string())), - } + Ok(parsed_response) } /// Activate your debug device by linking it to your integration @@ -221,18 +213,16 @@ impl TerminalEndpoints { "serial_number": serial_number }); - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Terminal(e.to_string())), - } + Ok(parsed_response) } /// Unlink your debug device from your integration @@ -251,17 +241,15 @@ impl TerminalEndpoints { "serial_number": serial_number }); - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Terminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Terminal(e.to_string())), - } + Ok(parsed_response) } } diff --git a/src/endpoints/transaction.rs b/src/endpoints/transaction.rs index ec3f638..e4d9033 100644 --- a/src/endpoints/transaction.rs +++ b/src/endpoints/transaction.rs @@ -55,20 +55,15 @@ impl TransactionEndpoints { let body = serde_json::to_value(transaction_request) .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; - - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => { - // convert the error to a transaction error - Err(PaystackAPIError::Transaction(e.to_string())) - } - } + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + Ok(parsed_response) } /// Verifies the status of a transaction @@ -84,18 +79,16 @@ impl TransactionEndpoints { ) -> PaystackResult { let url = format!("{}/verify/{}", self.base_url, reference); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Transaction(e.to_string())), - } + Ok(parsed_response) } /// Lists transactions carried out on your integration @@ -117,18 +110,16 @@ impl TransactionEndpoints { let status = status.unwrap_or(Status::Success).to_string(); let query = vec![("perPage", per_page.as_str()), ("status", status.as_str())]; - let response = self.http.get(&url, &self.key, Some(&query)).await; + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Transaction(e.to_string())), - } + Ok(parsed_response) } /// Gets details of a specific transaction @@ -144,18 +135,16 @@ impl TransactionEndpoints { ) -> PaystackResult { let url = format!("{}/{}", self.base_url, transaction_id); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Transaction(e.to_string())), - } + Ok(parsed_response) } /// Charges a reusable authorization @@ -174,17 +163,16 @@ impl TransactionEndpoints { let body = serde_json::to_value(charge_request) .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Charge(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response).map_err(|e| PaystackAPIError::Charge(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Transaction(e.to_string())), - } + Ok(parsed_response) } /// Views the timeline of a transaction @@ -207,18 +195,16 @@ impl TransactionEndpoints { } }?; // propagate the error upstream - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Transaction(e.to_string())), - } + Ok(parsed_response) } /// Gets the total amount received on your account @@ -228,18 +214,16 @@ impl TransactionEndpoints { pub async fn total_transactions(&self) -> PaystackResult { let url = format!("{}/totals", self.base_url); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Transaction(e.to_string())), - } + Ok(parsed_response) } /// Exports a list of transactions @@ -274,17 +258,16 @@ impl TransactionEndpoints { ("settled", settled.as_str()), ]; - let response = self.http.get(&url, &self.key, Some(&query)).await; + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response = serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + let parsed_response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Transaction(e.to_string())), - } + Ok(parsed_response) } /// Performs a partial debit on a transaction @@ -303,17 +286,15 @@ impl TransactionEndpoints { let body = serde_json::to_value(partial_debit_transaction_request) .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Transaction(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::Transaction(e.to_string())), - } + Ok(parsed_response) } } diff --git a/src/endpoints/transaction_split.rs b/src/endpoints/transaction_split.rs index 7eba03c..92c0683 100644 --- a/src/endpoints/transaction_split.rs +++ b/src/endpoints/transaction_split.rs @@ -54,17 +54,16 @@ impl TransactionSplitEndpoints { let body = serde_json::to_value(split_body) .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; - - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::TransactionSplit(e.to_string())), - } + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; + + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; + Ok(parsed_response) } /// Lists transaction splits available on your integration @@ -93,18 +92,17 @@ impl TransactionSplitEndpoints { ("active", &split_active), ]; - let response = self.http.get(&url, &self.key, Some(&query)).await; + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; + let parsed_response: Response> = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::TransactionSplit(e.to_string())), - } + Ok(parsed_response) } /// Gets details of a split on your integration @@ -120,18 +118,17 @@ impl TransactionSplitEndpoints { ) -> PaystackResult { let url = format!("{}/{}", self.base_url, split_id); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::TransactionSplit(e.to_string())), - } + Ok(parsed_response) } /// Updates a transaction split's details on your integration @@ -152,17 +149,16 @@ impl TransactionSplitEndpoints { let body = serde_json::to_value(update_body) .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - let response = self.http.put(&url, &self.key, &body).await; + let response = self + .http + .put(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::TransactionSplit(e.to_string())), - } + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; + Ok(parsed_response) } /// Adds a subaccount to a transaction split or updates an existing subaccount's share @@ -183,18 +179,17 @@ impl TransactionSplitEndpoints { let body = serde_json::to_value(body) .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::TransactionSplit(e.to_string())), - } + Ok(parsed_response) } /// Removes a subaccount from a transaction split @@ -215,16 +210,15 @@ impl TransactionSplitEndpoints { let body = serde_json::to_value(subaccount) .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::TransactionSplit(e.to_string())), - } + Ok(parsed_response) } } diff --git a/src/endpoints/virtual_terminal.rs b/src/endpoints/virtual_terminal.rs index 0841228..9e63b84 100644 --- a/src/endpoints/virtual_terminal.rs +++ b/src/endpoints/virtual_terminal.rs @@ -56,18 +56,17 @@ impl VirtualTerminalEndpoints { let body = serde_json::to_value(virtual_terminal_request) .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } /// Lists virtual terminals available on your integration @@ -89,18 +88,17 @@ impl VirtualTerminalEndpoints { let query = vec![("status", status.as_str()), ("perPage", per_page.as_str())]; - let response = self.http.get(&url, &self.key, Some(&query)).await; + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response> = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } /// Gets details of a virtual terminal on your integration @@ -116,18 +114,17 @@ impl VirtualTerminalEndpoints { ) -> PaystackResult { let url = format!("{}/{}", self.base_url, code); - let response = self.http.get(&url, &self.key, None).await; + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } /// Updates a virtual terminal on your integration @@ -148,18 +145,16 @@ impl VirtualTerminalEndpoints { "name": name }); - let response = self.http.put(&url, &self.key, &body).await; + let response = self + .http + .put(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } /// Deactivates a virtual terminal on your integration @@ -176,18 +171,16 @@ impl VirtualTerminalEndpoints { let url = format!("{}/{}/deactivate", self.base_url, code); let body = json!({}); // empty body cause the route takes none - let response = self.http.put(&url, &self.key, &body).await; + let response = self + .http + .put(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } /// Adds a WhatsApp destination number to a virtual terminal @@ -208,18 +201,17 @@ impl VirtualTerminalEndpoints { "destinations": destinations }); - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response> = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } /// Removes a WhatsApp destination number from a virtual terminal @@ -240,18 +232,16 @@ impl VirtualTerminalEndpoints { "targets": targets }); - let response = self.http.post(&url, &self.key, &body).await; + let response = self + .http + .post(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } /// Adds a split payment code to a virtual terminal @@ -272,18 +262,17 @@ impl VirtualTerminalEndpoints { "split_code": split_code }); - let response = self.http.put(&url, &self.key, &body).await; + let response = self + .http + .put(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } /// Removes a split payment code from a virtual terminal @@ -304,17 +293,15 @@ impl VirtualTerminalEndpoints { "split_code": split_code }); - let response = self.http.delete(&url, &self.key, &body).await; + let response = self + .http + .delete(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - match response { - Ok(response) => { - let parsed_response: Response> = - serde_json::from_str(&response) - .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; + let parsed_response: Response> = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; - Ok(parsed_response) - } - Err(e) => Err(PaystackAPIError::VirtualTerminal(e.to_string())), - } + Ok(parsed_response) } } diff --git a/src/models/apple_pay.rs b/src/models/apple_pay_models.rs similarity index 100% rename from src/models/apple_pay.rs rename to src/models/apple_pay_models.rs diff --git a/src/models/authorization.rs b/src/models/authorization_models.rs similarity index 100% rename from src/models/authorization.rs rename to src/models/authorization_models.rs diff --git a/src/models/bearer.rs b/src/models/bearer_models.rs similarity index 100% rename from src/models/bearer.rs rename to src/models/bearer_models.rs diff --git a/src/models/channel.rs b/src/models/channel_models.rs similarity index 100% rename from src/models/channel.rs rename to src/models/channel_models.rs diff --git a/src/models/charge.rs b/src/models/charge_models.rs similarity index 100% rename from src/models/charge.rs rename to src/models/charge_models.rs diff --git a/src/models/currency.rs b/src/models/currency_models.rs similarity index 100% rename from src/models/currency.rs rename to src/models/currency_models.rs diff --git a/src/models/customer.rs b/src/models/customer_models.rs similarity index 100% rename from src/models/customer.rs rename to src/models/customer_models.rs diff --git a/src/models/dedicated_virtual_account.rs b/src/models/dedicated_virtual_account_models.rs similarity index 100% rename from src/models/dedicated_virtual_account.rs rename to src/models/dedicated_virtual_account_models.rs diff --git a/src/models/mod.rs b/src/models/mod.rs index 3af72eb..13b7358 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,36 +1,36 @@ -pub mod apple_pay; -pub mod authorization; -pub mod bearer; -pub mod channel; -pub mod charge; -pub mod currency; -pub mod customer; -pub mod dedicated_virtual_account; -pub mod response; -pub mod split; -pub mod status; -pub mod subaccount; -pub mod subscription; -pub mod terminal; -pub mod transaction; -pub mod transaction_split; -pub mod virtual_terminal; +pub mod apple_pay_models; +pub mod authorization_models; +pub mod bearer_models; +pub mod channel_models; +pub mod charge_models; +pub mod currency_models; +pub mod customer_models; +pub mod dedicated_virtual_account_models; +pub mod response_models; +pub mod split_models; +pub mod status_models; +pub mod subaccount_models; +pub mod subscription_models; +pub mod terminal_models; +pub mod transaction_models; +pub mod transaction_split_models; +pub mod virtual_terminal_models; // public re-export -pub use apple_pay::*; -pub use authorization::*; -pub use bearer::*; -pub use channel::*; -pub use charge::*; -pub use currency::*; -pub use customer::*; -pub use dedicated_virtual_account::*; -pub use response::*; -pub use split::*; -pub use status::*; -pub use subaccount::*; -pub use subscription::*; -pub use terminal::*; -pub use transaction::*; -pub use transaction_split::*; -pub use virtual_terminal::*; +pub use apple_pay_models::*; +pub use authorization_models::*; +pub use bearer_models::*; +pub use channel_models::*; +pub use charge_models::*; +pub use currency_models::*; +pub use customer_models::*; +pub use dedicated_virtual_account_models::*; +pub use response_models::*; +pub use split_models::*; +pub use status_models::*; +pub use subaccount_models::*; +pub use subscription_models::*; +pub use terminal_models::*; +pub use transaction_models::*; +pub use transaction_split_models::*; +pub use virtual_terminal_models::*; diff --git a/src/models/response.rs b/src/models/response_models.rs similarity index 100% rename from src/models/response.rs rename to src/models/response_models.rs diff --git a/src/models/split.rs b/src/models/split_models.rs similarity index 100% rename from src/models/split.rs rename to src/models/split_models.rs diff --git a/src/models/status.rs b/src/models/status_models.rs similarity index 100% rename from src/models/status.rs rename to src/models/status_models.rs diff --git a/src/models/subaccount.rs b/src/models/subaccount_models.rs similarity index 100% rename from src/models/subaccount.rs rename to src/models/subaccount_models.rs diff --git a/src/models/subscription.rs b/src/models/subscription_models.rs similarity index 100% rename from src/models/subscription.rs rename to src/models/subscription_models.rs diff --git a/src/models/terminal.rs b/src/models/terminal_models.rs similarity index 100% rename from src/models/terminal.rs rename to src/models/terminal_models.rs diff --git a/src/models/transaction.rs b/src/models/transaction_models.rs similarity index 100% rename from src/models/transaction.rs rename to src/models/transaction_models.rs diff --git a/src/models/transaction_split.rs b/src/models/transaction_split_models.rs similarity index 100% rename from src/models/transaction_split.rs rename to src/models/transaction_split_models.rs diff --git a/src/models/virtual_terminal.rs b/src/models/virtual_terminal_models.rs similarity index 100% rename from src/models/virtual_terminal.rs rename to src/models/virtual_terminal_models.rs From 3cdefcc04fe0ff7f188f34a6255301e5eee26a85 Mon Sep 17 00:00:00 2001 From: Oghenemarho Orukele Date: Fri, 29 Aug 2025 10:55:20 +0200 Subject: [PATCH 2/3] chore: made base url a constant and moved it to the mod.rs file for top level access --- src/endpoints/apple_pay.rs | 15 +++++++-------- src/endpoints/customers.rs | 13 ++++++------- src/endpoints/dedicated_virtual_account.rs | 19 +++++++++---------- src/endpoints/mod.rs | 3 +++ src/endpoints/subaccount.rs | 22 ++++++++++++++++++++-- src/endpoints/terminal.rs | 9 +++++---- src/endpoints/transaction.rs | 5 +++-- src/endpoints/transaction_split.rs | 9 +++++---- src/endpoints/virtual_terminal.rs | 13 ++++++------- src/models/subaccount_models.rs | 8 ++++++++ 10 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/endpoints/apple_pay.rs b/src/endpoints/apple_pay.rs index 370bddc..1e07701 100644 --- a/src/endpoints/apple_pay.rs +++ b/src/endpoints/apple_pay.rs @@ -1,11 +1,10 @@ //! Apple Pay //! THe Apple Pay API allows you register your application's top-level domain or subdomain. -use std::{marker::PhantomData, sync::Arc}; - -use serde_json::json; - +use super::BASE_URL; use crate::{ApplePayResponseData, HttpClient, PaystackAPIError, PaystackResult}; +use serde_json::json; +use std::{marker::PhantomData, sync::Arc}; #[derive(Debug, Clone)] pub struct ApplePayEndpoints { @@ -28,7 +27,7 @@ impl ApplePayEndpoints { /// # Returns /// A new ApplePayEndpoints instance pub fn new(key: Arc, http: Arc) -> ApplePayEndpoints { - let base_url = String::from("https://api.paystack.co/apple-pay/domain"); + let base_url = format!("{}/apple-pay/domain", BASE_URL); ApplePayEndpoints { key: key.to_string(), base_url, @@ -47,7 +46,7 @@ impl ApplePayEndpoints { &self, domain_name: String, ) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = json!({ "domainName": domain_name }); @@ -69,7 +68,7 @@ impl ApplePayEndpoints { /// # Returns /// A Result containing the list of registered domains or an error pub async fn list_domains(&self) -> PaystackResult { - let url = format!("{}", self.base_url); + let url = &self.base_url; let response = self .http @@ -94,7 +93,7 @@ impl ApplePayEndpoints { &self, domain_name: String, ) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = json!({ "domainName": domain_name }); diff --git a/src/endpoints/customers.rs b/src/endpoints/customers.rs index 6eee8d5..a36d40b 100644 --- a/src/endpoints/customers.rs +++ b/src/endpoints/customers.rs @@ -2,14 +2,13 @@ //! ========= //! Thse Customers API allows you to create and maange customers on your integration -use std::{marker::PhantomData, sync::Arc}; - -use serde_json::json; - +use super::BASE_URL; use crate::{ CreateCustomerRequest, CustomerResponseData, HttpClient, PaystackAPIError, PaystackResult, Response, RiskAction, UpdateCustomerRequest, ValidateCustomerRequest, }; +use serde_json::json; +use std::{marker::PhantomData, sync::Arc}; /// A struct to hold all the functions of the customers API endpoint #[derive(Debug, Clone)] @@ -32,7 +31,7 @@ impl CustomersEndpoints { /// # Returns /// A new CustomersEndpoints instance pub fn new(key: Arc, http: Arc) -> CustomersEndpoints { - let base_url = String::from("https://api.paystack.co/customer"); + let base_url = format!("{}/customer", BASE_URL); CustomersEndpoints { key: key.to_string(), base_url, @@ -52,7 +51,7 @@ impl CustomersEndpoints { &self, create_customer_request: CreateCustomerRequest, ) -> PaystackResult { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = serde_json::to_value(create_customer_request) .map_err(|e| PaystackAPIError::Customer(e.to_string()))?; @@ -81,7 +80,7 @@ impl CustomersEndpoints { per_page: Option, page: Option, ) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let per_page = per_page.unwrap_or(50).to_string(); let page = page.unwrap_or(1).to_string(); diff --git a/src/endpoints/dedicated_virtual_account.rs b/src/endpoints/dedicated_virtual_account.rs index c114376..c5835dc 100644 --- a/src/endpoints/dedicated_virtual_account.rs +++ b/src/endpoints/dedicated_virtual_account.rs @@ -2,15 +2,14 @@ //! ========================= //! The Dedicated Virtual Account API enables Nigerian and Ghanaian merchants to manage unique payment accounts of their customers. -use std::{marker::PhantomData, sync::Arc}; - -use serde_json::json; - +use super::BASE_URL; use crate::{ BankProviderData, DedicatedVirtualAccountRequest, DedicatedVirtualAccountResponseData, HttpClient, ListDedicatedAccountFilter, PaystackAPIError, PaystackResult, Response, SplitDedicatedAccountTransactionRequest, }; +use serde_json::json; +use std::{marker::PhantomData, sync::Arc}; #[derive(Debug, Clone)] pub struct DedicatedVirtualAccountEndpoints { @@ -30,7 +29,7 @@ impl DedicatedVirtualAccountEndpoints { /// # Returns /// A new DedicatedVirtualAccountEndpoints instance pub fn new(key: Arc, http: Arc) -> DedicatedVirtualAccountEndpoints { - let base_url = String::from("https://api.paystack.co/dedicated_account"); + let base_url = format!("{}/dedicated_account", BASE_URL); DedicatedVirtualAccountEndpoints { key: key.to_string(), base_url, @@ -50,7 +49,7 @@ impl DedicatedVirtualAccountEndpoints { &self, create_dedicated_virtual_account_request: DedicatedVirtualAccountRequest, ) -> PaystackResult { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = serde_json::to_value(create_dedicated_virtual_account_request) .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; @@ -79,7 +78,7 @@ impl DedicatedVirtualAccountEndpoints { &self, assign_dedicated_virtual_account_request: DedicatedVirtualAccountRequest, ) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = serde_json::to_value(assign_dedicated_virtual_account_request) .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; @@ -107,7 +106,7 @@ impl DedicatedVirtualAccountEndpoints { &self, filter: Option, ) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let mut query = vec![]; // Build the query vec with the value in the filter struct if let Some(filter) = filter { @@ -248,7 +247,7 @@ impl DedicatedVirtualAccountEndpoints { &self, split_dedocated_account_transaction_request: SplitDedicatedAccountTransactionRequest, ) -> PaystackResult { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = serde_json::to_value(split_dedocated_account_transaction_request) .map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?; @@ -276,7 +275,7 @@ impl DedicatedVirtualAccountEndpoints { &self, account_number: String, ) -> PaystackResult { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = json!({ "account_number": account_number }); diff --git a/src/endpoints/mod.rs b/src/endpoints/mod.rs index f52dbb7..54f9ed0 100644 --- a/src/endpoints/mod.rs +++ b/src/endpoints/mod.rs @@ -16,3 +16,6 @@ pub use terminal::*; pub use transaction::*; pub use transaction_split::*; pub use virtual_terminal::*; + +// Const for the base url, since it is used multiple times +pub const BASE_URL: &str = "https://api.paystack.co"; diff --git a/src/endpoints/subaccount.rs b/src/endpoints/subaccount.rs index e21cd21..169ccee 100644 --- a/src/endpoints/subaccount.rs +++ b/src/endpoints/subaccount.rs @@ -3,6 +3,7 @@ //! The Subaccounts API allows you to create and manage subaccounts on your integration. //! Subaccounts can be used to split payment between two accounts (your main account and a subaccount). +use super::BASE_URL; use crate::{ HttpClient, PaystackAPIError, PaystackResult, Response, SubaccountRequest, SubaccountsResponseData, @@ -30,7 +31,7 @@ impl SubaccountEndpoints { /// # Returns /// A new SubaccountEndpoints instance pub fn new(key: Arc, http: Arc) -> SubaccountEndpoints { - let base_url = String::from("https://api.paystack.co/subaccount"); + let base_url = format!("{}/subaccount", BASE_URL); SubaccountEndpoints { key: key.to_string(), base_url, @@ -50,7 +51,7 @@ impl SubaccountEndpoints { &self, subaccount_request: SubaccountRequest, ) -> PaystackResult { - let url = self.base_url.to_string(); + let url = &self.base_url; let body = serde_json::to_value(subaccount_request) .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; @@ -64,4 +65,21 @@ impl SubaccountEndpoints { .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; Ok(parsed_response) } + + /// List subaccounts available on your integration. + /// + /// # Arguments + /// * `per_page` - Optional number of subaccounts to return per page. Defaults to 50 if None. + /// * `page` - Specify exactly what page you want to retrieve. Defaults to 1 if None. + /// + /// # Returns + /// A Result containing a vector of subaccount data or an error. + pub async fn list_subaccounts( + &self, + per_page: Option, + page: Option, + ) -> PaystackResult> { + let url = self.base_url.to_string(); + todo!() + } } diff --git a/src/endpoints/terminal.rs b/src/endpoints/terminal.rs index 92bd185..ad772ad 100644 --- a/src/endpoints/terminal.rs +++ b/src/endpoints/terminal.rs @@ -2,13 +2,14 @@ //! ======== //! The Terminal API allows you to build delightful in-person payment experiences. -use std::{marker::PhantomData, sync::Arc}; - use crate::{ EventRequest, FetchEventStatusResponseData, FetchTerminalStatusResponseData, HttpClient, PaystackAPIError, PaystackResult, Response, SendEventResponseData, TerminalData, UpdateTerminalRequest, }; +use std::{marker::PhantomData, sync::Arc}; + +use super::BASE_URL; /// A struct to hold all the functions of the terminal API endpoint #[derive(Debug, Clone)] @@ -31,7 +32,7 @@ impl TerminalEndpoints { /// # Returns /// A new TerminalEndpoints instance pub fn new(key: Arc, http: Arc) -> TerminalEndpoints { - let base_url = String::from("https://api.paystack.co/terminal"); + let base_url = format!("{}/terminal", BASE_URL); TerminalEndpoints { key: key.to_string(), base_url, @@ -130,7 +131,7 @@ impl TerminalEndpoints { /// # Returns /// A Result containing a vector of terminal data or an error pub async fn list_terminals(&self, per_page: Option) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let per_page = per_page.unwrap_or(50).to_string(); let query = vec![("perPage", per_page.as_str())]; diff --git a/src/endpoints/transaction.rs b/src/endpoints/transaction.rs index e4d9033..71a9d7a 100644 --- a/src/endpoints/transaction.rs +++ b/src/endpoints/transaction.rs @@ -2,6 +2,7 @@ //! ============= //! The Transaction route allows to create and manage payments on your integration. +use super::BASE_URL; use crate::{ ChargeRequest, ChargeResponseData, Currency, ExportTransactionData, HttpClient, PartialDebitTransactionRequest, PaystackAPIError, PaystackResult, Response, Status, @@ -31,7 +32,7 @@ impl TransactionEndpoints { /// # Returns /// A new TransactionEndpoints instance pub fn new(key: Arc, http: Arc) -> TransactionEndpoints { - let base_url = String::from("https://api.paystack.co/transaction"); + let base_url = format!("{}/transaction", BASE_URL); TransactionEndpoints { key: key.to_string(), base_url, @@ -104,7 +105,7 @@ impl TransactionEndpoints { per_page: Option, status: Option, ) -> PaystackResult> { - let url = self.base_url.to_string(); + let url = &self.base_url; let per_page = per_page.unwrap_or(10).to_string(); let status = status.unwrap_or(Status::Success).to_string(); diff --git a/src/endpoints/transaction_split.rs b/src/endpoints/transaction_split.rs index 92c0683..4ef38c2 100644 --- a/src/endpoints/transaction_split.rs +++ b/src/endpoints/transaction_split.rs @@ -3,6 +3,7 @@ //! The Transaction Splits API enables merchants split the settlement for a //! transaction across their payout account, and one or more subaccounts. +use super::BASE_URL; use crate::{ DeleteSubAccountBody, HttpClient, PaystackAPIError, PaystackResult, Response, SubaccountBody, TransactionSplitRequest, TransactionSplitResponseData, UpdateTransactionSplitRequest, @@ -30,7 +31,7 @@ impl TransactionSplitEndpoints { /// # Returns /// A new TransactionSplitEndpoints instance pub fn new(key: Arc, http: Arc) -> TransactionSplitEndpoints { - let base_url = String::from("https://api.paystack.co/split"); + let base_url = format!("{}/split", BASE_URL); TransactionSplitEndpoints { key: key.to_string(), base_url, @@ -50,7 +51,7 @@ impl TransactionSplitEndpoints { &self, split_body: TransactionSplitRequest, ) -> PaystackResult { - let url = self.base_url.to_string(); + let url = &self.base_url; let body = serde_json::to_value(split_body) .map_err(|e| PaystackAPIError::TransactionSplit(e.to_string()))?; @@ -79,7 +80,7 @@ impl TransactionSplitEndpoints { split_name: Option<&str>, split_active: Option, ) -> PaystackResult> { - let url = self.base_url.to_string(); + let url = &self.base_url; // Specify a default option for active splits let split_active = match split_active { @@ -195,7 +196,7 @@ impl TransactionSplitEndpoints { /// Removes a subaccount from a transaction split /// /// # Arguments - /// * `split_id` - ID of the transaction split + /// * `split_id` - ID of the transaction split. /// * `subaccount` - The subaccount data to remove. /// It should be created with a `DeleteSubAccountBody` struct. /// diff --git a/src/endpoints/virtual_terminal.rs b/src/endpoints/virtual_terminal.rs index 9e63b84..00fa77c 100644 --- a/src/endpoints/virtual_terminal.rs +++ b/src/endpoints/virtual_terminal.rs @@ -2,15 +2,14 @@ //! ================ //! The Virtual Terminal API allows you to accept in-person payments without a POS device. -use std::{marker::PhantomData, sync::Arc}; - -use serde_json::json; - +use super::BASE_URL; use crate::{ DestinationRequest, DestinationResponse, HttpClient, PaystackAPIError, PaystackResult, Response, TransactionSplitResponseData, VirtualTerminalRequestData, VirtualTerminalResponseData, VirtualTerminalStatus, }; +use serde_json::json; +use std::{marker::PhantomData, sync::Arc}; #[derive(Debug, Clone)] pub struct VirtualTerminalEndpoints { @@ -32,7 +31,7 @@ impl VirtualTerminalEndpoints { /// # Returns /// A new VirtualTerminalEndpoints instance pub fn new(key: Arc, http: Arc) -> VirtualTerminalEndpoints { - let base_url = String::from("https://api.paystack.co/virtual_terminal"); + let base_url = format!("{}/virtual_terminal", BASE_URL); VirtualTerminalEndpoints { key: key.to_string(), base_url, @@ -52,7 +51,7 @@ impl VirtualTerminalEndpoints { &self, virtual_terminal_request: VirtualTerminalRequestData, ) -> PaystackResult { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = serde_json::to_value(virtual_terminal_request) .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?; @@ -82,7 +81,7 @@ impl VirtualTerminalEndpoints { status: VirtualTerminalStatus, per_page: i32, ) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let status = status.to_string(); let per_page = per_page.to_string(); diff --git a/src/models/subaccount_models.rs b/src/models/subaccount_models.rs index 69db4bc..18cae57 100644 --- a/src/models/subaccount_models.rs +++ b/src/models/subaccount_models.rs @@ -5,6 +5,8 @@ use derive_builder::Builder; use serde::{Deserialize, Serialize}; +use super::Currency; + /// This struct is used to create the body for creating a subaccount on your integration. /// Use the `SubaccountRequestBuilder` to create this object. #[derive(Serialize, Debug, Builder, Default)] @@ -85,8 +87,14 @@ pub struct SubaccountsResponseData { pub is_verified: Option, /// The name of the settlement bank for the subaccount. pub settlement_bank: String, + /// The id of the settlement bank for the subaccount. + pub bank_id: Option, /// The account number of the subaccount. pub account_number: String, + /// Currency of the subaccount + pub currency: Option, + /// If the account is active or not, should be 1 for active and 0 for inactive + pub active: Option, /// Settlement schedule of subaccount. pub settlement_schedule: Option, /// The ID of the subaccount. From 7ed60e5afd1c3e3dae8046cd4b6e5b4fbeefadd0 Mon Sep 17 00:00:00 2001 From: Oghenemarho Orukele Date: Fri, 29 Aug 2025 12:58:31 +0200 Subject: [PATCH 3/3] feat: added support for the subaccounts api route --- README.md | 2 +- src/endpoints/apple_pay.rs | 4 +- src/endpoints/customers.rs | 4 +- src/endpoints/dedicated_virtual_account.rs | 4 +- src/endpoints/mod.rs | 2 +- src/endpoints/subaccount.rs | 82 ++++++++++++- src/endpoints/terminal.rs | 4 +- src/endpoints/transaction.rs | 4 +- src/endpoints/transaction_split.rs | 4 +- src/endpoints/virtual_terminal.rs | 4 +- src/models/subaccount_models.rs | 44 ++++--- src/models/transaction_split_models.rs | 1 + src/utils.rs | 21 ++++ tests/api/main.rs | 1 + tests/api/subaccount.rs | 135 +++++++++++++++++++++ tests/api/transaction_split.rs | 9 +- 16 files changed, 280 insertions(+), 45 deletions(-) create mode 100644 tests/api/subaccount.rs diff --git a/README.md b/README.md index e7e23d1..c958a1a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The client currently covers the following section of the API, and the sections t - [x] Customers - [x] Dedicated Virtual Account - [x] Apple Pay -- [ ] Subaccounts +- [x] Subaccounts - [ ] Plans - [ ] Subscriptions - [ ] Transfer Recipients diff --git a/src/endpoints/apple_pay.rs b/src/endpoints/apple_pay.rs index 1e07701..0866046 100644 --- a/src/endpoints/apple_pay.rs +++ b/src/endpoints/apple_pay.rs @@ -1,7 +1,7 @@ //! Apple Pay //! THe Apple Pay API allows you register your application's top-level domain or subdomain. -use super::BASE_URL; +use super::PAYSTACK_BASE_URL; use crate::{ApplePayResponseData, HttpClient, PaystackAPIError, PaystackResult}; use serde_json::json; use std::{marker::PhantomData, sync::Arc}; @@ -27,7 +27,7 @@ impl ApplePayEndpoints { /// # Returns /// A new ApplePayEndpoints instance pub fn new(key: Arc, http: Arc) -> ApplePayEndpoints { - let base_url = format!("{}/apple-pay/domain", BASE_URL); + let base_url = format!("{}/apple-pay/domain", PAYSTACK_BASE_URL); ApplePayEndpoints { key: key.to_string(), base_url, diff --git a/src/endpoints/customers.rs b/src/endpoints/customers.rs index a36d40b..b39b69d 100644 --- a/src/endpoints/customers.rs +++ b/src/endpoints/customers.rs @@ -2,7 +2,7 @@ //! ========= //! Thse Customers API allows you to create and maange customers on your integration -use super::BASE_URL; +use super::PAYSTACK_BASE_URL; use crate::{ CreateCustomerRequest, CustomerResponseData, HttpClient, PaystackAPIError, PaystackResult, Response, RiskAction, UpdateCustomerRequest, ValidateCustomerRequest, @@ -31,7 +31,7 @@ impl CustomersEndpoints { /// # Returns /// A new CustomersEndpoints instance pub fn new(key: Arc, http: Arc) -> CustomersEndpoints { - let base_url = format!("{}/customer", BASE_URL); + let base_url = format!("{}/customer", PAYSTACK_BASE_URL); CustomersEndpoints { key: key.to_string(), base_url, diff --git a/src/endpoints/dedicated_virtual_account.rs b/src/endpoints/dedicated_virtual_account.rs index c5835dc..e53a244 100644 --- a/src/endpoints/dedicated_virtual_account.rs +++ b/src/endpoints/dedicated_virtual_account.rs @@ -2,7 +2,7 @@ //! ========================= //! The Dedicated Virtual Account API enables Nigerian and Ghanaian merchants to manage unique payment accounts of their customers. -use super::BASE_URL; +use super::PAYSTACK_BASE_URL; use crate::{ BankProviderData, DedicatedVirtualAccountRequest, DedicatedVirtualAccountResponseData, HttpClient, ListDedicatedAccountFilter, PaystackAPIError, PaystackResult, Response, @@ -29,7 +29,7 @@ impl DedicatedVirtualAccountEndpoints { /// # Returns /// A new DedicatedVirtualAccountEndpoints instance pub fn new(key: Arc, http: Arc) -> DedicatedVirtualAccountEndpoints { - let base_url = format!("{}/dedicated_account", BASE_URL); + let base_url = format!("{}/dedicated_account", PAYSTACK_BASE_URL); DedicatedVirtualAccountEndpoints { key: key.to_string(), base_url, diff --git a/src/endpoints/mod.rs b/src/endpoints/mod.rs index 54f9ed0..2138151 100644 --- a/src/endpoints/mod.rs +++ b/src/endpoints/mod.rs @@ -18,4 +18,4 @@ pub use transaction_split::*; pub use virtual_terminal::*; // Const for the base url, since it is used multiple times -pub const BASE_URL: &str = "https://api.paystack.co"; +pub const PAYSTACK_BASE_URL: &str = "https://api.paystack.co"; diff --git a/src/endpoints/subaccount.rs b/src/endpoints/subaccount.rs index 169ccee..b821a04 100644 --- a/src/endpoints/subaccount.rs +++ b/src/endpoints/subaccount.rs @@ -3,9 +3,9 @@ //! The Subaccounts API allows you to create and manage subaccounts on your integration. //! Subaccounts can be used to split payment between two accounts (your main account and a subaccount). -use super::BASE_URL; +use super::PAYSTACK_BASE_URL; use crate::{ - HttpClient, PaystackAPIError, PaystackResult, Response, SubaccountRequest, + CreateSubaccountRequest, HttpClient, PaystackAPIError, PaystackResult, Response, SubaccountsResponseData, }; use std::sync::Arc; @@ -31,7 +31,7 @@ impl SubaccountEndpoints { /// # Returns /// A new SubaccountEndpoints instance pub fn new(key: Arc, http: Arc) -> SubaccountEndpoints { - let base_url = format!("{}/subaccount", BASE_URL); + let base_url = format!("{}/subaccount", PAYSTACK_BASE_URL); SubaccountEndpoints { key: key.to_string(), base_url, @@ -43,13 +43,13 @@ impl SubaccountEndpoints { /// /// # Arguments /// * `subaccount_request` - The request data to create the subaccount. - /// It should be created with the `SubaccountRequestBuilder` struct. + /// It should be created with the `CreateSubaccountRequestBuilder` struct. /// /// # Returns /// A Result containing the subaccount response data or an error pub async fn create_subaccount( &self, - subaccount_request: SubaccountRequest, + subaccount_request: CreateSubaccountRequest, ) -> PaystackResult { let url = &self.base_url; let body = serde_json::to_value(subaccount_request) @@ -80,6 +80,76 @@ impl SubaccountEndpoints { page: Option, ) -> PaystackResult> { let url = self.base_url.to_string(); - todo!() + + let per_page = per_page.unwrap_or(50).to_string(); + let page = page.unwrap_or(1).to_string(); + let query = vec![("perPage", per_page.as_str()), ("page", page.as_str())]; + + let response = self + .http + .get(&url, &self.key, Some(&query)) + .await + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; + + let parsed_response: Response> = + serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; + + Ok(parsed_response) + } + + /// Get the details of a subaccount on your integration + /// + /// # Arguments + /// * `id_or_code` - The subaccount ID or code you want to fetch + /// + /// # Returns + /// A Result containing the details of the subaccount or an error. + pub async fn fetch_subaccount( + &self, + id_or_code: String, + ) -> PaystackResult { + let url = format!("{}/{}", self.base_url, id_or_code); + + let response = self + .http + .get(&url, &self.key, None) + .await + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; + + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; + + Ok(parsed_response) + } + + /// Update a subaccount details in your integration + /// + /// # Arguments + /// * `id_or_code` - Subaccount's ID or code + /// * `update_request` - The request data to update the subaccount. + /// It should be created with the `CreateSubaccountRequestBuilder` struct. + /// + /// # Returns + /// A Result containing the updated subaccount response data or an error + pub async fn update_subaccount( + &self, + id_or_code: String, + update_request: CreateSubaccountRequest, + ) -> PaystackResult { + let url = format!("{}/{}", self.base_url, id_or_code); + let body = serde_json::to_value(update_request) + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; + + let response = self + .http + .put(&url, &self.key, &body) + .await + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; + + let parsed_response: Response = serde_json::from_str(&response) + .map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?; + + Ok(parsed_response) } } diff --git a/src/endpoints/terminal.rs b/src/endpoints/terminal.rs index ad772ad..ec1aff1 100644 --- a/src/endpoints/terminal.rs +++ b/src/endpoints/terminal.rs @@ -9,7 +9,7 @@ use crate::{ }; use std::{marker::PhantomData, sync::Arc}; -use super::BASE_URL; +use super::PAYSTACK_BASE_URL; /// A struct to hold all the functions of the terminal API endpoint #[derive(Debug, Clone)] @@ -32,7 +32,7 @@ impl TerminalEndpoints { /// # Returns /// A new TerminalEndpoints instance pub fn new(key: Arc, http: Arc) -> TerminalEndpoints { - let base_url = format!("{}/terminal", BASE_URL); + let base_url = format!("{}/terminal", PAYSTACK_BASE_URL); TerminalEndpoints { key: key.to_string(), base_url, diff --git a/src/endpoints/transaction.rs b/src/endpoints/transaction.rs index 71a9d7a..f588340 100644 --- a/src/endpoints/transaction.rs +++ b/src/endpoints/transaction.rs @@ -2,7 +2,7 @@ //! ============= //! The Transaction route allows to create and manage payments on your integration. -use super::BASE_URL; +use super::PAYSTACK_BASE_URL; use crate::{ ChargeRequest, ChargeResponseData, Currency, ExportTransactionData, HttpClient, PartialDebitTransactionRequest, PaystackAPIError, PaystackResult, Response, Status, @@ -32,7 +32,7 @@ impl TransactionEndpoints { /// # Returns /// A new TransactionEndpoints instance pub fn new(key: Arc, http: Arc) -> TransactionEndpoints { - let base_url = format!("{}/transaction", BASE_URL); + let base_url = format!("{}/transaction", PAYSTACK_BASE_URL); TransactionEndpoints { key: key.to_string(), base_url, diff --git a/src/endpoints/transaction_split.rs b/src/endpoints/transaction_split.rs index 4ef38c2..fda13f9 100644 --- a/src/endpoints/transaction_split.rs +++ b/src/endpoints/transaction_split.rs @@ -3,7 +3,7 @@ //! The Transaction Splits API enables merchants split the settlement for a //! transaction across their payout account, and one or more subaccounts. -use super::BASE_URL; +use super::PAYSTACK_BASE_URL; use crate::{ DeleteSubAccountBody, HttpClient, PaystackAPIError, PaystackResult, Response, SubaccountBody, TransactionSplitRequest, TransactionSplitResponseData, UpdateTransactionSplitRequest, @@ -31,7 +31,7 @@ impl TransactionSplitEndpoints { /// # Returns /// A new TransactionSplitEndpoints instance pub fn new(key: Arc, http: Arc) -> TransactionSplitEndpoints { - let base_url = format!("{}/split", BASE_URL); + let base_url = format!("{}/split", PAYSTACK_BASE_URL); TransactionSplitEndpoints { key: key.to_string(), base_url, diff --git a/src/endpoints/virtual_terminal.rs b/src/endpoints/virtual_terminal.rs index 00fa77c..6c8e2aa 100644 --- a/src/endpoints/virtual_terminal.rs +++ b/src/endpoints/virtual_terminal.rs @@ -2,7 +2,7 @@ //! ================ //! The Virtual Terminal API allows you to accept in-person payments without a POS device. -use super::BASE_URL; +use super::PAYSTACK_BASE_URL; use crate::{ DestinationRequest, DestinationResponse, HttpClient, PaystackAPIError, PaystackResult, Response, TransactionSplitResponseData, VirtualTerminalRequestData, @@ -31,7 +31,7 @@ impl VirtualTerminalEndpoints { /// # Returns /// A new VirtualTerminalEndpoints instance pub fn new(key: Arc, http: Arc) -> VirtualTerminalEndpoints { - let base_url = format!("{}/virtual_terminal", BASE_URL); + let base_url = format!("{}/virtual_terminal", PAYSTACK_BASE_URL); VirtualTerminalEndpoints { key: key.to_string(), base_url, diff --git a/src/models/subaccount_models.rs b/src/models/subaccount_models.rs index 18cae57..3ddb4e8 100644 --- a/src/models/subaccount_models.rs +++ b/src/models/subaccount_models.rs @@ -2,40 +2,54 @@ //! ============== //! This file contains the models for working with the subaccounts endpoint. +use super::Currency; +use crate::utils::bool_from_int_or_bool; use derive_builder::Builder; use serde::{Deserialize, Serialize}; -use super::Currency; - /// This struct is used to create the body for creating a subaccount on your integration. /// Use the `SubaccountRequestBuilder` to create this object. #[derive(Serialize, Debug, Builder, Default)] -pub struct SubaccountRequest { +pub struct CreateSubaccountRequest { /// Name of business for subaccount - business_name: String, + #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] + business_name: Option, /// Bank Code for the bank. /// You can get the list of Bank Codes by calling the List Banks endpoint. - settlement_bank: String, + #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] + settlement_bank: Option, /// Bank Account Number - account_number: String, + #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] + account_number: Option, /// The default percentage charged when receiving on behalf of this subaccount - percentage_charge: f32, + #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] + percentage_charge: Option, /// A description for this subaccount - description: String, + #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] + description: Option, /// A contact email for the subaccount #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] primary_contact_email: Option, /// A name for the contact person for this subaccount #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] primary_contact_name: Option, /// A phone number to call for this subaccount #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] primary_contact_phone: Option, /// Stringified JSON object. /// Add a custom_fields attribute which has an array of objects if you would like the fields to be /// added to your transaction when displayed on the dashboard. /// Sample: {"custom_fields":[{"display_name":"Cart ID","variable_name": "cart_id","value": "8393"}]} #[builder(setter(strip_option), default)] + #[serde(skip_serializing_if = "Option::is_none")] metadata: Option, } @@ -94,6 +108,7 @@ pub struct SubaccountsResponseData { /// Currency of the subaccount pub currency: Option, /// If the account is active or not, should be 1 for active and 0 for inactive + #[serde(default, deserialize_with = "bool_from_int_or_bool")] pub active: Option, /// Settlement schedule of subaccount. pub settlement_schedule: Option, @@ -105,17 +120,8 @@ pub struct SubaccountsResponseData { /// Last update time of subaccount. #[serde(rename = "updatedAt")] pub updated_at: Option, -} - -/// Represents the JSON response for fetch subaccount. -#[derive(Debug, Deserialize, Serialize, Default)] -pub struct FetchSubaccountResponse { - /// The status of the JSON response. - pub status: bool, - /// The message associated with the JSON response. - pub message: String, - /// Fetch Subaccount response data. - pub data: SubaccountsResponseData, + pub product: Option, + pub managed_by_integration: Option, } /// This struct is used to create the body for deleting a subaccount on your integration. diff --git a/src/models/transaction_split_models.rs b/src/models/transaction_split_models.rs index 96d4808..101b88f 100644 --- a/src/models/transaction_split_models.rs +++ b/src/models/transaction_split_models.rs @@ -44,6 +44,7 @@ pub struct TransactionSplitResponseData { /// The split code of the percentage split. pub split_code: String, /// Indicates whether the percentage split is active or not. + #[serde(default)] pub active: Option, /// The bearer type of the percentage split. pub bearer_type: String, diff --git a/src/utils.rs b/src/utils.rs index 4bcad73..c9834e6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,6 @@ use serde::de::Error; +use serde::{Deserialize, Deserializer}; +use serde_json::Value; use std::fmt::Formatter; use std::str::FromStr; @@ -152,3 +154,22 @@ where deserializer.deserialize_option(OptionStringOrNumberVisitor) } + +pub fn bool_from_int_or_bool<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let v: Option = Option::deserialize(deserializer)?; + match v { + Some(Value::Bool(b)) => Ok(Some(b)), + Some(Value::Number(n)) => { + if let Some(i) = n.as_i64() { + Ok(Some(i != 0)) + } else { + Err(serde::de::Error::custom("Invalid number for bool")) + } + } + Some(Value::Null) | None => Ok(None), + _ => Err(serde::de::Error::custom("Expected bool or int")), + } +} diff --git a/tests/api/main.rs b/tests/api/main.rs index dedc5e2..81740fa 100644 --- a/tests/api/main.rs +++ b/tests/api/main.rs @@ -3,6 +3,7 @@ pub mod charge; pub mod customer; pub mod dedicated_virtual_account; pub mod helpers; +pub mod subaccount; pub mod terminal; pub mod transaction; pub mod transaction_split; diff --git a/tests/api/subaccount.rs b/tests/api/subaccount.rs new file mode 100644 index 0000000..d3d65da --- /dev/null +++ b/tests/api/subaccount.rs @@ -0,0 +1,135 @@ +use fake::{ + faker::{company::zh_tw::CompanyName, lorem::en::Sentence}, + Fake, +}; +use paystack::CreateSubaccountRequestBuilder; + +use crate::helpers::{get_bank_account_number_and_code, get_paystack_client}; + +#[tokio::test] +async fn create_a_subaccount() { + // Arrange + let client = get_paystack_client(); + let (account_number, bank_code, _) = get_bank_account_number_and_code(); + + // Act + let business_name: String = CompanyName().fake(); + let description: String = Sentence(5..10).fake(); + let body = CreateSubaccountRequestBuilder::default() + .business_name(business_name.clone()) + .settlement_bank(bank_code.clone()) + .account_number(account_number) + .percentage_charge(30.0) + .description(description.clone()) + .build() + .expect("unable to build sub account request"); + + let res = client + .subaccount + .create_subaccount(body) + .await + .expect("unable to create subaccount"); + + // Assert + assert!(&res.status); + assert_eq!("Subaccount created", &res.message); + let data = res.data.unwrap(); // had to unwrap here + assert_eq!(data.business_name, business_name); + assert_eq!(data.description.unwrap(), description); +} + +#[tokio::test] +async fn list_all_subaccounts_in_the_integration() { + // Arrange + let client = get_paystack_client(); + + // Act + let res = client + .subaccount + .list_subaccounts(Some(5), None) + .await + .expect("unable to get list of subaccounts in the integration"); + + // Assert + assert!(res.status); + assert_eq!(res.message, "Subaccounts retrieved"); + assert!(res.data.unwrap().len() > 0); +} + +#[tokio::test] +async fn fetch_subaccount() { + // Arrange + let client = get_paystack_client(); + + // get an exisiting subaccount or error out + let sub_account = client + .subaccount + .list_subaccounts(Some(1), None) + .await + .expect("unable to get exisiting subaccounts"); + let sub_account_data = sub_account.data.unwrap(); + assert!( + sub_account_data.len() > 0, + "No exisiting subaccounts, create one and try again" + ); + + // get subaccount code from exisiting subaccount + let sub_account_code = sub_account_data[0].subaccount_code.clone(); + + // Act + let res = client + .subaccount + .fetch_subaccount(sub_account_code) + .await + .expect("unable to fetch sub account with code"); + + // Assert + assert!(res.status); + assert_eq!(res.message, "Subaccount retrieved"); + assert_eq!( + res.data.unwrap().subaccount_code, + sub_account_data[0].subaccount_code, + ) +} + +#[tokio::test] +async fn update_subaccount() { + // Arrange + let client = get_paystack_client(); + + // get an exisiting subaccount or error out + let sub_accounts = client + .subaccount + .list_subaccounts(Some(2), None) + .await + .expect("unable to get exisiting subaccounts"); + let sub_accounts_data = sub_accounts.data.as_ref().unwrap(); + assert!( + sub_accounts_data.len() > 1, + "No exisiting subaccounts, create one and try again" + ); + + // get subaccount code and prepare update request + let sub_account = &sub_accounts_data[0]; + + let sub_account_code = sub_account.subaccount_code.clone(); + let update_request = CreateSubaccountRequestBuilder::default() + .business_name("New business name".to_string()) + .description("new description".to_string()) + .build() + .expect("unable to build subaccount update request"); + + // Act + let res = client + .subaccount + .update_subaccount(sub_account_code, update_request) + .await + .expect("unable to update subaccount"); + + // Assert + assert!(res.status); + assert_eq!(res.message, "Subaccount updated"); + let updated_data = res.data.as_ref().unwrap(); + assert_ne!(updated_data.business_name, sub_account.business_name); + assert_eq!(updated_data.business_name, "New business name"); +} diff --git a/tests/api/transaction_split.rs b/tests/api/transaction_split.rs index eba601d..52c88ab 100644 --- a/tests/api/transaction_split.rs +++ b/tests/api/transaction_split.rs @@ -4,9 +4,9 @@ use fake::{ Fake, }; use paystack::{ - Currency, DeleteSubAccountBody, PaystackClient, ReqwestClient, SubaccountBody, - SubaccountBodyBuilder, SubaccountRequestBuilder, TransactionSplitRequest, - TransactionSplitRequestBuilder, UpdateTransactionSplitRequestBuilder, + CreateSubaccountRequestBuilder, Currency, DeleteSubAccountBody, PaystackClient, ReqwestClient, + SubaccountBody, SubaccountBodyBuilder, TransactionSplitRequest, TransactionSplitRequestBuilder, + UpdateTransactionSplitRequestBuilder, }; async fn create_subaccount_body( @@ -19,7 +19,7 @@ async fn create_subaccount_body( let business_name: String = CompanyName().fake(); let description: String = Sentence(5..10).fake(); - let body = SubaccountRequestBuilder::default() + let body = CreateSubaccountRequestBuilder::default() .business_name(business_name) .settlement_bank(bank_code.clone()) .account_number(account_number.clone()) @@ -334,6 +334,7 @@ async fn remove_a_subaccount_from_a_transaction_split_passes_with_valid_data() { .create_transaction_split(split_body) .await .expect("Failed to create transaction split"); + let data = transaction_split.data.unwrap(); let split_id = data.id.to_string();