Skip to content

Commit 28a435c

Browse files
committed
Reapply "feat(plan): Added support for the plan API endpoint."
This reverts commit 8009827.
1 parent a5f9672 commit 28a435c

33 files changed

Lines changed: 830 additions & 66 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The client currently covers the following section of the API, and the sections t
2121
- [x] Dedicated Virtual Account
2222
- [x] Apple Pay
2323
- [x] Subaccounts
24-
- [ ] Plans
24+
- [x] Plans
2525
- [ ] Subscriptions
2626
- [ ] Transfer Recipients
2727
- [ ] Transfers

src/client.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
//! This file contains the Paystack API client, and it associated endpoints.
44
use crate::{
55
ApplePayEndpoints, CustomersEndpoints, DedicatedVirtualAccountEndpoints, HttpClient,
6-
SubaccountEndpoints, TerminalEndpoints, TransactionEndpoints, TransactionSplitEndpoints,
7-
VirtualTerminalEndpoints,
6+
PlansEndpoints, SubaccountEndpoints, TerminalEndpoints, TransactionEndpoints,
7+
TransactionSplitEndpoints, VirtualTerminalEndpoints,
88
};
99
use std::sync::Arc;
1010

@@ -27,6 +27,8 @@ pub struct PaystackClient<T: HttpClient + Default> {
2727
pub dedicated_virtual_account: DedicatedVirtualAccountEndpoints<T>,
2828
/// Apple Pay API route
2929
pub apple_pay: ApplePayEndpoints<T>,
30+
/// Plans API route
31+
pub plans: PlansEndpoints<T>,
3032
}
3133

3234
impl<T: HttpClient + Default> PaystackClient<T> {
@@ -45,6 +47,7 @@ impl<T: HttpClient + Default> PaystackClient<T> {
4547
Arc::clone(&http),
4648
),
4749
apple_pay: ApplePayEndpoints::new(Arc::clone(&key), Arc::clone(&http)),
50+
plans: PlansEndpoints::new(Arc::clone(&key), Arc::clone(&http)),
4851
}
4952
}
5053
}

