## File: transaction.py (New transaction router) from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session import enum # Import locally to avoid circular imports import sys import os sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) from app import crud, database, schemas, models from app.services import payment_service # Create a router for transaction endpoints transaction_router = APIRouter() class TransactionStatus(enum.IntEnum): APPROVED = 0 DECLINED = 1 # Test endpoint to verify router is working @transaction_router.get("/test/", summary="Test transaction router") def test_transaction_router(): """Test endpoint to verify transaction router is loaded""" return {"test": "transaction router is working"} @transaction_router.get("/transaction/delivery/{delivery_id}", summary="Get pre-authorization transaction for a delivery") def get_delivery_transaction(delivery_id: int, db: Session = Depends(database.get_db)): """ Get the pre-authorization transaction for a specific delivery. This endpoint is used to retrieve transaction details for delivery finalization. """ transaction = crud.get_transaction_by_delivery_id(db, delivery_id=delivery_id) if not transaction: raise HTTPException(status_code=404, detail="No pre-authorization transaction found for this delivery") return { "id": transaction.id, "transaction_type": transaction.transaction_type, "status": transaction.status, "auth_net_transaction_id": transaction.auth_net_transaction_id, "preauthorize_amount": transaction.preauthorize_amount } @transaction_router.put("/transaction/delivery/{delivery_id}/update/{new_delivery_id}", summary="Update delivery_id for transaction") def update_transaction_delivery(delivery_id: int, new_delivery_id: int, db: Session = Depends(database.get_db)): """ Update the delivery_id of a transaction based on the current delivery_id. Used to change transaction from auto_delivery.id to auto_ticket.id """ transaction = crud.get_transaction_by_delivery_id(db, delivery_id=delivery_id) if not transaction: raise HTTPException(status_code=404, detail="No transaction found for this delivery") transaction.delivery_id = new_delivery_id db.commit() return {"message": "Transaction delivery_id updated"} @transaction_router.put("/transaction/{transaction_id}/update_auto_id/{new_auto_id}", summary="Update auto_id for transaction") def update_transaction_auto_id(transaction_id: int, new_auto_id: int, db: Session = Depends(database.get_db)): """ Update the auto_id of a transaction by its ID. Used to set auto_id to auto_ticket.id after ticket creation. """ transaction = db.query(models.Transaction).filter(models.Transaction.id == transaction_id).first() if not transaction: raise HTTPException(status_code=404, detail="Transaction not found") transaction.auto_id = new_auto_id db.commit() return {"message": "Transaction auto_id updated"} @transaction_router.post("/capture/", response_model=schemas.Transaction, summary="Capture a previously authorized amount") def capture_authorized_amount(transaction: schemas.TransactionCapture, db: Session = Depends(database.get_db)): # This endpoint captures a previously authorized transaction # It finds the original transaction by its ID and captures the funds 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") # Call the capture service function auth_net_response = payment_service.capture_authorized_transaction(transaction) status, _, rejection_reason = _parse_authnet_response(auth_net_response) # Use the existing CRUD function to update the transaction 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 ) def _parse_authnet_response(response): """ Parse Authorize.Net response for transaction status """ if response.messages.resultCode == "Ok": status = TransactionStatus.APPROVED rejection_reason = None else: status = TransactionStatus.DECLINED rejection_reason = "Payment declined by gateway." return status, None, rejection_reason