1use crate::application::dto::{CreatePaymentRequest, RefundPaymentRequest};
2use crate::domain::entities::TransactionStatus;
3use crate::infrastructure::audit::{AuditEventType, AuditLogEntry};
4use crate::infrastructure::web::{AppState, AuthenticatedUser};
5use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
6use uuid::Uuid;
7
8#[utoipa::path(
11 post,
12 path = "/payments",
13 tag = "Payments",
14 summary = "Create a payment",
15 request_body = CreatePaymentRequest,
16 responses(
17 (status = 201, description = "Payment created"),
18 (status = 400, description = "Bad request"),
19 (status = 401, description = "Unauthorized"),
20 ),
21 security(("bearer_auth" = []))
22)]
23#[post("/payments")]
24pub async fn create_payment(
25 state: web::Data<AppState>,
26 user: AuthenticatedUser,
27 request: web::Json<CreatePaymentRequest>,
28) -> impl Responder {
29 let organization_id = match user.require_organization() {
30 Ok(org_id) => org_id,
31 Err(e) => {
32 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
33 }
34 };
35
36 match state
37 .payment_use_cases
38 .create_payment(organization_id, request.into_inner())
39 .await
40 {
41 Ok(payment) => {
42 AuditLogEntry::new(
43 AuditEventType::PaymentCreated,
44 Some(user.user_id),
45 Some(organization_id),
46 )
47 .with_resource("Payment", payment.id)
48 .log();
49
50 HttpResponse::Created().json(payment)
51 }
52 Err(err) => {
53 AuditLogEntry::new(
54 AuditEventType::PaymentCreated,
55 Some(user.user_id),
56 Some(organization_id),
57 )
58 .with_error(err.clone())
59 .log();
60
61 HttpResponse::BadRequest().json(serde_json::json!({"error": err}))
62 }
63 }
64}
65
66#[utoipa::path(
67 get,
68 path = "/payments/{id}",
69 tag = "Payments",
70 summary = "Get a payment by ID",
71 params(("id" = Uuid, Path, description = "Payment ID")),
72 responses(
73 (status = 200, description = "Payment found"),
74 (status = 404, description = "Payment not found"),
75 (status = 500, description = "Internal server error"),
76 ),
77 security(("bearer_auth" = []))
78)]
79#[get("/payments/{id}")]
80pub async fn get_payment(
81 state: web::Data<AppState>,
82 user: AuthenticatedUser,
83 id: web::Path<Uuid>,
84) -> impl Responder {
85 match state.payment_use_cases.get_payment(*id).await {
86 Ok(Some(payment)) => {
87 if let Err(e) = user.verify_org_access(payment.organization_id) {
89 return HttpResponse::Forbidden().json(serde_json::json!({ "error": e }));
90 }
91 HttpResponse::Ok().json(payment)
92 }
93 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
94 "error": "Payment not found"
95 })),
96 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
97 }
98}
99
100#[utoipa::path(
101 get,
102 path = "/payments/stripe/{stripe_payment_intent_id}",
103 tag = "Payments",
104 summary = "Get a payment by Stripe payment intent ID",
105 params(("stripe_payment_intent_id" = String, Path, description = "Stripe payment intent ID")),
106 responses(
107 (status = 200, description = "Payment found"),
108 (status = 404, description = "Payment not found"),
109 (status = 500, description = "Internal server error"),
110 ),
111 security(("bearer_auth" = []))
112)]
113#[get("/payments/stripe/{stripe_payment_intent_id}")]
114pub async fn get_payment_by_stripe_intent(
115 state: web::Data<AppState>,
116 stripe_payment_intent_id: web::Path<String>,
117) -> impl Responder {
118 match state
119 .payment_use_cases
120 .get_payment_by_stripe_intent(&stripe_payment_intent_id)
121 .await
122 {
123 Ok(Some(payment)) => HttpResponse::Ok().json(payment),
124 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
125 "error": "Payment not found"
126 })),
127 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
128 }
129}
130
131#[utoipa::path(
132 get,
133 path = "/owners/{owner_id}/payments",
134 tag = "Payments",
135 summary = "List all payments for an owner",
136 params(("owner_id" = Uuid, Path, description = "Owner ID")),
137 responses(
138 (status = 200, description = "List of owner payments"),
139 (status = 500, description = "Internal server error"),
140 ),
141 security(("bearer_auth" = []))
142)]
143#[get("/owners/{owner_id}/payments")]
144pub async fn list_owner_payments(
145 state: web::Data<AppState>,
146 owner_id: web::Path<Uuid>,
147) -> impl Responder {
148 match state.payment_use_cases.list_owner_payments(*owner_id).await {
149 Ok(payments) => HttpResponse::Ok().json(payments),
150 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
151 }
152}
153
154#[utoipa::path(
155 get,
156 path = "/buildings/{building_id}/payments",
157 tag = "Payments",
158 summary = "List all payments for a building",
159 params(("building_id" = Uuid, Path, description = "Building ID")),
160 responses(
161 (status = 200, description = "List of building payments"),
162 (status = 500, description = "Internal server error"),
163 ),
164 security(("bearer_auth" = []))
165)]
166#[get("/buildings/{building_id}/payments")]
167pub async fn list_building_payments(
168 state: web::Data<AppState>,
169 building_id: web::Path<Uuid>,
170) -> impl Responder {
171 match state
172 .payment_use_cases
173 .list_building_payments(*building_id)
174 .await
175 {
176 Ok(payments) => HttpResponse::Ok().json(payments),
177 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
178 }
179}
180
181#[utoipa::path(
182 get,
183 path = "/expenses/{expense_id}/payments",
184 tag = "Payments",
185 summary = "List all payments for an expense",
186 params(("expense_id" = Uuid, Path, description = "Expense ID")),
187 responses(
188 (status = 200, description = "List of expense payments"),
189 (status = 500, description = "Internal server error"),
190 ),
191 security(("bearer_auth" = []))
192)]
193#[get("/expenses/{expense_id}/payments")]
194pub async fn list_expense_payments(
195 state: web::Data<AppState>,
196 expense_id: web::Path<Uuid>,
197) -> impl Responder {
198 match state
199 .payment_use_cases
200 .list_expense_payments(*expense_id)
201 .await
202 {
203 Ok(payments) => HttpResponse::Ok().json(payments),
204 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
205 }
206}
207
208#[utoipa::path(
209 get,
210 path = "/organizations/{organization_id}/payments",
211 tag = "Payments",
212 summary = "List all payments for an organization",
213 params(("organization_id" = Uuid, Path, description = "Organization ID")),
214 responses(
215 (status = 200, description = "List of organization payments"),
216 (status = 500, description = "Internal server error"),
217 ),
218 security(("bearer_auth" = []))
219)]
220#[get("/organizations/{organization_id}/payments")]
221pub async fn list_organization_payments(
222 state: web::Data<AppState>,
223 user: AuthenticatedUser,
224 organization_id: web::Path<Uuid>,
225) -> impl Responder {
226 if let Err(e) = user.verify_org_access(*organization_id) {
227 return HttpResponse::Forbidden().json(serde_json::json!({"error": e}));
228 }
229 match state
230 .payment_use_cases
231 .list_organization_payments(*organization_id)
232 .await
233 {
234 Ok(payments) => HttpResponse::Ok().json(payments),
235 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
236 }
237}
238
239#[utoipa::path(
240 get,
241 path = "/payments/status/{status}",
242 tag = "Payments",
243 summary = "List payments by transaction status",
244 params(("status" = String, Path, description = "Transaction status (pending, processing, requires_action, succeeded, failed, cancelled, refunded)")),
245 responses(
246 (status = 200, description = "List of payments with given status"),
247 (status = 400, description = "Invalid status value"),
248 (status = 401, description = "Unauthorized"),
249 (status = 500, description = "Internal server error"),
250 ),
251 security(("bearer_auth" = []))
252)]
253#[get("/payments/status/{status}")]
254pub async fn list_payments_by_status(
255 state: web::Data<AppState>,
256 user: AuthenticatedUser,
257 status_str: web::Path<String>,
258) -> impl Responder {
259 let organization_id = match user.require_organization() {
260 Ok(org_id) => org_id,
261 Err(e) => {
262 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
263 }
264 };
265
266 let status = match status_str.as_str() {
268 "pending" => TransactionStatus::Pending,
269 "processing" => TransactionStatus::Processing,
270 "requires_action" => TransactionStatus::RequiresAction,
271 "succeeded" => TransactionStatus::Succeeded,
272 "failed" => TransactionStatus::Failed,
273 "cancelled" => TransactionStatus::Cancelled,
274 "refunded" => TransactionStatus::Refunded,
275 _ => {
276 return HttpResponse::BadRequest().json(serde_json::json!({
277 "error": "Invalid status. Must be one of: pending, processing, requires_action, succeeded, failed, cancelled, refunded"
278 }))
279 }
280 };
281
282 match state
283 .payment_use_cases
284 .list_payments_by_status(organization_id, status)
285 .await
286 {
287 Ok(payments) => HttpResponse::Ok().json(payments),
288 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
289 }
290}
291
292#[utoipa::path(
293 get,
294 path = "/payments/pending",
295 tag = "Payments",
296 summary = "List all pending payments for the organization",
297 responses(
298 (status = 200, description = "List of pending payments"),
299 (status = 401, description = "Unauthorized"),
300 (status = 500, description = "Internal server error"),
301 ),
302 security(("bearer_auth" = []))
303)]
304#[get("/payments/pending")]
305pub async fn list_pending_payments(
306 state: web::Data<AppState>,
307 user: AuthenticatedUser,
308) -> impl Responder {
309 let organization_id = match user.require_organization() {
310 Ok(org_id) => org_id,
311 Err(e) => {
312 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
313 }
314 };
315
316 match state
317 .payment_use_cases
318 .list_pending_payments(organization_id)
319 .await
320 {
321 Ok(payments) => HttpResponse::Ok().json(payments),
322 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
323 }
324}
325
326#[utoipa::path(
327 get,
328 path = "/payments/failed",
329 tag = "Payments",
330 summary = "List all failed payments for the organization",
331 responses(
332 (status = 200, description = "List of failed payments"),
333 (status = 401, description = "Unauthorized"),
334 (status = 500, description = "Internal server error"),
335 ),
336 security(("bearer_auth" = []))
337)]
338#[get("/payments/failed")]
339pub async fn list_failed_payments(
340 state: web::Data<AppState>,
341 user: AuthenticatedUser,
342) -> impl Responder {
343 let organization_id = match user.require_organization() {
344 Ok(org_id) => org_id,
345 Err(e) => {
346 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
347 }
348 };
349
350 match state
351 .payment_use_cases
352 .list_failed_payments(organization_id)
353 .await
354 {
355 Ok(payments) => HttpResponse::Ok().json(payments),
356 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
357 }
358}
359
360#[utoipa::path(
363 put,
364 path = "/payments/{id}/processing",
365 tag = "Payments",
366 summary = "Mark a payment as processing",
367 params(("id" = Uuid, Path, description = "Payment ID")),
368 responses(
369 (status = 200, description = "Payment marked as processing"),
370 (status = 400, description = "Invalid state transition"),
371 (status = 401, description = "Unauthorized"),
372 ),
373 security(("bearer_auth" = []))
374)]
375#[put("/payments/{id}/processing")]
376pub async fn mark_payment_processing(
377 state: web::Data<AppState>,
378 user: AuthenticatedUser,
379 id: web::Path<Uuid>,
380) -> impl Responder {
381 let organization_id = match user.require_organization() {
382 Ok(org_id) => org_id,
383 Err(e) => {
384 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
385 }
386 };
387
388 match state.payment_use_cases.mark_processing(*id).await {
389 Ok(payment) => {
390 AuditLogEntry::new(
391 AuditEventType::PaymentProcessing,
392 Some(user.user_id),
393 Some(organization_id),
394 )
395 .with_resource("Payment", payment.id)
396 .log();
397
398 HttpResponse::Ok().json(payment)
399 }
400 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
401 }
402}
403
404#[utoipa::path(
405 put,
406 path = "/payments/{id}/requires-action",
407 tag = "Payments",
408 summary = "Mark a payment as requiring action",
409 params(("id" = Uuid, Path, description = "Payment ID")),
410 responses(
411 (status = 200, description = "Payment marked as requires action"),
412 (status = 400, description = "Invalid state transition"),
413 (status = 401, description = "Unauthorized"),
414 ),
415 security(("bearer_auth" = []))
416)]
417#[put("/payments/{id}/requires-action")]
418pub async fn mark_payment_requires_action(
419 state: web::Data<AppState>,
420 user: AuthenticatedUser,
421 id: web::Path<Uuid>,
422) -> impl Responder {
423 let organization_id = match user.require_organization() {
424 Ok(org_id) => org_id,
425 Err(e) => {
426 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
427 }
428 };
429
430 match state.payment_use_cases.mark_requires_action(*id).await {
431 Ok(payment) => {
432 AuditLogEntry::new(
433 AuditEventType::PaymentRequiresAction,
434 Some(user.user_id),
435 Some(organization_id),
436 )
437 .with_resource("Payment", payment.id)
438 .log();
439
440 HttpResponse::Ok().json(payment)
441 }
442 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
443 }
444}
445
446#[utoipa::path(
447 put,
448 path = "/payments/{id}/succeeded",
449 tag = "Payments",
450 summary = "Mark a payment as succeeded",
451 params(("id" = Uuid, Path, description = "Payment ID")),
452 responses(
453 (status = 200, description = "Payment marked as succeeded"),
454 (status = 400, description = "Invalid state transition"),
455 (status = 401, description = "Unauthorized"),
456 ),
457 security(("bearer_auth" = []))
458)]
459#[put("/payments/{id}/succeeded")]
460pub async fn mark_payment_succeeded(
461 state: web::Data<AppState>,
462 user: AuthenticatedUser,
463 id: web::Path<Uuid>,
464) -> impl Responder {
465 let organization_id = match user.require_organization() {
466 Ok(org_id) => org_id,
467 Err(e) => {
468 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
469 }
470 };
471
472 match state.payment_use_cases.mark_succeeded(*id).await {
473 Ok(payment) => {
474 AuditLogEntry::new(
475 AuditEventType::PaymentSucceeded,
476 Some(user.user_id),
477 Some(organization_id),
478 )
479 .with_resource("Payment", payment.id)
480 .log();
481
482 HttpResponse::Ok().json(payment)
483 }
484 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
485 }
486}
487
488#[utoipa::path(
489 put,
490 path = "/payments/{id}/failed",
491 tag = "Payments",
492 summary = "Mark a payment as failed",
493 params(("id" = Uuid, Path, description = "Payment ID")),
494 request_body = inline(serde_json::Value),
495 responses(
496 (status = 200, description = "Payment marked as failed"),
497 (status = 400, description = "Invalid state transition"),
498 (status = 401, description = "Unauthorized"),
499 ),
500 security(("bearer_auth" = []))
501)]
502#[put("/payments/{id}/failed")]
503pub async fn mark_payment_failed(
504 state: web::Data<AppState>,
505 user: AuthenticatedUser,
506 id: web::Path<Uuid>,
507 request: web::Json<serde_json::Value>,
508) -> impl Responder {
509 let organization_id = match user.require_organization() {
510 Ok(org_id) => org_id,
511 Err(e) => {
512 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
513 }
514 };
515
516 let reason = request
517 .get("reason")
518 .and_then(|v| v.as_str())
519 .unwrap_or("Unknown failure reason")
520 .to_string();
521
522 match state.payment_use_cases.mark_failed(*id, reason).await {
523 Ok(payment) => {
524 AuditLogEntry::new(
525 AuditEventType::PaymentFailed,
526 Some(user.user_id),
527 Some(organization_id),
528 )
529 .with_resource("Payment", payment.id)
530 .log();
531
532 HttpResponse::Ok().json(payment)
533 }
534 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
535 }
536}
537
538#[utoipa::path(
539 put,
540 path = "/payments/{id}/cancelled",
541 tag = "Payments",
542 summary = "Mark a payment as cancelled",
543 params(("id" = Uuid, Path, description = "Payment ID")),
544 responses(
545 (status = 200, description = "Payment marked as cancelled"),
546 (status = 400, description = "Invalid state transition"),
547 (status = 401, description = "Unauthorized"),
548 ),
549 security(("bearer_auth" = []))
550)]
551#[put("/payments/{id}/cancelled")]
552pub async fn mark_payment_cancelled(
553 state: web::Data<AppState>,
554 user: AuthenticatedUser,
555 id: web::Path<Uuid>,
556) -> impl Responder {
557 let organization_id = match user.require_organization() {
558 Ok(org_id) => org_id,
559 Err(e) => {
560 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
561 }
562 };
563
564 match state.payment_use_cases.mark_cancelled(*id).await {
565 Ok(payment) => {
566 AuditLogEntry::new(
567 AuditEventType::PaymentCancelled,
568 Some(user.user_id),
569 Some(organization_id),
570 )
571 .with_resource("Payment", payment.id)
572 .log();
573
574 HttpResponse::Ok().json(payment)
575 }
576 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
577 }
578}
579
580#[utoipa::path(
581 post,
582 path = "/payments/{id}/refund",
583 tag = "Payments",
584 summary = "Refund a payment (partial or full)",
585 params(("id" = Uuid, Path, description = "Payment ID")),
586 request_body = RefundPaymentRequest,
587 responses(
588 (status = 200, description = "Payment refunded"),
589 (status = 400, description = "Refund not allowed or exceeds payment amount"),
590 (status = 401, description = "Unauthorized"),
591 ),
592 security(("bearer_auth" = []))
593)]
594#[post("/payments/{id}/refund")]
595pub async fn refund_payment(
596 state: web::Data<AppState>,
597 user: AuthenticatedUser,
598 id: web::Path<Uuid>,
599 request: web::Json<RefundPaymentRequest>,
600) -> impl Responder {
601 let organization_id = match user.require_organization() {
602 Ok(org_id) => org_id,
603 Err(e) => {
604 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
605 }
606 };
607
608 match state
609 .payment_use_cases
610 .refund_payment(*id, request.into_inner())
611 .await
612 {
613 Ok(payment) => {
614 AuditLogEntry::new(
615 AuditEventType::PaymentRefunded,
616 Some(user.user_id),
617 Some(organization_id),
618 )
619 .with_resource("Payment", payment.id)
620 .log();
621
622 HttpResponse::Ok().json(payment)
623 }
624 Err(err) => {
625 AuditLogEntry::new(
626 AuditEventType::PaymentRefunded,
627 Some(user.user_id),
628 Some(organization_id),
629 )
630 .with_error(err.clone())
631 .log();
632
633 HttpResponse::BadRequest().json(serde_json::json!({"error": err}))
634 }
635 }
636}
637
638#[utoipa::path(
639 delete,
640 path = "/payments/{id}",
641 tag = "Payments",
642 summary = "Delete a payment",
643 params(("id" = Uuid, Path, description = "Payment ID")),
644 responses(
645 (status = 204, description = "Payment deleted"),
646 (status = 401, description = "Unauthorized"),
647 (status = 404, description = "Payment not found"),
648 (status = 500, description = "Internal server error"),
649 ),
650 security(("bearer_auth" = []))
651)]
652#[delete("/payments/{id}")]
653pub async fn delete_payment(
654 state: web::Data<AppState>,
655 user: AuthenticatedUser,
656 id: web::Path<Uuid>,
657) -> impl Responder {
658 let organization_id = match user.require_organization() {
659 Ok(org_id) => org_id,
660 Err(e) => {
661 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
662 }
663 };
664
665 match state.payment_use_cases.delete_payment(*id).await {
666 Ok(true) => {
667 AuditLogEntry::new(
668 AuditEventType::PaymentDeleted,
669 Some(user.user_id),
670 Some(organization_id),
671 )
672 .with_resource("Payment", *id)
673 .log();
674
675 HttpResponse::NoContent().finish()
676 }
677 Ok(false) => HttpResponse::NotFound().json(serde_json::json!({
678 "error": "Payment not found"
679 })),
680 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
681 }
682}
683
684#[utoipa::path(
687 get,
688 path = "/owners/{owner_id}/payments/stats",
689 tag = "Payments",
690 summary = "Get payment statistics for an owner",
691 params(("owner_id" = Uuid, Path, description = "Owner ID")),
692 responses(
693 (status = 200, description = "Owner payment statistics"),
694 (status = 500, description = "Internal server error"),
695 ),
696 security(("bearer_auth" = []))
697)]
698#[get("/owners/{owner_id}/payments/stats")]
699pub async fn get_owner_payment_stats(
700 state: web::Data<AppState>,
701 owner_id: web::Path<Uuid>,
702) -> impl Responder {
703 match state
704 .payment_use_cases
705 .get_owner_payment_stats(*owner_id)
706 .await
707 {
708 Ok(stats) => HttpResponse::Ok().json(stats),
709 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
710 }
711}
712
713#[utoipa::path(
714 get,
715 path = "/buildings/{building_id}/payments/stats",
716 tag = "Payments",
717 summary = "Get payment statistics for a building",
718 params(("building_id" = Uuid, Path, description = "Building ID")),
719 responses(
720 (status = 200, description = "Building payment statistics"),
721 (status = 500, description = "Internal server error"),
722 ),
723 security(("bearer_auth" = []))
724)]
725#[get("/buildings/{building_id}/payments/stats")]
726pub async fn get_building_payment_stats(
727 state: web::Data<AppState>,
728 building_id: web::Path<Uuid>,
729) -> impl Responder {
730 match state
731 .payment_use_cases
732 .get_building_payment_stats(*building_id)
733 .await
734 {
735 Ok(stats) => HttpResponse::Ok().json(stats),
736 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
737 }
738}
739
740#[utoipa::path(
741 get,
742 path = "/expenses/{expense_id}/payments/total",
743 tag = "Payments",
744 summary = "Get total amount paid for an expense",
745 params(("expense_id" = Uuid, Path, description = "Expense ID")),
746 responses(
747 (status = 200, description = "Total paid amount in cents"),
748 (status = 500, description = "Internal server error"),
749 ),
750 security(("bearer_auth" = []))
751)]
752#[get("/expenses/{expense_id}/payments/total")]
753pub async fn get_expense_total_paid(
754 state: web::Data<AppState>,
755 expense_id: web::Path<Uuid>,
756) -> impl Responder {
757 match state
758 .payment_use_cases
759 .get_total_paid_for_expense(*expense_id)
760 .await
761 {
762 Ok(total) => HttpResponse::Ok().json(serde_json::json!({
763 "expense_id": *expense_id,
764 "total_paid_cents": total
765 })),
766 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
767 }
768}
769
770#[utoipa::path(
771 get,
772 path = "/owners/{owner_id}/payments/total",
773 tag = "Payments",
774 summary = "Get total amount paid by an owner",
775 params(("owner_id" = Uuid, Path, description = "Owner ID")),
776 responses(
777 (status = 200, description = "Total paid amount in cents"),
778 (status = 500, description = "Internal server error"),
779 ),
780 security(("bearer_auth" = []))
781)]
782#[get("/owners/{owner_id}/payments/total")]
783pub async fn get_owner_total_paid(
784 state: web::Data<AppState>,
785 owner_id: web::Path<Uuid>,
786) -> impl Responder {
787 match state
788 .payment_use_cases
789 .get_total_paid_by_owner(*owner_id)
790 .await
791 {
792 Ok(total) => HttpResponse::Ok().json(serde_json::json!({
793 "owner_id": *owner_id,
794 "total_paid_cents": total
795 })),
796 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
797 }
798}
799
800#[utoipa::path(
801 get,
802 path = "/buildings/{building_id}/payments/total",
803 tag = "Payments",
804 summary = "Get total amount paid for a building",
805 params(("building_id" = Uuid, Path, description = "Building ID")),
806 responses(
807 (status = 200, description = "Total paid amount in cents"),
808 (status = 500, description = "Internal server error"),
809 ),
810 security(("bearer_auth" = []))
811)]
812#[get("/buildings/{building_id}/payments/total")]
813pub async fn get_building_total_paid(
814 state: web::Data<AppState>,
815 building_id: web::Path<Uuid>,
816) -> impl Responder {
817 match state
818 .payment_use_cases
819 .get_total_paid_for_building(*building_id)
820 .await
821 {
822 Ok(total) => HttpResponse::Ok().json(serde_json::json!({
823 "building_id": *building_id,
824 "total_paid_cents": total
825 })),
826 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
827 }
828}