Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The client currently covers the following section of the API, and the sections t
- [x] Dedicated Virtual Account
- [x] Apple Pay
- [x] Subaccounts
- [ ] Plans
- [x] Plans
- [ ] Subscriptions
- [ ] Transfer Recipients
- [ ] Transfers
Expand Down
7 changes: 5 additions & 2 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
//! This file contains the Paystack API client, and it associated endpoints.
use crate::{
ApplePayEndpoints, CustomersEndpoints, DedicatedVirtualAccountEndpoints, HttpClient,
SubaccountEndpoints, TerminalEndpoints, TransactionEndpoints, TransactionSplitEndpoints,
VirtualTerminalEndpoints,
PlansEndpoints, SubaccountEndpoints, TerminalEndpoints, TransactionEndpoints,
TransactionSplitEndpoints, VirtualTerminalEndpoints,
};
use std::sync::Arc;

Expand All @@ -27,6 +27,8 @@ pub struct PaystackClient<T: HttpClient + Default> {
pub dedicated_virtual_account: DedicatedVirtualAccountEndpoints<T>,
/// Apple Pay API route
pub apple_pay: ApplePayEndpoints<T>,
/// Plans API route
pub plans: PlansEndpoints<T>,
}

impl<T: HttpClient + Default> PaystackClient<T> {
Expand All @@ -45,6 +47,7 @@ impl<T: HttpClient + Default> PaystackClient<T> {
Arc::clone(&http),
),
apple_pay: ApplePayEndpoints::new(Arc::clone(&key), Arc::clone(&http)),
plans: PlansEndpoints::new(Arc::clone(&key), Arc::clone(&http)),
}
}
}
8 changes: 4 additions & 4 deletions src/endpoints/apple_pay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl<T: HttpClient + Default> ApplePayEndpoints<T> {
/// # Returns
/// A new ApplePayEndpoints instance
pub fn new(key: Arc<String>, http: Arc<T>) -> ApplePayEndpoints<T> {
let base_url = format!("{}/apple-pay/domain", PAYSTACK_BASE_URL);
let base_url = format!("{PAYSTACK_BASE_URL}/apple-pay/domain");
ApplePayEndpoints {
key: key.to_string(),
base_url,
Expand All @@ -53,7 +53,7 @@ impl<T: HttpClient + Default> ApplePayEndpoints<T> {

let response = self
.http
.post(&url, &self.key, &body)
.post(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?;

Expand All @@ -72,7 +72,7 @@ impl<T: HttpClient + Default> ApplePayEndpoints<T> {

let response = self
.http
.get(&url, &self.key, None)
.get(url, &self.key, None)
.await
.map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?;

Expand Down Expand Up @@ -100,7 +100,7 @@ impl<T: HttpClient + Default> ApplePayEndpoints<T> {

let response = self
.http
.delete(&url, &self.key, &body)
.delete(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?;

Expand Down
6 changes: 3 additions & 3 deletions src/endpoints/customers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl<T: HttpClient + Default> CustomersEndpoints<T> {
/// # Returns
/// A new CustomersEndpoints instance
pub fn new(key: Arc<String>, http: Arc<T>) -> CustomersEndpoints<T> {
let base_url = format!("{}/customer", PAYSTACK_BASE_URL);
let base_url = format!("{PAYSTACK_BASE_URL}/customer");
CustomersEndpoints {
key: key.to_string(),
base_url,
Expand All @@ -57,7 +57,7 @@ impl<T: HttpClient + Default> CustomersEndpoints<T> {

let response = self
.http
.post(&url, &self.key, &body)
.post(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::Customer(e.to_string()))?;

Expand Down Expand Up @@ -88,7 +88,7 @@ impl<T: HttpClient + Default> CustomersEndpoints<T> {

let response = self
.http
.get(&url, &self.key, Some(&query))
.get(url, &self.key, Some(&query))
.await
.map_err(|e| PaystackAPIError::Customer(e.to_string()))?;

Expand Down
17 changes: 8 additions & 9 deletions src/endpoints/dedicated_virtual_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
/// # Returns
/// A new DedicatedVirtualAccountEndpoints instance
pub fn new(key: Arc<String>, http: Arc<T>) -> DedicatedVirtualAccountEndpoints<T> {
let base_url = format!("{}/dedicated_account", PAYSTACK_BASE_URL);
let base_url = format!("{PAYSTACK_BASE_URL}/dedicated_account");
DedicatedVirtualAccountEndpoints {
key: key.to_string(),
base_url,
Expand All @@ -55,7 +55,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {

let response = self
.http
.post(&url, &self.key, &body)
.post(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;

Expand Down Expand Up @@ -84,7 +84,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {

let response = self
.http
.post(&url, &self.key, &body)
.post(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;

Expand Down Expand Up @@ -131,7 +131,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
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))
.get(url, &self.key, Some(&query))
.await
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;

Expand Down Expand Up @@ -188,8 +188,8 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
("account_number", account_number),
("provider_slug", provider_slug),
];
if date.is_some() {
query.push(("date", date.unwrap()));
if let Some(value) = date {
query.push(("date", value));
}

// convert Vec<(&str, String)> to Vec<(&str, &str)>
Expand Down Expand Up @@ -242,7 +242,6 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
///
/// # Returns
/// A Result containing the dedicated virtual account response data or an error

pub async fn split_dedicated_account_transaction(
&self,
split_dedocated_account_transaction_request: SplitDedicatedAccountTransactionRequest,
Expand All @@ -253,7 +252,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {

let response = self
.http
.post(&url, &self.key, &body)
.post(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;

Expand Down Expand Up @@ -282,7 +281,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {

let response = self
.http
.delete(&url, &self.key, &body)
.delete(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;

Expand Down
2 changes: 2 additions & 0 deletions src/endpoints/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod apple_pay;
pub mod customers;
pub mod dedicated_virtual_account;
pub mod plans;
pub mod subaccount;
pub mod terminal;
pub mod transaction;
Expand All @@ -11,6 +12,7 @@ pub mod virtual_terminal;
pub use apple_pay::*;
pub use customers::*;
pub use dedicated_virtual_account::*;
pub use plans::*;
pub use subaccount::*;
pub use terminal::*;
pub use transaction::*;
Expand Down
167 changes: 167 additions & 0 deletions src/endpoints/plans.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
use std::{marker::PhantomData, sync::Arc};

use super::PAYSTACK_BASE_URL;
use crate::{
HttpClient, Interval, PaystackAPIError, PaystackResult, PlanRequest, PlanResponseData,
PlanStatus, PlanUpdateRequest, Response,
};

pub struct PlansEndpoints<T: HttpClient + Default> {
/// Paystack API Key
key: String,
/// Base URL for the plans route
base_url: String,
/// Http client for the route
http: Arc<T>,
}

/// Create a new `PlansEndpoints<T>` instance
///
/// # Arguments
/// - `key` - The Paystack API key
/// - `http`: The HTTP client implementation to use for the API requests
///
/// # Returns
/// A new PlansEndpoints instance
impl<T: HttpClient + Default> PlansEndpoints<T> {
pub fn new(key: Arc<String>, http: Arc<T>) -> PlansEndpoints<T> {
let base_url = format!("{PAYSTACK_BASE_URL}/plan");
PlansEndpoints {
key: key.to_string(),
base_url,
http,
}
}

/// Create a plan on your integration
///
/// # Arguments
/// * `plan_request` - The request data to create the plan.
/// Should be created with a `PlanRequestBuilder` struct.
///
/// # Returns
/// A Result containing the plan response data or an error
pub async fn create_plan(&self, plan_request: PlanRequest) -> PaystackResult<PlanResponseData> {
let url = &self.base_url;
let body = serde_json::to_value(plan_request)
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

let response = self
.http
.post(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

let parsed_response: Response<PlanResponseData> =
serde_json::from_str(&response).map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

Ok(parsed_response)
}

/// Lists plans available in your integration
///
/// # Arguments
/// * `per_page` - specify how many records you want to retrieve per page. Defaults to 50 if None
/// * `page` - specify exactly what page you want to retrieve. Defaults to 1 if None
/// * `status` - Optional parameter to filter list by plans with specified status
/// * `interval` - Optional parameter to filter list by plans with specified interval
/// * `amount`- Optional parameter to filter list by plans with specified amount using the supported currency
///
/// # Returns
/// A Result containing a vector of plan response data or an error
pub async fn list_plans(
&self,
per_page: Option<u8>,
page: Option<u8>,
status: Option<PlanStatus>,
interval: Option<Interval>,
amount: Option<u32>,
) -> PaystackResult<Vec<PlanResponseData>> {
let url = &self.base_url;

let per_page = per_page.unwrap_or(50).to_string();
let page = page.unwrap_or(1).to_string();

let mut query = vec![("perPage", per_page), ("page", page)];

// Process optional parameters
if let Some(s) = status {
query.push(("status", s.to_string()));
}

if let Some(i) = interval {
query.push(("interval", i.to_string()));
}

if let Some(a) = amount {
query.push(("amount", a.to_string()));
}

// convert all string to &str
// TODO: there has to be a cleaner way of doing this
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
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

let parsed_response: Response<Vec<PlanResponseData>> =
serde_json::from_str(&response).map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

Ok(parsed_response)
}

/// Get details of a plan on your integration
///
/// # Arguments
/// * `id_or_code` - the plan `ID` or `code` you want to fetch
///
/// # Returns
/// A Result containing the plan response data or an error
pub async fn fetch_plan(&self, id_or_code: String) -> PaystackResult<PlanResponseData> {
let url = format!("{}/{}", &self.base_url, id_or_code);

let response = self
.http
.get(&url, &self.key, None)
.await
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

let parsed_response: Response<PlanResponseData> =
serde_json::from_str(&response).map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

Ok(parsed_response)
}

/// Update a plan details on your integration
///
/// # Arguments
/// * `id_or_code` - the plan `ID` or `code` you want to update
/// * `plan_update_request` - The request data to update the plan with.
/// Should be created with a `PlanUpdateRequestBuilder` struct.
///
/// # Returns
/// A Result containing a success message if the plan has been updated
pub async fn update_plan(
&self,
id_or_code: String,
plan_update_request: PlanUpdateRequest,
) -> PaystackResult<PhantomData<String>> {
let url = format!("{}/{}", self.base_url, id_or_code);
let body = serde_json::to_value(plan_update_request)
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

let response = self
.http
.put(&url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

let parsed_response: Response<PhantomData<String>> =
serde_json::from_str(&response).map_err(|e| PaystackAPIError::Plan(e.to_string()))?;

Ok(parsed_response)
}
}
6 changes: 3 additions & 3 deletions src/endpoints/subaccount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::sync::Arc;
pub struct SubaccountEndpoints<T: HttpClient + Default> {
/// Paystack API Key
key: String,
/// Base URL for the transaction route
/// Base URL for the subaccount route
base_url: String,
/// Http client for the route
http: Arc<T>,
Expand All @@ -31,7 +31,7 @@ impl<T: HttpClient + Default> SubaccountEndpoints<T> {
/// # Returns
/// A new SubaccountEndpoints instance
pub fn new(key: Arc<String>, http: Arc<T>) -> SubaccountEndpoints<T> {
let base_url = format!("{}/subaccount", PAYSTACK_BASE_URL);
let base_url = format!("{PAYSTACK_BASE_URL}/subaccount");
SubaccountEndpoints {
key: key.to_string(),
base_url,
Expand All @@ -57,7 +57,7 @@ impl<T: HttpClient + Default> SubaccountEndpoints<T> {

let response = self
.http
.post(&url, &self.key, &body)
.post(url, &self.key, &body)
.await
.map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?;

Expand Down
Loading