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