koprogo_api/infrastructure/web/handlers/
payment_method_handlers.rs1use crate::application::dto::{CreatePaymentMethodRequest, UpdatePaymentMethodRequest};
2use crate::domain::entities::payment_method::PaymentMethodType;
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#[post("/payment-methods")]
11pub async fn create_payment_method(
12 state: web::Data<AppState>,
13 user: AuthenticatedUser,
14 request: web::Json<CreatePaymentMethodRequest>,
15) -> impl Responder {
16 let organization_id = match user.require_organization() {
17 Ok(org_id) => org_id,
18 Err(e) => {
19 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
20 }
21 };
22
23 match state
24 .payment_method_use_cases
25 .create_payment_method(organization_id, request.into_inner())
26 .await
27 {
28 Ok(payment_method) => {
29 AuditLogEntry::new(
30 AuditEventType::PaymentMethodCreated,
31 Some(user.user_id),
32 Some(organization_id),
33 )
34 .with_resource("PaymentMethod", payment_method.id)
35 .log();
36
37 HttpResponse::Created().json(payment_method)
38 }
39 Err(err) => {
40 AuditLogEntry::new(
41 AuditEventType::PaymentMethodCreated,
42 Some(user.user_id),
43 Some(organization_id),
44 )
45 .with_error(err.clone())
46 .log();
47
48 HttpResponse::BadRequest().json(serde_json::json!({"error": err}))
49 }
50 }
51}
52
53#[get("/payment-methods/{id}")]
54pub async fn get_payment_method(
55 state: web::Data<AppState>,
56 user: AuthenticatedUser,
57 id: web::Path<Uuid>,
58) -> impl Responder {
59 match state.payment_method_use_cases.get_payment_method(*id).await {
60 Ok(Some(method)) => {
61 if let Err(err) = user.verify_org_access(method.organization_id) {
63 return HttpResponse::Forbidden().json(serde_json::json!({"error": err}));
64 }
65 HttpResponse::Ok().json(method)
66 }
67 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
68 "error": "Payment method not found"
69 })),
70 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
71 }
72}
73
74#[get("/payment-methods/stripe/{stripe_payment_method_id}")]
75pub async fn get_payment_method_by_stripe_id(
76 state: web::Data<AppState>,
77 stripe_payment_method_id: web::Path<String>,
78) -> impl Responder {
79 match state
80 .payment_method_use_cases
81 .get_payment_method_by_stripe_id(&stripe_payment_method_id)
82 .await
83 {
84 Ok(Some(method)) => HttpResponse::Ok().json(method),
85 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
86 "error": "Payment method not found"
87 })),
88 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
89 }
90}
91
92#[get("/owners/{owner_id}/payment-methods")]
93pub async fn list_owner_payment_methods(
94 state: web::Data<AppState>,
95 owner_id: web::Path<Uuid>,
96) -> impl Responder {
97 match state
98 .payment_method_use_cases
99 .list_owner_payment_methods(*owner_id)
100 .await
101 {
102 Ok(methods) => HttpResponse::Ok().json(methods),
103 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
104 }
105}
106
107#[get("/owners/{owner_id}/payment-methods/active")]
108pub async fn list_active_owner_payment_methods(
109 state: web::Data<AppState>,
110 owner_id: web::Path<Uuid>,
111) -> impl Responder {
112 match state
113 .payment_method_use_cases
114 .list_active_owner_payment_methods(*owner_id)
115 .await
116 {
117 Ok(methods) => HttpResponse::Ok().json(methods),
118 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
119 }
120}
121
122#[get("/owners/{owner_id}/payment-methods/default")]
123pub async fn get_default_payment_method(
124 state: web::Data<AppState>,
125 owner_id: web::Path<Uuid>,
126) -> impl Responder {
127 match state
128 .payment_method_use_cases
129 .get_default_payment_method(*owner_id)
130 .await
131 {
132 Ok(Some(method)) => HttpResponse::Ok().json(method),
133 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
134 "error": "No default payment method found for owner"
135 })),
136 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
137 }
138}
139
140#[get("/organizations/{organization_id}/payment-methods")]
141pub async fn list_organization_payment_methods(
142 state: web::Data<AppState>,
143 user: AuthenticatedUser,
144 organization_id: web::Path<Uuid>,
145) -> impl Responder {
146 if let Err(e) = user.verify_org_access(*organization_id) {
147 return HttpResponse::Forbidden().json(serde_json::json!({"error": e}));
148 }
149 match state
150 .payment_method_use_cases
151 .list_organization_payment_methods(*organization_id)
152 .await
153 {
154 Ok(methods) => HttpResponse::Ok().json(methods),
155 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
156 }
157}
158
159#[get("/owners/{owner_id}/payment-methods/type/{method_type}")]
160pub async fn list_payment_methods_by_type(
161 state: web::Data<AppState>,
162 path: web::Path<(Uuid, String)>,
163) -> impl Responder {
164 let (owner_id, method_type_str) = path.into_inner();
165
166 let method_type = match method_type_str.as_str() {
168 "card" => PaymentMethodType::Card,
169 "sepa_debit" => PaymentMethodType::SepaDebit,
170 _ => {
171 return HttpResponse::BadRequest().json(serde_json::json!({
172 "error": "Invalid payment method type. Must be one of: card, sepa_debit"
173 }))
174 }
175 };
176
177 match state
178 .payment_method_use_cases
179 .list_payment_methods_by_type(owner_id, method_type)
180 .await
181 {
182 Ok(methods) => HttpResponse::Ok().json(methods),
183 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
184 }
185}
186
187#[put("/payment-methods/{id}")]
188pub async fn update_payment_method(
189 state: web::Data<AppState>,
190 user: AuthenticatedUser,
191 id: web::Path<Uuid>,
192 request: web::Json<UpdatePaymentMethodRequest>,
193) -> impl Responder {
194 let organization_id = match user.require_organization() {
195 Ok(org_id) => org_id,
196 Err(e) => {
197 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
198 }
199 };
200
201 match state
202 .payment_method_use_cases
203 .update_payment_method(*id, request.into_inner())
204 .await
205 {
206 Ok(method) => {
207 AuditLogEntry::new(
208 AuditEventType::PaymentMethodUpdated,
209 Some(user.user_id),
210 Some(organization_id),
211 )
212 .with_resource("PaymentMethod", method.id)
213 .log();
214
215 HttpResponse::Ok().json(method)
216 }
217 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
218 }
219}
220
221#[put("/payment-methods/{id}/set-default")]
222pub async fn set_payment_method_as_default(
223 state: web::Data<AppState>,
224 user: AuthenticatedUser,
225 id: web::Path<Uuid>,
226 owner_id_json: web::Json<serde_json::Value>,
227) -> impl Responder {
228 let organization_id = match user.require_organization() {
229 Ok(org_id) => org_id,
230 Err(e) => {
231 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
232 }
233 };
234
235 let owner_id = match owner_id_json.get("owner_id").and_then(|v| v.as_str()) {
236 Some(id_str) => match Uuid::parse_str(id_str) {
237 Ok(id) => id,
238 Err(_) => {
239 return HttpResponse::BadRequest().json(serde_json::json!({
240 "error": "Invalid owner_id format"
241 }))
242 }
243 },
244 None => {
245 return HttpResponse::BadRequest().json(serde_json::json!({
246 "error": "owner_id is required"
247 }))
248 }
249 };
250
251 match state
252 .payment_method_use_cases
253 .set_as_default(*id, owner_id)
254 .await
255 {
256 Ok(method) => {
257 AuditLogEntry::new(
258 AuditEventType::PaymentMethodSetDefault,
259 Some(user.user_id),
260 Some(organization_id),
261 )
262 .with_resource("PaymentMethod", method.id)
263 .log();
264
265 HttpResponse::Ok().json(method)
266 }
267 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
268 }
269}
270
271#[put("/payment-methods/{id}/deactivate")]
272pub async fn deactivate_payment_method(
273 state: web::Data<AppState>,
274 user: AuthenticatedUser,
275 id: web::Path<Uuid>,
276) -> impl Responder {
277 let organization_id = match user.require_organization() {
278 Ok(org_id) => org_id,
279 Err(e) => {
280 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
281 }
282 };
283
284 match state
285 .payment_method_use_cases
286 .deactivate_payment_method(*id)
287 .await
288 {
289 Ok(method) => {
290 AuditLogEntry::new(
291 AuditEventType::PaymentMethodDeactivated,
292 Some(user.user_id),
293 Some(organization_id),
294 )
295 .with_resource("PaymentMethod", method.id)
296 .log();
297
298 HttpResponse::Ok().json(method)
299 }
300 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
301 }
302}
303
304#[put("/payment-methods/{id}/reactivate")]
305pub async fn reactivate_payment_method(
306 state: web::Data<AppState>,
307 user: AuthenticatedUser,
308 id: web::Path<Uuid>,
309) -> impl Responder {
310 let organization_id = match user.require_organization() {
311 Ok(org_id) => org_id,
312 Err(e) => {
313 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
314 }
315 };
316
317 match state
318 .payment_method_use_cases
319 .reactivate_payment_method(*id)
320 .await
321 {
322 Ok(method) => {
323 AuditLogEntry::new(
324 AuditEventType::PaymentMethodReactivated,
325 Some(user.user_id),
326 Some(organization_id),
327 )
328 .with_resource("PaymentMethod", method.id)
329 .log();
330
331 HttpResponse::Ok().json(method)
332 }
333 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
334 }
335}
336
337#[delete("/payment-methods/{id}")]
338pub async fn delete_payment_method(
339 state: web::Data<AppState>,
340 user: AuthenticatedUser,
341 id: web::Path<Uuid>,
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_method_use_cases
352 .delete_payment_method(*id)
353 .await
354 {
355 Ok(true) => {
356 AuditLogEntry::new(
357 AuditEventType::PaymentMethodDeleted,
358 Some(user.user_id),
359 Some(organization_id),
360 )
361 .with_resource("PaymentMethod", *id)
362 .log();
363
364 HttpResponse::NoContent().finish()
365 }
366 Ok(false) => HttpResponse::NotFound().json(serde_json::json!({
367 "error": "Payment method not found"
368 })),
369 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
370 }
371}
372
373#[get("/owners/{owner_id}/payment-methods/count")]
376pub async fn count_active_payment_methods(
377 state: web::Data<AppState>,
378 owner_id: web::Path<Uuid>,
379) -> impl Responder {
380 match state
381 .payment_method_use_cases
382 .count_active_payment_methods(*owner_id)
383 .await
384 {
385 Ok(count) => HttpResponse::Ok().json(serde_json::json!({
386 "owner_id": *owner_id,
387 "active_payment_methods_count": count
388 })),
389 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
390 }
391}
392
393#[get("/owners/{owner_id}/payment-methods/has-active")]
394pub async fn has_active_payment_methods(
395 state: web::Data<AppState>,
396 owner_id: web::Path<Uuid>,
397) -> impl Responder {
398 match state
399 .payment_method_use_cases
400 .has_active_payment_methods(*owner_id)
401 .await
402 {
403 Ok(has_active) => HttpResponse::Ok().json(serde_json::json!({
404 "owner_id": *owner_id,
405 "has_active_payment_methods": has_active
406 })),
407 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
408 }
409}