|
from fastapi import APIRouter, HTTPException, status, Query, Depends |
|
from typing import List |
|
from datetime import datetime |
|
from .Model import Payment |
|
from App.Users.Model import User |
|
from App.Plans.Model import Plan |
|
from .Schema import ( |
|
CreatePaymentRequest, |
|
UpdatePaymentStatusRequest, |
|
PaymentResponse, |
|
UpdatePaymentUserRequest, |
|
BaseResponse, |
|
PaymentListResponse, |
|
) |
|
from .Schema import PaymentMethod |
|
from App.Users.dependencies import ( |
|
get_current_active_user, |
|
UserType, |
|
) |
|
|
|
|
|
payment_router = APIRouter(tags=["Payments"]) |
|
|
|
|
|
@payment_router.post("/payment/create", response_model=BaseResponse) |
|
async def create_payment(request: CreatePaymentRequest, internal: bool = False): |
|
|
|
if ( |
|
request.payment_method == PaymentMethod.LIPA_NUMBER |
|
and not request.transaction_id |
|
): |
|
raise HTTPException( |
|
status_code=status.HTTP_400_BAD_REQUEST, |
|
detail="Transaction ID is required for Lipa Number payments", |
|
) |
|
|
|
|
|
plan = await Plan.get_or_none(id=request.plan_id) |
|
if request.plan_id and not plan: |
|
raise HTTPException( |
|
status_code=status.HTTP_404_NOT_FOUND, detail="Plan not found" |
|
) |
|
|
|
payment = await Payment.get_or_none(transaction_id=request.transaction_id) |
|
|
|
if payment: |
|
return BaseResponse( |
|
code=404, |
|
message="Payment already exists", |
|
payload={"payment_id": str(payment.id)}, |
|
) |
|
|
|
payment = await Payment.create( |
|
user_id=request.user_id, |
|
plan=plan, |
|
amount=request.amount, |
|
payment_method=request.payment_method, |
|
transaction_id=request.transaction_id, |
|
status="pending", |
|
) |
|
|
|
if internal: |
|
return payment |
|
|
|
|
|
if payment.payment_method == PaymentMethod.CASH: |
|
await payment.create_subscription_if_cash() |
|
|
|
await payment.save() |
|
|
|
return BaseResponse( |
|
code=200, |
|
message="Payment created successfully", |
|
payload={"payment_id": str(payment.id)}, |
|
) |
|
|
|
|
|
@payment_router.get("/payment/{payment_id}", response_model=PaymentResponse) |
|
async def get_payment(payment_id: str): |
|
payment = await Payment.get_or_none(id=payment_id) |
|
if not payment: |
|
raise HTTPException( |
|
status_code=status.HTTP_404_NOT_FOUND, detail="Payment not found" |
|
) |
|
|
|
return PaymentResponse( |
|
id=str(payment.id), |
|
user_id=payment.user_id, |
|
plan_id=payment.plan_id, |
|
amount=payment.amount, |
|
payment_method=payment.payment_method, |
|
status=payment.status, |
|
transaction_id=payment.transaction_id, |
|
created_time=payment.created_time, |
|
updated_time=payment.updated_time, |
|
) |
|
|
|
|
|
@payment_router.get("/payments/unlinked", response_model=PaymentListResponse) |
|
async def get_unlinked_payments(): |
|
unlinked_payments = await Payment.filter(user_id=None) |
|
result = [ |
|
PaymentResponse( |
|
id=str(payment.id), |
|
user_id=payment.user_id, |
|
plan_id=payment.plan_id, |
|
amount=payment.amount, |
|
payment_method=payment.payment_method, |
|
status=payment.status, |
|
transaction_id=payment.transaction_id, |
|
created_time=payment.created_time, |
|
updated_time=payment.updated_time, |
|
) |
|
for payment in unlinked_payments |
|
] |
|
|
|
return PaymentListResponse(payments=result, total_count=len(result)) |
|
|
|
|
|
@payment_router.put("/payment/{payment_id}/link-user", response_model=BaseResponse) |
|
async def link_user_to_payment(payment_id: str, request: UpdatePaymentUserRequest): |
|
payment = await Payment.get_or_none(id=payment_id) |
|
if not payment: |
|
raise HTTPException( |
|
status_code=status.HTTP_404_NOT_FOUND, detail="Payment not found" |
|
) |
|
|
|
user = await User.get_or_none(id=request.user_id) |
|
if not user: |
|
raise HTTPException( |
|
status_code=status.HTTP_404_NOT_FOUND, detail="User not found" |
|
) |
|
|
|
|
|
payment.user_id = request.user_id |
|
await payment.save() |
|
|
|
|
|
if ( |
|
payment.payment_method in [PaymentMethod.LIPA_NUMBER, PaymentMethod.MPESA] |
|
and not payment.plan |
|
): |
|
user.balance += payment.amount |
|
await user.save() |
|
|
|
|
|
payment.status = "balance-assigned" |
|
await payment.save() |
|
|
|
return BaseResponse( |
|
code=200, |
|
message="Payment linked to user and amount added to balance successfully", |
|
payload={ |
|
"payment_id": str(payment.id), |
|
"user_id": request.user_id, |
|
"new_balance": user.balance, |
|
}, |
|
) |
|
|
|
return BaseResponse( |
|
code=200, |
|
message="Payment linked to user successfully", |
|
payload={"payment_id": str(payment.id), "user_id": request.user_id}, |
|
) |
|
|
|
|
|
@payment_router.get("/payments/date-range", response_model=PaymentListResponse) |
|
async def get_payments_by_date_range( |
|
start_date: datetime = Query(..., description="Start date in ISO format"), |
|
end_date: datetime = Query(..., description="End date in ISO format"), |
|
): |
|
if start_date > end_date: |
|
raise HTTPException( |
|
status_code=400, detail="Start date must be less than or equal to end date" |
|
) |
|
|
|
payments = await Payment.filter(created_time__range=(start_date, end_date)) |
|
|
|
result = [ |
|
PaymentResponse( |
|
id=str(payment.id), |
|
user_id=payment.user_id, |
|
plan_id=payment.plan_id, |
|
amount=payment.amount, |
|
payment_method=payment.payment_method, |
|
status=payment.status, |
|
transaction_id=payment.transaction_id, |
|
created_time=payment.created_time, |
|
updated_time=payment.updated_time, |
|
) |
|
for payment in payments |
|
] |
|
|
|
return PaymentListResponse(payments=result, total_count=len(result)) |
|
|
|
|
|
@payment_router.get("/payments/user/{user_id}", response_model=PaymentListResponse) |
|
async def get_user_payment_history( |
|
user_id: str, current_user: User = Depends(get_current_active_user) |
|
): |
|
|
|
if current_user.user_type != UserType.ADMIN and current_user.id != user_id: |
|
raise HTTPException( |
|
status_code=403, |
|
detail="User does not have permission to view this payment history", |
|
) |
|
|
|
payments = await Payment.filter(user_id=user_id) |
|
|
|
result = [ |
|
PaymentResponse( |
|
id=str(payment.id), |
|
user_id=payment.user_id, |
|
plan_id=payment.plan_id, |
|
amount=payment.amount, |
|
payment_method=payment.payment_method, |
|
status=payment.status, |
|
transaction_id=payment.transaction_id, |
|
created_time=payment.created_time, |
|
updated_time=payment.updated_time, |
|
) |
|
for payment in payments |
|
] |
|
|
|
return PaymentListResponse(payments=result, total_count=len(result)) |
|
|