diff --git a/README.md b/README.md index c95be62..bc51662 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 fee6e6b..0866046 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 super::PAYSTACK_BASE_URL; +use crate::{ApplePayResponseData, HttpClient, PaystackAPIError, PaystackResult}; use serde_json::json; - -use crate::{ApplePayResponseData, HttpClient, PaystackAPIError, PaystackResult, Response}; +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", PAYSTACK_BASE_URL); ApplePayEndpoints { key: key.to_string(), base_url, @@ -47,23 +46,21 @@ impl ApplePayEndpoints { &self, domain_name: String, ) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = json!({ "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 @@ -71,42 +68,45 @@ 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.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, ) -> PaystackResult> { - let url = format!("{}", self.base_url); + let url = &self.base_url; let body = json!({ "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..b39b69d 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::PAYSTACK_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", PAYSTACK_BASE_URL); CustomersEndpoints { key: key.to_string(), base_url, @@ -52,22 +51,20 @@ 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()))?; - 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 @@ -83,24 +80,22 @@ 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(); 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 +111,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 +141,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 +171,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 +202,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 +230,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..e53a244 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::PAYSTACK_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", PAYSTACK_BASE_URL); DedicatedVirtualAccountEndpoints { key: key.to_string(), base_url, @@ -50,22 +49,21 @@ 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()))?; - 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. @@ -80,22 +78,20 @@ 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()))?; - 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. @@ -110,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 { @@ -133,18 +129,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 +155,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 +195,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 +221,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. @@ -256,22 +247,21 @@ 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()))?; - 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 @@ -285,23 +275,22 @@ 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 }); - 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 +303,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/mod.rs b/src/endpoints/mod.rs index f52dbb7..2138151 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 PAYSTACK_BASE_URL: &str = "https://api.paystack.co"; diff --git a/src/endpoints/subaccount.rs b/src/endpoints/subaccount.rs index e255b51..b821a04 100644 --- a/src/endpoints/subaccount.rs +++ b/src/endpoints/subaccount.rs @@ -3,8 +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::PAYSTACK_BASE_URL; use crate::{ - HttpClient, PaystackAPIError, PaystackResult, Response, SubaccountRequest, + CreateSubaccountRequest, HttpClient, PaystackAPIError, PaystackResult, Response, SubaccountsResponseData, }; use std::sync::Arc; @@ -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", PAYSTACK_BASE_URL); SubaccountEndpoints { key: key.to_string(), base_url, @@ -42,28 +43,113 @@ 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.to_string(); + let url = &self.base_url; 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) + } + + /// 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(); + + 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 dc53ff1..ec1aff1 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::PAYSTACK_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", PAYSTACK_BASE_URL); TerminalEndpoints { key: key.to_string(), base_url, @@ -56,18 +57,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 +84,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 +110,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 @@ -134,21 +131,20 @@ 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())]; - 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 +157,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 +186,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 +214,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 +242,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..f588340 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::PAYSTACK_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", PAYSTACK_BASE_URL); TransactionEndpoints { key: key.to_string(), base_url, @@ -55,20 +56,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; + 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()))?; - Ok(parsed_response) - } - Err(e) => { - // convert the error to a transaction error - Err(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 +80,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 @@ -111,24 +105,22 @@ 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(); 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 +136,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 +164,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 +196,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 +215,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 +259,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 +287,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..fda13f9 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::PAYSTACK_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", PAYSTACK_BASE_URL); TransactionSplitEndpoints { key: key.to_string(), base_url, @@ -50,21 +51,20 @@ 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()))?; - 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()))?; - 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) } /// Lists transaction splits available on your integration @@ -80,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 { @@ -93,18 +93,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 +119,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 +150,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,24 +180,23 @@ 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 /// /// # 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. /// @@ -215,16 +211,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..6c8e2aa 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::PAYSTACK_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", PAYSTACK_BASE_URL); VirtualTerminalEndpoints { key: key.to_string(), base_url, @@ -52,22 +51,21 @@ 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()))?; - 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 @@ -83,24 +81,23 @@ 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(); 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 +113,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 +144,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 +170,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 +200,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 +231,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 +261,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 +292,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 75% rename from src/models/subaccount.rs rename to src/models/subaccount_models.rs index 69db4bc..3ddb4e8 100644 --- a/src/models/subaccount.rs +++ b/src/models/subaccount_models.rs @@ -2,38 +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}; /// 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, } @@ -85,8 +101,15 @@ 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 + #[serde(default, deserialize_with = "bool_from_int_or_bool")] + pub active: Option, /// Settlement schedule of subaccount. pub settlement_schedule: Option, /// The ID of the subaccount. @@ -97,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/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 99% rename from src/models/transaction_split.rs rename to src/models/transaction_split_models.rs index 96d4808..101b88f 100644 --- a/src/models/transaction_split.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/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 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();