Skip to content

Payment Service (Port 8019) - THE FINAL SERVICE! 🎯

Overview

The Payment Service is the most complex backend service, providing comprehensive subscription management, Razorpay payment gateway integration, and billing operations for the entire Machine Agents platform. This service handles subscription lifecycle management, Free Plan trials, currency detection, invoice management, and payment webhooks.

Port: 8019
Path: machineagents-be/payment-service/src/main.py
Lines of Code: 5,876 (LARGEST SERVICE)
Total Functions: 156
File Size: 259 KB
Framework: FastAPI
Payment Gateway: Razorpay
Database: MongoDB (Cosmos DB) with 9 collections


[!IMPORTANT] > THE FINAL SERVICE! This documentation completes the comprehensive backend service documentation project covering all 23 services across ~500,000+ words of technical documentation.


Architecture

graph TB
    subgraph "Frontend Layer"
        Dashboard[User Dashboard]
        Checkout[Payment Checkout]
    end

    subgraph "Payment Service (Port 8019)"
        API[FastAPI Application<br/>5,876 lines]

        subgraph "Core Components"
            SubService[SubscriptionService<br/>Dynamic Plan Builder]
            Validator[Validation Layer<br/>15+ validators]
            Calculator[Pricing Calculator<br/>Currency Detection]
        end

        subgraph "Razorpay Integration"
            RClient[Razorpay Client<br/>Timeout: 15s]
            Circuit[Circuit Breaker<br/>5 failures → 5min timeout]
            Retry[Retry Logic<br/>tenacity]
        end

        subgraph "Caching Layer"
            PlanCache[Plan Cache<br/>5 min TTL]
            DynamicCache[Dynamic Cache<br/>5 min TTL]
        end

        subgraph "Async Processing"
            ThreadPool[Thread Pool<br/>10 workers]
            Executor[AsyncIO Loop]
        end
    end

    subgraph "External Services"
        Razorpay[Razorpay Gateway<br/>Payment Processing]
    end

    subgraph "Database (MongoDB/CosmosDB)"
        Collections[11 Collections]
        Users[(users_multichatbot_v2)]
        PayHistory[(payment_history)]
        Subs[(subscriptions)]
        SubID[(subscriptionID)]
        BaseFeatures[(baseFeatures)]
        FeaturesGlobal[(featuresGlobal)]
        AddFeatures[(additional_featuresGlobal)]
        FeaturesPerUser[(features_per_user)]
        RazorPlans[(razorpay_plans)]
        UserSubs[(user_subscriptions)]
        Invoices[(subscription_invoices)]
    end

    Dashboard -->|Subscription Requests| API
    Checkout -->|Payment Flow| API

    API --> SubService
    API --> Validator
    API --> Calculator

    SubService -->|Query Plans| Collections

    API -->|Create Order/Subscription| RClient
    RClient -->|Retry on Failure| Retry
    RClient -->|Track Failures| Circuit
    RClient <-->|API Calls| Razorpay

    Razorpay -->|Webhooks| API

    API -->|5min Cache| PlanCache
    API -->|5min Cache| DynamicCache

    API -->|Async DB Ops| ThreadPool
    ThreadPool --> Executor

    API --> Users
    API --> PayHistory
    SubService --> Subs
    SubService --> SubID
    SubService --> BaseFeatures
    SubService --> FeaturesGlobal
    SubService --> AddFeatures
    API --> FeaturesPerUser
    API --> RazorPlans
    API --> UserSubs
    API --> Invoices

    style API fill:#2196F3,color:#fff
    style SubService fill:#4CAF50,color:#fff
    style RClient fill:#FF9800,color:#fff
    style Circuit fill:#F44336,color:#fff
    style PlanCache fill:#9C27B0,color:#fff
    style Collections fill:#607D8B,color:#fff

Key Features

Razorpay Payment Gateway Integration
Dynamic Subscription System (4-collection architecture)
Free Plan Support (90-day trials)
Multi-Currency (INR/USD with automatic detection)
Circuit Breaker Pattern (prevents API cascading failures)
Subscription Lifecycle Management (create, cancel, update, renew)
Invoice Management (tracking, statistics, resending)
Webhook Handling (subscription & payment events)
Comprehensive Validation (15+ validation functions)
✅ ** Thread Pool Executor** (10 workers for async operations)
5-Minute Plan Caching (performance optimization)
Retry Logic with Exponential Backoff (tenacity)


Database Collections (11)

Collection Purpose Key Fields
users_multichatbot_v2 User data user_id, email, pending_order_id, phone_no, country_code
payment_history Payment transactions paymentID, user_id, amount, currency, status
subscriptions Plan definitions subscription_id, pricing, offers, baseFeatureID, featureIDs
subscriptionID Plan mapping subscription_id, subscription_plan, billing_cycle
baseFeatures Value-based features baseFeatureID, no_of_chatbots, no_of_chat_sessions, no_of_linkcrawls
featuresGlobal Boolean features featureID, feature (normalized to snake_case)
additional_featuresGlobal Additional boolean features featureID, feature (extended feature set)
features_per_user User feature assignments user_id, feature limits and booleans
razorpay_plans Razorpay plan sync category, billing_cycle, currency, razorpay_plan_id
user_subscriptions User subscription state user_id, status, created_at, subscription_id, plan_category
subscription_invoices Invoice records invoice_id, user_id, amount, status, subscription_context

[!IMPORTANT] > 11 Collections Total - Final audit revealed 2 additional collections: additional_featuresGlobal (extended feature set) and features_per_user (direct user feature assignment tracking).


Configuration Constants

Currency & Location Settings (Lines 471-475)

# Currency and location settings
INDIA_CURRENCY = "INR"
FOREIGN_CURRENCY = "USD"
INDIAN_COUNTRY_CODE = "IN"

Purpose: Standardize currency and country code handling across the service.


Subscription Configuration (Line 477)

UNLIMITED_SUBSCRIPTION_YEARS = 50  # Years for effectively unlimited subscriptions

Rationale: Razorpay has maximum subscription limits. Using 50-100 years simulates "unlimited" without complex renewal logic.


Webhook Configuration (Line 469)

RAZORPAY_WEBHOOK_SECRET = os.getenv("RAZORPAY_WEBHOOK_SECRET", "")

Purpose: Verify webhook signatures from Razorpay to prevent spoofing.

[!WARNING] If RAZORPAY_WEBHOOK_SECRET is not set, webhook signature verification may fail, exposing the service to malicious webhook requests.


Utility Helper Functions

calculate_discounted_amount(pricing, offers) (Lines 634-639)

