rain_sdk/api/
cards.rs

1//! Cards API
2//!
3//! This module provides functionality to manage cards.
4
5use crate::client::RainClient;
6use crate::error::Result;
7use crate::models::cards::*;
8use uuid::Uuid;
9
10impl RainClient {
11    /// Get all cards for a user or company
12    ///
13    /// # Arguments
14    ///
15    /// * `params` - Query parameters for filtering cards
16    ///
17    /// # Returns
18    ///
19    /// Returns a [`Vec<Card>`] containing the list of cards.
20    ///
21    /// # Errors
22    ///
23    /// This method can return the following errors:
24    /// - `401` - Invalid authorization
25    /// - `500` - Internal server error
26    ///
27    /// # Examples
28    ///
29    /// ```no_run
30    /// use rain_sdk::{RainClient, Config, Environment, AuthConfig};
31    /// use rain_sdk::models::cards::ListCardsParams;
32    /// use uuid::Uuid;
33    ///
34    /// # #[cfg(feature = "async")]
35    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
36    /// let config = Config::new(Environment::Dev);
37    /// let auth = AuthConfig::with_api_key("your-api-key".to_string());
38    /// let client = RainClient::new(config, auth)?;
39    ///
40    /// let user_id = Uuid::new_v4();
41    /// let params = ListCardsParams {
42    ///     user_id: Some(user_id),
43    ///     company_id: None,
44    ///     status: None,
45    ///     cursor: None,
46    ///     limit: Some(20),
47    /// };
48    /// let cards = client.list_cards(&params).await?;
49    /// # Ok(())
50    /// # }
51    /// ```
52    #[cfg(feature = "async")]
53    pub async fn list_cards(&self, params: &ListCardsParams) -> Result<ListCardsResponse> {
54        let mut path = "/cards".to_string();
55        let mut query_parts = Vec::new();
56
57        if let Some(ref company_id) = params.company_id {
58            query_parts.push(format!("companyId={company_id}"));
59        }
60        if let Some(ref user_id) = params.user_id {
61            query_parts.push(format!("userId={user_id}"));
62        }
63        if let Some(ref status) = params.status {
64            let status_str = serde_json::to_string(status)?;
65            query_parts.push(format!("status={}", status_str.trim_matches('"')));
66        }
67        if let Some(ref cursor) = params.cursor {
68            query_parts.push(format!("cursor={cursor}"));
69        }
70        if let Some(limit) = params.limit {
71            query_parts.push(format!("limit={limit}"));
72        }
73
74        if !query_parts.is_empty() {
75            path.push('?');
76            path.push_str(&query_parts.join("&"));
77        }
78
79        self.get(&path).await
80    }
81
82    /// Get a card by its ID
83    ///
84    /// # Arguments
85    ///
86    /// * `card_id` - The unique identifier of the card
87    ///
88    /// # Returns
89    ///
90    /// Returns a [`Card`] containing the card information.
91    ///
92    /// # Errors
93    ///
94    /// This method can return the following errors:
95    /// - `401` - Invalid authorization
96    /// - `404` - Card not found
97    /// - `500` - Internal server error
98    ///
99    /// # Examples
100    ///
101    /// ```no_run
102    /// use rain_sdk::{RainClient, Config, Environment, AuthConfig};
103    /// use uuid::Uuid;
104    ///
105    /// # #[cfg(feature = "async")]
106    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
107    /// let config = Config::new(Environment::Dev);
108    /// let auth = AuthConfig::with_api_key("your-api-key".to_string());
109    /// let client = RainClient::new(config, auth)?;
110    ///
111    /// let card_id = Uuid::new_v4();
112    /// let card = client.get_card(&card_id).await?;
113    /// # Ok(())
114    /// # }
115    /// ```
116    #[cfg(feature = "async")]
117    pub async fn get_card(&self, card_id: &Uuid) -> Result<Card> {
118        let path = format!("/cards/{card_id}");
119        self.get(&path).await
120    }
121
122    /// Update a card
123    ///
124    /// # Arguments
125    ///
126    /// * `card_id` - The unique identifier of the card
127    /// * `request` - The update request
128    ///
129    /// # Returns
130    ///
131    /// Returns a [`Card`] containing the updated card information.
132    ///
133    /// # Errors
134    ///
135    /// This method can return the following errors:
136    /// - `400` - Invalid request
137    /// - `401` - Invalid authorization
138    /// - `404` - Card not found
139    /// - `500` - Internal server error
140    ///
141    /// # Examples
142    ///
143    /// ```no_run
144    /// use rain_sdk::{RainClient, Config, Environment, AuthConfig};
145    /// use rain_sdk::models::cards::{UpdateCardRequest, CardStatus};
146    /// use uuid::Uuid;
147    ///
148    /// # #[cfg(feature = "async")]
149    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
150    /// let config = Config::new(Environment::Dev);
151    /// let auth = AuthConfig::with_api_key("your-api-key".to_string());
152    /// let client = RainClient::new(config, auth)?;
153    ///
154    /// let card_id = Uuid::new_v4();
155    /// let request = UpdateCardRequest {
156    ///     status: Some(CardStatus::Active),
157    ///     limit: None,
158    ///     billing: None,
159    ///     configuration: None,
160    /// };
161    /// let card = client.update_card(&card_id, &request).await?;
162    /// # Ok(())
163    /// # }
164    /// ```
165    #[cfg(feature = "async")]
166    pub async fn update_card(&self, card_id: &Uuid, request: &UpdateCardRequest) -> Result<Card> {
167        let path = format!("/cards/{card_id}");
168        self.patch(&path, request).await
169    }
170
171    /// Get a card's encrypted data (PAN and CVC)
172    ///
173    /// # Arguments
174    ///
175    /// * `card_id` - The unique identifier of the card
176    /// * `session_id` - The encrypted session ID
177    ///
178    /// # Returns
179    ///
180    /// Returns a [`CardSecrets`] containing the encrypted PAN and CVC.
181    ///
182    /// # Examples
183    ///
184    /// ```no_run
185    /// use rain_sdk::{RainClient, Config, Environment, AuthConfig};
186    /// use uuid::Uuid;
187    ///
188    /// # #[cfg(feature = "async")]
189    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
190    /// let config = Config::new(Environment::Dev);
191    /// let auth = AuthConfig::with_api_key("your-api-key".to_string());
192    /// let client = RainClient::new(config, auth)?;
193    ///
194    /// let card_id = Uuid::new_v4();
195    /// let session_id = "your-session-id".to_string();
196    /// let secrets = client.get_card_secrets(&card_id, &session_id).await?;
197    /// # Ok(())
198    /// # }
199    /// ```
200    #[cfg(feature = "async")]
201    pub async fn get_card_secrets(&self, card_id: &Uuid, session_id: &str) -> Result<CardSecrets> {
202        let path = format!("/cards/{card_id}/secrets");
203        self.get_with_headers(&path, vec![("SessionId", session_id)])
204            .await
205    }
206
207    /// Get processor details of a card
208    ///
209    /// # Arguments
210    ///
211    /// * `card_id` - The unique identifier of the card
212    ///
213    /// # Returns
214    ///
215    /// Returns a [`ProcessorDetails`] containing the processor card ID.
216    ///
217    /// # Examples
218    ///
219    /// ```no_run
220    /// use rain_sdk::{RainClient, Config, Environment, AuthConfig};
221    /// use uuid::Uuid;
222    ///
223    /// # #[cfg(feature = "async")]
224    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
225    /// let config = Config::new(Environment::Dev);
226    /// let auth = AuthConfig::with_api_key("your-api-key".to_string());
227    /// let client = RainClient::new(config, auth)?;
228    ///
229    /// let card_id = Uuid::new_v4();
230    /// let details = client.get_card_processor_details(&card_id).await?;
231    /// # Ok(())
232    /// # }
233    /// ```
234    #[cfg(feature = "async")]
235    pub async fn get_card_processor_details(&self, card_id: &Uuid) -> Result<ProcessorDetails> {
236        let path = format!("/cards/{card_id}/processorDetails");
237        self.get(&path).await
238    }
239
240    /// Get a card's PIN
241    ///
242    /// # Arguments
243    ///
244    /// * `card_id` - The unique identifier of the card
245    /// * `session_id` - The encrypted session ID
246    ///
247    /// # Returns
248    ///
249    /// Returns a [`CardPin`] containing the encrypted PIN.
250    ///
251    /// # Examples
252    ///
253    /// ```no_run
254    /// use rain_sdk::{RainClient, Config, Environment, AuthConfig};
255    /// use uuid::Uuid;
256    ///
257    /// # #[cfg(feature = "async")]
258    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
259    /// let config = Config::new(Environment::Dev);
260    /// let auth = AuthConfig::with_api_key("your-api-key".to_string());
261    /// let client = RainClient::new(config, auth)?;
262    ///
263    /// let card_id = Uuid::new_v4();
264    /// let session_id = "your-session-id".to_string();
265    /// let pin = client.get_card_pin(&card_id, &session_id).await?;
266    /// # Ok(())
267    /// # }
268    /// ```
269    #[cfg(feature = "async")]
270    pub async fn get_card_pin(&self, card_id: &Uuid, session_id: &str) -> Result<CardPin> {
271        let path = format!("/cards/{card_id}/pin");
272        self.get_with_headers(&path, vec![("SessionId", session_id)])
273            .await
274    }
275
276    /// Update a card's PIN
277    ///
278    /// # Arguments
279    ///
280    /// * `card_id` - The unique identifier of the card
281    /// * `request` - The PIN update request with encrypted PIN
282    /// * `session_id` - The encrypted session ID
283    ///
284    /// # Returns
285    ///
286    /// Returns success (200 OK) with no response body.
287    ///
288    /// # Examples
289    ///
290    /// ```no_run
291    /// use rain_sdk::{RainClient, Config, Environment, AuthConfig};
292    /// use rain_sdk::models::cards::{UpdateCardPinRequest, EncryptedData};
293    /// use uuid::Uuid;
294    ///
295    /// # #[cfg(feature = "async")]
296    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
297    /// let config = Config::new(Environment::Dev);
298    /// let auth = AuthConfig::with_api_key("your-api-key".to_string());
299    /// let client = RainClient::new(config, auth)?;
300    ///
301    /// let card_id = Uuid::new_v4();
302    /// let session_id = "your-session-id".to_string();
303    /// let request = UpdateCardPinRequest {
304    ///     encrypted_pin: EncryptedData {
305    ///         iv: "initialization-vector".to_string(),
306    ///         data: "encrypted-data".to_string(),
307    ///     },
308    /// };
309    /// client.update_card_pin(&card_id, &request, &session_id).await?;
310    /// # Ok(())
311    /// # }
312    /// ```
313    #[cfg(feature = "async")]
314    pub async fn update_card_pin(
315        &self,
316        card_id: &Uuid,
317        request: &UpdateCardPinRequest,
318        session_id: &str,
319    ) -> Result<()> {
320        let path = format!("/cards/{card_id}/pin");
321        // Use a dummy deserializable type for empty response
322        let _: serde_json::Value = self
323            .put_with_headers(&path, request, vec![("SessionId", session_id)])
324            .await?;
325        Ok(())
326    }
327
328    /// Create a card for a user
329    ///
330    /// # Arguments
331    ///
332    /// * `user_id` - The unique identifier of the user
333    /// * `request` - The card creation request
334    ///
335    /// # Returns
336    ///
337    /// Returns a [`Card`] containing the created card information.
338    ///
339    /// # Examples
340    ///
341    /// ```no_run
342    /// use rain_sdk::{RainClient, Config, Environment, AuthConfig};
343    /// use rain_sdk::models::cards::{CreateCardRequest, CardType};
344    /// use uuid::Uuid;
345    ///
346    /// # #[cfg(feature = "async")]
347    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
348    /// let config = Config::new(Environment::Dev);
349    /// let auth = AuthConfig::with_api_key("your-api-key".to_string());
350    /// let client = RainClient::new(config, auth)?;
351    ///
352    /// let user_id = Uuid::new_v4();
353    /// let request = CreateCardRequest {
354    ///     r#type: CardType::Virtual,
355    ///     status: None,
356    ///     limit: None,
357    ///     configuration: None,
358    ///     shipping: None,
359    ///     bulk_shipping_group_id: None,
360    ///     billing: None,
361    /// };
362    /// let card = client.create_user_card(&user_id, &request).await?;
363    /// # Ok(())
364    /// # }
365    /// ```
366    #[cfg(feature = "async")]
367    pub async fn create_user_card(
368        &self,
369        user_id: &Uuid,
370        request: &CreateCardRequest,
371    ) -> Result<Card> {
372        let path = format!("/users/{user_id}/cards");
373        self.post(&path, request).await
374    }
375
376    // ============================================================================
377    // Blocking Methods
378    // ============================================================================
379
380    /// Get all cards for a user or company (blocking)
381    #[cfg(feature = "sync")]
382    pub fn list_cards_blocking(&self, params: &ListCardsParams) -> Result<Vec<Card>> {
383        let mut path = "/cards".to_string();
384        let mut query_parts = Vec::new();
385
386        if let Some(ref company_id) = params.company_id {
387            query_parts.push(format!("companyId={company_id}"));
388        }
389        if let Some(ref user_id) = params.user_id {
390            query_parts.push(format!("userId={user_id}"));
391        }
392        if let Some(ref status) = params.status {
393            let status_str = serde_json::to_string(status)?;
394            query_parts.push(format!("status={}", status_str.trim_matches('"')));
395        }
396        if let Some(ref cursor) = params.cursor {
397            query_parts.push(format!("cursor={cursor}"));
398        }
399        if let Some(limit) = params.limit {
400            query_parts.push(format!("limit={limit}"));
401        }
402
403        if !query_parts.is_empty() {
404            path.push('?');
405            path.push_str(&query_parts.join("&"));
406        }
407
408        self.get_blocking(&path)
409    }
410
411    /// Get a card by its ID (blocking)
412    #[cfg(feature = "sync")]
413    pub fn get_card_blocking(&self, card_id: &Uuid) -> Result<Card> {
414        let path = format!("/cards/{card_id}");
415        self.get_blocking(&path)
416    }
417
418    /// Update a card (blocking)
419    #[cfg(feature = "sync")]
420    pub fn update_card_blocking(
421        &self,
422        card_id: &Uuid,
423        request: &UpdateCardRequest,
424    ) -> Result<Card> {
425        let path = format!("/cards/{card_id}");
426        self.patch_blocking(&path, request)
427    }
428
429    /// Create a card for a user (blocking)
430    #[cfg(feature = "sync")]
431    pub fn create_user_card_blocking(
432        &self,
433        user_id: &Uuid,
434        request: &CreateCardRequest,
435    ) -> Result<Card> {
436        let path = format!("/users/{user_id}/cards");
437        self.post_blocking(&path, request)
438    }
439}