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 organization_id: web::Path<Uuid>,
224) -> impl Responder {
225 match state
226 .payment_use_cases
227 .list_organization_payments(*organization_id)
228 .await
229 {
230 Ok(payments) => HttpResponse::Ok().json(payments),
231 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
232 }
233}
234
235#[utoipa::path(
236 get,
237 path = "/payments/status/{status}",
238 tag = "Payments",
239 summary = "List payments by transaction status",
240 params(("status" = String, Path, description = "Transaction status (pending, processing, requires_action, succeeded, failed, cancelled, refunded)")),
241 responses(
242 (status = 200, description = "List of payments with given status"),
243 (status = 400, description = "Invalid status value"),
244 (status = 401, description = "Unauthorized"),
245 (status = 500, description = "Internal server error"),
246 ),
247 security(("bearer_auth" = []))
248)]
249#[get("/payments/status/{status}")]
250pub async fn list_payments_by_status(
251 state: web::Data<AppState>,
252 user: AuthenticatedUser,
253 status_str: web::Path<String>,
254) -> impl Responder {
255 let organization_id = match user.require_organization() {
256 Ok(org_id) => org_id,
257 Err(e) => {
258 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
259 }
260 };
261
262 let status = match status_str.as_str() {
264 "pending" => TransactionStatus::Pending,
265 "processing" => TransactionStatus::Processing,
266 "requires_action" => TransactionStatus::RequiresAction,
267 "succeeded" => TransactionStatus::Succeeded,
268 "failed" => TransactionStatus::Failed,
269 "cancelled" => TransactionStatus::Cancelled,
270 "refunded" => TransactionStatus::Refunded,
271 _ => {
272 return HttpResponse::BadRequest().json(serde_json::json!({
273 "error": "Invalid status. Must be one of: pending, processing, requires_action, succeeded, failed, cancelled, refunded"
274 }))
275 }
276 };
277
278 match state
279 .payment_use_cases
280 .list_payments_by_status(organization_id, status)
281 .await
282 {
283 Ok(payments) => HttpResponse::Ok().json(payments),
284 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
285 }
286}
287
288#[utoipa::path(
289 get,
290 path = "/payments/pending",
291 tag = "Payments",
292 summary = "List all pending payments for the organization",
293 responses(
294 (status = 200, description = "List of pending payments"),
295 (status = 401, description = "Unauthorized"),
296 (status = 500, description = "Internal server error"),
297 ),
298 security(("bearer_auth" = []))
299)]
300#[get("/payments/pending")]
301pub async fn list_pending_payments(
302 state: web::Data<AppState>,
303 user: AuthenticatedUser,
304) -> impl Responder {
305 let organization_id = match user.require_organization() {
306 Ok(org_id) => org_id,
307 Err(e) => {
308 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
309 }
310 };
311
312 match state
313 .payment_use_cases
314 .list_pending_payments(organization_id)
315 .await
316 {
317 Ok(payments) => HttpResponse::Ok().json(payments),
318 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
319 }
320}
321
322#[utoipa::path(
323 get,
324 path = "/payments/failed",
325 tag = "Payments",
326 summary = "List all failed payments for the organization",
327 responses(
328 (status = 200, description = "List of failed payments"),
329 (status = 401, description = "Unauthorized"),
330 (status = 500, description = "Internal server error"),
331 ),
332 security(("bearer_auth" = []))
333)]
334#[get("/payments/failed")]
335pub async fn list_failed_payments(
336 state: web::Data<AppState>,
337 user: AuthenticatedUser,
338) -> impl Responder {
339 let organization_id = match user.require_organization() {
340 Ok(org_id) => org_id,
341 Err(e) => {
342 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
343 }
344 };
345
346 match state
347 .payment_use_cases
348 .list_failed_payments(organization_id)
349 .await
350 {
351 Ok(payments) => HttpResponse::Ok().json(payments),
352 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
353 }
354}
355
356#[utoipa::path(
359 put,
360 path = "/payments/{id}/processing",
361 tag = "Payments",
362 summary = "Mark a payment as processing",
363 params(("id" = Uuid, Path, description = "Payment ID")),
364 responses(
365 (status = 200, description = "Payment marked as processing"),
366 (status = 400, description = "Invalid state transition"),
367 (status = 401, description = "Unauthorized"),
368 ),
369 security(("bearer_auth" = []))
370)]
371#[put("/payments/{id}/processing")]
372pub async fn mark_payment_processing(
373 state: web::Data<AppState>,
374 user: AuthenticatedUser,
375 id: web::Path<Uuid>,
376) -> impl Responder {
377 let organization_id = match user.require_organization() {
378 Ok(org_id) => org_id,
379 Err(e) => {
380 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
381 }
382 };
383
384 match state.payment_use_cases.mark_processing(*id).await {
385 Ok(payment) => {
386 AuditLogEntry::new(
387 AuditEventType::PaymentProcessing,
388 Some(user.user_id),
389 Some(organization_id),
390 )
391 .with_resource("Payment", payment.id)
392 .log();
393
394 HttpResponse::Ok().json(payment)
395 }
396 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
397 }
398}
399
400#[utoipa::path(
401 put,
402 path = "/payments/{id}/requires-action",
403 tag = "Payments",
404 summary = "Mark a payment as requiring action",
405 params(("id" = Uuid, Path, description = "Payment ID")),
406 responses(
407 (status = 200, description = "Payment marked as requires action"),
408 (status = 400, description = "Invalid state transition"),
409 (status = 401, description = "Unauthorized"),
410 ),
411 security(("bearer_auth" = []))
412)]
413#[put("/payments/{id}/requires-action")]
414pub async fn mark_payment_requires_action(
415 state: web::Data<AppState>,
416 user: AuthenticatedUser,
417 id: web::Path<Uuid>,
418) -> impl Responder {
419 let organization_id = match user.require_organization() {
420 Ok(org_id) => org_id,
421 Err(e) => {
422 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
423 }
424 };
425
426 match state.payment_use_cases.mark_requires_action(*id).await {
427 Ok(payment) => {
428 AuditLogEntry::new(
429 AuditEventType::PaymentRequiresAction,
430 Some(user.user_id),
431 Some(organization_id),
432 )
433 .with_resource("Payment", payment.id)
434 .log();
435
436 HttpResponse::Ok().json(payment)
437 }
438 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
439 }
440}
441
442#[utoipa::path(
443 put,
444 path = "/payments/{id}/succeeded",
445 tag = "Payments",
446 summary = "Mark a payment as succeeded",
447 params(("id" = Uuid, Path, description = "Payment ID")),
448 responses(
449 (status = 200, description = "Payment marked as succeeded"),
450 (status = 400, description = "Invalid state transition"),
451 (status = 401, description = "Unauthorized"),
452 ),
453 security(("bearer_auth" = []))
454)]
455#[put("/payments/{id}/succeeded")]
456pub async fn mark_payment_succeeded(
457 state: web::Data<AppState>,
458 user: AuthenticatedUser,
459 id: web::Path<Uuid>,
460) -> impl Responder {
461 let organization_id = match user.require_organization() {
462 Ok(org_id) => org_id,
463 Err(e) => {
464 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
465 }
466 };
467
468 match state.payment_use_cases.mark_succeeded(*id).await {
469 Ok(payment) => {
470 AuditLogEntry::new(
471 AuditEventType::PaymentSucceeded,
472 Some(user.user_id),
473 Some(organization_id),
474 )
475 .with_resource("Payment", payment.id)
476 .log();
477
478 HttpResponse::Ok().json(payment)
479 }
480 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
481 }
482}
483
484#[utoipa::path(
485 put,
486 path = "/payments/{id}/failed",
487 tag = "Payments",
488 summary = "Mark a payment as failed",
489 params(("id" = Uuid, Path, description = "Payment ID")),
490 request_body = inline(serde_json::Value),
491 responses(
492 (status = 200, description = "Payment marked as failed"),
493 (status = 400, description = "Invalid state transition"),
494 (status = 401, description = "Unauthorized"),
495 ),
496 security(("bearer_auth" = []))
497)]
498#[put("/payments/{id}/failed")]
499pub async fn mark_payment_failed(
500 state: web::Data<AppState>,
501 user: AuthenticatedUser,
502 id: web::Path<Uuid>,
503 request: web::Json<serde_json::Value>,
504) -> impl Responder {
505 let organization_id = match user.require_organization() {
506 Ok(org_id) => org_id,
507 Err(e) => {
508 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
509 }
510 };
511
512 let reason = request
513 .get("reason")
514 .and_then(|v| v.as_str())
515 .unwrap_or("Unknown failure reason")
516 .to_string();
517
518 match state.payment_use_cases.mark_failed(*id, reason).await {
519 Ok(payment) => {
520 AuditLogEntry::new(
521 AuditEventType::PaymentFailed,
522 Some(user.user_id),
523 Some(organization_id),
524 )
525 .with_resource("Payment", payment.id)
526 .log();
527
528 HttpResponse::Ok().json(payment)
529 }
530 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
531 }
532}
533
534#[utoipa::path(
535 put,
536 path = "/payments/{id}/cancelled",
537 tag = "Payments",
538 summary = "Mark a payment as cancelled",
539 params(("id" = Uuid, Path, description = "Payment ID")),
540 responses(
541 (status = 200, description = "Payment marked as cancelled"),
542 (status = 400, description = "Invalid state transition"),
543 (status = 401, description = "Unauthorized"),
544 ),
545 security(("bearer_auth" = []))
546)]
547#[put("/payments/{id}/cancelled")]
548pub async fn mark_payment_cancelled(
549 state: web::Data<AppState>,
550 user: AuthenticatedUser,
551 id: web::Path<Uuid>,
552) -> impl Responder {
553 let organization_id = match user.require_organization() {
554 Ok(org_id) => org_id,
555 Err(e) => {
556 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
557 }
558 };
559
560 match state.payment_use_cases.mark_cancelled(*id).await {
561 Ok(payment) => {
562 AuditLogEntry::new(
563 AuditEventType::PaymentCancelled,
564 Some(user.user_id),
565 Some(organization_id),
566 )
567 .with_resource("Payment", payment.id)
568 .log();
569
570 HttpResponse::Ok().json(payment)
571 }
572 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
573 }
574}
575
576#[utoipa::path(
577 post,
578 path = "/payments/{id}/refund",
579 tag = "Payments",
580 summary = "Refund a payment (partial or full)",
581 params(("id" = Uuid, Path, description = "Payment ID")),
582 request_body = RefundPaymentRequest,
583 responses(
584 (status = 200, description = "Payment refunded"),
585 (status = 400, description = "Refund not allowed or exceeds payment amount"),
586 (status = 401, description = "Unauthorized"),
587 ),
588 security(("bearer_auth" = []))
589)]
590#[post("/payments/{id}/refund")]
591pub async fn refund_payment(
592 state: web::Data<AppState>,
593 user: AuthenticatedUser,
594 id: web::Path<Uuid>,
595 request: web::Json<RefundPaymentRequest>,
596) -> impl Responder {
597 let organization_id = match user.require_organization() {
598 Ok(org_id) => org_id,
599 Err(e) => {
600 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
601 }
602 };
603
604 match state
605 .payment_use_cases
606 .refund_payment(*id, request.into_inner())
607 .await
608 {
609 Ok(payment) => {
610 AuditLogEntry::new(
611 AuditEventType::PaymentRefunded,
612 Some(user.user_id),
613 Some(organization_id),
614 )
615 .with_resource("Payment", payment.id)
616 .log();
617
618 HttpResponse::Ok().json(payment)
619 }
620 Err(err) => {
621 AuditLogEntry::new(
622 AuditEventType::PaymentRefunded,
623 Some(user.user_id),
624 Some(organization_id),
625 )
626 .with_error(err.clone())
627 .log();
628
629 HttpResponse::BadRequest().json(serde_json::json!({"error": err}))
630 }
631 }
632}
633
634#[utoipa::path(
635 delete,
636 path = "/payments/{id}",
637 tag = "Payments",
638 summary = "Delete a payment",
639 params(("id" = Uuid, Path, description = "Payment ID")),
640 responses(
641 (status = 204, description = "Payment deleted"),
642 (status = 401, description = "Unauthorized"),
643 (status = 404, description = "Payment not found"),
644 (status = 500, description = "Internal server error"),
645 ),
646 security(("bearer_auth" = []))
647)]
648#[delete("/payments/{id}")]
649pub async fn delete_payment(
650 state: web::Data<AppState>,
651 user: AuthenticatedUser,
652 id: web::Path<Uuid>,
653) -> impl Responder {
654 let organization_id = match user.require_organization() {
655 Ok(org_id) => org_id,
656 Err(e) => {
657 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
658 }
659 };
660
661 match state.payment_use_cases.delete_payment(*id).await {
662 Ok(true) => {
663 AuditLogEntry::new(
664 AuditEventType::PaymentDeleted,
665 Some(user.user_id),
666 Some(organization_id),
667 )
668 .with_resource("Payment", *id)
669 .log();
670
671 HttpResponse::NoContent().finish()
672 }
673 Ok(false) => HttpResponse::NotFound().json(serde_json::json!({
674 "error": "Payment not found"
675 })),
676 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
677 }
678}
679
680#[utoipa::path(
683 get,
684 path = "/owners/{owner_id}/payments/stats",
685 tag = "Payments",
686 summary = "Get payment statistics for an owner",
687 params(("owner_id" = Uuid, Path, description = "Owner ID")),
688 responses(
689 (status = 200, description = "Owner payment statistics"),
690 (status = 500, description = "Internal server error"),
691 ),
692 security(("bearer_auth" = []))
693)]
694#[get("/owners/{owner_id}/payments/stats")]
695pub async fn get_owner_payment_stats(
696 state: web::Data<AppState>,
697 owner_id: web::Path<Uuid>,
698) -> impl Responder {
699 match state
700 .payment_use_cases
701 .get_owner_payment_stats(*owner_id)
702 .await
703 {
704 Ok(stats) => HttpResponse::Ok().json(stats),
705 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
706 }
707}
708
709#[utoipa::path(
710 get,
711 path = "/buildings/{building_id}/payments/stats",
712 tag = "Payments",
713 summary = "Get payment statistics for a building",
714 params(("building_id" = Uuid, Path, description = "Building ID")),
715 responses(
716 (status = 200, description = "Building payment statistics"),
717 (status = 500, description = "Internal server error"),
718 ),
719 security(("bearer_auth" = []))
720)]
721#[get("/buildings/{building_id}/payments/stats")]
722pub async fn get_building_payment_stats(
723 state: web::Data<AppState>,
724 building_id: web::Path<Uuid>,
725) -> impl Responder {
726 match state
727 .payment_use_cases
728 .get_building_payment_stats(*building_id)
729 .await
730 {
731 Ok(stats) => HttpResponse::Ok().json(stats),
732 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
733 }
734}
735
736#[utoipa::path(
737 get,
738 path = "/expenses/{expense_id}/payments/total",
739 tag = "Payments",
740 summary = "Get total amount paid for an expense",
741 params(("expense_id" = Uuid, Path, description = "Expense ID")),
742 responses(
743 (status = 200, description = "Total paid amount in cents"),
744 (status = 500, description = "Internal server error"),
745 ),
746 security(("bearer_auth" = []))
747)]
748#[get("/expenses/{expense_id}/payments/total")]
749pub async fn get_expense_total_paid(
750 state: web::Data<AppState>,
751 expense_id: web::Path<Uuid>,
752) -> impl Responder {
753 match state
754 .payment_use_cases
755 .get_total_paid_for_expense(*expense_id)
756 .await
757 {
758 Ok(total) => HttpResponse::Ok().json(serde_json::json!({
759 "expense_id": *expense_id,
760 "total_paid_cents": total
761 })),
762 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
763 }
764}
765
766#[utoipa::path(
767 get,
768 path = "/owners/{owner_id}/payments/total",
769 tag = "Payments",
770 summary = "Get total amount paid by an owner",
771 params(("owner_id" = Uuid, Path, description = "Owner ID")),
772 responses(
773 (status = 200, description = "Total paid amount in cents"),
774 (status = 500, description = "Internal server error"),
775 ),
776 security(("bearer_auth" = []))
777)]
778#[get("/owners/{owner_id}/payments/total")]
779pub async fn get_owner_total_paid(
780 state: web::Data<AppState>,
781 owner_id: web::Path<Uuid>,
782) -> impl Responder {
783 match state
784 .payment_use_cases
785 .get_total_paid_by_owner(*owner_id)
786 .await
787 {
788 Ok(total) => HttpResponse::Ok().json(serde_json::json!({
789 "owner_id": *owner_id,
790 "total_paid_cents": total
791 })),
792 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
793 }
794}
795
796#[utoipa::path(
797 get,
798 path = "/buildings/{building_id}/payments/total",
799 tag = "Payments",
800 summary = "Get total amount paid for a building",
801 params(("building_id" = Uuid, Path, description = "Building ID")),
802 responses(
803 (status = 200, description = "Total paid amount in cents"),
804 (status = 500, description = "Internal server error"),
805 ),
806 security(("bearer_auth" = []))
807)]
808#[get("/buildings/{building_id}/payments/total")]
809pub async fn get_building_total_paid(
810 state: web::Data<AppState>,
811 building_id: web::Path<Uuid>,
812) -> impl Responder {
813 match state
814 .payment_use_cases
815 .get_total_paid_for_building(*building_id)
816 .await
817 {
818 Ok(total) => HttpResponse::Ok().json(serde_json::json!({
819 "building_id": *building_id,
820 "total_paid_cents": total
821 })),
822 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
823 }
824}