# # your_app/views.py (or wherever this file is located) # import datetime from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from typing import Tuple, Optional import enum # Assuming these are your project's modules from .. import crud, models, schemas, database from ..services import payment_service # Placeholder for the Authorize.Net response object type AuthNetResponse = object router = APIRouter( tags=["Transactions"], # Tags are for documentation only, they don't affect URLs ) class TransactionStatus(enum.IntEnum): APPROVED = 0 DECLINED = 1 class TransactionType(enum.IntEnum): CHARGE = 0 AUTHORIZE = 1 CAPTURE = 3 # --- Helper function to avoid repeating response parsing logic --- def _parse_authnet_response(response: Optional[AuthNetResponse]) -> Tuple[TransactionStatus, Optional[str], Optional[str]]: """ Parses the response from the Authorize.Net service. (This is the same helper from before, it's a good change to keep) """ if response is not None and hasattr(response, 'messages') and response.messages.resultCode == "Ok": status = TransactionStatus.APPROVED auth_net_transaction_id = str(response.transactionResponse.transId) if hasattr(response, 'transactionResponse') else None rejection_reason = None else: status = TransactionStatus.DECLINED auth_net_transaction_id = None # Improved rejection reason extraction rejection_reason = "Payment declined by gateway." if hasattr(response, '_rejection_reason') and response._rejection_reason: rejection_reason = str(response._rejection_reason) elif response is not None: # Check transaction response for errors if hasattr(response, 'transactionResponse') and response.transactionResponse: tr = response.transactionResponse if hasattr(tr, 'errors') and tr.errors: for error in tr.errors: if hasattr(error, 'errorCode') and hasattr(error, 'errorText'): error_code = error.errorCode.text if error.errorCode else "Unknown" error_text = error.errorText.text if error.errorText else "No error details" rejection_reason = f"{error_code}: {error_text}" break elif hasattr(tr, 'messages') and tr.messages: if len(tr.messages) > 0: msg = tr.messages[0] if hasattr(msg, 'code') and hasattr(msg, 'description'): code = msg.code.text if msg.code else "Unknown" desc = msg.description.text if msg.description else "No description" rejection_reason = f"{code}: {desc}" elif response is None: rejection_reason = "No response received from payment gateway." return status, auth_net_transaction_id, rejection_reason @router.post("/authorize/", response_model=schemas.Transaction) def authorize_card(customer_id: int, transaction: schemas.TransactionAuthorize, db: Session = Depends(database.get_db)): auth_net_response = payment_service.authorize_credit_card(transaction) status, auth_net_transaction_id, rejection_reason = _parse_authnet_response(auth_net_response) transaction_data = schemas.TransactionBase( preauthorize_amount=transaction.preauthorize_amount, transaction_type=TransactionType.AUTHORIZE, service_id=transaction.service_id, delivery_id=transaction.delivery_id, card_id=transaction.card_id, rejection_reason=rejection_reason ) return crud.create_transaction( db=db, transaction=transaction_data, customer_id=customer_id, status=status, auth_net_transaction_id=auth_net_transaction_id ) @router.post("/charge/{customer_id}", response_model=schemas.Transaction) def charge_card(customer_id: int, transaction: schemas.TransactionCreate, db: Session = Depends(database.get_db)): try: auth_net_response = payment_service.charge_credit_card(transaction) status, auth_net_transaction_id, rejection_reason = _parse_authnet_response(auth_net_response) transaction_data = schemas.TransactionBase( charge_amount=transaction.charge_amount, transaction_type=TransactionType.CHARGE, service_id=transaction.service_id, delivery_id=transaction.delivery_id, card_id=transaction.card_id, rejection_reason=rejection_reason ) result = crud.create_transaction( db=db, transaction=transaction_data, customer_id=customer_id, status=status, auth_net_transaction_id=auth_net_transaction_id ) return result except Exception as e: print(f"DEBUG: Exception in charge_card: {str(e)}") import traceback print(f"DEBUG: Traceback: {traceback.format_exc()}") raise @router.post("/capture/", response_model=schemas.Transaction) def capture_authorized_amount(transaction: schemas.TransactionCapture, db: Session = Depends(database.get_db)): auth_transaction = crud.get_transaction_by_auth_id(db, auth_net_transaction_id=transaction.auth_net_transaction_id) if not auth_transaction: raise HTTPException(status_code=404, detail="Authorization transaction not found") auth_net_response = payment_service.capture_authorized_transaction(transaction) status, _, rejection_reason = _parse_authnet_response(auth_net_response) return crud.update_transaction_for_capture( db=db, auth_net_transaction_id=transaction.auth_net_transaction_id, charge_amount=transaction.charge_amount, status=status, rejection_reason=rejection_reason ) @router.get("/transaction/delivery/{delivery_id}", response_model=schemas.Transaction) def get_transaction_by_delivery(delivery_id: int, db: Session = Depends(database.get_db)): transaction = crud.get_transaction_by_delivery_id(db, delivery_id=delivery_id) if not transaction: raise HTTPException(status_code=404, detail="No pre-authorized transaction found for this delivery") return transaction