1use crate::client::SquareClient;
6use crate::api::{Verb, SquareAPI};
7use crate::errors::{CardBuildError, SquareError, ValidationError};
8use crate::response::SquareResponse;
9use crate::objects::{Address, Card};
10
11use serde::{Deserialize, Serialize};
12use uuid::Uuid;
13use square_ox_derive::Builder;
14use crate::builder::{AddField, Builder, ParentBuilder, Validate, Buildable};
15use crate::objects::enums::SortOrder;
16
17impl SquareClient {
18 pub fn cards(&self) -> Cards {
19 Cards {
20 client: &self,
21 }
22 }
23}
24
25pub struct Cards<'a> {
26 client: &'a SquareClient,
27}
28
29impl<'a> Cards<'a> {
30 pub async fn retrieve(self, card_id: String)
49 -> Result<SquareResponse, SquareError> {
50 self.client.request(
51 Verb::GET,
52 SquareAPI::Cards(format!("/{}", card_id)),
53 None::<&Card>,
54 None,
55 ).await
56 }
57
58 pub async fn list(self, search_query: Option<Vec<(String, String)>>)
76 -> Result<SquareResponse, SquareError> {
77 self.client.request(
78 Verb::GET,
79 SquareAPI::Cards("".to_string()),
80 None::<&Card>,
81 search_query,
82 ).await
83 }
84
85 pub async fn create(self, card: CardWrapper)
113 -> Result<SquareResponse, SquareError> {
114 self.client.request(
115 Verb::POST,
116 SquareAPI::Cards("".to_string()),
117 Some(&card),
118 None,
119 ).await
120 }
121
122 pub async fn disable(self, card_id: String)
141 -> Result<SquareResponse, SquareError> {
142 self.client.request(
143 Verb::POST,
144 SquareAPI::Cards(format!("/{}/disable", card_id)),
145 None::<&Card>,
146 None,
147 ).await
148 }
149}
150
151#[derive(Default)]
152pub struct ListCardsQueryBuilder {
153 cursor: Option<String>,
154 customer_id: Option<String>,
155 include_disabled: Option<bool>,
156 reference_id: Option<String>,
157 sort_order: Option<SortOrder>,
158}
159
160impl ListCardsQueryBuilder {
161 pub fn new() -> Self {
162 Default::default()
163 }
164
165 pub fn cursor(mut self, cursor: String) -> Self {
166 self.cursor = Some(cursor);
167
168 self
169 }
170
171 pub fn customer_id(mut self, customer_id: String) -> Self {
172 self.customer_id = Some(customer_id);
173
174 self
175 }
176
177 pub fn include_disabled(mut self) -> Self {
178 self.include_disabled = Some(true);
179
180 self
181 }
182
183 pub fn exclude_disabled(mut self) -> Self {
184 self.include_disabled = Some(false);
185
186 self
187 }
188
189 pub fn reference_id(mut self, reference_id: String) -> Self {
190 self.reference_id = Some(reference_id);
191
192 self
193 }
194
195 pub fn sort_ascending(mut self) -> Self {
196 self.sort_order = Some(SortOrder::Asc);
197
198 self
199 }
200
201 pub fn sort_descending(mut self) -> Self {
202 self.sort_order = Some(SortOrder::Desc);
203
204 self
205 }
206
207 pub async fn build(self) -> Vec<(String, String)> {
208 let ListCardsQueryBuilder {
209 cursor,
210 customer_id,
211 include_disabled,
212 reference_id,
213 sort_order,
214 } = self;
215
216 let mut res = vec![];
217
218 if let Some(cursor) = cursor {
219 res.push(("cursor".to_string(), cursor))
220 }
221 if let Some(customer_id) = customer_id {
222 res.push(("customer_id".to_string(), customer_id))
223 }
224 if let Some(include_disabled) = include_disabled {
225 res.push(("include_disabled".to_string(), include_disabled.to_string()))
226 }
227 if let Some(reference_id) = reference_id {
228 res.push(("reference_id".to_string(), reference_id))
229 }
230 if let Some(sort_order) = sort_order {
231 res.push(("sort_order".to_string(), sort_order.to_string()))
232 }
233
234 res
235 }
236}
237
238#[derive(Clone, Serialize, Debug, Deserialize, Default, Builder)]
239pub struct CardWrapper {
240 pub(crate) card: Card,
241 #[builder_rand("uuid")]
242 #[builder_into]
243 pub(crate) idempotency_key: Option<String>,
244 #[builder_validate("is_some")]
245 #[builder_into]
246 pub(crate) source_id: Option<String>,
247 #[builder_vis("private")]
248 pub(crate) verification_token: Option<String>,
249}
250
251#[cfg(test)]
252mod test_cards {
253 use super::*;
254
255 #[tokio::test]
256 async fn test_list_cards_query_builder() {
257 let expected = vec![
258 ("cursor".to_string(), "dwcsdaw2390rec92".to_string()),
259 ("include_disabled".to_string(), "false".to_string()),
260 ("sort_order".to_string(), "ASC".to_string()),
261 ];
262 let actual = ListCardsQueryBuilder::new()
263 .exclude_disabled()
264 .sort_ascending()
265 .cursor("dwcsdaw2390rec92".to_string())
266 .build()
267 .await;
268
269 assert_eq!(expected, actual)
270 }
271
272 #[tokio::test]
273 async fn test_list_cards() {
274 use dotenv::dotenv;
275 use std::env;
276
277 dotenv().ok();
278 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
279 let sut = SquareClient::new(&access_token);
280
281 let input = vec![
282 ("include_disabled".to_string(), "false".to_string()),
283 ("sort_order".to_string(), "ASC".to_string()),
284 ];
285
286 let res = sut.cards()
287 .list(Some(input))
288 .await;
289
290 assert!(res.is_ok())
291
292 }
293
294 #[tokio::test]
295 async fn test_retrieve_card() {
296 use dotenv::dotenv;
297 use std::env;
298
299 dotenv().ok();
300 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
301 let sut = SquareClient::new(&access_token);
302
303 let res = sut.cards()
304 .retrieve("ccof:Es7R2xLyCWzmrKGI4GB".to_string())
305 .await;
306
307 assert!(res.is_ok())
308
309 }
310
311 #[tokio::test]
312 async fn test_card_wrapper_builder() {
313 let expected = CardWrapper {
314 card: Card {
315 id: None,
316 billing_address: None,
317 bin: None,
318 card_brand: None,
319 card_co_brand: None,
320 card_type: None,
321 cardholder_name: None,
322 customer_id: Some("EDH2RWZCFCRGZCZ99GMG8ZF59R".to_string()),
323 enabled: None,
324 exp_month: None,
325 exp_year: None,
326 fingerprint: None,
327 last_4: None,
328 merchant_id: None,
329 prepaid_type: None,
330 reference_id: None,
331 version: None
332 },
333 idempotency_key: None,
334 source_id: Some("cnon:card-nonce-ok".to_string()),
335 verification_token: None
336 };
337
338 let mut actual = Builder::from(CardWrapper::default())
339 .card(
340 Builder::from(Card::default())
341 .customer_id("EDH2RWZCFCRGZCZ99GMG8ZF59R".to_string())
342 .build()
343 .unwrap()
344 )
345 .source_id("cnon:card-nonce-ok".to_string())
346 .build()
347 .unwrap();
348
349 assert!(actual.idempotency_key.is_some());
350
351 actual.idempotency_key = None;
352
353 assert_eq!(format!("{:?}", expected), format!("{:?}", actual));
354 }
355
356 #[tokio::test]
357 async fn test_card_wrapper_builder_fail() {
358 let res = Builder::from(CardWrapper::default())
359 .card(
360 Builder::from(Card::default())
361 .customer_id("EDH2RWZCFCRGZCZ99GMG8ZF59R".to_string())
362 .build()
363 .unwrap()
364 )
365 .build();
366
367 assert!(res.is_err())
368 }
369
370 async fn test_create_card() {
372 use dotenv::dotenv;
373 use std::env;
374
375 dotenv().ok();
376 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
377 let sut = SquareClient::new(&access_token);
378
379 let input = CardWrapper {
380 card: Card {
381 id: None,
382 billing_address: None,
383 bin: None,
384 card_brand: None,
385 card_co_brand: None,
386 card_type: None,
387 cardholder_name: None,
388 customer_id: Some("EDH2RWZCFCRGZCZ99GMG8ZF59R".to_string()),
389 enabled: None,
390 exp_month: None,
391 exp_year: None,
392 fingerprint: None,
393 last_4: None,
394 merchant_id: None,
395 prepaid_type: None,
396 reference_id: None,
397 version: None
398 },
399 idempotency_key: Some(Uuid::new_v4().to_string()),
400 source_id: Some("cnon:card-nonce-ok".to_string()),
401 verification_token: None
402 };
403
404 let res = sut.cards()
405 .create(input)
406 .await;
407
408 assert!(res.is_ok())
409 }
410
411 #[tokio::test]
412 async fn test_disable_card() {
413 use dotenv::dotenv;
414 use std::env;
415
416 dotenv().ok();
417 let access_token = env::var("ACCESS_TOKEN").expect("ACCESS_TOKEN to be set");
418 let sut = SquareClient::new(&access_token);
419
420 let res = sut.cards()
421 .disable("ccof:ce0ogxL3KIHfNd4Z4GB".to_string())
422 .await;
423
424 assert!(res.is_ok())
425
426 }
427
428
429}