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