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