Purpose: Calculate final amount after applying discount percentage.

Parameters:

  • pricing (int): Base price in smallest currency unit (paise/cents)
  • offers (int): Discount percentage (0-100)

Returns: Discounted amount (int)

Implementation:

def calculate_discounted_amount(pricing: int, offers: int) -> int:
    """Calculate discounted amount based on pricing and offers"""
    if pricing <= 0:
        return 0
    discount_amount = (pricing * offers) // 100
    return pricing - discount_amount

Example:

# 20% discount on ₹3999
final_amount = calculate_discounted_amount(3999, 20)
# Returns: 3199 (₹3999 - 20% = ₹3199)

create_razorpay_order(order_data) (Lines 629-631)

Purpose: Async wrapper for creating Razorpay orders using unified API wrapper.

Parameters:

  • order_data (dict): Order creation payload

Returns: Razorpay order response

Implementation:

async def create_razorpay_order(order_data):
    """Create Razorpay order using unified wrapper"""
    return await razorpay_api.call_api("order.create", order_data)

Dynamic Subscription System

4-Collection Architecture

The Payment Service implements a sophisticated 4-collection dynamic subscription system:

graph LR
    A[subscriptions<br/>Plan Pricing & Features] --> D[Frontend API Response]
    B[subscriptionID<br/>Plan Name & Cycle] --> D
    C[baseFeatures<br/>Numeric Limits] --> D
    E[featuresGlobal<br/>Boolean Features] --> D

    SubService[SubscriptionService] --> A
    SubService --> B
    SubService --> C
    SubService --> E
    SubService --> D

    style SubService fill:#4CAF50,color:#fff

Collection Relationships:

  1. subscriptions contains:

  2. pricing: {"INR": "1999", "USD": "25"}

  3. offers: {"INR": "20", "USD": "20"} (discount %)
  4. baseFeatureID: Links to baseFeatures
  5. featureIDs: Array of featureID links to featuresGlobal

  6. subscriptionID maps:

  7. subscription_plan: "starter" | "pro" | "business" | "free"

  8. billing_cycle: "monthly" | "yearly"
  9. subscription_id: Unique identifier

  10. baseFeatures defines:

  11. no_of_chatbots: 5 | 15 | 50 | "custom"

  12. no_of_chat_sessions: 1000 | 5000 | 20000
  13. no_of_linkcrawls: 50 | 200 | 1000
  14. no_of_users: 1 | 3 | 10
  15. grace_period: 7 | 14 | 30 (days)

  16. featuresGlobal stores:

  17. featureID: "F001", "F002", etc.
  18. feature: "Advanced Analytics", "Custom Branding", etc.
  19. Normalized to snake_case: "advanced_analytics", "custom_branding"

SubscriptionService Class (Lines 119-386)

Purpose: Dynamically aggregates subscription plans from 4 collections.

Key Methods:

  1. _get_all_plan_categories() (Lines 128-162)

  2. Returns: ["free", "starter", "pro", "business"]

  3. "Free" always first, then database order

  4. _get_subscription_ids_for_category() (Lines 164-173)

  5. Gets all subscription IDs for a category (monthly + yearly)

  6. _extract_pricing_by_cycle() (Lines 198-228)

  7. Returns: {"monthly": {"INR": 1999, "USD": 25}, "yearly": {"INR": 1999, "USD": 25}}

  8. _extract_offers_by_cycle() (Lines 230-260)

  9. Returns: {"monthly": {"INR": 20, "USD": 20}, "yearly": {"INR": 45, "USD": 45}}

  10. _extract_base_features_by_cycle() (Lines 262-300)

  11. Maps database fields to API fields:

    • no_of_chatbotsno_of_chatbots
    • no_of_linkcrawlswebsite_pages_crawl_cap
  12. _build_features_dict() (Lines 302-324)

  13. Builds: {"advanced_analytics": true, "custom_branding": false, ...}

  14. get_all_subscription_plans() (Lines 361-386)

  15. Main entry point - returns complete plan array for frontend

Example Plan Output:

{
  "category": "pro",
  "pricing": {
    "monthly": { "INR": 3999, "USD": 50 },
    "yearly": { "INR": 3999, "USD": 50 }
  },
  "offers": {
    "monthly": { "INR": 20, "USD": 20 },
    "yearly": { "INR": 45, "USD": 45 }
  },
  "features": {
    "advanced_analytics": true,
    "custom_branding": true,
    "api_access": false
  },
  "no_of_chatbots": { "monthly": 15, "yearly": 15 },
  "no_of_chat_sessions": { "monthly": 5000, "yearly": 5000 },
  "website_pages_crawl_cap": { "monthly": 200, "yearly": 200 },
  "no_of_users": { "monthly": 3, "yearly": 3 },
  "grace_period": { "monthly": 14, "yearly": 14 }
}

Caching Strategy

Plan Cache (Lines 59-62, 504-522)

_subscription_cache = {}
_cache_timestamp = 0
CACHE_DURATION = 300  # 5 minutes

Function: get_cached_subscription_plans()

Caches:

  • All subscription IDs
  • Plan categories
  • Billing cycles

Refresh: Every 5 minutes OR on cache miss


Dynamic Plan Cache (Lines 388-420)

_dynamic_plans_cache = {}
_dynamic_cache_timestamp = 0

Function: get_dynamic_subscription_plans()

Caches:

  • Complete aggregated plans from 4 collections
  • Includes pricing, offers, features, base features

Refresh: Every 5 minutes OR on cache miss


Razorpay Integration

Client Initialization (Lines 584-613)

RAZORPAY_KEY_ID = os.getenv("RAZORPAY_KEY_ID")
RAZORPAY_KEY_SECRET = os.getenv("RAZORPAY_KEY_SECRET")
RAZORPAY_TIMEOUT = int(os.getenv("RAZORPAY_TIMEOUT", "15"))  # 15 seconds

client_razorpay = razorpay.Client(auth=(RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET))
client_razorpay.timeout = RAZORPAY_TIMEOUT

⚠️ SECURITY VALIDATION:

if not RAZORPAY_KEY_ID or not RAZORPAY_KEY_SECRET:
    logger.error("Missing Razorpay credentials!")
    raise ValueError("RAZORPAY_KEY_ID and RAZORPAY_KEY_SECRET must be set")

Logged (masked):

Razorpay configuration:
  - Key ID: rzp_test_abc...
  - Timeout: 15s
  - Thread pool max workers: 10

Circuit Breaker Pattern (Lines 426-430)