src/endpoints/apple_pay.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl<T: HttpClient + Default> ApplePayEndpoints<T> {
2727
/// # Returns
2828
/// A new ApplePayEndpoints instance
2929
pub fn new(key: Arc<String>, http: Arc<T>) -> ApplePayEndpoints<T> {
30-
let base_url = format!("{}/apple-pay/domain", PAYSTACK_BASE_URL);
30+
let base_url = format!("{PAYSTACK_BASE_URL}/apple-pay/domain");
3131
ApplePayEndpoints {
3232
key: key.to_string(),
3333
base_url,
@@ -53,7 +53,7 @@ impl<T: HttpClient + Default> ApplePayEndpoints<T> {
5353

5454
let response = self
5555
.http
56-
.post(&url, &self.key, &body)
56+
.post(url, &self.key, &body)
5757
.await
5858
.map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?;
5959

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

7373
let response = self
7474
.http
75-
.get(&url, &self.key, None)
75+
.get(url, &self.key, None)
7676
.await
7777
.map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?;
7878

@@ -100,7 +100,7 @@ impl<T: HttpClient + Default> ApplePayEndpoints<T> {
100100

101101
let response = self
102102
.http
103-
.delete(&url, &self.key, &body)
103+
.delete(url, &self.key, &body)
104104
.await
105105
.map_err(|e| PaystackAPIError::ApplePay(e.to_string()))?;
106106

src/endpoints/customers.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ impl<T: HttpClient + Default> CustomersEndpoints<T> {
3131
/// # Returns
3232
/// A new CustomersEndpoints instance
3333
pub fn new(key: Arc<String>, http: Arc<T>) -> CustomersEndpoints<T> {
34-
let base_url = format!("{}/customer", PAYSTACK_BASE_URL);
34+
let base_url = format!("{PAYSTACK_BASE_URL}/customer");
3535
CustomersEndpoints {
3636
key: key.to_string(),
3737
base_url,
@@ -57,7 +57,7 @@ impl<T: HttpClient + Default> CustomersEndpoints<T> {
5757

5858
let response = self
5959
.http
60-
.post(&url, &self.key, &body)
60+
.post(url, &self.key, &body)
6161
.await
6262
.map_err(|e| PaystackAPIError::Customer(e.to_string()))?;
6363

@@ -88,7 +88,7 @@ impl<T: HttpClient + Default> CustomersEndpoints<T> {
8888

8989
let response = self
9090
.http
91-
.get(&url, &self.key, Some(&query))
91+
.get(url, &self.key, Some(&query))
9292
.await
9393
.map_err(|e| PaystackAPIError::Customer(e.to_string()))?;
9494

src/endpoints/dedicated_virtual_account.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
2929
/// # Returns
3030
/// A new DedicatedVirtualAccountEndpoints instance
3131
pub fn new(key: Arc<String>, http: Arc<T>) -> DedicatedVirtualAccountEndpoints<T> {
32-
let base_url = format!("{}/dedicated_account", PAYSTACK_BASE_URL);
32+
let base_url = format!("{PAYSTACK_BASE_URL}/dedicated_account");
3333
DedicatedVirtualAccountEndpoints {
3434
key: key.to_string(),
3535
base_url,
@@ -55,7 +55,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
5555

5656
let response = self
5757
.http
58-
.post(&url, &self.key, &body)
58+
.post(url, &self.key, &body)
5959
.await
6060
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
6161

@@ -84,7 +84,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
8484

8585
let response = self
8686
.http
87-
.post(&url, &self.key, &body)
87+
.post(url, &self.key, &body)
8888
.await
8989
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
9090

@@ -131,7 +131,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
131131
let query: Vec<(&str, &str)> = query.iter().map(|(k, v)| (*k, v.as_str())).collect();
132132
let response = self
133133
.http
134-
.get(&url, &self.key, Some(&query))
134+
.get(url, &self.key, Some(&query))
135135
.await
136136
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
137137

@@ -188,8 +188,8 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
188188
("account_number", account_number),
189189
("provider_slug", provider_slug),
190190
];
191-
if date.is_some() {
192-
query.push(("date", date.unwrap()));
191+
if let Some(value) = date {
192+
query.push(("date", value));
193193
}
194194

195195
// convert Vec<(&str, String)> to Vec<(&str, &str)>
@@ -242,7 +242,6 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
242242
///
243243
/// # Returns
244244
/// A Result containing the dedicated virtual account response data or an error
245-
246245
pub async fn split_dedicated_account_transaction(
247246
&self,
248247
split_dedocated_account_transaction_request: SplitDedicatedAccountTransactionRequest,
@@ -253,7 +252,7 @@ impl<T: HttpClient + Default> DedicatedVirtualAccountEndpoints<T> {
253252

254253
let response = self
255254
.http
256-
.post(&url, &self.key, &body)
255+
.post(url, &self.key, &body)
257256
.await
258257
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
259258

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

283282
let response = self
284283
.http
285-
.delete(&url, &self.key, &body)
284+
.delete(url, &self.key, &body)
286285
.await
287286
.map_err(|e| PaystackAPIError::DedicatedVirtualAccount(e.to_string()))?;
288287

src/endpoints/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod apple_pay;
22
pub mod customers;
33
pub mod dedicated_virtual_account;
4+
pub mod plans;
45
pub mod subaccount;
56
pub mod terminal;
67
pub mod transaction;
@@ -11,6 +12,7 @@ pub mod virtual_terminal;
1112
pub use apple_pay::*;
1213
pub use customers::*;
1314
pub use dedicated_virtual_account::*;
15+
pub use plans::*;
1416
pub use subaccount::*;
1517
pub use terminal::*;
1618
pub use transaction::*;

src/endpoints/plans.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
use std::{marker::PhantomData, sync::Arc};
2+
3+
use super::PAYSTACK_BASE_URL;
4+
use crate::{
5+
HttpClient, Interval, PaystackAPIError, PaystackResult, PlanRequest, PlanResponseData,
6+
PlanStatus, PlanUpdateRequest, Response,
7+
};
8+
9+
pub struct PlansEndpoints<T: HttpClient + Default> {
10+
/// Paystack API Key
11+
key: String,
12+
/// Base URL for the plans route
13+
base_url: String,
14+
/// Http client for the route
15+
http: Arc<T>,
16+
}
17+
18+
/// Create a new `PlansEndpoints<T>` instance
19+
///
20+
/// # Arguments
21+
/// - `key` - The Paystack API key
22+
/// - `http`: The HTTP client implementation to use for the API requests
23+
///
24+
/// # Returns
25+
/// A new PlansEndpoints instance
26+
impl<T: HttpClient + Default> PlansEndpoints<T> {
27+
pub fn new(key: Arc<String>, http: Arc<T>) -> PlansEndpoints<T> {
28+
let base_url = format!("{PAYSTACK_BASE_URL}/plan");
29+
PlansEndpoints {
30+
key: key.to_string(),
31+
base_url,
32+
http,
33+
}
34+
}
35+
36+
/// Create a plan on your integration
37+
///
38+
/// # Arguments
39+
/// * `plan_request` - The request data to create the plan.
40+
/// Should be created with a `PlanRequestBuilder` struct.
41+
///
42+
/// # Returns
43+
/// A Result containing the plan response data or an error
44+
pub async fn create_plan(&self, plan_request: PlanRequest) -> PaystackResult<PlanResponseData> {
45+
let url = &self.base_url;
46+
let body = serde_json::to_value(plan_request)
47+
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
48+
49+
let response = self
50+
.http
51+
.post(url, &self.key, &body)
52+
.await
53+
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
54+
55+
let parsed_response: Response<PlanResponseData> =
56+
serde_json::from_str(&response).map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
57+
58+
Ok(parsed_response)
59+
}
60+
61+
/// Lists plans available in your integration
62+
///
63+
/// # Arguments
64+
/// * `per_page` - specify how many records you want to retrieve per page. Defaults to 50 if None
65+
/// * `page` - specify exactly what page you want to retrieve. Defaults to 1 if None
66+
/// * `status` - Optional parameter to filter list by plans with specified status
67+
/// * `interval` - Optional parameter to filter list by plans with specified interval
68+
/// * `amount`- Optional parameter to filter list by plans with specified amount using the supported currency
69+
///
70+
/// # Returns
71+
/// A Result containing a vector of plan response data or an error
72+
pub async fn list_plans(
73+
&self,
74+
per_page: Option<u8>,
75+
page: Option<u8>,
76+
status: Option<PlanStatus>,
77+
interval: Option<Interval>,
78+
amount: Option<u32>,
79+
) -> PaystackResult<Vec<PlanResponseData>> {
80+
let url = &self.base_url;
81+
82+
let per_page = per_page.unwrap_or(50).to_string();
83+
let page = page.unwrap_or(1).to_string();
84+
85+
let mut query = vec![("perPage", per_page), ("page", page)];
86+
87+
// Process optional parameters
88+
if let Some(s) = status {
89+
query.push(("status", s.to_string()));
90+
}
91+
92+
if let Some(i) = interval {
93+
query.push(("interval", i.to_string()));
94+
}
95+
96+
if let Some(a) = amount {
97+
query.push(("amount", a.to_string()));
98+
}
99+
100+
// convert all string to &str
101+
// TODO: there has to be a cleaner way of doing this
102+
let query: Vec<(&str, &str)> = query.iter().map(|(k, v)| (*k, v.as_str())).collect();
103+
104+
let response = self
105+
.http
106+
.get(url, &self.key, Some(&query))
107+
.await
108+
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
109+
110+
let parsed_response: Response<Vec<PlanResponseData>> =
111+
serde_json::from_str(&response).map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
112+
113+
Ok(parsed_response)
114+
}
115+
116+
/// Get details of a plan on your integration
117+
///
118+
/// # Arguments
119+
/// * `id_or_code` - the plan `ID` or `code` you want to fetch
120+
///
121+
/// # Returns
122+
/// A Result containing the plan response data or an error
123+
pub async fn fetch_plan(&self, id_or_code: String) -> PaystackResult<PlanResponseData> {
124+
let url = format!("{}/{}", &self.base_url, id_or_code);
125+
126+
let response = self
127+
.http
128+
.get(&url, &self.key, None)
129+
.await
130+
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
131+
132+
let parsed_response: Response<PlanResponseData> =
133+
serde_json::from_str(&response).map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
134+
135+
Ok(parsed_response)
136+
}
137+
138+
/// Update a plan details on your integration
139+
///
140+
/// # Arguments
141+
/// * `id_or_code` - the plan `ID` or `code` you want to update
142+
/// * `plan_update_request` - The request data to update the plan with.
143+
/// Should be created with a `PlanUpdateRequestBuilder` struct.
144+
///
145+
/// # Returns
146+
/// A Result containing a success message if the plan has been updated
147+
pub async fn update_plan(
148+
&self,
149+
id_or_code: String,
150+
plan_update_request: PlanUpdateRequest,
151+
) -> PaystackResult<PhantomData<String>> {
152+
let url = format!("{}/{}", self.base_url, id_or_code);
153+
let body = serde_json::to_value(plan_update_request)
154+
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
155+
156+
let response = self
157+
.http
158+
.put(&url, &self.key, &body)
159+
.await
160+
.map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
161+
162+
let parsed_response: Response<PhantomData<String>> =
163+
serde_json::from_str(&response).map_err(|e| PaystackAPIError::Plan(e.to_string()))?;
164+
165+
Ok(parsed_response)
166+
}
167+
}

src/endpoints/subaccount.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::sync::Arc;
1515
pub struct SubaccountEndpoints<T: HttpClient + Default> {
1616
/// Paystack API Key
1717
key: String,
18-
/// Base URL for the transaction route
18+
/// Base URL for the subaccount route
1919
base_url: String,
2020
/// Http client for the route
2121
http: Arc<T>,
@@ -31,7 +31,7 @@ impl<T: HttpClient + Default> SubaccountEndpoints<T> {
3131
/// # Returns
3232
/// A new SubaccountEndpoints instance
3333
pub fn new(key: Arc<String>, http: Arc<T>) -> SubaccountEndpoints<T> {
34-
let base_url = format!("{}/subaccount", PAYSTACK_BASE_URL);
34+
let base_url = format!("{PAYSTACK_BASE_URL}/subaccount");
3535
SubaccountEndpoints {
3636
key: key.to_string(),
3737
base_url,
@@ -57,7 +57,7 @@ impl<T: HttpClient + Default> SubaccountEndpoints<T> {
5757

5858
let response = self
5959
.http
60-
.post(&url, &self.key, &body)
60+
.post(url, &self.key, &body)
6161
.await
6262
.map_err(|e| PaystackAPIError::Subaccount(e.to_string()))?;
6363

0 commit comments

Comments
 (0)