paystack/endpoints/
virtual_terminal.rs

1//! Virtual Terminal
2//! ================
3//! The Virtual Terminal API allows you to accept in-person payments without a POS device.
4
5use super::PAYSTACK_BASE_URL;
6use crate::{
7    DestinationRequest, DestinationResponse, HttpClient, PaystackAPIError, PaystackResult,
8    Response, TransactionSplitResponseData, VirtualTerminalRequestData,
9    VirtualTerminalResponseData, VirtualTerminalStatus,
10};
11use serde_json::json;
12use std::{marker::PhantomData, sync::Arc};
13
14#[derive(Debug, Clone)]
15pub struct VirtualTerminalEndpoints<T: HttpClient + Default> {
16    /// Paystack API key
17    key: String,
18    /// Base URL for the transaction route
19    base_url: String,
20    /// Http client for the route
21    http: Arc<T>,
22}
23
24impl<T: HttpClient + Default> VirtualTerminalEndpoints<T> {
25    /// Creates a new VirtualTerminalEndpoints instance
26    ///
27    /// # Arguments
28    /// * `key` - The Paystack API key
29    /// * `http` - The HTTP client implementation to use for API requests
30    ///
31    /// # Returns
32    /// A new VirtualTerminalEndpoints instance
33    pub fn new(key: Arc<String>, http: Arc<T>) -> VirtualTerminalEndpoints<T> {
34        let base_url = format!("{}/virtual_terminal", PAYSTACK_BASE_URL);
35        VirtualTerminalEndpoints {
36            key: key.to_string(),
37            base_url,
38            http,
39        }
40    }
41
42    /// Creates a virtual terminal on your integration
43    ///
44    /// # Arguments
45    /// * `virtual_terminal_request` - The request data to create the virtual terminal.
46    ///   It should be created with the `VirtualTerminalRequestDataBuilder` struct.
47    ///
48    /// # Returns
49    /// A Result containing the virtual terminal response data or an error
50    pub async fn create_virtual_terminal(
51        &self,
52        virtual_terminal_request: VirtualTerminalRequestData,
53    ) -> PaystackResult<VirtualTerminalResponseData> {
54        let url = &self.base_url;
55        let body = serde_json::to_value(virtual_terminal_request)
56            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
57
58        let response = self
59            .http
60            .post(&url, &self.key, &body)
61            .await
62            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
63
64        let parsed_response: Response<VirtualTerminalResponseData> =
65            serde_json::from_str(&response)
66                .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
67
68        Ok(parsed_response)
69    }
70
71    /// Lists virtual terminals available on your integration
72    ///
73    /// # Arguments
74    /// * `status` - Filter terminal by status
75    /// * `per_page` - Number of records per page
76    ///
77    /// # Returns
78    /// A Result containing a vector of virtual terminal response data or an error
79    pub async fn list_virtual_terminals(
80        &self,
81        status: VirtualTerminalStatus,
82        per_page: i32,
83    ) -> PaystackResult<Vec<VirtualTerminalResponseData>> {
84        let url = &self.base_url;
85        let status = status.to_string();
86        let per_page = per_page.to_string();
87
88        let query = vec![("status", status.as_str()), ("perPage", per_page.as_str())];
89
90        let response = self
91            .http
92            .get(&url, &self.key, Some(&query))
93            .await
94            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
95
96        let parsed_response: Response<Vec<VirtualTerminalResponseData>> =
97            serde_json::from_str(&response)
98                .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
99
100        Ok(parsed_response)
101    }
102
103    /// Gets details of a virtual terminal on your integration
104    ///
105    /// # Arguments
106    /// * `code` - Code of the virtual terminal to fetch
107    ///
108    /// # Returns
109    /// A Result containing the virtual terminal response data or an error
110    pub async fn fetch_virtual_terminal(
111        self,
112        code: String,
113    ) -> PaystackResult<VirtualTerminalResponseData> {
114        let url = format!("{}/{}", self.base_url, code);
115
116        let response = self
117            .http
118            .get(&url, &self.key, None)
119            .await
120            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
121
122        let parsed_response: Response<VirtualTerminalResponseData> =
123            serde_json::from_str(&response)
124                .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
125
126        Ok(parsed_response)
127    }
128
129    /// Updates a virtual terminal on your integration
130    ///
131    /// # Arguments
132    /// * `code` - Code of the virtual terminal to update
133    /// * `name` - New name for the virtual terminal
134    ///
135    /// # Returns
136    /// A Result containing the response or an error
137    pub async fn update_virtual_terminal(
138        &self,
139        code: String,
140        name: String,
141    ) -> PaystackResult<PhantomData<String>> {
142        let url = format!("{}/{}", self.base_url, code);
143        let body = json!({
144            "name": name
145        });
146
147        let response = self
148            .http
149            .put(&url, &self.key, &body)
150            .await
151            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
152
153        let parsed_response: Response<PhantomData<String>> = serde_json::from_str(&response)
154            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
155
156        Ok(parsed_response)
157    }
158
159    /// Deactivates a virtual terminal on your integration
160    ///
161    /// # Arguments
162    /// * `code` - Code of the virtual terminal to deactivate
163    ///
164    /// # Returns
165    /// A Result containing the response or an error
166    pub async fn deactivate_virtual_terminal(
167        &self,
168        code: String,
169    ) -> PaystackResult<PhantomData<String>> {
170        let url = format!("{}/{}/deactivate", self.base_url, code);
171        let body = json!({}); // empty body cause the route takes none
172
173        let response = self
174            .http
175            .put(&url, &self.key, &body)
176            .await
177            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
178
179        let parsed_response: Response<PhantomData<String>> = serde_json::from_str(&response)
180            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
181
182        Ok(parsed_response)
183    }
184
185    /// Adds a WhatsApp destination number to a virtual terminal
186    ///
187    /// # Arguments
188    /// * `code` - Code of the virtual terminal
189    /// * `destinations` - Vector of destination requests containing notification recipients
190    ///
191    /// # Returns
192    /// A Result containing a vector of destination responses or an error
193    pub async fn assign_virtual_terminal_destination(
194        &self,
195        code: String,
196        destinations: Vec<DestinationRequest>,
197    ) -> PaystackResult<Vec<DestinationResponse>> {
198        let url = format!("{}/{}/destination/assign", self.base_url, code);
199        let body = json!({
200            "destinations": destinations
201        });
202
203        let response = self
204            .http
205            .post(&url, &self.key, &body)
206            .await
207            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
208
209        let parsed_response: Response<Vec<DestinationResponse>> =
210            serde_json::from_str(&response)
211                .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
212
213        Ok(parsed_response)
214    }
215
216    /// Removes a WhatsApp destination number from a virtual terminal
217    ///
218    /// # Arguments
219    /// * `code` - Code of the virtual terminal
220    /// * `targets` - Vector of destination targets to unassign
221    ///
222    /// # Returns
223    /// A Result containing the response or an error
224    pub async fn unassign_virtual_terminal_destination(
225        &self,
226        code: String,
227        targets: Vec<String>,
228    ) -> PaystackResult<PhantomData<String>> {
229        let url = format!("{}/{}/destination/unassign", self.base_url, code);
230        let body = json!({
231            "targets": targets
232        });
233
234        let response = self
235            .http
236            .post(&url, &self.key, &body)
237            .await
238            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
239
240        let parsed_response: Response<PhantomData<String>> = serde_json::from_str(&response)
241            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
242
243        Ok(parsed_response)
244    }
245
246    /// Adds a split payment code to a virtual terminal
247    ///
248    /// # Arguments
249    /// * `code` - Code of the virtual terminal
250    /// * `split_code` - Split code to add
251    ///
252    /// # Returns
253    /// A Result containing the transaction split response data or an error
254    pub async fn add_split_code_to_virtual_terminal(
255        &self,
256        code: String,
257        split_code: String,
258    ) -> PaystackResult<TransactionSplitResponseData> {
259        let url = format!("{}/{}/split_code", self.base_url, code);
260        let body = json!({
261            "split_code": split_code
262        });
263
264        let response = self
265            .http
266            .put(&url, &self.key, &body)
267            .await
268            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
269
270        let parsed_response: Response<TransactionSplitResponseData> =
271            serde_json::from_str(&response)
272                .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
273
274        Ok(parsed_response)
275    }
276
277    /// Removes a split payment code from a virtual terminal
278    ///
279    /// # Arguments
280    /// * `code` - Code of the virtual terminal
281    /// * `split_code` - Split code to remove
282    ///
283    /// # Returns
284    /// A Result containing the response or an error
285    pub async fn remove_split_code_from_virtual_terminal(
286        &self,
287        code: String,
288        split_code: String,
289    ) -> PaystackResult<PhantomData<String>> {
290        let url = format!("{}/{}/split_code", self.base_url, code);
291        let body = json!({
292            "split_code": split_code
293        });
294
295        let response = self
296            .http
297            .delete(&url, &self.key, &body)
298            .await
299            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
300
301        let parsed_response: Response<PhantomData<String>> = serde_json::from_str(&response)
302            .map_err(|e| PaystackAPIError::VirtualTerminal(e.to_string()))?;
303
304        Ok(parsed_response)
305    }
306}