paystack/endpoints/
plans.rs

1use std::{marker::PhantomData, sync::Arc};
2
3use super::PAYSTACK_BASE_URL;
4use crate::{
5    HttpClient, Interval, PaystackAPIError, PaystackResult, PlanRequest, PlanResponseData,
6    PlanStatus, PlanUpdateRequest, Response,
7};
8
9pub 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
26impl<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}