crypto_pay_api/api/
check.rs

1use async_trait::async_trait;
2
3use crate::{
4    client::CryptoBot,
5    error::CryptoBotError,
6    models::{
7        APIEndpoint, APIMethod, Check, CreateCheckParams, DeleteCheckParams, GetChecksParams, GetChecksResponse, Method,
8    },
9};
10
11use super::CheckAPI;
12
13#[async_trait]
14impl CheckAPI for CryptoBot {
15    /// Creates a new cryptocurrency check
16    ///
17    /// A check is a unique link that can be used once to transfer cryptocurrency.
18    /// Anyone who opens the link first can activate the check and get the cryptocurrency.
19    ///
20    /// # Arguments
21    /// * `params` - Parameters for creating a new check. See [`CreateCheckParams`] for details.
22    ///
23    /// # Returns
24    /// * `Ok(Check)` - The created check
25    /// * `Err(CryptoBotError)` - If validation fails or the request fails
26    ///
27    /// # Errors
28    /// This method will return an error if:
29    /// * The parameters are invalid (e.g., negative amount)
30    /// * The currency is not supported
31    /// * The API request fails
32    /// * The exchange rate validation fails
33    ///
34    /// # Example
35    /// ```no_run
36    /// use crypto_pay_api::prelude::*;
37    ///
38    /// #[tokio::main]
39    /// async fn main() -> Result<(), CryptoBotError> {
40    ///     let client = CryptoBot::builder().api_token("YOUR_API_TOKEN").build().unwrap();
41    ///     
42    ///     let params = CreateCheckParamsBuilder::new()
43    ///         .asset(CryptoCurrencyCode::Ton)
44    ///         .amount(dec!(10.5))
45    ///         .build(&client).await?;
46    ///     
47    ///     let check = client.create_check(&params).await?;
48    ///     println!("Check created: {}", check.check_id);
49    ///     
50    ///     Ok(())
51    /// }
52    /// ```
53    ///
54    /// # See Also
55    /// * [Check](struct.Check.html) - The structure representing a check
56    /// * [CreateCheckParams](struct.CreateCheckParams.html) - The parameters for creating a check
57    async fn create_check(&self, params: &CreateCheckParams) -> Result<Check, CryptoBotError> {
58        self.make_request(
59            &APIMethod {
60                endpoint: APIEndpoint::CreateCheck,
61                method: Method::POST,
62            },
63            Some(params),
64        )
65        .await
66    }
67
68    /// Deletes an existing cryptocurrency check
69    ///
70    /// Once deleted, the check link will become invalid and cannot be activated.
71    ///
72    /// # Arguments
73    /// * `check_id` - The unique identifier of the check to delete
74    ///
75    /// # Returns
76    /// * `Ok(true)` - If the check was successfully deleted
77    /// * `Err(CryptoBotError)` - If the check doesn't exist or the request fails
78    ///
79    /// # Example
80    /// ```no_run
81    /// use crypto_pay_api::prelude::*;
82    ///
83    /// #[tokio::main]
84    /// async fn main() -> Result<(), CryptoBotError> {
85    ///     let client = CryptoBot::builder().api_token("YOUR_API_TOKEN").build().unwrap();
86    ///     
87    ///     match client.delete_check(12345).await {
88    ///         Ok(_) => println!("Check deleted successfully"),
89    ///         Err(e) => eprintln!("Failed to delete check: {}", e),
90    ///     }
91    ///     
92    ///     Ok(())
93    /// }
94    /// ```
95    async fn delete_check(&self, check_id: u64) -> Result<bool, CryptoBotError> {
96        let params = DeleteCheckParams { check_id };
97
98        self.make_request(
99            &APIMethod {
100                endpoint: APIEndpoint::DeleteCheck,
101                method: Method::DELETE,
102            },
103            Some(&params),
104        )
105        .await
106    }
107
108    /// Gets a list of created cryptocurrency checks
109    ///
110    /// Retrieves all checks matching the specified filter parameters.
111    /// If no parameters are provided, returns all checks.
112    ///
113    /// # Arguments
114    /// * `params` - Optional filter parameters. See [`GetChecksParams`] for available filters.
115    ///
116    /// # Returns
117    /// * `Ok(Vec<Check>)` - List of checks matching the filter criteria
118    /// * `Err(CryptoBotError)` - If the parameters are invalid or the request fails
119    ///
120    /// # Example
121    /// ```no_run
122    /// use crypto_pay_api::prelude::*;
123    ///
124    /// #[tokio::main]
125    /// async fn main() -> Result<(), CryptoBotError> {
126    ///     let client = CryptoBot::builder().api_token("YOUR_API_TOKEN").build().unwrap();
127    ///     
128    ///     // Get all checks
129    ///     let all_checks = client.get_checks(None).await?;
130    ///     
131    ///     // Get checks with filters
132    ///     let params = GetChecksParamsBuilder::new()
133    ///         .asset(CryptoCurrencyCode::Ton)
134    ///         .status(CheckStatus::Active)
135    ///         .build()
136    ///         .unwrap();
137    ///     
138    ///     let filtered_checks = client.get_checks(Some(&params)).await?;
139    ///     
140    ///     for check in filtered_checks {
141    ///         println!("Check ID: {}, Amount: {}",
142    ///             check.check_id,
143    ///             check.amount,
144    ///         );
145    ///     }
146    ///     
147    ///     Ok(())
148    /// }
149    /// ```
150    ///
151    /// # See Also
152    /// * [Check](struct.Check.html) - The structure representing a check
153    /// * [GetChecksParams](struct.GetChecksParams.html) - Available filter parameters
154    /// * [CryptoBot API Documentation](https://help.crypt.bot/crypto-pay-api#getChecks)
155    async fn get_checks(&self, params: Option<&GetChecksParams>) -> Result<Vec<Check>, CryptoBotError> {
156        let response: GetChecksResponse = self
157            .make_request(
158                &APIMethod {
159                    endpoint: APIEndpoint::GetChecks,
160                    method: Method::GET,
161                },
162                params,
163            )
164            .await?;
165
166        Ok(response.items)
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use mockito::Mock;
173    use rust_decimal_macros::dec;
174    use serde_json::json;
175
176    use crate::{
177        models::{CreateCheckParamsBuilder, CryptoCurrencyCode, GetChecksParamsBuilder},
178        utils::test_utils::TestContext,
179    };
180
181    use super::*;
182
183    impl TestContext {
184        pub fn mock_create_check_response(&mut self) -> Mock {
185            self.server
186                .mock("POST", "/createCheck")
187                .with_header("content-type", "application/json")
188                .with_header("Crypto-Pay-API-Token", "test_token")
189                .with_body(
190                    json!({
191                        "ok": true,
192                        "result": {
193                            "check_id": 123,
194                            "hash": "hash",
195                            "asset": "TON",
196                            "amount": "10.00",
197                            "bot_check_url": "https://example.com/check",
198                            "status": "active",
199                            "created_at": "2021-01-01T00:00:00Z",
200                            "activated_at": "2021-01-01T00:00:00Z",
201                        }
202                    })
203                    .to_string(),
204                )
205                .create()
206        }
207
208        pub fn mock_get_checks_response_without_params(&mut self) -> Mock {
209            self.server
210                .mock("GET", "/getChecks")
211                .with_header("content-type", "application/json")
212                .with_header("Crypto-Pay-API-Token", "test_token")
213                .with_body(
214                    json!({
215                        "ok": true,
216                        "result": {
217                            "items": [
218                                {
219                                    "check_id": 123,
220                                    "hash": "hash",
221                                    "asset": "TON",
222                                    "amount": "10.00",
223                                    "bot_check_url": "https://example.com/check",
224                                    "status": "active",
225                                    "created_at": "2021-01-01T00:00:00Z",
226                                    "activated_at": "2021-01-01T00:00:00Z",
227                                }
228                            ]
229                        }
230                    })
231                    .to_string(),
232                )
233                .create()
234        }
235
236        pub fn mock_get_checks_response_with_check_ids(&mut self) -> Mock {
237            self.server
238                .mock("GET", "/getChecks")
239                .match_body(json!({ "check_ids": "123" }).to_string().as_str())
240                .with_header("content-type", "application/json")
241                .with_header("Crypto-Pay-API-Token", "test_token")
242                .with_body(
243                    json!({
244                        "ok": true,
245                        "result": {
246                            "items": [
247                                {
248                                    "check_id": 123,
249                                    "hash": "hash",
250                                    "asset": "TON",
251                                    "amount": "10.00",
252                                    "bot_check_url": "https://example.com/check",
253                                    "status": "active",
254                                    "created_at": "2021-01-01T00:00:00Z",
255                                    "activated_at": "2021-01-01T00:00:00Z",
256                                }
257                            ]
258                        }
259                    })
260                    .to_string(),
261                )
262                .create()
263        }
264
265        pub fn mock_delete_check_response(&mut self) -> Mock {
266            self.server
267                .mock("DELETE", "/deleteCheck")
268                .with_header("content-type", "application/json")
269                .with_header("Crypto-Pay-API-Token", "test_token")
270                .with_body(json!({ "ok": true, "result": true }).to_string())
271                .create()
272        }
273    }
274
275    #[test]
276    fn test_create_check() {
277        let mut ctx = TestContext::new();
278        let _m = ctx.mock_exchange_rates_response();
279        let _m = ctx.mock_create_check_response();
280
281        let client = CryptoBot::builder()
282            .api_token("test_token")
283            .base_url(ctx.server.url())
284            .build()
285            .unwrap();
286
287        let result = ctx.run(async {
288            let params = CreateCheckParamsBuilder::new()
289                .asset(CryptoCurrencyCode::Ton)
290                .amount(dec!(10.0))
291                .build(&client)
292                .await
293                .unwrap();
294
295            client.create_check(&params).await
296        });
297
298        assert!(result.is_ok());
299
300        let check = result.unwrap();
301        assert_eq!(check.check_id, 123);
302        assert_eq!(check.asset, CryptoCurrencyCode::Ton);
303        assert_eq!(check.amount, dec!(10.0));
304    }
305
306    #[test]
307    fn test_get_checks_without_params() {
308        let mut ctx = TestContext::new();
309        let _m = ctx.mock_get_checks_response_without_params();
310
311        let client = CryptoBot::builder()
312            .api_token("test_token")
313            .base_url(ctx.server.url())
314            .build()
315            .unwrap();
316
317        let result = ctx.run(async { client.get_checks(None).await });
318
319        assert!(result.is_ok());
320
321        let checks = result.unwrap();
322        assert_eq!(checks.len(), 1);
323        assert_eq!(checks[0].check_id, 123);
324    }
325
326    #[test]
327    fn test_get_checks_with_check_ids() {
328        let mut ctx = TestContext::new();
329        let _m = ctx.mock_get_checks_response_with_check_ids();
330
331        let client = CryptoBot::builder()
332            .api_token("test_token")
333            .base_url(ctx.server.url())
334            .build()
335            .unwrap();
336
337        let params = GetChecksParamsBuilder::new().check_ids(vec![123]).build().unwrap();
338
339        let result = ctx.run(async { client.get_checks(Some(&params)).await });
340
341        assert!(result.is_ok());
342
343        let checks = result.unwrap();
344        assert_eq!(checks.len(), 1);
345        assert_eq!(checks[0].check_id, 123);
346    }
347
348    #[test]
349    fn test_delete_check() {
350        let mut ctx = TestContext::new();
351        let _m = ctx.mock_delete_check_response();
352
353        let client = CryptoBot::builder()
354            .api_token("test_token")
355            .base_url(ctx.server.url())
356            .build()
357            .unwrap();
358
359        let result = ctx.run(async { client.delete_check(123).await });
360
361        assert!(result.is_ok());
362        assert!(result.unwrap());
363    }
364}