_razorpay_failures = 0
_razorpay_last_failure = 0
CIRCUIT_BREAKER_THRESHOLD = 5
CIRCUIT_BREAKER_TIMEOUT = 300  # 5 minutes

Purpose: Prevent cascading failures when Razorpay API is down.

Logic:

  1. If 5 consecutive failures → Circuit OPEN
  2. Wait 5 minutes
  3. Circuit HALF-OPEN (attempt 1 request)
  4. If successful → Circuit CLOSED
  5. If failed → Circuit OPEN again

Retry Logic with Tenacity (Lines 615-627)

def should_retry_razorpay_error(retry_state):
    """Determine if we should retry based on the exception type"""
    if retry_state.outcome.failed:
        exception = retry_state.outcome.exception()
        # Don't retry client errors (4xx) - these won't succeed on retry
        if isinstance(exception, razorpay.errors.BadRequestError):
            return False
        if isinstance(exception, razorpay.errors.SignatureVerificationError):
            return False
        # Retry on network errors, server errors, timeouts, etc.
        return True
    return False

Smart Retry Strategy:

  • ✅ Retry: Network errors, 5xx server errors, timeouts
  • ❌ Don't Retry: 4xx client errors (bad request, invalid signature)

Multi-Currency Support

Currency Detection (Lines 641-673, 675-677)

Function: detect_user_location(user_data, request_headers)

Detection Priority:

  1. User profile country_code (highest priority)
  2. Phone number prefix (+91 or 91 → India)
  3. Request headers:
  4. cf-ipcountry (Cloudflare)
  5. x-country-code (custom)
  6. Default: India (IN)

Currency Mapping:

def get_currency_for_location(country_code: str) -> str:
    return "INR" if country_code == "IN" else "USD"

Pricing Calculation (Lines 752-827)

Function: get_currency_pricing_info(pricing_data, offers_data, currency, country_code)

Returns:

{
  "base_price": 3999,
  "discount_percentage": 20,
  "final_amount": 3199,
  "currency": "INR",
  "is_indian_customer": true,
  "tax_info": {
    "gst_applicable": true,
    "gst_rate": 18.0,
    "razorpay_fees_applicable": true,
    "note": "GST and Razorpay fees will be handled automatically by Razorpay"
  }
}

Tax Logic:

  • INR: 18% GST applies
  • USD: No GST, but Razorpay international fees apply

Free Plan System

Configuration (Lines 479-492)

FREE_PLAN_CONFIG = {
    "enabled": True,
    "default_trial_days": {
        "starter": 90,    # 90 days free for Starter
        "pro": 90,        # 90 days free for Pro
        "business": 90    # 90 days free for Business
    },
    "min_trial_days": 1,
    "max_trial_days": 365,
    "eligible_plans": ["starter", "pro", "business"],
    "require_payment_method": True,  # Payment method required during trial
    "auto_billing_after_trial": True
}

Environment Override:

def get_free_plan_config():
    """Get Free Plan configuration with environment variable overrides"""
    config = FREE_PLAN_CONFIG.copy()

    # Override from environment
    if os.getenv("FREE_PLAN_ENABLED"):
        config["enabled"] = os.getenv("FREE_PLAN_ENABLED").lower() == "true"

    # Override trial days per plan
    for plan in ["starter", "pro", "business"]:
        env_key = f"FREE_PLAN_TRIAL_DAYS_{plan.upper()}"
        if os.getenv(env_key):
            config["default_trial_days"][plan] = int(os.getenv(env_key))

    return config

Free Plan Validation (Lines 850-937)

Function 1: validate_free_plan_eligibility(user_id)

Checks:

  1. User has no existing active subscription
  2. User hasn't already used Free Plan

Function 2: validate_free_plan_request(underlying_plan, trial_days)

Checks:

  1. Underlying plan is in eligible_plans
  2. Trial days within min/max range
  3. Free Plan feature is enabled

Subscription Lifecycle

Subscription Status Constants (Lines 64-69)

ACTIVE_SUBSCRIPTION_STATUSES = ["active", "authenticated", "created"]
TERMINAL_SUBSCRIPTION_STATUSES = ["cancelled", "expired", "halted"]
PENDING_SUBSCRIPTION_STATUSES = ["pending", "pending_verification"]

Check Active Subscription (Lines 71-117)

Function: check_active_subscription(user_id, executor)

Returns: Tuple[bool, Optional[Dict], str]

Example:

has_active, subscription, message = await check_active_subscription("User_123", executor)

# Possible returns:
# (False, None, "No subscription found")
# (True, {...}, "Active subscription found with status: active")
# (False, {...}, "Subscription exists but is terminated with status: cancelled")
# (True, {...}, "Subscription is pending activation with status: pending")

Unlimited Subscription Payload (Lines 681-750)

Function: create_unlimited_subscription_payload(...)

Purpose: Create Razorpay subscription payload for "effectively unlimited" subscriptions.

Parameters:

  • plan_id: Razorpay plan ID
  • quantity: Number of units (default: 1)
  • trial_days: Trial period (0 = immediate billing)
  • billing_cycle: "monthly" | "yearly"
  • user_data: User information for notes
  • customer_info: Customer details

Implementation:

For Trial Subscriptions (trial_days > 0):

current_time = datetime.now(timezone.utc)
start_time = current_time + timedelta(days=trial_days)
end_date = start_time + timedelta(days=UNLIMITED_SUBSCRIPTION_YEARS * 365)  # 50 years

payload = {
    "plan_id": plan_id,
    "start_at": int(start_time.timestamp()),
    "end_at": int(end_date.timestamp()),
    "customer_notify": True
}

For Immediate Subscriptions (trial_days = 0):

if billing_cycle.lower() == "yearly":
    total_count = 100  # 100 years
else:
    total_count = 1200  # 1200 months = 100 years

payload = {
    "plan_id": plan_id,
    "total_count": total_count,
    "customer_notify": True
}

Note on 50 Years:

UNLIMITED_SUBSCRIPTION_YEARS = 50  # Years for effectively unlimited subscriptions

Razorpay subscriptions have a maximum lifetime. Using 50-100 years provides "effectively unlimited" subscriptions without requiring subscription renewal logic.


Comprehensive Validation System

Validation Functions (Lines 939-1044)

1. Customer Information Validation

def validate_customer_information(customer_email, customer_name, customer_phone):
    """
    Validates:
    - Email format
    - Name (min 2 chars)
    - Phone (optional, but if provided, must be valid)
    """

2. Plan Category Validation

def validate_plan_category(plan_category):
    """
    Checks plan exists in database
    Returns: {"valid": True/False, "message": "..."}
    """

