import time from dataclasses import dataclass, field from enum import Enum from typing import Any, Dict, List, Optional class DocumentType(str, Enum): PAN = "pan" PAN_V5 = "pan_v5" PAN_VERIFY = "pan_verify" PAN_ADVANCED = "pan_advanced" PAN_DETAILED = "pan_detailed" AADHAAR = "aadhaar" DRIVING_LICENSE = "driving_license" VOTER_ID = "voter_id" PASSPORT = "passport" BANK_ACCOUNT = "bank_account" BANK_VERIFY_PD = "bank_verify_pd" BANK_VERIFY_V5 = "bank_verify_v5" BANK_PD = "bank_pd" BANK_VERIFY_FULL = "bank_verify_full" BANK_VERIFY_V4 = "bank_verify_v4" GSTIN = "gstin" GST = "gst" GST_VERIFY = "gst_verify" GST_BY_PAN = "gst_by_pan" FETCH_GST = "fetch_gst" MSME = "msme" MSME_BY_PAN = "msme_by_pan" UDYAM = "udyam" CUSTOM = "custom" class VerificationStatus(str, Enum): SUCCESS = "SUCCESS" FAILED = "FAILED" ERROR = "ERROR" SKIPPED = "SKIPPED" @dataclass class VendorResult: """Standardized result returned by every vendor verification adapter.""" vendor_name: str is_success: bool status: VerificationStatus data: Dict[str, Any] = field(default_factory=dict) raw_response: Any = None error_code: Optional[str] = None error_message: Optional[str] = None latency_ms: float = 0.0 retryable: bool = False def to_dict(self) -> Dict[str, Any]: return { "vendor_name": self.vendor_name, "is_success": self.is_success, "status": self.status.value if isinstance(self.status, VerificationStatus) else str(self.status), "data": self.data, "raw_response": self.raw_response, "error_code": self.error_code, "error_message": self.error_message, "latency_ms": round(self.latency_ms, 2), "retryable": self.retryable, } class BaseVendor: """ Abstract Base Class for all Verification Vendors (e.g. Codeplus, Cashfree, Karza, Surepass). Subclasses implement vendor-specific logic while adhering to this unified contract. """ name: str = "base_vendor" display_name: str = "Base Vendor" priority: int = 100 is_active: bool = True supported_documents: List[str] = [] def __init__(self, config: Optional[Dict[str, Any]] = None): self.config = config or {} def supports(self, document_type: str) -> bool: """Check if this vendor supports the requested document type.""" doc_key = document_type.lower() return doc_key in [d.lower() for d in self.supported_documents] or "all" in self.supported_documents def verify( self, document_type: str, data: Dict[str, Any], files: Optional[Dict[str, Any]] = None, ) -> VendorResult: """ Executes verification with latency tracking and universal exception handling. Delegates actual execution to `execute_verification`. """ start_time = time.time() doc_type_clean = document_type.lower() if not self.supports(doc_type_clean): latency = (time.time() - start_time) * 1000 return VendorResult( vendor_name=self.name, is_success=False, status=VerificationStatus.SKIPPED, error_code="DOC_NOT_SUPPORTED", error_message=f"Vendor '{self.name}' does not support document type '{document_type}'", latency_ms=latency, retryable=True, ) try: result = self.execute_verification(doc_type_clean, data, files) result.latency_ms = (time.time() - start_time) * 1000 result.vendor_name = self.name return result except Exception as exc: latency = (time.time() - start_time) * 1000 return VendorResult( vendor_name=self.name, is_success=False, status=VerificationStatus.ERROR, error_code=exc.__class__.__name__, error_message=str(exc), latency_ms=latency, retryable=True, ) def execute_verification( self, document_type: str, data: Dict[str, Any], files: Optional[Dict[str, Any]] = None, ) -> VendorResult: """Must be implemented by subclasses.""" raise NotImplementedError("Subclasses must implement execute_verification()")