from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks from sqlalchemy.orm import Session from datetime import datetime, timedelta from app.db.session import get_db from app.core.deps import get_current_active_user from app.models.user import User from app.models.membership import MembershipPlan from app.models.subscription import Subscription, SubscriptionStatus from app.models.transaction import Transaction, TransactionStatus from app.schemas.transaction import PaymentRequest, Transaction as TransactionSchema from app.services.payment import payment_service router = APIRouter() @router.post("/initialize") def initialize_payment( payment_request: PaymentRequest, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user), ): plan = ( db.query(MembershipPlan) .filter( MembershipPlan.id == payment_request.membership_plan_id, MembershipPlan.is_active, ) .first() ) if not plan: raise HTTPException(status_code=404, detail="Membership plan not found") existing_active_subscription = ( db.query(Subscription) .filter( Subscription.user_id == current_user.id, Subscription.membership_plan_id == payment_request.membership_plan_id, Subscription.status == SubscriptionStatus.ACTIVE, ) .first() ) if existing_active_subscription: raise HTTPException( status_code=400, detail="Active subscription already exists for this plan" ) # Create transaction record transaction = Transaction( user_id=current_user.id, amount=plan.price, payment_gateway=payment_request.payment_gateway, description=f"Subscription to {plan.name}", ) db.add(transaction) db.commit() db.refresh(transaction) metadata = { "user_id": str(current_user.id), "transaction_id": str(transaction.id), "plan_id": str(plan.id), } try: payment_data = payment_service.initialize_payment( gateway=payment_request.payment_gateway, amount=plan.price, currency="USD" if payment_request.payment_gateway.value == "stripe" else "NGN", email=current_user.email, metadata=metadata, ) # Update transaction with gateway reference if payment_request.payment_gateway.value == "stripe": transaction.gateway_reference = payment_data.get("payment_intent_id") else: transaction.gateway_reference = payment_data.get("reference") db.commit() return {"transaction_id": transaction.id, "payment_data": payment_data} except Exception as e: transaction.status = TransactionStatus.FAILED db.commit() raise e @router.post("/verify/{transaction_id}") def verify_payment( transaction_id: int, background_tasks: BackgroundTasks, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user), ): transaction = ( db.query(Transaction) .filter( Transaction.id == transaction_id, Transaction.user_id == current_user.id ) .first() ) if not transaction: raise HTTPException(status_code=404, detail="Transaction not found") if transaction.status == TransactionStatus.COMPLETED: return {"message": "Payment already verified"} try: verification_data = payment_service.verify_payment( gateway=transaction.payment_gateway, reference=transaction.gateway_reference ) if verification_data["status"] in ["succeeded", "success"]: transaction.status = TransactionStatus.COMPLETED transaction.gateway_transaction_id = verification_data.get( "reference" ) or verification_data.get("payment_intent_id") # Create subscription plan = ( db.query(MembershipPlan) .filter(MembershipPlan.id == transaction.subscription_id) .first() ) if not plan: # Find plan from metadata or transaction description # This is a fallback - ideally we'd store plan_id in transaction raise HTTPException( status_code=500, detail="Cannot create subscription: plan not found" ) start_date = datetime.utcnow() end_date = start_date + timedelta(days=plan.duration_days) subscription = Subscription( user_id=current_user.id, membership_plan_id=plan.id, start_date=start_date, end_date=end_date, amount_paid=transaction.amount, payment_reference=transaction.gateway_reference, status=SubscriptionStatus.ACTIVE, ) transaction.subscription_id = subscription.id db.add(subscription) db.commit() return {"message": "Payment verified and subscription created successfully"} else: transaction.status = TransactionStatus.FAILED db.commit() raise HTTPException(status_code=400, detail="Payment verification failed") except Exception as e: transaction.status = TransactionStatus.FAILED db.commit() raise HTTPException( status_code=400, detail=f"Payment verification error: {str(e)}" ) @router.get("/transactions", response_model=list[TransactionSchema]) def get_user_transactions( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user), ): transactions = ( db.query(Transaction) .filter(Transaction.user_id == current_user.id) .offset(skip) .limit(limit) .all() ) return transactions