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 organization_id: web::Path<Uuid>,
144) -> impl Responder {
145 match state
146 .payment_method_use_cases
147 .list_organization_payment_methods(*organization_id)
148 .await
149 {
150 Ok(methods) => HttpResponse::Ok().json(methods),
151 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
152 }
153}
154
155#[get("/owners/{owner_id}/payment-methods/type/{method_type}")]
156pub async fn list_payment_methods_by_type(
157 state: web::Data<AppState>,
158 path: web::Path<(Uuid, String)>,
159) -> impl Responder {
160 let (owner_id, method_type_str) = path.into_inner();
161
162 let method_type = match method_type_str.as_str() {
164 "card" => PaymentMethodType::Card,
165 "sepa_debit" => PaymentMethodType::SepaDebit,
166 _ => {
167 return HttpResponse::BadRequest().json(serde_json::json!({
168 "error": "Invalid payment method type. Must be one of: card, sepa_debit"
169 }))
170 }
171 };
172
173 match state
174 .payment_method_use_cases
175 .list_payment_methods_by_type(owner_id, method_type)
176 .await
177 {
178 Ok(methods) => HttpResponse::Ok().json(methods),
179 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
180 }
181}
182
183#[put("/payment-methods/{id}")]
184pub async fn update_payment_method(
185 state: web::Data<AppState>,
186 user: AuthenticatedUser,
187 id: web::Path<Uuid>,
188 request: web::Json<UpdatePaymentMethodRequest>,
189) -> impl Responder {
190 let organization_id = match user.require_organization() {
191 Ok(org_id) => org_id,
192 Err(e) => {
193 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
194 }
195 };
196
197 match state
198 .payment_method_use_cases
199 .update_payment_method(*id, request.into_inner())
200 .await
201 {
202 Ok(method) => {
203 AuditLogEntry::new(
204 AuditEventType::PaymentMethodUpdated,
205 Some(user.user_id),
206 Some(organization_id),
207 )
208 .with_resource("PaymentMethod", method.id)
209 .log();
210
211 HttpResponse::Ok().json(method)
212 }
213 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
214 }
215}
216
217#[put("/payment-methods/{id}/set-default")]
218pub async fn set_payment_method_as_default(
219 state: web::Data<AppState>,
220 user: AuthenticatedUser,
221 id: web::Path<Uuid>,
222 owner_id_json: web::Json<serde_json::Value>,
223) -> impl Responder {
224 let organization_id = match user.require_organization() {
225 Ok(org_id) => org_id,
226 Err(e) => {
227 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
228 }
229 };
230
231 let owner_id = match owner_id_json.get("owner_id").and_then(|v| v.as_str()) {
232 Some(id_str) => match Uuid::parse_str(id_str) {
233 Ok(id) => id,
234 Err(_) => {
235 return HttpResponse::BadRequest().json(serde_json::json!({
236 "error": "Invalid owner_id format"
237 }))
238 }
239 },
240 None => {
241 return HttpResponse::BadRequest().json(serde_json::json!({
242 "error": "owner_id is required"
243 }))
244 }
245 };
246
247 match state
248 .payment_method_use_cases
249 .set_as_default(*id, owner_id)
250 .await
251 {
252 Ok(method) => {
253 AuditLogEntry::new(
254 AuditEventType::PaymentMethodSetDefault,
255 Some(user.user_id),
256 Some(organization_id),
257 )
258 .with_resource("PaymentMethod", method.id)
259 .log();
260
261 HttpResponse::Ok().json(method)
262 }
263 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
264 }
265}
266
267#[put("/payment-methods/{id}/deactivate")]
268pub async fn deactivate_payment_method(
269 state: web::Data<AppState>,
270 user: AuthenticatedUser,
271 id: web::Path<Uuid>,
272) -> impl Responder {
273 let organization_id = match user.require_organization() {
274 Ok(org_id) => org_id,
275 Err(e) => {
276 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
277 }
278 };
279
280 match state
281 .payment_method_use_cases
282 .deactivate_payment_method(*id)
283 .await
284 {
285 Ok(method) => {
286 AuditLogEntry::new(
287 AuditEventType::PaymentMethodDeactivated,
288 Some(user.user_id),
289 Some(organization_id),
290 )
291 .with_resource("PaymentMethod", method.id)
292 .log();
293
294 HttpResponse::Ok().json(method)
295 }
296 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
297 }
298}
299
300#[put("/payment-methods/{id}/reactivate")]
301pub async fn reactivate_payment_method(
302 state: web::Data<AppState>,
303 user: AuthenticatedUser,
304 id: web::Path<Uuid>,
305) -> impl Responder {
306 let organization_id = match user.require_organization() {
307 Ok(org_id) => org_id,
308 Err(e) => {
309 return HttpResponse::Unauthorized().json(serde_json::json!({"error": e.to_string()}))
310 }
311 };
312
313 match state
314 .payment_method_use_cases
315 .reactivate_payment_method(*id)
316 .await
317 {
318 Ok(method) => {
319 AuditLogEntry::new(
320 AuditEventType::PaymentMethodReactivated,
321 Some(user.user_id),
322 Some(organization_id),
323 )
324 .with_resource("PaymentMethod", method.id)
325 .log();
326
327 HttpResponse::Ok().json(method)
328 }
329 Err(err) => HttpResponse::BadRequest().json(serde_json::json!({"error": err})),
330 }
331}
332
333#[delete("/payment-methods/{id}")]
334pub async fn delete_payment_method(
335 state: web::Data<AppState>,
336 user: AuthenticatedUser,
337 id: web::Path<Uuid>,
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_method_use_cases
348 .delete_payment_method(*id)
349 .await
350 {
351 Ok(true) => {
352 AuditLogEntry::new(
353 AuditEventType::PaymentMethodDeleted,
354 Some(user.user_id),
355 Some(organization_id),
356 )
357 .with_resource("PaymentMethod", *id)
358 .log();
359
360 HttpResponse::NoContent().finish()
361 }
362 Ok(false) => HttpResponse::NotFound().json(serde_json::json!({
363 "error": "Payment method not found"
364 })),
365 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
366 }
367}
368
369#[get("/owners/{owner_id}/payment-methods/count")]
372pub async fn count_active_payment_methods(
373 state: web::Data<AppState>,
374 owner_id: web::Path<Uuid>,
375) -> impl Responder {
376 match state
377 .payment_method_use_cases
378 .count_active_payment_methods(*owner_id)
379 .await
380 {
381 Ok(count) => HttpResponse::Ok().json(serde_json::json!({
382 "owner_id": *owner_id,
383 "active_payment_methods_count": count
384 })),
385 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
386 }
387}
388
389#[get("/owners/{owner_id}/payment-methods/has-active")]
390pub async fn has_active_payment_methods(
391 state: web::Data<AppState>,
392 owner_id: web::Path<Uuid>,
393) -> impl Responder {
394 match state
395 .payment_method_use_cases
396 .has_active_payment_methods(*owner_id)
397 .await
398 {
399 Ok(has_active) => HttpResponse::Ok().json(serde_json::json!({
400 "owner_id": *owner_id,
401 "has_active_payment_methods": has_active
402 })),
403 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({"error": err})),
404 }
405}