Spaces:
Running
Running
| # Wallet Management API - Quick Reference | |
| ## Authentication | |
| All wallet endpoints require JWT authentication. Include the token in the Authorization header: | |
| ``` | |
| Authorization: Bearer <your_jwt_token> | |
| ``` | |
| The `partner_id` is automatically extracted from the JWT token - you don't need to provide it in the request body. | |
| ## Base URL | |
| ``` | |
| http://localhost:8000/wallet | |
| ``` | |
| ## Endpoints | |
| ### 1. Create Wallet | |
| **POST** `/wallet/create` | |
| Create a new wallet for the authenticated service professional. | |
| **Headers:** | |
| ``` | |
| Authorization: Bearer <jwt_token> | |
| Content-Type: application/json | |
| ``` | |
| **Request:** | |
| ```json | |
| { | |
| "initial_balance": 5000.00, | |
| "currency": "INR" | |
| } | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Wallet created successfully", | |
| "data": { | |
| "wallet_id": "550e8400-e29b-41d4-a716-446655440000", | |
| "partner_id": "staff_123", | |
| "balance": 5000.00, | |
| "currency": "INR", | |
| "status": "active", | |
| "last_transaction_at": null, | |
| "created_at": "2024-01-20T10:00:00", | |
| "updated_at": "2024-01-20T10:00:00" | |
| } | |
| } | |
| ``` | |
| --- | |
| ### 2. Get My Wallet | |
| **GET** `/wallet/me` | |
| Retrieve complete wallet information for the authenticated user. | |
| **Headers:** | |
| ``` | |
| Authorization: Bearer <jwt_token> | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Wallet retrieved successfully", | |
| "data": { | |
| "wallet_id": "550e8400-e29b-41d4-a716-446655440000", | |
| "partner_id": "staff_123", | |
| "balance": 5000.00, | |
| "currency": "INR", | |
| "status": "active", | |
| "last_transaction_at": "2024-01-20T10:30:00", | |
| "created_at": "2024-01-20T10:00:00", | |
| "updated_at": "2024-01-20T10:30:00" | |
| } | |
| } | |
| ``` | |
| --- | |
| ### 3. Get My Balance | |
| **GET** `/wallet/balance` | |
| Quick endpoint to check current balance for the authenticated user. | |
| **Headers:** | |
| ``` | |
| Authorization: Bearer <jwt_token> | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Balance retrieved successfully", | |
| "data": { | |
| "partner_id": "staff_123", | |
| "balance": 5000.00, | |
| "currency": "INR", | |
| "status": "active" | |
| } | |
| } | |
| ``` | |
| --- | |
| ### 4. Create Transaction | |
| **POST** `/wallet/transaction` | |
| Add or deduct money from the authenticated user's wallet. | |
| **Headers:** | |
| ``` | |
| Authorization: Bearer <jwt_token> | |
| Content-Type: application/json | |
| ``` | |
| **Credit Transaction (Add Money):** | |
| ```json | |
| { | |
| "transaction_type": "credit", | |
| "amount": 1000.00, | |
| "reference_type": "topup", | |
| "reference_id": "TOPUP-2024-001", | |
| "description": "Wallet recharge via UPI", | |
| "metadata": { | |
| "payment_method": "upi", | |
| "upi_id": "user@paytm", | |
| "transaction_ref": "UPI123456" | |
| } | |
| } | |
| ``` | |
| **Debit Transaction (Deduct Money):** | |
| ```json | |
| { | |
| "transaction_type": "debit", | |
| "amount": 500.00, | |
| "reference_type": "order", | |
| "reference_id": "ORDER-123", | |
| "description": "Payment for order ORDER-123" | |
| } | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Transaction completed successfully", | |
| "data": { | |
| "transaction_id": "660e8400-e29b-41d4-a716-446655440000", | |
| "wallet_id": "550e8400-e29b-41d4-a716-446655440000", | |
| "partner_id": "staff_123", | |
| "transaction_type": "credit", | |
| "amount": 1000.00, | |
| "balance_after": 6000.00, | |
| "reference_type": "topup", | |
| "reference_id": "TOPUP-2024-001", | |
| "description": "Wallet recharge via UPI", | |
| "metadata": { | |
| "payment_method": "upi", | |
| "upi_id": "user@paytm" | |
| }, | |
| "created_at": "2024-01-20T10:30:00" | |
| } | |
| } | |
| ``` | |
| **Transaction Types:** | |
| - `credit` - Add money | |
| - `debit` - Deduct money | |
| **Reference Types:** | |
| - `order` - Payment for orders | |
| - `refund` - Refund from cancelled orders | |
| - `topup` - Manual wallet recharge | |
| - `commission` - Commission earnings | |
| - `withdrawal` - Money withdrawal | |
| --- | |
| ### 5. List My Transactions | |
| **POST** `/wallet/transactions/list` | |
| Get transaction history for the authenticated user with filters. | |
| **Headers:** | |
| ``` | |
| Authorization: Bearer <jwt_token> | |
| Content-Type: application/json | |
| ``` | |
| **Request:** | |
| ```json | |
| { | |
| "transaction_type": "credit", | |
| "reference_type": "topup", | |
| "start_date": "2024-01-01T00:00:00", | |
| "end_date": "2024-12-31T23:59:59", | |
| "limit": 20, | |
| "offset": 0 | |
| } | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Transactions retrieved successfully", | |
| "data": { | |
| "transactions": [ | |
| { | |
| "transaction_id": "660e8400-e29b-41d4-a716-446655440000", | |
| "wallet_id": "550e8400-e29b-41d4-a716-446655440000", | |
| "partner_id": "staff_123", | |
| "transaction_type": "credit", | |
| "amount": 1000.00, | |
| "balance_after": 6000.00, | |
| "reference_type": "topup", | |
| "reference_id": "TOPUP-2024-001", | |
| "description": "Wallet recharge", | |
| "metadata": {}, | |
| "created_at": "2024-01-20T10:30:00" | |
| } | |
| ], | |
| "pagination": { | |
| "limit": 20, | |
| "offset": 0, | |
| "total": 45 | |
| } | |
| } | |
| } | |
| ``` | |
| --- | |
| ## Admin Endpoints | |
| ### 6. [Admin] Get Wallet by Partner ID | |
| **GET** `/wallet/admin/{partner_id}` | |
| Admin endpoint to retrieve any wallet by partner ID. | |
| **Headers:** | |
| ``` | |
| Authorization: Bearer <admin_jwt_token> | |
| ``` | |
| **Response:** Same as "Get My Wallet" | |
| --- | |
| ### 7. [Admin] Update Wallet Status | |
| **PATCH** `/wallet/admin/{partner_id}/status?status={status}` | |
| Admin endpoint to change wallet status. | |
| **Headers:** | |
| ``` | |
| Authorization: Bearer <admin_jwt_token> | |
| ``` | |
| **Query Parameters:** | |
| - `status` - New status: `active` | `suspended` | `closed` | |
| **Example:** | |
| ``` | |
| PATCH /wallet/admin/staff_123/status?status=suspended | |
| ``` | |
| **Status Values:** | |
| - `active` - Normal operation (default) | |
| - `suspended` - Temporarily disabled, no transactions allowed | |
| - `closed` - Permanently disabled | |
| --- | |
| ## Error Responses | |
| ### Unauthorized (Missing/Invalid Token) | |
| ```json | |
| { | |
| "detail": "Authorization header missing" | |
| } | |
| ``` | |
| **Status Code:** 401 | |
| ### Wallet Not Found | |
| ```json | |
| { | |
| "detail": "Wallet not found. Please create a wallet first." | |
| } | |
| ``` | |
| **Status Code:** 404 | |
| ### Wallet Already Exists | |
| ```json | |
| { | |
| "detail": "Wallet already exists for partner_id: staff_123" | |
| } | |
| ``` | |
| **Status Code:** 400 | |
| ### Insufficient Balance | |
| ```json | |
| { | |
| "detail": "Insufficient balance. Current: 100.00, Required: 500.00" | |
| } | |
| ``` | |
| **Status Code:** 400 | |
| ### Wallet Not Active | |
| ```json | |
| { | |
| "detail": "Wallet is not active. Current status: suspended" | |
| } | |
| ``` | |
| **Status Code:** 400 | |
| --- | |
| ## cURL Examples | |
| ### Create Wallet | |
| ```bash | |
| curl -X POST "http://localhost:8000/wallet/create" \ | |
| -H "Authorization: Bearer YOUR_JWT_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "initial_balance": 5000.00 | |
| }' | |
| ``` | |
| ### Get My Balance | |
| ```bash | |
| curl "http://localhost:8000/wallet/balance" \ | |
| -H "Authorization: Bearer YOUR_JWT_TOKEN" | |
| ``` | |
| ### Add Money (Credit) | |
| ```bash | |
| curl -X POST "http://localhost:8000/wallet/transaction" \ | |
| -H "Authorization: Bearer YOUR_JWT_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "transaction_type": "credit", | |
| "amount": 1000.00, | |
| "reference_type": "topup", | |
| "description": "Wallet top-up" | |
| }' | |
| ``` | |
| ### Deduct Money (Debit) | |
| ```bash | |
| curl -X POST "http://localhost:8000/wallet/transaction" \ | |
| -H "Authorization: Bearer YOUR_JWT_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "transaction_type": "debit", | |
| "amount": 500.00, | |
| "reference_type": "order", | |
| "reference_id": "ORDER-123", | |
| "description": "Payment for order" | |
| }' | |
| ``` | |
| ### List My Transactions | |
| ```bash | |
| curl -X POST "http://localhost:8000/wallet/transactions/list" \ | |
| -H "Authorization: Bearer YOUR_JWT_TOKEN" \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "limit": 20, | |
| "offset": 0 | |
| }' | |
| ``` | |
| --- | |
| ## JWT Token Structure | |
| Your JWT token should contain the following claims: | |
| ```json | |
| { | |
| "partner_id": "staff_123", | |
| "user_id": "staff_123", | |
| "merchant_id": "merchant_456", | |
| "role": "service_professional", | |
| "email": "staff@example.com", | |
| "phone": "+919876543210", | |
| "exp": 1234567890 | |
| } | |
| ``` | |
| The API will extract `partner_id` from the token. If `partner_id` is not present, it will fall back to `user_id` or `staff_id`. | |
| --- | |
| ## Security Notes | |
| 1. **JWT Authentication**: All endpoints require valid JWT token | |
| 2. **Encrypted Balance**: Wallet balance is encrypted in the database using AES encryption | |
| 3. **Partner Isolation**: Users can only access their own wallet data | |
| 4. **Admin Endpoints**: Admin endpoints should verify admin role (TODO: implement role check) | |
| 5. **Rate Limiting**: Implement rate limiting in production to prevent abuse | |
| --- | |
| ## Integration Example | |
| ### Order Payment with Wallet | |
| ```python | |
| from app.wallet.services.service import WalletService | |
| from app.wallet.schemas.schema import WalletTransactionRequest | |
| from app.dependencies.auth import get_partner_id | |
| # In your order endpoint | |
| @router.post("/orders/create") | |
| async def create_order( | |
| order_data: OrderCreateRequest, | |
| db: AsyncSession = Depends(get_db), | |
| token_data: dict = Depends(verify_token) | |
| ): | |
| partner_id = get_partner_id(token_data) | |
| # Create order | |
| order = await create_order_logic(order_data, partner_id) | |
| # If payment mode is wallet, debit the wallet | |
| if order_data.payment_mode == "wallet": | |
| try: | |
| transaction = await WalletService.create_transaction( | |
| db, | |
| partner_id, | |
| WalletTransactionRequest( | |
| transaction_type="debit", | |
| amount=order.net_amount, | |
| reference_type="order", | |
| reference_id=str(order.order_id), | |
| description=f"Payment for order {order.order_number}", | |
| metadata={ | |
| "order_number": order.order_number, | |
| "items_count": len(order.items) | |
| } | |
| ) | |
| ) | |
| order.payment_status = "paid" | |
| await db.commit() | |
| except ValueError as e: | |
| # Handle insufficient balance | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| return order | |
| ``` | |
| ### Refund to Wallet | |
| ```python | |
| # When cancelling an order | |
| @router.post("/orders/{order_id}/cancel") | |
| async def cancel_order( | |
| order_id: UUID, | |
| db: AsyncSession = Depends(get_db), | |
| token_data: dict = Depends(verify_token) | |
| ): | |
| partner_id = get_partner_id(token_data) | |
| # Get order | |
| order = await get_order_by_id(order_id, partner_id) | |
| # Refund to wallet if paid via wallet | |
| if order.payment_mode == "wallet" and order.payment_status == "paid": | |
| transaction = await WalletService.create_transaction( | |
| db, | |
| partner_id, | |
| WalletTransactionRequest( | |
| transaction_type="credit", | |
| amount=order.net_amount, | |
| reference_type="refund", | |
| reference_id=str(order.order_id), | |
| description=f"Refund for cancelled order {order.order_number}" | |
| ) | |
| ) | |
| # Cancel order | |
| order.order_status = "cancelled" | |
| order.payment_status = "refunded" | |
| await db.commit() | |
| return order | |
| ``` | |