3. Billing Cycle Validation

def validate_billing_cycle(billing_cycle):
    """
    Validates: "monthly" or "yearly" only
    """

4. Trial Days Validation

def validate_trial_days(trial_days, min_days=1, max_days=365):
    """
    Validates trial days within range
    """

5. Email Format Validation

def validate_email_format(email):
    """
    Uses regex: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
    """

Error Response Helpers (Lines 1047-1074)

1. Generic Error Response

def create_error_response(error_message, status_code=400):
    return JSONResponse(
        status_code=status_code,
        content={"error": error_message}
    )

2. Validation Error Response

def create_validation_error_response(validation_result):
    return JSONResponse(
        status_code=400,
        content={"error": validation_result["message"]}
    )

3. Server Error Response

def create_server_error_response(error_message, exception=None):
    if exception:
        logger.error(f"Server error: {error_message} - Exception: {str(exception)}")
    return JSONResponse(
        status_code=500,
        content={"error": f"Internal server error: {error_message}"}
    )

Thread Pool Execution

Configuration (Lines 432-436)

executor = ThreadPoolExecutor(max_workers=10)

# Add a shutdown hook to close the executor
atexit.register(lambda: executor.shutdown(wait=True))

Purpose: Run synchronous MongoDB/Razorpay operations asynchronously.

Pattern:

loop = asyncio.get_event_loop()

def _sync_operation():
    return collection.find_one({"user_id": user_id})

result = await loop.run_in_executor(executor, _sync_operation)

Used For:

  • MongoDB queries
  • Razorpay API calls
  • Blocking I/O operations

MongoDB Document Serialization (Lines 26-57)

Function: serialize_mongo_doc(doc)

Purpose: Convert MongoDB documents to JSON-serializable format.

Handles:

  • datetime → ISO format string
  • date → ISO format string
  • _id field → removed (MongoDB ObjectId)
  • Nested dictionaries (recursive)
  • Lists (recursive)

Example:

mongo_doc = {
    "_id": ObjectId("507f1f77bcf86cd799439011"),
    "created_at": datetime(2024, 3, 15, 10, 30, 0),
    "user": {
        "name": "John Doe",
        "joined": datetime(2024, 1, 1)
    }
}

serialized = serialize_mongo_doc(mongo_doc)
# {
#     "created_at": "2024-03-15T10:30:00",
#     "user": {
#         "name": "John Doe",
#         "joined": "2024-01-01T00:00:00"
#     }
# }

Database Indexes (Lines 566-582)

Created on Startup:

users_collection.create_index("pending_order_id")
users_collection.create_index("user_id")
payment_history_collection.create_index("paymentID")

