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