crypto_pay_api/api/
misc.rs

1use crate::{
2    client::CryptoBot,
3    error::CryptoBotResult,
4    models::{APIEndpoint, APIMethod, AppStats, Currency, GetMeResponse, GetStatsParams, Method},
5};
6use async_trait::async_trait;
7
8use super::MiscAPI;
9
10#[async_trait]
11impl MiscAPI for CryptoBot {
12    /// Gets basic information about your application
13    ///
14    /// Retrieves information about your application, including app ID, name,
15    /// and payment processing bot username.
16    ///
17    /// # Returns
18    /// * `Ok(GetMeResponse)` - Basic information about your application
19    /// * `Err(CryptoBotError)` - If the request fails
20    ///
21    /// # Example
22    /// ```no_run
23    /// use crypto_pay_api::prelude::*;
24    ///
25    /// #[tokio::main]
26    /// async fn main() -> Result<(), CryptoBotError> {
27    ///     let client = CryptoBot::builder().api_token("YOUR_API_TOKEN").build().unwrap();
28    ///     
29    ///     let app_info = client.get_me().await?;
30    ///     println!("App ID: {}", app_info.app_id);
31    ///     println!("App Name: {}", app_info.name);
32    ///     println!("Bot Username: {}", app_info.payment_processing_bot_username);
33    ///     
34    ///     Ok(())
35    /// }
36    /// ```
37    ///
38    /// # See Also
39    /// * [GetMeResponse](struct.GetMeResponse.html) - The structure representing the response
40    async fn get_me(&self) -> CryptoBotResult<GetMeResponse> {
41        self.make_request(
42            &APIMethod {
43                endpoint: APIEndpoint::GetMe,
44                method: Method::GET,
45            },
46            None::<()>.as_ref(),
47        )
48        .await
49    }
50
51    /// Gets a list of all supported cryptocurrencies
52    ///
53    /// Returns information about all cryptocurrencies supported by CryptoBot,
54    /// including both crypto and fiat currencies.
55    ///
56    /// # Returns
57    /// * `Ok(Vec<Currency>)` - List of supported currencies with their properties
58    /// * `Err(CryptoBotError)` - If the request fails
59    ///
60    /// # Example
61    /// ```no_run
62    /// use crypto_pay_api::prelude::*;
63    ///
64    /// #[tokio::main]
65    /// async fn main() -> Result<(), CryptoBotError> {
66    ///     let client = CryptoBot::builder().api_token("YOUR_API_TOKEN").build().unwrap();
67    ///     
68    ///     let currencies = client.get_currencies().await?;
69    ///     
70    ///     for currency in currencies {
71    ///         println!("Currency: {}", currency.name);
72    ///         println!("Type: {}", if currency.is_blockchain { "Crypto" }
73    ///             else if currency.is_fiat { "Fiat" }
74    ///             else { "Stablecoin" });
75    ///         println!("Decimals: {}", currency.decimals);
76    ///         if let Some(url) = currency.url {
77    ///             println!("Website: {}", url);
78    ///         }
79    ///         println!("---");
80    ///     }
81    ///     
82    ///     Ok(())
83    /// }
84    /// ```
85    ///
86    /// # See Also
87    /// * [Currency](struct.Currency.html) - The structure representing a currency
88    async fn get_currencies(&self) -> CryptoBotResult<Vec<Currency>> {
89        self.make_request(
90            &APIMethod {
91                endpoint: APIEndpoint::GetCurrencies,
92                method: Method::GET,
93            },
94            None::<()>.as_ref(),
95        )
96        .await
97    }
98
99    /// Gets application statistics for a specified time period
100    ///
101    /// Retrieves statistics about your application's usage, including
102    /// transaction volumes, number of invoices, and user counts.
103    ///
104    /// # Arguments
105    /// * `params` - Optional parameters to filter statistics by date range.
106    ///             See [`GetStatsParams`] for available options.
107    ///
108    /// # Returns
109    /// * `Ok(AppStats)` - Application statistics for the specified period
110    /// * `Err(CryptoBotError)` - If the parameters are invalid or the request fails
111    ///
112    /// # Example
113    /// ```no_run
114    /// use crypto_pay_api::prelude::*;
115    /// use chrono::{Utc, Duration};
116    ///
117    /// #[tokio::main]
118    /// async fn main() -> Result<(), CryptoBotError> {
119    ///     let client = CryptoBot::builder().api_token("YOUR_API_TOKEN").build().unwrap();
120    ///     
121    ///     // Get stats for the last 7 days
122    ///     let end_date = Utc::now();
123    ///     let start_date = end_date - Duration::days(7);
124    ///     
125    ///     let params = GetStatsParamsBuilder::new()
126    ///         .start_at(start_date)
127    ///         .end_at(end_date)
128    ///         .build()
129    ///         .unwrap();
130    ///     
131    ///     let stats = client.get_stats(Some(&params)).await?;
132    ///     
133    ///     println!("Statistics for the last 7 days:");
134    ///     println!("Total volume: {}", stats.volume);
135    ///     println!("Number of invoices created: {}", stats.created_invoice_count);
136    ///     println!("Number of paid invoices: {}", stats.paid_invoice_count);
137    ///     println!("Unique users: {}", stats.unique_users_count);
138    ///     
139    ///     Ok(())
140    /// }
141    /// ```
142    ///
143    /// # See Also
144    /// * [AppStats](struct.AppStats.html) - The structure representing application statistics
145    /// * [GetStatsParams](struct.GetStatsParams.html) - Parameters for filtering statistics
146    async fn get_stats(&self, params: Option<&GetStatsParams>) -> CryptoBotResult<AppStats> {
147        self.make_request(
148            &APIMethod {
149                endpoint: APIEndpoint::GetStats,
150                method: Method::GET,
151            },
152            params,
153        )
154        .await
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use mockito::Mock;
161    use rust_decimal::Decimal;
162    use serde_json::json;
163
164    use crate::{
165        api::MiscAPI,
166        client::CryptoBot,
167        models::{CryptoCurrencyCode, CurrencyCode, GetStatsParams},
168        utils::test_utils::TestContext,
169    };
170
171    impl TestContext {
172        pub fn mock_get_me_response(&mut self) -> Mock {
173            self.server
174                .mock("GET", "/getMe")
175                .with_header("content-type", "application/json")
176                .with_header("Crypto-Pay-API-Token", "test_token")
177                .with_body(
178                    json!({
179                        "ok": true,
180                        "result": {
181                            "app_id": 28692,
182                            "name": "Stated Seaslug App",
183                            "payment_processing_bot_username": "CryptoTestnetBot"
184                        }
185                    })
186                    .to_string(),
187                )
188                .create()
189        }
190
191        pub fn mock_currencies_response(&mut self) -> Mock {
192            println!("Setting up mock response");
193            self.server
194                .mock("GET", "/getCurrencies")
195                .with_header("content-type", "application/json")
196                .with_header("Crypto-Pay-API-Token", "test_token")
197                .with_body(
198                    json!({
199                        "ok": true,
200                        "result": [
201                            {
202                                "is_blockchain": false,
203                                "is_stablecoin": true,
204                                "is_fiat": false,
205                                "name": "Tether",
206                                "code": "USDT",
207                                "url": "https://tether.to/",
208                                "decimals": 18
209                            },
210                            {
211                                "is_blockchain": true,
212                                "is_stablecoin": false,
213                                "is_fiat": false,
214                                "name": "Toncoin",
215                                "code": "TON",
216                                "url": "https://ton.org/",
217                                "decimals": 9
218                            },
219                            {
220                                "is_blockchain": true,
221                                "is_stablecoin": false,
222                                "is_fiat": false,
223                                "name": "Bitcoin",
224                                "code": "BTC",
225                                "url": "https://bitcoin.org/",
226                                "decimals": 8
227                            },
228                            {
229                                "is_blockchain": true,
230                                "is_stablecoin": false,
231                                "is_fiat": false,
232                                "name": "Dogecoin",
233                                "code": "DOGE",
234                                "url": "https://dogecoin.org/",
235                                "decimals": 8
236                            },
237                            {
238                                "is_blockchain": true,
239                                "is_stablecoin": false,
240                                "is_fiat": false,
241                                "name": "Litecoin",
242                                "code": "LTC",
243                                "url": "https://litecoin.org/",
244                                "decimals": 8
245                            },
246                            {
247                                "is_blockchain": true,
248                                "is_stablecoin": false,
249                                "is_fiat": false,
250                                "name": "Ethereum",
251                                "code": "ETH",
252                                "url": "https://ethereum.org/",
253                                "decimals": 18
254                            },
255                            {
256                                "is_blockchain": true,
257                                "is_stablecoin": false,
258                                "is_fiat": false,
259                                "name": "Binance Coin",
260                                "code": "BNB",
261                                "url": "https://binance.org/",
262                                "decimals": 18
263                            },
264                            {
265                                "is_blockchain": true,
266                                "is_stablecoin": false,
267                                "is_fiat": false,
268                                "name": "TRON",
269                                "code": "TRX",
270                                "url": "https://tron.network/",
271                                "decimals": 6
272                            },
273                            {
274                                "is_blockchain": false,
275                                "is_stablecoin": true,
276                                "is_fiat": false,
277                                "name": "USD Coin",
278                                "code": "USDC",
279                                "url": "https://www.centre.io/usdc",
280                                "decimals": 18
281                            },
282                            {
283                                "is_blockchain": false,
284                                "is_stablecoin": true,
285                                "is_fiat": false,
286                                "name": "TON Jetton",
287                                "code": "JET",
288                                "url": "https://ton.org",
289                                "decimals": 9
290                            },
291                            {
292                                "is_blockchain": true,
293                                "is_stablecoin": false,
294                                "is_fiat": false,
295                                "name": "Crypto Bot",
296                                "code": "SEND",
297                                "url": "https://send.tg/",
298                                "decimals": 9
299                            },
300                            {
301                                "is_blockchain": false,
302                                "is_stablecoin": false,
303                                "is_fiat": true,
304                                "name": "Russian ruble",
305                                "code": "RUB",
306                                "decimals": 8
307                            },
308                            {
309                                "is_blockchain": false,
310                                "is_stablecoin": false,
311                                "is_fiat": true,
312                                "name": "United States Dollar",
313                                "code": "USD",
314                                "decimals": 8
315                            },
316                            {
317                                "is_blockchain": false,
318                                "is_stablecoin": false,
319                                "is_fiat": true,
320                                "name": "Euro",
321                                "code": "EUR",
322                                "decimals": 8
323                            },
324                            {
325                                "is_blockchain": false,
326                                "is_stablecoin": false,
327                                "is_fiat": true,
328                                "name": "Belarusian ruble",
329                                "code": "BYN",
330                                "decimals": 8
331                            },
332                            {
333                                "is_blockchain": false,
334                                "is_stablecoin": false,
335                                "is_fiat": true,
336                                "name": "Ukrainian hryvnia",
337                                "code": "UAH",
338                                "decimals": 8
339                            },
340                            {
341                                "is_blockchain": false,
342                                "is_stablecoin": false,
343                                "is_fiat": true,
344                                "name": "Pound sterling",
345                                "code": "GBP",
346                                "decimals": 8
347                            },
348                            {
349                                "is_blockchain": false,
350                                "is_stablecoin": false,
351                                "is_fiat": true,
352                                "name": "Chinese yuan renminbi",
353                                "code": "CNY",
354                                "decimals": 8
355                            },
356                            {
357                                "is_blockchain": false,
358                                "is_stablecoin": false,
359                                "is_fiat": true,
360                                "name": "Kazakhstani tenge",
361                                "code": "KZT",
362                                "decimals": 8
363                            },
364                            {
365                                "is_blockchain": false,
366                                "is_stablecoin": false,
367                                "is_fiat": true,
368                                "name": "Uzbekistani som",
369                                "code": "UZS",
370                                "decimals": 8
371                            },
372                            {
373                                "is_blockchain": false,
374                                "is_stablecoin": false,
375                                "is_fiat": true,
376                                "name": "Georgian lari",
377                                "code": "GEL",
378                                "decimals": 8
379                            },
380                            {
381                                "is_blockchain": false,
382                                "is_stablecoin": false,
383                                "is_fiat": true,
384                                "name": "Turkish lira",
385                                "code": "TRY",
386                                "decimals": 8
387                            },
388                            {
389                                "is_blockchain": false,
390                                "is_stablecoin": false,
391                                "is_fiat": true,
392                                "name": "Armenian dram",
393                                "code": "AMD",
394                                "decimals": 8
395                            },
396                            {
397                                "is_blockchain": false,
398                                "is_stablecoin": false,
399                                "is_fiat": true,
400                                "name": "Thai baht",
401                                "code": "THB",
402                                "decimals": 8
403                            },
404                            {
405                                "is_blockchain": false,
406                                "is_stablecoin": false,
407                                "is_fiat": true,
408                                "name": "Indian rupee",
409                                "code": "INR",
410                                "decimals": 8
411                            },
412                            {
413                                "is_blockchain": false,
414                                "is_stablecoin": false,
415                                "is_fiat": true,
416                                "name": "Brazilian real",
417                                "code": "BRL",
418                                "decimals": 8
419                            },
420                            {
421                                "is_blockchain": false,
422                                "is_stablecoin": false,
423                                "is_fiat": true,
424                                "name": "Indonesian rupiah",
425                                "code": "IDR",
426                                "decimals": 8
427                            },
428                            {
429                                "is_blockchain": false,
430                                "is_stablecoin": false,
431                                "is_fiat": true,
432                                "name": "Azerbaijani manat",
433                                "code": "AZN",
434                                "decimals": 8
435                            },
436                            {
437                                "is_blockchain": false,
438                                "is_stablecoin": false,
439                                "is_fiat": true,
440                                "name": "United Arab Emirates dirham",
441                                "code": "AED",
442                                "decimals": 8
443                            },
444                            {
445                                "is_blockchain": false,
446                                "is_stablecoin": false,
447                                "is_fiat": true,
448                                "name": "Polish zloty",
449                                "code": "PLN",
450                                "decimals": 8
451                            },
452                            {
453                                "is_blockchain": false,
454                                "is_stablecoin": false,
455                                "is_fiat": true,
456                                "name": "Israeli new shekel",
457                                "code": "ILS",
458                                "decimals": 8
459                            },
460                            {
461                                "is_blockchain": false,
462                                "is_stablecoin": false,
463                                "is_fiat": true,
464                                "name": "Kyrgystani som",
465                                "code": "KGS",
466                                "decimals": 8
467                            },
468                            {
469                                "is_blockchain": false,
470                                "is_stablecoin": false,
471                                "is_fiat": true,
472                                "name": "Tajikistani somoni",
473                                "code": "TJS",
474                                "decimals": 8
475                            }
476                        ]
477                    })
478                    .to_string(),
479                )
480                .create()
481        }
482        // TODO add more data
483        pub fn mock_get_stats_response(&mut self) -> Mock {
484            self.server
485                .mock("GET", "/getStats")
486                .with_header("content-type", "application/json")
487                .with_header("Crypto-Pay-API-Token", "test_token")
488                .with_body(
489                    json!({
490                        "ok": true,
491                        "result": {
492                            "volume": 0,
493                            "conversion": 0,
494                            "unique_users_count": 0,
495                            "created_invoice_count": 0,
496                            "paid_invoice_count": 0,
497                            "start_at": "2025-02-07T10:55:17.438Z",
498                            "end_at": "2025-02-08T10:55:17.438Z"
499                        }
500                    })
501                    .to_string(),
502                )
503                .create()
504        }
505    }
506
507    #[test]
508    fn test_get_me() {
509        let mut ctx = TestContext::new();
510        let _m = ctx.mock_get_me_response();
511
512        let client = CryptoBot::builder()
513            .api_token("test_token")
514            .base_url(ctx.server.url())
515            .build()
516            .unwrap();
517
518        let result = ctx.run(async { client.get_me().await });
519
520        println!("Result: {:?}", result);
521
522        assert!(result.is_ok());
523        let me = result.unwrap();
524        assert_eq!(me.app_id, 28692);
525        assert_eq!(me.name, "Stated Seaslug App");
526        assert_eq!(me.payment_processing_bot_username, "CryptoTestnetBot");
527        assert_eq!(me.webhook_endpoint, None);
528    }
529
530    #[test]
531    fn test_get_currencies() {
532        let mut ctx = TestContext::new();
533        let _m = ctx.mock_currencies_response();
534
535        let client = CryptoBot::builder()
536            .api_token("test_token")
537            .base_url(ctx.server.url())
538            .build()
539            .unwrap();
540
541        let result = ctx.run(async { client.get_currencies().await });
542
543        assert!(result.is_ok());
544        let currencies = result.unwrap();
545
546        assert_eq!(currencies.len(), 33);
547        assert_eq!(currencies[0].code, CurrencyCode::Crypto(CryptoCurrencyCode::Usdt));
548        assert_eq!(currencies[1].code, CurrencyCode::Crypto(CryptoCurrencyCode::Ton));
549    }
550
551    #[test]
552    fn test_get_stats_without_params() {
553        let mut ctx = TestContext::new();
554        let _m = ctx.mock_get_stats_response();
555
556        let client = CryptoBot::builder()
557            .api_token("test_token")
558            .base_url(ctx.server.url())
559            .build()
560            .unwrap();
561
562        let result = ctx.run(async { client.get_stats(None::<&GetStatsParams>).await });
563
564        println!("result: {:?}", result);
565
566        assert!(result.is_ok());
567        let stats = result.unwrap();
568        assert_eq!(stats.volume, Decimal::from(0));
569        assert_eq!(stats.conversion, Decimal::from(0));
570    }
571
572    // #[test]
573    // fn test_get_stats_with_params() {
574    //     let mut ctx = TestContext::new();
575    //     let _m = ctx.mock_get_stats_response();
576
577    //     let client = CryptoBot::builder()
578    //         .api_token("test_token")
579    //         .base_url(ctx.server.url())
580    //         .build()
581    //         .unwrap();
582
583    //     let params = GetStatsParamsBuilder::new()
584    //         .start_at(Utc::now() - Duration::days(7))
585    //         .end_at(Utc::now())
586    //         .build()
587    //         .unwrap();
588
589    //     let result = ctx.run(async { client.get_stats(Some(&params)).await });
590
591    //     assert!(result.is_ok());
592    //     let stats = result.unwrap();
593    //     assert_eq!(stats.volume, Decimal::from(0));
594    //     assert_eq!(stats.conversion, Decimal::from(0));
595    // }
596}