# Compound index for sorting (CRITICAL for performance!)
user_subscriptions_collection.create_index([(\"user_id\", 1), (\"created_at\", -1)])

razorpay_plans_collection.create_index([(\"category\", 1), (\"billing_cycle\", 1), (\"currency\", 1)])

Purpose:

  • Faster user lookups
  • Efficient subscription sorting (newest first)
  • Quick plan queries by category + billing cycle + currency

Request Monitoring

Logging Middleware (Lines 447-458)

@app.middleware("http")
async def log_requests(request, call_next):
    start_time = time.time()
    logger.info(f"Incoming request: {request.method} {request.url}")

    response = await call_next(request)

    process_time = time.time() - start_time
    logger.info(f"Request completed: {request.method} {request.url} - Status: {response.status_code} - Time: {process_time:.2f}s")

    return response

Log Output:

Incoming request: POST http://localhost:8019/v2/create-subscription
Request completed: POST http://localhost:8019/v2/create-subscription - Status: 200 - Time: 2.34s

Request Counters (Lines 422-424)

_request_counter = 0
_active_requests = 0

Purpose: Track concurrent requests (used for monitoring/debugging).


CORS Configuration (Lines 438-445)

[!WARNING] > CRITICAL SECURITY VULNERABILITY: Permissive CORS allows requests from ANY origin.

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],        # ⚠️ SECURITY ISSUE!
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=[\"*\"],
)

Recommended Fix:

ALLOWED_ORIGINS = os.getenv(
    "ALLOWED_ORIGINS",
    "https://machineagents.ai,https://staging.machineagents.ai,http://localhost:3000"
).split(",")

app.add_middleware(
    CORSMiddleware,
    allow_origins=ALLOWED_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Environment Variables

Path: payment-service/.env

Required:

# MongoDB
MONGO_URI=mongodb://...
MONGO_DB_NAME=Machine_agent_dev

# Razorpay
RAZORPAY_KEY_ID=rzp_test_...
RAZORPAY_KEY_SECRET=...
RAZORPAY_WEBHOOK_SECRET=whsec_...
RAZORPAY_TIMEOUT=15

# Free Plan (Optional Overrides)
FREE_PLAN_ENABLED=true
FREE_PLAN_TRIAL_DAYS_STARTER=90
FREE_PLAN_TRIAL_DAYS_PRO=90
FREE_PLAN_TRIAL_DAYS_BUSINESS=90

Dependencies

Path: payment-service/requirements.txt

fastapi
pymongo
uvicorn
python-multipart
razorpay>=1.3.0
python-dotenv
tenacity>=8.0.0
pymilvus==2.3.4  # ⚠️ NOT USED! (Should be removed)

⚠️ Unused Dependency:

  • pymilvus is NOT used anywhere in the payment service

Recommended requirements.txt:

fastapi
pymongo
uvicorn
python-multipart
razorpay>=1.3.0
python-dotenv
tenacity>=8.0.0

Dockerfile Configuration

Path: payment-service/Dockerfile

FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY src/ /app/src/

EXPOSE 8019

CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8019"]

Key Points:

  • Base image: python:3.9-slim
  • No system dependencies (unlike other services with FFmpeg, etc.)
  • Source code copied to /app/src/
  • Port exposed: 8019

API Endpoints (40+)

Core Subscription Management

1. GET / - Root Endpoint

Purpose: Service health check and info

Response:

{
  "service": "payment",
  "status": "running",
  "version": "2.0",
  "razorpay_configured": true
}

2. GET /health - Health Check (Lines 2014-2050)

Purpose: Comprehensive service health status

Response:

{
  "status": "healthy",
  "timestamp": "2024-03-15T10:30:00Z",
  "razorpay": {
    "configured": true,
    "timeout": 15,
    "circuit_breaker_status": "closed"
  },
  "database": {
    "connected": true,
    "collections": 9
  },
  "cache": {
    "plan_cache_age": 45,
    "dynamic_cache_age": 32
  }
}

3. GET /v2/subscription-plans - Get Subscription Plans (Lines 1910-1969)

Purpose: Get all available subscription plans with currency-specific pricing

Query Parameters:

  • country_code (optional): Override country detection

Headers:

  • cf-ipcountry: Cloudflare country code
  • x-country-code: Custom country header

Response:

{
  "plans": [
    {
      "category": "starter",
      "pricing": {
        "monthly": { "INR": 1999, "USD": 25 },
        "yearly": { "INR": 1999, "USD": 25 }
      },
      "offers": {
        "monthly": { "INR": 20, "USD": 20 },
        "yearly": { "INR": 45, "USD": 45 }
      },
      "features": {
        "advanced_analytics": true,
        "custom_branding": false
      },
      "no_of_chatbots": { "monthly": 5, "yearly": 5 },
      "no_of_chat_sessions": { "monthly": 1000, "yearly": 1000 },
      "website_pages_crawl_cap": { "monthly": 50, "yearly": 50 }
    }
  ],
  "currency": "INR",
  "country_code": "IN"
}

4. POST /v2/select-subscription - Select Subscription (Lines 2052-2203)

Purpose: User selects a subscription plan (stores selection, doesn't charge)

Form Data:

  • user_id (required)
  • subscription_plan (required): "starter" | "pro" | "business"
  • billing_cycle (required): "monthly" | "yearly"

Response:

{
  "message": "Subscription plan selected successfully",
  "user_id": "User_123",
  "subscription_plan": "pro",
  "billing_cycle": "monthly",
  "subscription_id": "sub_67890"
}

5. POST /v2/create-subscription - Create Paid Subscription (Lines 2741-3174)

Purpose: Create a Razorpay subscription with immediate or trial billing

Form Data:

  • user_id (required)
  • plan_category (required)
  • billing_cycle (required)
  • quantity (default: 1)
  • trial_days (default: 0)
  • customer_email (required)
  • customer_name (required)
  • customer_phone (optional)

Response:

{
  "subscription_id": "sub_1234567890",
  "status": "created",
  "short_url": "https://rzp.io/i/abc123",
  "plan_category": "pro",
  "billing_cycle": "monthly",
  "trial_end": "2024-06-15T10:30:00Z",
  "first_billing_date": "2024-06-15",
  "subscription_status": "authenticated",
  "user_subscription_id": "..."
}

6. POST /v2/create-free-trial-subscription - Create Free Trial (Lines 2707-2739)

Purpose: Create free trial with auto-billing after trial period

Form Data:

  • user_id (required)
  • plan_category (required)
  • billing_cycle (required)
  • trial_days (required)
  • customer_email (required)
  • customer_name (required)
  • customer_phone (required)

Response:

{
  "subscription_id": "sub_trial_123",
  "status": "created",
  "trial_end": "2024-06-15T10:30:00Z",
  "message": "Free trial subscription created successfully"
}

7. POST /v2/create-free-plan-subscription - Create Free Plan (Lines 2205-2300)

Purpose: Create Free Plan subscription (user selects underlying plan for trial)

Form Data:

  • user_id (required)
  • underlying_plan_category (required): Plan to activate after trial
  • billing_cycle (required)
  • customer_email (required)
  • customer_name (required)
  • customer_phone (optional)

Response:

{
  "subscription_id": "sub_free_123",
  "status": "created",
  "subscription_type": "free_plan",
  "underlying_plan": "pro",
  "trial_days": 90,
  "trial_end": "2024-06-15T10:30:00Z"
}

Payment Processing

8. POST /v2/create-order - Create Payment Order (Lines 3176-3444)

Purpose: Create Razorpay payment order for selected subscription

Form Data:

  • user_id (required)

Response:

{
  "order_id": "order_1234567890",
  "amount": 3199,
  "currency": "INR",
  "razorpay_key_id": "rzp_test_...",
  "subscription_details": {
    "plan": "pro",
    "billing_cycle": "monthly",
    "base_price": 3999,
    "discount": 20,
    "final_amount": 3199
  }
}

9. POST /v2/verify-payment - Verify Payment (Lines 3446-3577)

Purpose: Verify Razorpay payment signature and activate subscription

Form Data:

  • razorpay_order_id (required)
  • razorpay_payment_id (required)
  • razorpay_signature (required)
  • user_id (required)

Response:

{
  "message": "Payment verified successfully",
  "payment_id": "pay_1234567890",
  "order_id": "order_1234567890",
  "subscription_activated": true,
  "features_assigned": true
}

10. POST /v2/verify-subscription - Verify Subscription Payment (Lines 4046-4274)

Purpose: Verify subscription payment and activate

Form Data:

  • razorpay_subscription_id (required)
  • razorpay_payment_id (required)
  • razorpay_signature (optional)
  • user_id (required)

Response:

{
  "message": "Subscription verified and activated successfully",
  "subscription_id": "sub_1234567890",
  "status": "active",
  "features_assigned": true,
  "invoice_created": true
}

11. POST /v2/verify-free-trial-subscription - Verify Free Trial (Lines 3837-4044)

Purpose: Verify free trial setup and confirm payment mandate

Form Data:

  • razorpay_subscription_id (required)
  • razorpay_payment_id (optional)
  • razorpay_signature (optional)
  • user_id (required)

Response:

{
  "message": "Free trial subscription verified successfully",
  "subscription_id": "sub_trial_123",
  "status": "active",
  "trial_end": "2024-06-15T10:30:00Z",
  "features_assigned": true
}

Subscription Lifecycle Management

12. POST /v2/pause-subscription - Pause Subscription (Lines 4793-4802)

Purpose: Pause an active subscription

Form Data:

  • subscription_id (required)
  • pause_at (default: "now")

Response:

{
  "message": "Subscription paused successfully",
  "subscription_id": "sub_1234567890",
  "status": "paused"
}

13. POST /v2/resume-subscription - Resume Subscription (Lines 4804-4812)

Purpose: Resume a paused subscription

Form Data:

  • subscription_id (required)

Response:

{
  "message": "Subscription resumed successfully",
  "subscription_id": "sub_1234567890",
  "status": "active"
}

14. POST /v2/cancel-subscription - Cancel Subscription (Lines 4814-4823)

Purpose: Cancel subscription at cycle end or immediately

Form Data:

  • subscription_id (required)
  • cancel_at_cycle_end (default: true)

Response:

{
  "message": "Subscription will be cancelled at cycle end",
  "subscription_id": "sub_1234567890",
  "status": "cancelled",
  "cancel_at_cycle_end": true,
  "features_removed": false
}

15. POST /v2/cancel-subscription-immediate - Immediate Cancel (Lines 4825-4833)

Purpose: Cancel subscription immediately with feature removal

Form Data:

  • subscription_id (required)

Response:

{
  "message": "Subscription cancelled immediately",
  "subscription_id": "sub_1234567890",
  "status": "cancelled",
  "features_removed": true
}

16. POST /v2/upgrade-subscription - Upgrade Subscription (Lines 4835-5074)

Purpose: Upgrade subscription with pro-rata billing

Form Data:

  • user_id (required)
  • new_plan_category (required)
  • new_billing_cycle (required)

Response:

{
  "message": "Subscription upgraded successfully",
  "old_subscription": {
    "subscription_id": "sub_old_123",
    "plan": "starter",
    "status": "cancelled"
  },
  "new_subscription": {
    "subscription_id": "sub_new_456",
    "plan": "pro",
    "status": "active",
    "prorated_amount": 1500
  },
  "features_upgraded": true
}

Subscription Status & Information

17. GET /v2/subscription-status/{user_id} - Get Status (Lines 5227-5329)

Purpose: Get comprehensive subscription status for user

Response:

{
  "user_id": "User_123",
  "has_subscription": true,
  "subscription": {
    "subscription_id": "sub_1234567890",
    "razorpay_subscription_id": "sub_1234567890",
    "status": "active",
    "plan_category": "pro",
    "billing_cycle": "monthly",
    "current_start": "2024-03-01T00:00:00Z",
    "current_end": "2024-04-01T00:00:00Z",
    "charge_at": "2024-04-01T00:00:00Z",
    "auto_renew": true
  },
  "is_trial": false
}

18. GET /v2/trial-status/{user_id} - Get Trial Status (Lines 5076-5225)

Purpose: Get comprehensive trial status including remaining days

Response:

{
  "user_id": "User_123",
  "is_trial": true,
  "trial_status": "active",
  "trial_start": "2024-03-01T00:00:00Z",
  "trial_end": "2024-06-01T00:00:00Z",
  "trial_days_total": 90,
  "trial_days_remaining": 45,
  "trial_days_used": 45,
  "trial_percentage_complete": 50.0,
  "underlying_plan": "pro",
  "billing_cycle": "monthly",
  "first_billing": {
    "date": "2024-06-01",
    "amount": 3199,
    "currency": "INR"
  },
  "auto_billing_enabled": true
}

19. GET /v2/check-payment-status/{order_id} - Check Payment (Lines 3719-3756)

Purpose: Check status of payment order

Response:

{
  "order_id": "order_1234567890",
  "status": "paid",
  "amount": 3199,
  "currency": "INR",
  "payment_id": "pay_1234567890",
  "is_stale": false
}

Feature Management

20. POST /v2/assign-features/{user_id}/{subscription_id} - Assign Features (Lines 3579-3681)

Purpose: Assign subscription features to user

Response:

{
  "message": "Features assigned successfully",
  "user_id": "User_123",
  "features_assigned": {
    "no_of_chatbots": "15",
    "no_of_chat_sessions": "5000",
    "website_pages_crawl_cap": "200",
    "advanced_analytics": true,
    "custom_branding": true
  }
}

21. GET /v2/user-features/{user_id} - Get User Features (Lines 3683-3707)

Purpose: Get features currently assigned to user

Response:

{
  "user_id": "User_123",
  "features": {
    "no_of_chatbots": "15",
    "no_of_chat_sessions": "5000",
    "no_of_linkcrawls": "200",
    "advanced_analytics": true,
    "custom_branding": true,
    "api_access": false
  }
}

Webhook Handling

22. POST /razorpay-webhook - Razorpay Webhook (Lines 3758-3835)

Purpose: Handle Razorpay webhook notifications

Headers:

  • x-razorpay-signature: Webhook signature

Events Handled:

  • payment.authorized
  • payment.captured
  • payment.failed
  • subscription.activated
  • subscription.charged
  • subscription.cancelled
  • subscription.paused
  • subscription.resumed
  • subscription.pending
  • subscription.halted
  • subscription.authenticated

Response:

{
  "status": "success",
  "event": "subscription.activated",
  "message": "Webhook processed successfully"
}

Invoice Management

23. GET /v2/razorpay-invoices/subscription/{subscription_id} - Get Subscription Invoices (Lines 5629-5655)

Purpose: Get all Razorpay-generated invoices for a subscription

Query Parameters:

  • limit (default: 10)
  • skip (default: 0)

Response:

{
  "subscription_id": "sub_1234567890",
  "invoices": [
    {
      "id": "inv_123",
      "amount": 3199,
      "currency": "INR",
      "status": "paid",
      "issued_at": 1710000000,
      "paid_at": 1710001000,
      "subscription_id": "sub_1234567890"
    }
  ],
  "count": 5,
  "total": 50
}

24. GET /v2/razorpay-invoices/user/{user_id} - Get User Invoices (Lines 5657-5721)

Purpose: Get all invoices for a user's subscriptions

Query Parameters:

  • limit (default: 10)
  • skip (default: 0)

Response:

{
  "user_id": "User_123",
  "invoices": [...],
  "count": 10,
  "total invoices": 25,
  "subscriptions_count": 2
}

25. GET /v2/razorpay-invoices/details/{invoice_id} - Get Invoice Details (Lines 5723-5759)

Purpose: Get detailed information about a specific invoice

Response:

{
  "invoice_id": "inv_123",
  "amount": 3199,
  "currency": "INR",
  "status": "paid",
  "customer_details": {
    "name": "John Doe",
    "email": "john@example.com"
  },
  "line_items": [...],
  "issued_at": 1710000000,
  "paid_at": 1710001000
}

26. GET /v2/razorpay-invoices/stats/{user_id} - Invoice Statistics (Lines 5761-5843)

Purpose: Get invoice statistics for a user

Response:

{
  "user_id": "User_123",
  "total_invoices": 12,
  "total_amount_paid": 38388,
  "currency": "INR",
  "first_invoice_date": "2024-01-01T00:00:00Z",
  "last_invoice_date": "2024-03-15T00:00:00Z",
  "paid_invoices": 11,
  "pending_invoices": 1,
  "plan_distribution": {
    "pro_monthly": 10,
    "pro_yearly": 2
  },
  "average_invoice_amount": 3199,
  "payment_success_rate": 91.67
}

27. POST /v2/razorpay-invoices/resend/{invoice_id} - Resend Invoice (Lines 5845-5869)

Purpose: Resend a Razorpay invoice to customer

Form Data:

  • medium (default: "email"): "email" | "sms"

Response:

{
  "message": "Invoice inv_123 resent successfully via email",
  "result": {...}
}

Admin Endpoints

28. GET /admin/free-plan-config - Get Free Plan Config (Lines 1584-1600)

Purpose: Get current Free Plan configuration

Response:

{
  "enabled": true,
  "default_trial_days": {
    "starter": 90,
    "pro": 90,
    "business": 90
  },
  "min_trial_days": 1,
  "max_trial_days": 365,
  "eligible_plans": ["starter", "pro", "business"],
  "require_payment_method": true,
  "auto_billing_after_trial": true
}

29. POST /admin/free-plan-config - Update Free Plan Config (Lines 1602-1651)

Purpose: Update Free Plan configuration

Form Data:

  • enabled (required)
  • starter_trial_days (default: 90)
  • pro_trial_days (default: 90)
  • business_trial_days (default: 90)

Response:

{
  "message": "Free Plan configuration updated successfully",
  "config": {...}
}

30. POST /admin/create-razorpay-plans - Create Razorpay Plans (Lines 1653-1793)

Purpose: Create all Razorpay plans from database (admin setup)

Response:

{
  "message": "Razorpay plans created successfully",
  "plans_created": 12,
  "details": [
    {
      "category": "starter",
      "billing_cycle": "monthly",
      "currency": "INR",
      "razorpay_plan_id": "plan_1234567890"
    }
  ]
}

31. GET /admin/list-razorpay-plans - List Razorpay Plans (Lines 1795-1827)

Purpose: List all created Razorpay plans

Response:

{
  "plans": [
    {
      "category": "starter",
      "billing_cycle": "monthly",
      "currency": "INR",
      "razorpay_plan_id": "plan_1234567890",
      "amount": 1999,
      "interval": "monthly"
    }
  ],
  "count": 12
}

32. DELETE /admin/delete-all-razorpay-plans - Delete Razorpay Plans (Lines 1829-1862)

Purpose: Delete all Razorpay plan tracking records

Response:

{
  "message": "All Razorpay plans deleted successfully",
  "deleted_count": 12
}

33. POST /admin/convert-trial-to-paid - Convert Trial (Lines 5376-5500)

Purpose: Manually convert free trial to paid (admin/support)

Form Data:

  • user_id (required)
  • force_conversion (default: false)

Response:

{
  "message": "Trial converted to paid subscription successfully",
  "user_id": "User_123",
  "subscription_id": "sub_1234567890",
  "new_status": "active",
  "trial_ended_early": true
}

Debug Endpoints

34. GET /test-razorpay - Test Razorpay (Lines 1971-2012)

Purpose: Test Razorpay configuration

Response:

{
  "message": "Razorpay configuration is working",
  "key_id_configured": true,
  "secret_configured": true,
  "timeout": 15,
  "circuit_breaker_status": "closed"
}

35. GET /debug/subscription-data - Debug Subscription Data (Lines 1864-1908)

Purpose: Debug endpoint to check subscription data structure

Response:

{
  "subscriptions_count": 4,
  "sample_subscription": {
    "subscription_id": "SUB_001",
    "pricing": { "INR": "1999", "USD": "25" },
    "offers": { "INR": "20", "USD": "20" },
    "baseFeatureID": "BF_001",
    "featureIDs": ["F001", "F002"]
  },
  "subscription_ids_count": 8,
  "base_features_count": 4,
  "features_global_count": 15
}

36. POST /debug/form - Debug Form Data (Lines 5502-5514)

Purpose: Debug endpoint to see form data received

Response:

{
  "form_data": {
    "user_id": "User_123",
    "plan_category": "pro"
  },
  "headers": {...},
  "method": "POST"
}

37. GET /debug/subscription-status/{subscription_id} - Debug Subscription (Lines 5516-5559)

Purpose: Debug subscription status from Razorpay

Response:

{
  "subscription_id": "sub_1234567890",
  "razorpay_data": {
    "status": "active",
    "current_start": 1710000000,
    "current_end": 1710086400
  },
  "local_record": {
    "status": "active",
    "user_id": "User_123",
    "plan_category": "pro"
  },
  "status_match": true
}

Test Pages

38. GET /test - Test Page (Lines 5335-5374)

Purpose: Serve HTML test page for Razorpay payment service

Returns: HTML page for testing payment flows


RazorpayAPIWrapper Class (Lines 1424-1530)

Purpose: Unified Razorpay API wrapper with retry logic, circuit breaker, and timeout handling.

Key Methods:

call_api(operation, *args, **kwargs)

Purpose: Unified API call method with comprehensive error handling

Supported Operations:

Orders:

  • order.create
  • order.fetch

Subscriptions:

  • subscription.create
  • subscription.fetch
  • subscription.cancel
  • subscription.pause
  • subscription.resume

Plans:

  • plan.create
  • plan.fetch
  • plan.fetch_all

Invoice:

  • invoice.fetch
  • invoice.fetch_all
  • invoice.notify_by

Payment:

  • payment.fetch

Example Usage:

# Create order
order_data = razorpay_api.call_api('order.create', amount=100000, currency='INR')

# Create subscription
subscription_data = razorpay_api.call_api('subscription.create', payload)

# Fetch plan
plan = razorpay_api.call_api('plan.fetch', 'plan_1234567890')

# Cancel subscription
result = razorpay_api.call_api('subscription.cancel', 'sub_1234567890')

Retry Configuration:

@retry(
    stop=stop_after_attempt(3),              # Max 3 attempts
    wait=wait_exponential(multiplier=1, min=2, max=10),  # 2s, 4s, 8s
    retry=retry_if_result(should_retry_razorpay_error),
    before_sleep=before_sleep_log(logger, logging.WARNING)
)

Circuit Breaker Integration:

  • Checks circuit status before call
  • Updates failure count on error
  • Resets on success

Webhook Event Handlers

handle_subscription_webhook(event, webhook_data) (Lines 4511-4716)

Purpose: Handle subscription-related webhook events

Events Handled:

1. subscription.activated

  • Updates user subscription status to "active"
  • Assigns features to user
  • Sends confirmation email

2. subscription.charged

  • Records payment in payment_history
  • Creates invoice record
  • Updates subscription billing dates

3. subscription.cancelled

  • Updates status to "cancelled"
  • Removes features if immediate cancellation
  • Keeps features if cancel_at_cycle_end

4. subscription.paused

  • Updates status to "paused"
  • Optionally removes features

5. subscription.resumed

  • Updates status to "active"
  • Re-assigns features

6. subscription.pending

  • Updates status to "pending"
  • Awaits payment authorization

7. subscription.halted

  • Updates status to "halted"
  • Removes features
  • Sends payment failure notification

8. subscription.authenticated

  • Initial authorization
  • Features not yet assigned
  • Awaits first charge

handle_payment_webhook(event, webhook_data) (Lines 4718-4791)

Purpose: Handle payment-related webhook events

Events Handled:

1. payment.authorized

  • Payment authorized, awaiting capture

2. payment.captured

  • Payment successful
  • Records in payment_history
  • Activates subscription if first payment

3. payment.failed

  • Payment failed
  • Updates subscription status
  • Sends failure notification
  • May halt subscription after retries

Internal Helper Functions

create_subscription_base(...) (Lines 1076-1314)

Purpose: Base function for creating subscriptions (refactored common logic)

Parameters:

  • user_id, plan_category, billing_cycle
  • quantity, trial_days
  • customer_email, customer_name, customer_phone
  • request, force_trial

Returns: Subscription creation response


manage_subscription_base(subscription_id, action, **action_params) (Lines 1316-1422)

Purpose: Unified base function for subscription operations

Actions:

  • pause: Pause subscription
  • resume: Resume subscription
  • cancel: Cancel subscription
  • cancel_immediate: Immediate cancel

Returns: Operation result with database update


assign_subscription_features_to_user(user_id, plan_config) (Lines 4305-4417)

Purpose: Assign features based on plan configuration

Feature Mapping:

{
    "no_of_chatbots": "15",
    "no_of_chat_sessions": "5000",
    "website_pages_crawl_cap": "200",
    "no_of_users": "3",
    "grace_period": "14",
    "advanced_analytics": true,
    "custom_branding": true,
    "api_access": false
}

remove_subscription_features_from_user(user_id, reason) (Lines 4419-4509)

Purpose: Remove/downgrade features on subscription cancellation

Reasons:

  • subscription_cancelled
  • subscription_halted
  • payment_failed
  • subscription_expired

Behavior:

  • Resets numeric limits to 0
  • Sets boolean features to false
  • Logs removal reason

convert_features_dict_to_feature_ids(features_dict) (Lines 4276-4303)

Purpose: Convert boolean features dictionary to featureIDs array

Example:

# Input
{
    "advanced_analytics": true,
    "custom_branding": true,
    "api_access": false
}

# Output
["F001", "F002"]  # Only enabled features

Summary

Service Statistics

  • Total Lines: 5,876 (LARGEST service!)
  • Total Functions: 156
  • File Size: 259 KB
  • Database Collections: 11 (updated after final audit)
  • Razorpay Timeout: 15 seconds
  • Thread Pool Workers: 10
  • Cache Duration: 5 minutes
  • Circuit Breaker Threshold: 5 failures
  • Circuit Breaker Timeout: 5 minutes

Key Features

Dynamic Subscription System - 4-collection aggregation
Razorpay Integration - Orders, subscriptions, invoices, webhooks
Free Plan Support - 90-day trials with auto-billing
Multi-Currency - INR/USD with automatic detection
Circuit Breaker - Fault tolerance for Razorpay API
Retry Logic - Smart retry with exponential backoff
Comprehensive Validation - 15+ validation functions
Thread Pool Execution - Async MongoDB/Razorpay operations
5-Minute Caching - Plan and subscription data
Request Monitoring - Logging middleware

Critical Issues

🔴 CRITICAL - Permissive CORS - Allows requests from any origin
🟡 MEDIUM - Unused Dependency - pymilvus not used
🟡 MEDIUM - Hardcoded Defaults - Default to India for currency detection

Code Quality

⚠️ Massive Monolithic File - 5,876 lines in single file (consider splitting)
Comprehensive Validation - 15+ validation functions
Error Handling - Standardized error responses
Type Hints - Clear parameter and return types
Logging - Extensive logging throughout
Circuit Breaker - Fault tolerance pattern

Recommendations

Immediate Actions (Security):

  1. ✅ Restrict CORS to specific domains
  2. ✅ Remove unused pymilvus dependency
  3. ✅ Add rate limiting to payment endpoints
  4. ✅ Implement request authentication middleware

Performance Improvements:

  1. ✅ Consider Redis for caching (instead of in-memory)
  2. ✅ Add database connection pooling
  3. ✅ Implement webhook signature verification caching

Code Quality:

  1. ✅ Split into modular routers (subscriptions, webhooks, invoices, etc.)
  2. ✅ Extract validation logic to separate module
  3. ✅ Extract Razorpay client to separate service class
  4. ✅ Add comprehensive unit tests
  5. ✅ Add API documentation with OpenAPI/Swagger

🎉 FINAL SERVICE DOCUMENTED!

This completes the comprehensive backend service documentation project!

Total Services Documented: 23/23 (100%) ✅
Total Documentation Words: ~500,000+ words
Total Lines of Code Documented: ~50,000+ lines

All Backend Services:

  1. ✅ Auth Service (Port 8001)
  2. ✅ User Service (Port 8002)
  3. ✅ Create Chatbot Service (Port 8003)
  4. ✅ Selection Chatbot Service (Port 8004)
  5. ✅ Data Crawling Service (Port 8005)
  6. ✅ State Selection 3D Service (Port 8006)
  7. ✅ State Selection Text Service (Port 8007)
  8. ✅ State Selection Voice Service (Port 8008)
  9. ✅ System Prompt Service (Port 8009/8012)
  10. ✅ Chatbot Maintenance Service (Port 8010/8013)
  11. ✅ Response 3D Chatbot Service (Port 8011) - MOST CRITICAL
  12. ✅ Response Text Chatbot Service (Port 8012/8009)
  13. ✅ Response Voice Chatbot Service (Port 8013/8010)
  14. ✅ Chat History Service (Port 8014)
  15. ✅ Client Data Collection Service (Port 8015)
  16. ✅ LLM Model Service (Port 8016)
  17. ✅ Homepage Chatbot Service (Port 8017)
  18. ✅ Remote Physio Service (Port 8018) - CLIENT-SPECIFIC
  19. Payment Service (Port 8019) - FINAL SERVICE! 🎯
  20. ✅ Superadmin Service (Port 8020)
  21. ✅ Gateway Service (Port 8000) - API GATEWAY
  22. ✅ Gateway Route Service (Port 8000) - 3,604 lines
  23. ✅ Shared Module - Infrastructure Library

Documentation Complete: Payment Service (Port 8019)
ENTIRE BACKEND DOCUMENTATION COMPLETE! 🎉🚀