MukeshKapoor25's picture
route changes
08edeb4
"""
Wallet API endpoints.
All routes extract partner_id from JWT token for security.
"""
from typing import Dict, Any
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.sql import get_db
from app.wallet.services.service import WalletService
from app.wallet.schemas.schema import (
WalletResponse,
WalletCreateRequest,
WalletTransactionRequest,
WalletTransactionResponse,
WalletBalanceResponse,
WalletTransactionListRequest,
WalletTransactionListResponse,
)
from app.core.schemas import StandardResponse
from app.dependencies.auth import get_current_user, get_partner_id, TokenUser
router = APIRouter(prefix="/wallet", tags=["Wallet Management"])
@router.post(
"/create",
response_model=StandardResponse[WalletResponse],
status_code=status.HTTP_201_CREATED,
summary="Create Wallet",
description="Create a new wallet for authenticated service professional with encrypted balance storage"
)
async def create_wallet(
request: WalletCreateRequest,
db: AsyncSession = Depends(get_db),
current_user: TokenUser = Depends(get_current_user)
):
"""
Create a new wallet for the authenticated service professional.
Partner ID is extracted from JWT token.
- **initial_balance**: Starting balance (default: 0.00)
- **currency**: Currency code (default: INR)
The wallet balance is encrypted using AES encryption for security.
"""
try:
partner_id = get_partner_id(current_user)
wallet = await WalletService.create_wallet(db, partner_id, request)
return StandardResponse(
success=True,
message="Wallet created successfully",
data=wallet
)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e)
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to create wallet: {str(e)}"
)
@router.get(
"/me",
response_model=StandardResponse[WalletResponse],
summary="Get My Wallet",
description="Retrieve wallet details for authenticated user"
)
async def get_my_wallet(
db: AsyncSession = Depends(get_db),
current_user: TokenUser = Depends(get_current_user)
):
"""
Get wallet details for the authenticated service professional.
Partner ID is extracted from JWT token.
Returns decrypted balance and wallet information.
"""
try:
partner_id = get_partner_id(current_user)
wallet = await WalletService.get_wallet(db, partner_id)
if not wallet:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Wallet not found. Please create a wallet first."
)
return StandardResponse(
success=True,
message="Wallet retrieved successfully",
data=wallet
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to retrieve wallet: {str(e)}"
)
@router.get(
"/balance",
response_model=StandardResponse[WalletBalanceResponse],
summary="Get My Wallet Balance",
description="Retrieve current wallet balance for authenticated user"
)
async def get_my_balance(
db: AsyncSession = Depends(get_db),
current_user: TokenUser = Depends(get_current_user)
):
"""
Get current wallet balance for the authenticated service professional.
Partner ID is extracted from JWT token.
Returns decrypted balance information.
"""
try:
partner_id = get_partner_id(current_user)
balance = await WalletService.get_balance(db, partner_id)
if not balance:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Wallet not found. Please create a wallet first."
)
return StandardResponse(
success=True,
message="Balance retrieved successfully",
data=balance
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to retrieve balance: {str(e)}"
)
@router.post(
"/transaction",
response_model=StandardResponse[WalletTransactionResponse],
status_code=status.HTTP_201_CREATED,
summary="Create Transaction",
description="Create a wallet transaction (credit or debit) for authenticated user"
)
async def create_transaction(
request: WalletTransactionRequest,
db: AsyncSession = Depends(get_db),
current_user: TokenUser = Depends(get_current_user)
):
"""
Create a wallet transaction for the authenticated service professional.
Partner ID is extracted from JWT token.
- **transaction_type**: credit or debit
- **amount**: Transaction amount (must be positive)
- **reference_type**: order | refund | topup | commission | withdrawal
- **reference_id**: Related transaction/order reference
- **description**: Transaction description
- **meta_data**: Additional transaction metadata
For debit transactions, validates sufficient balance.
Updates encrypted balance securely.
"""
try:
partner_id = get_partner_id(current_user)
transaction = await WalletService.create_transaction(db, partner_id, request)
return StandardResponse(
success=True,
message="Transaction completed successfully",
data=transaction
)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e)
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to create transaction: {str(e)}"
)
@router.post(
"/transactions/list",
summary="List My Transactions",
description="List wallet transactions for authenticated user with filters, pagination, and optional projection"
)
async def list_my_transactions(
request: WalletTransactionListRequest,
db: AsyncSession = Depends(get_db),
current_user: TokenUser = Depends(get_current_user)
):
"""
List wallet transactions for the authenticated service professional.
Partner ID is extracted from JWT token.
- **transaction_type**: Filter by credit or debit
- **reference_type**: Filter by reference type
- **start_date**: Filter transactions from this date
- **end_date**: Filter transactions until this date
- **limit**: Maximum records per page (1-100)
- **offset**: Pagination offset
- **projection_list**: Optional list of fields to include in response
Returns paginated transaction history, or raw list if projection_list is provided.
"""
try:
partner_id = get_partner_id(current_user)
result = await WalletService.list_transactions(db, partner_id, request)
# Return raw list if projection used
if request.projection_list:
return result
return StandardResponse(
success=True,
message="Transactions retrieved successfully",
data=result
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to retrieve transactions: {str(e)}"
)
# Admin endpoints (optional - for admin/support to manage wallets)
@router.get(
"/admin/{partner_id}",
response_model=StandardResponse[WalletResponse],
summary="[Admin] Get Wallet by Partner ID",
description="Admin endpoint to retrieve any wallet by partner ID"
)
async def admin_get_wallet(
partner_id: str,
db: AsyncSession = Depends(get_db),
current_user: TokenUser = Depends(get_current_user)
):
"""
Admin endpoint to get wallet details for any service professional.
TODO: Add admin role verification
- **partner_id**: Service professional identifier
Returns decrypted balance and wallet information.
"""
try:
# TODO: Verify admin role from current_user
wallet = await WalletService.get_wallet(db, partner_id)
if not wallet:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Wallet not found for partner_id: {partner_id}"
)
return StandardResponse(
success=True,
message="Wallet retrieved successfully",
data=wallet
)
except HTTPException:
raise
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to retrieve wallet: {str(e)}"
)
@router.patch(
"/admin/{partner_id}/status",
response_model=StandardResponse[WalletResponse],
summary="[Admin] Update Wallet Status",
description="Admin endpoint to update wallet status (active | suspended | closed)"
)
async def admin_update_wallet_status(
partner_id: str,
status_value: str = Query(..., alias="status", description="New status: active | suspended | closed"),
db: AsyncSession = Depends(get_db),
current_user: TokenUser = Depends(get_current_user)
):
"""
Admin endpoint to update wallet status.
TODO: Add admin role verification
- **partner_id**: Service professional identifier
- **status**: New status (active | suspended | closed)
Suspended wallets cannot perform transactions.
Closed wallets are permanently disabled.
"""
try:
# TODO: Verify admin role from token_data
wallet = await WalletService.update_wallet_status(db, partner_id, status_value)
return StandardResponse(
success=True,
message=f"Wallet status updated to {status_value}",
data=wallet
)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e)
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to update wallet status: {str(e)}"
)