1use crate::application::dto::{
2 CreatePaymentMethodRequest, PaymentMethodResponse, UpdatePaymentMethodRequest,
3};
4use crate::application::ports::PaymentMethodRepository;
5use crate::domain::entities::payment_method::{PaymentMethod, PaymentMethodType};
6use std::sync::Arc;
7use uuid::Uuid;
8
9pub struct PaymentMethodUseCases {
10 payment_method_repository: Arc<dyn PaymentMethodRepository>,
11}
12
13impl PaymentMethodUseCases {
14 pub fn new(payment_method_repository: Arc<dyn PaymentMethodRepository>) -> Self {
15 Self {
16 payment_method_repository,
17 }
18 }
19
20 pub async fn create_payment_method(
24 &self,
25 organization_id: Uuid,
26 request: CreatePaymentMethodRequest,
27 ) -> Result<PaymentMethodResponse, String> {
28 let payment_method = PaymentMethod::new(
29 organization_id,
30 request.owner_id,
31 request.method_type,
32 request.stripe_payment_method_id,
33 request.stripe_customer_id,
34 request.display_label,
35 request.is_default,
36 )?;
37
38 let mut payment_method = payment_method;
40 if let Some(metadata) = request.metadata {
41 payment_method.set_metadata(metadata);
42 }
43
44 if let Some(expires_at) = request.expires_at {
46 payment_method.set_expiry(expires_at)?;
47 }
48
49 let created = self
50 .payment_method_repository
51 .create(&payment_method)
52 .await?;
53
54 if created.is_default {
56 let _ = self
57 .payment_method_repository
58 .set_as_default(created.id, created.owner_id)
59 .await?;
60 }
61
62 Ok(PaymentMethodResponse::from(created))
63 }
64
65 pub async fn get_payment_method(
67 &self,
68 id: Uuid,
69 ) -> Result<Option<PaymentMethodResponse>, String> {
70 match self.payment_method_repository.find_by_id(id).await? {
71 Some(method) => Ok(Some(PaymentMethodResponse::from(method))),
72 None => Ok(None),
73 }
74 }
75
76 pub async fn get_payment_method_by_stripe_id(
78 &self,
79 stripe_payment_method_id: &str,
80 ) -> Result<Option<PaymentMethodResponse>, String> {
81 match self
82 .payment_method_repository
83 .find_by_stripe_payment_method_id(stripe_payment_method_id)
84 .await?
85 {
86 Some(method) => Ok(Some(PaymentMethodResponse::from(method))),
87 None => Ok(None),
88 }
89 }
90
91 pub async fn list_owner_payment_methods(
93 &self,
94 owner_id: Uuid,
95 ) -> Result<Vec<PaymentMethodResponse>, String> {
96 let methods = self
97 .payment_method_repository
98 .find_by_owner(owner_id)
99 .await?;
100 Ok(methods
101 .into_iter()
102 .map(PaymentMethodResponse::from)
103 .collect())
104 }
105
106 pub async fn list_active_owner_payment_methods(
108 &self,
109 owner_id: Uuid,
110 ) -> Result<Vec<PaymentMethodResponse>, String> {
111 let methods = self
112 .payment_method_repository
113 .find_active_by_owner(owner_id)
114 .await?;
115 Ok(methods
116 .into_iter()
117 .map(PaymentMethodResponse::from)
118 .collect())
119 }
120
121 pub async fn get_default_payment_method(
123 &self,
124 owner_id: Uuid,
125 ) -> Result<Option<PaymentMethodResponse>, String> {
126 match self
127 .payment_method_repository
128 .find_default_by_owner(owner_id)
129 .await?
130 {
131 Some(method) => Ok(Some(PaymentMethodResponse::from(method))),
132 None => Ok(None),
133 }
134 }
135
136 pub async fn list_organization_payment_methods(
138 &self,
139 organization_id: Uuid,
140 ) -> Result<Vec<PaymentMethodResponse>, String> {
141 let methods = self
142 .payment_method_repository
143 .find_by_organization(organization_id)
144 .await?;
145 Ok(methods
146 .into_iter()
147 .map(PaymentMethodResponse::from)
148 .collect())
149 }
150
151 pub async fn list_payment_methods_by_type(
153 &self,
154 owner_id: Uuid,
155 method_type: PaymentMethodType,
156 ) -> Result<Vec<PaymentMethodResponse>, String> {
157 let methods = self
158 .payment_method_repository
159 .find_by_owner_and_type(owner_id, method_type)
160 .await?;
161 Ok(methods
162 .into_iter()
163 .map(PaymentMethodResponse::from)
164 .collect())
165 }
166
167 pub async fn update_payment_method(
169 &self,
170 id: Uuid,
171 request: UpdatePaymentMethodRequest,
172 ) -> Result<PaymentMethodResponse, String> {
173 let mut payment_method = self
174 .payment_method_repository
175 .find_by_id(id)
176 .await?
177 .ok_or_else(|| "Payment method not found".to_string())?;
178
179 if let Some(display_label) = request.display_label {
181 if display_label.trim().is_empty() {
182 return Err("Display label cannot be empty".to_string());
183 }
184 payment_method.display_label = display_label;
185 payment_method.updated_at = chrono::Utc::now();
186 }
187
188 if let Some(metadata) = request.metadata {
190 payment_method.set_metadata(metadata);
191 }
192
193 if let Some(is_default) = request.is_default {
195 if is_default && !payment_method.is_default {
196 return Ok(PaymentMethodResponse::from(
198 self.payment_method_repository
199 .set_as_default(id, payment_method.owner_id)
200 .await?,
201 ));
202 } else if !is_default && payment_method.is_default {
203 payment_method.unset_default();
205 }
206 }
207
208 let updated = self
209 .payment_method_repository
210 .update(&payment_method)
211 .await?;
212 Ok(PaymentMethodResponse::from(updated))
213 }
214
215 pub async fn set_as_default(
217 &self,
218 id: Uuid,
219 owner_id: Uuid,
220 ) -> Result<PaymentMethodResponse, String> {
221 let payment_method = self
222 .payment_method_repository
223 .set_as_default(id, owner_id)
224 .await?;
225 Ok(PaymentMethodResponse::from(payment_method))
226 }
227
228 pub async fn deactivate_payment_method(
230 &self,
231 id: Uuid,
232 ) -> Result<PaymentMethodResponse, String> {
233 let mut payment_method = self
234 .payment_method_repository
235 .find_by_id(id)
236 .await?
237 .ok_or_else(|| "Payment method not found".to_string())?;
238
239 payment_method.deactivate()?;
240
241 let updated = self
242 .payment_method_repository
243 .update(&payment_method)
244 .await?;
245 Ok(PaymentMethodResponse::from(updated))
246 }
247
248 pub async fn reactivate_payment_method(
250 &self,
251 id: Uuid,
252 ) -> Result<PaymentMethodResponse, String> {
253 let mut payment_method = self
254 .payment_method_repository
255 .find_by_id(id)
256 .await?
257 .ok_or_else(|| "Payment method not found".to_string())?;
258
259 payment_method.reactivate()?;
260
261 let updated = self
262 .payment_method_repository
263 .update(&payment_method)
264 .await?;
265 Ok(PaymentMethodResponse::from(updated))
266 }
267
268 pub async fn delete_payment_method(&self, id: Uuid) -> Result<bool, String> {
270 self.payment_method_repository.delete(id).await
271 }
272
273 pub async fn count_active_payment_methods(&self, owner_id: Uuid) -> Result<i64, String> {
275 self.payment_method_repository
276 .count_active_by_owner(owner_id)
277 .await
278 }
279
280 pub async fn has_active_payment_methods(&self, owner_id: Uuid) -> Result<bool, String> {
282 self.payment_method_repository
283 .has_active_payment_methods(owner_id)
284 .await
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291 use async_trait::async_trait;
292 use std::collections::HashMap;
293 use std::sync::Mutex;
294
295 struct MockPaymentMethodRepository {
298 methods: Mutex<HashMap<Uuid, PaymentMethod>>,
299 }
300
301 impl MockPaymentMethodRepository {
302 fn new() -> Self {
303 Self {
304 methods: Mutex::new(HashMap::new()),
305 }
306 }
307 }
308
309 #[async_trait]
310 impl PaymentMethodRepository for MockPaymentMethodRepository {
311 async fn create(&self, payment_method: &PaymentMethod) -> Result<PaymentMethod, String> {
312 self.methods
313 .lock()
314 .unwrap()
315 .insert(payment_method.id, payment_method.clone());
316 Ok(payment_method.clone())
317 }
318
319 async fn find_by_id(&self, id: Uuid) -> Result<Option<PaymentMethod>, String> {
320 Ok(self.methods.lock().unwrap().get(&id).cloned())
321 }
322
323 async fn find_by_stripe_payment_method_id(
324 &self,
325 stripe_payment_method_id: &str,
326 ) -> Result<Option<PaymentMethod>, String> {
327 Ok(self
328 .methods
329 .lock()
330 .unwrap()
331 .values()
332 .find(|m| m.stripe_payment_method_id == stripe_payment_method_id)
333 .cloned())
334 }
335
336 async fn find_by_owner(&self, owner_id: Uuid) -> Result<Vec<PaymentMethod>, String> {
337 Ok(self
338 .methods
339 .lock()
340 .unwrap()
341 .values()
342 .filter(|m| m.owner_id == owner_id)
343 .cloned()
344 .collect())
345 }
346
347 async fn find_active_by_owner(&self, owner_id: Uuid) -> Result<Vec<PaymentMethod>, String> {
348 Ok(self
349 .methods
350 .lock()
351 .unwrap()
352 .values()
353 .filter(|m| m.owner_id == owner_id && m.is_active)
354 .cloned()
355 .collect())
356 }
357
358 async fn find_default_by_owner(
359 &self,
360 owner_id: Uuid,
361 ) -> Result<Option<PaymentMethod>, String> {
362 Ok(self
363 .methods
364 .lock()
365 .unwrap()
366 .values()
367 .find(|m| m.owner_id == owner_id && m.is_default)
368 .cloned())
369 }
370
371 async fn find_by_organization(
372 &self,
373 organization_id: Uuid,
374 ) -> Result<Vec<PaymentMethod>, String> {
375 Ok(self
376 .methods
377 .lock()
378 .unwrap()
379 .values()
380 .filter(|m| m.organization_id == organization_id)
381 .cloned()
382 .collect())
383 }
384
385 async fn find_by_owner_and_type(
386 &self,
387 owner_id: Uuid,
388 method_type: PaymentMethodType,
389 ) -> Result<Vec<PaymentMethod>, String> {
390 Ok(self
391 .methods
392 .lock()
393 .unwrap()
394 .values()
395 .filter(|m| m.owner_id == owner_id && m.method_type == method_type)
396 .cloned()
397 .collect())
398 }
399
400 async fn update(&self, payment_method: &PaymentMethod) -> Result<PaymentMethod, String> {
401 self.methods
402 .lock()
403 .unwrap()
404 .insert(payment_method.id, payment_method.clone());
405 Ok(payment_method.clone())
406 }
407
408 async fn delete(&self, id: Uuid) -> Result<bool, String> {
409 Ok(self.methods.lock().unwrap().remove(&id).is_some())
410 }
411
412 async fn set_as_default(&self, id: Uuid, owner_id: Uuid) -> Result<PaymentMethod, String> {
413 let mut store = self.methods.lock().unwrap();
414 for method in store.values_mut() {
416 if method.owner_id == owner_id && method.id != id {
417 method.is_default = false;
418 method.updated_at = chrono::Utc::now();
419 }
420 }
421 if let Some(method) = store.get_mut(&id) {
423 method.is_default = true;
424 method.updated_at = chrono::Utc::now();
425 Ok(method.clone())
426 } else {
427 Err("Payment method not found".to_string())
428 }
429 }
430
431 async fn count_active_by_owner(&self, owner_id: Uuid) -> Result<i64, String> {
432 Ok(self
433 .methods
434 .lock()
435 .unwrap()
436 .values()
437 .filter(|m| m.owner_id == owner_id && m.is_active)
438 .count() as i64)
439 }
440
441 async fn has_active_payment_methods(&self, owner_id: Uuid) -> Result<bool, String> {
442 Ok(self
443 .methods
444 .lock()
445 .unwrap()
446 .values()
447 .any(|m| m.owner_id == owner_id && m.is_active))
448 }
449 }
450
451 fn make_use_cases(repo: Arc<MockPaymentMethodRepository>) -> PaymentMethodUseCases {
454 PaymentMethodUseCases::new(repo)
455 }
456
457 fn make_create_request(
458 owner_id: Uuid,
459 method_type: PaymentMethodType,
460 label: &str,
461 is_default: bool,
462 ) -> CreatePaymentMethodRequest {
463 CreatePaymentMethodRequest {
464 owner_id,
465 method_type,
466 stripe_payment_method_id: format!("pm_test_{}", Uuid::new_v4()),
467 stripe_customer_id: format!("cus_test_{}", Uuid::new_v4()),
468 display_label: label.to_string(),
469 is_default,
470 metadata: None,
471 expires_at: None,
472 }
473 }
474
475 #[tokio::test]
478 async fn test_create_payment_method_success() {
479 let repo = Arc::new(MockPaymentMethodRepository::new());
480 let use_cases = make_use_cases(repo.clone());
481 let org_id = Uuid::new_v4();
482 let owner_id = Uuid::new_v4();
483
484 let request =
485 make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 4242", false);
486
487 let result = use_cases.create_payment_method(org_id, request).await;
488 assert!(result.is_ok());
489
490 let response = result.unwrap();
491 assert_eq!(response.owner_id, owner_id);
492 assert_eq!(response.organization_id, org_id);
493 assert_eq!(response.display_label, "Visa **** 4242");
494 assert!(!response.is_default);
495 assert!(response.is_active);
496
497 assert_eq!(repo.methods.lock().unwrap().len(), 1);
499 }
500
501 #[tokio::test]
502 async fn test_create_payment_method_with_default_flag() {
503 let repo = Arc::new(MockPaymentMethodRepository::new());
504 let use_cases = make_use_cases(repo.clone());
505 let org_id = Uuid::new_v4();
506 let owner_id = Uuid::new_v4();
507
508 let request = make_create_request(
509 owner_id,
510 PaymentMethodType::SepaDebit,
511 "SEPA BE68 5390 0754",
512 true,
513 );
514
515 let result = use_cases.create_payment_method(org_id, request).await;
516 assert!(result.is_ok());
517
518 let response = result.unwrap();
519 assert!(response.is_default);
520 assert_eq!(response.display_label, "SEPA BE68 5390 0754");
521 }
522
523 #[tokio::test]
524 async fn test_set_as_default_unsets_previous_default() {
525 let repo = Arc::new(MockPaymentMethodRepository::new());
526 let use_cases = make_use_cases(repo.clone());
527 let org_id = Uuid::new_v4();
528 let owner_id = Uuid::new_v4();
529
530 let req1 = make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 1111", true);
532 let method1 = use_cases.create_payment_method(org_id, req1).await.unwrap();
533 assert!(method1.is_default);
534
535 let req2 = make_create_request(
537 owner_id,
538 PaymentMethodType::SepaDebit,
539 "SEPA BE68 1234",
540 false,
541 );
542 let method2 = use_cases.create_payment_method(org_id, req2).await.unwrap();
543 assert!(!method2.is_default);
544
545 let result = use_cases.set_as_default(method2.id, owner_id).await;
547 assert!(result.is_ok());
548 let updated_method2 = result.unwrap();
549 assert!(updated_method2.is_default);
550
551 let first = use_cases
553 .get_payment_method(method1.id)
554 .await
555 .unwrap()
556 .unwrap();
557 assert!(!first.is_default);
558 }
559
560 #[tokio::test]
561 async fn test_deactivate_payment_method_success() {
562 let repo = Arc::new(MockPaymentMethodRepository::new());
563 let use_cases = make_use_cases(repo.clone());
564 let org_id = Uuid::new_v4();
565 let owner_id = Uuid::new_v4();
566
567 let request =
568 make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 4242", false);
569 let created = use_cases
570 .create_payment_method(org_id, request)
571 .await
572 .unwrap();
573 assert!(created.is_active);
574
575 let result = use_cases.deactivate_payment_method(created.id).await;
576 assert!(result.is_ok());
577
578 let deactivated = result.unwrap();
579 assert!(!deactivated.is_active);
580 assert!(!deactivated.is_usable);
581 }
582
583 #[tokio::test]
584 async fn test_deactivate_already_inactive_fails() {
585 let repo = Arc::new(MockPaymentMethodRepository::new());
586 let use_cases = make_use_cases(repo.clone());
587 let org_id = Uuid::new_v4();
588 let owner_id = Uuid::new_v4();
589
590 let request =
591 make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 4242", false);
592 let created = use_cases
593 .create_payment_method(org_id, request)
594 .await
595 .unwrap();
596
597 use_cases
599 .deactivate_payment_method(created.id)
600 .await
601 .unwrap();
602
603 let result = use_cases.deactivate_payment_method(created.id).await;
605 assert!(result.is_err());
606 assert!(result.unwrap_err().contains("already inactive"));
607 }
608
609 #[tokio::test]
610 async fn test_reactivate_payment_method_success() {
611 let repo = Arc::new(MockPaymentMethodRepository::new());
612 let use_cases = make_use_cases(repo.clone());
613 let org_id = Uuid::new_v4();
614 let owner_id = Uuid::new_v4();
615
616 let request =
617 make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 4242", false);
618 let created = use_cases
619 .create_payment_method(org_id, request)
620 .await
621 .unwrap();
622
623 use_cases
625 .deactivate_payment_method(created.id)
626 .await
627 .unwrap();
628
629 let result = use_cases.reactivate_payment_method(created.id).await;
631 assert!(result.is_ok());
632
633 let reactivated = result.unwrap();
634 assert!(reactivated.is_active);
635 assert!(reactivated.is_usable);
636 }
637
638 #[tokio::test]
639 async fn test_list_owner_payment_methods() {
640 let repo = Arc::new(MockPaymentMethodRepository::new());
641 let use_cases = make_use_cases(repo.clone());
642 let org_id = Uuid::new_v4();
643 let owner_id = Uuid::new_v4();
644 let other_owner_id = Uuid::new_v4();
645
646 let req1 = make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 1111", true);
648 let req2 = make_create_request(
649 owner_id,
650 PaymentMethodType::SepaDebit,
651 "SEPA BE68 1234",
652 false,
653 );
654 let req3 = make_create_request(
656 other_owner_id,
657 PaymentMethodType::Card,
658 "MC **** 5555",
659 false,
660 );
661
662 use_cases.create_payment_method(org_id, req1).await.unwrap();
663 use_cases.create_payment_method(org_id, req2).await.unwrap();
664 use_cases.create_payment_method(org_id, req3).await.unwrap();
665
666 let result = use_cases.list_owner_payment_methods(owner_id).await;
667 assert!(result.is_ok());
668 let methods = result.unwrap();
669 assert_eq!(methods.len(), 2);
670 assert!(methods.iter().all(|m| m.owner_id == owner_id));
671 }
672
673 #[tokio::test]
674 async fn test_deactivate_nonexistent_method_fails() {
675 let repo = Arc::new(MockPaymentMethodRepository::new());
676 let use_cases = make_use_cases(repo.clone());
677 let fake_id = Uuid::new_v4();
678
679 let result = use_cases.deactivate_payment_method(fake_id).await;
680 assert!(result.is_err());
681 assert!(result.unwrap_err().contains("not found"));
682 }
683
684 #[tokio::test]
685 async fn test_reactivate_nonexistent_method_fails() {
686 let repo = Arc::new(MockPaymentMethodRepository::new());
687 let use_cases = make_use_cases(repo.clone());
688 let fake_id = Uuid::new_v4();
689
690 let result = use_cases.reactivate_payment_method(fake_id).await;
691 assert!(result.is_err());
692 assert!(result.unwrap_err().contains("not found"));
693 }
694
695 #[tokio::test]
696 async fn test_count_and_has_active_payment_methods() {
697 let repo = Arc::new(MockPaymentMethodRepository::new());
698 let use_cases = make_use_cases(repo.clone());
699 let org_id = Uuid::new_v4();
700 let owner_id = Uuid::new_v4();
701
702 let count = use_cases
704 .count_active_payment_methods(owner_id)
705 .await
706 .unwrap();
707 assert_eq!(count, 0);
708 let has = use_cases
709 .has_active_payment_methods(owner_id)
710 .await
711 .unwrap();
712 assert!(!has);
713
714 let req1 = make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 1111", false);
716 let req2 = make_create_request(
717 owner_id,
718 PaymentMethodType::SepaDebit,
719 "SEPA BE68 9999",
720 false,
721 );
722 let m1 = use_cases.create_payment_method(org_id, req1).await.unwrap();
723 use_cases.create_payment_method(org_id, req2).await.unwrap();
724
725 let count = use_cases
726 .count_active_payment_methods(owner_id)
727 .await
728 .unwrap();
729 assert_eq!(count, 2);
730 let has = use_cases
731 .has_active_payment_methods(owner_id)
732 .await
733 .unwrap();
734 assert!(has);
735
736 use_cases.deactivate_payment_method(m1.id).await.unwrap();
738 let count = use_cases
739 .count_active_payment_methods(owner_id)
740 .await
741 .unwrap();
742 assert_eq!(count, 1);
743 }
744
745 #[tokio::test]
746 async fn test_get_default_payment_method() {
747 let repo = Arc::new(MockPaymentMethodRepository::new());
748 let use_cases = make_use_cases(repo.clone());
749 let org_id = Uuid::new_v4();
750 let owner_id = Uuid::new_v4();
751
752 let result = use_cases
754 .get_default_payment_method(owner_id)
755 .await
756 .unwrap();
757 assert!(result.is_none());
758
759 let req = make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 4242", true);
761 let created = use_cases.create_payment_method(org_id, req).await.unwrap();
762
763 let result = use_cases
764 .get_default_payment_method(owner_id)
765 .await
766 .unwrap();
767 assert!(result.is_some());
768 assert_eq!(result.unwrap().id, created.id);
769 }
770
771 #[tokio::test]
772 async fn test_delete_payment_method() {
773 let repo = Arc::new(MockPaymentMethodRepository::new());
774 let use_cases = make_use_cases(repo.clone());
775 let org_id = Uuid::new_v4();
776 let owner_id = Uuid::new_v4();
777
778 let req = make_create_request(owner_id, PaymentMethodType::Card, "Visa **** 4242", false);
779 let created = use_cases.create_payment_method(org_id, req).await.unwrap();
780
781 let deleted = use_cases.delete_payment_method(created.id).await.unwrap();
783 assert!(deleted);
784
785 let found = use_cases.get_payment_method(created.id).await.unwrap();
787 assert!(found.is_none());
788
789 let deleted_again = use_cases.delete_payment_method(created.id).await.unwrap();
791 assert!(!deleted_again);
792 }
793}