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(¶ms).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}