Stage 4 — PAN Confirmation
4.1 KRA RESTRICTED Entry Check
Before showing the PAN input screen, check the KRA status to determine if the customer can proceed, must enter PAN manually, or is permanently blocked.
BRD / PRD Requirement PRD
BRD Stage 4 — KRA Entry Gate: Before showing PAN, check kra_status_pan_stage. Three outcomes:
- RESTRICTED → immediate permanent block
DROP_KRA_RESTRICTED. Customer cannot proceed.
- INVALID_PAN (999) → discard prefilled PAN, present blank entry field. Customer types PAN manually.
- All other statuses → normal flow with prefilled PAN from Stage 2 KRA lookup.
Old System OLD
- SP:
USP_CHECKPANWITHOCR_SJET — detected staff PAN, validated PAN proof upload, checked DigiLocker PAN flag
- SP:
USP_GET_PAN_RELATED_FLAG_SJET — retrieved KRA flags for the lead
- SP:
USP_UPDATEKRACUSTFLAG_SJET — updated KRA customer flag after check
- File:
ExternalAPI/Pan/PanAPI.cs
- Notes: KRA restricted check was embedded within larger PAN validation flow, not a separate gate
New System NEW
- Method:
PanVerificationService.GetPanDetailsAsync()
- Logic: Checks
kra.KraStatusPanStage:
- If RESTRICTED → returns
EntryBlockReason = DropCodes.KraRestricted
- If INVALID_PAN → returns
HasPrefetchedPan = false (blank entry)
- Otherwise → returns prefilled PAN from KRA
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system had KRA check buried inside a multi-purpose SP (USP_CHECKPANWITHOCR_SJET). New system has an explicit entry gate in GetPanDetailsAsync() that checks KRA status as the first operation, cleanly separating RESTRICTED block, INVALID_PAN blank entry, and normal prefill.
4.2 PAN Format Validation
Validate the PAN number format: 10 characters, correct character types, and 4th character must be 'P' for individual.
BRD / PRD Requirement PRD
BRD Stage 4 — PAN Format:
- 10 characters: 5 alpha + 4 numeric + 1 alpha (regex:
[A-Z]{5}[0-9]{4}[A-Z])
- 4th character must be
P (individual PAN type)
- Auto-uppercase on input
Old System OLD
- Validation: PAN regex in
Validation.cs
- SP:
USP_CHECKPANWITHOCR_SJET — validated PAN against OCR-extracted PAN from document upload
- File:
Joruney_imp_code\Common\Validation.cs
New System NEW
- Methods:
IsValidPanFormat() + IsIndividualPan() extension methods
- Called in:
ConfirmPanAsync() as first validation step
- Error codes:
FE_PAN_001 (invalid format), FE_PAN_002 (not individual PAN)
- File:
Services/Stage4/PanVerificationService.cs, extension methods in Extensions/
Key Difference: Old system validated PAN format in a generic Validation.cs utility and re-validated against OCR in the SP. New system uses dedicated extension methods with specific error codes (FE_PAN_001, FE_PAN_002) and no OCR dependency at this stage.
4.3 Employee PAN Check (Staff Redirect)
If the PAN belongs to a MOSL employee, redirect to the staff account opening flow. Not a block — a different journey path.
BRD / PRD Requirement PRD
BRD Stage 4 — Staff PAN: If PAN belongs to MOSL employee → redirect to staff flow. This is not a permanent block; the customer is routed to the employee account opening journey instead.
Old System OLD
- SP:
CHECK_STAFF_PAN — checked against MOSL_SDLC_EMPLOYEEDETAILS
- Special case: PAN
"CHCPR2354C" explicitly excluded from staff check
- Status logic: Validated active / resigned / retention status of the employee
- File:
ExternalAPI/Pan/PanAPI.cs
New System NEW
- Method:
PanVerificationService.ConfirmPanAsync()
- Query: Queries
EmployeePanMaster table by panHash
- Status logic: Checks active / resigned / retention status
- Error code:
STAFF_PAN — triggers redirect to employee flow
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system used a separate SP (CHECK_STAFF_PAN) querying MOSL_SDLC_EMPLOYEEDETAILS with a hardcoded PAN exclusion. New system queries a dedicated EmployeePanMaster table by hashed PAN, with no hardcoded exclusions. Same active/resigned/retention logic.
4.4 Franchise PAN Whitelist
If the PAN belongs to a franchise partner, allow the journey to continue but tag for ops visibility.
BRD / PRD Requirement PRD
BRD Stage 4 — Franchise PAN: If PAN is found in the franchise whitelist → allow the customer to proceed but tag for operations visibility. This is NOT a block.
Old System OLD
- Not explicitly separated. Franchise PAN logic was embedded within the registration SP.
- No dedicated franchise whitelist table in old code — handled as part of overall PAN verification flow
New System NEW
- Query: Queries
FranchisePanWhitelist table by panHash
- If match: Logs the franchise PAN hit and tags the lead for ops visibility
- Does NOT block — journey continues normally
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system had no explicit franchise PAN whitelist — the logic was buried in registration SPs. New system has a dedicated FranchisePanWhitelist table and explicit tagging for ops visibility without blocking the journey.
4.5 Employee/Franchise Contact Restriction
If the lead's mobile or email belongs to an employee or franchise, but the PAN is NOT a staff PAN, permanently block the lead.
BRD / PRD Requirement PRD
BRD Stage 4 — Contact Cross-Check: If lead's mobile/email belongs to an employee or franchise AND the PAN is NOT a staff PAN → permanent block DROP_EMPLOYEE_CONTACT_USED. This prevents non-employees from using employee contact details to open accounts.
Old System OLD
- Not a separate check. Was embedded within the PAN verification flow, not explicitly separated.
- Cross-reference logic between employee contacts and PAN was implicit in old SPs
New System NEW
- Sequence: Runs after employee PAN check (4.3)
- Query: Cross-references
EmployeePanMaster + FranchisePanWhitelist against client_master.Mobile
- Logic: If mobile is employee/franchise contact BUT PAN is a client PAN (not staff) → permanent block
- Drop code:
DROP_EMPLOYEE_CONTACT_USED
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system had no explicit separation of this cross-check. New system introduces a dedicated step that cross-references employee/franchise contact details against the submitted PAN, catching cases where someone uses an employee's phone number with a different PAN.
4.6 PAN Duplicate Check (Post-eSign + Client Master)
Check if the PAN is already linked to a completed lead (post-eSign) or exists in the client master. Block duplicates.
BRD / PRD Requirement PRD
BRD Stage 4 — PAN Duplicate: PAN linked to a completed post-eSign lead → block with error BE_PAN_003. Also checks client master for existing accounts with the same PAN.
Old System OLD
- SP:
USP_CHECK_PAN_AADHAAR_EXISTS_EXP_SJET — checked PAN across multiple tables
- SP:
USP_CHECK_PAN_USED_SJET — checked if PAN already used in another lead
- Tables: Multiple tables checked for PAN existence
New System NEW
- Query: Joins
PanVerifications with Leads WHERE stage >= 13 (post-eSign)
- Also checks:
ClientMaster table by panHash
- Error code:
BE_PAN_003
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system used two separate SPs (USP_CHECK_PAN_AADHAAR_EXISTS_EXP_SJET and USP_CHECK_PAN_USED_SJET) querying across multiple tables. New system uses a single LINQ join on PanVerifications + Leads with a clear stage threshold (>=13 for post-eSign), plus a ClientMaster hash lookup.
4.7 Hyperverge Name+DOB Fetch (if not prefetched)
If PAN was not prefetched at Stage 2 (KRA lookup), call Hyperverge now to retrieve name and DOB associated with the PAN.
BRD / PRD Requirement PRD
BRD Stage 4 — Hyperverge Fetch: If PAN was not prefetched at Stage 2, call Hyperverge now for name + DOB. This data is required for NSDL validation and name matching.
Old System OLD
- Method:
PanAPI.GetPanDetailsHyperverge()
- API: POST to
HYPERVERGE_PAN_APIURL_STD
- File:
ExternalAPI/Pan/PanAPI.cs
New System NEW
- Method:
PanVerificationService.FetchPanDetailsFromHypervergeAsync()
- Provider: Calls
HypervergePanProvider abstraction
- Fallback: If Hyperverge fails → falls back to
lead.RegistrationName
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system called Hyperverge directly via PanAPI.GetPanDetailsHyperverge(). New system uses an abstracted HypervergePanProvider and has a fallback to lead.RegistrationName if the API fails.
4.8 NSDL PAN Validation (Stage 4 Re-validation)
Validate PAN + Name + DOB against NSDL. UTI fallback if NSDL is down. Both fail triggers CS Journey.
BRD / PRD Requirement PRD
BRD Stage 4 — NSDL Validation: NSDL validates PAN + Name + DOB. If NSDL is down, fall back to UTI. If both NSDL and UTI fail → CS Journey CS_NSDL_DOWN.
Old System OLD
- Method:
PanAPI.VerifyPAN_NSDL() with dual NSDL/UTI fallback
- Logging SP:
USP_PANVALIDATION_REQUEST_RESPONSE_LOG_SJET
- Response check SP:
USP_CHECK_NSDLRESP
- File:
ExternalAPI/Pan/PanAPI.cs
New System NEW
- Method:
PanVerificationService.ValidatePanWithNsdlUtiAsync()
- Flow: NSDL primary → UTI fallback → CS Journey (
CS_NSDL_DOWN) if both fail
- Storage: Result stored in
PanVerification entity
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system used VerifyPAN_NSDL() with two separate SPs for logging and response checking. New system consolidates into ValidatePanWithNsdlUtiAsync() with NSDL → UTI → CS Journey cascade, storing the result directly on the PanVerification entity.
4.9 Age Validation (<18 Minor, >100 Branch Visit)
Validate the customer's age from PAN DOB. Minors are stopped; customers over 100 are redirected to branch.
BRD / PRD Requirement PRD
BRD Stage 4 — Age Check:
- Age <18 → minor, stopped (Day Zero: coming soon via minor/guardian flow)
- Age >100 → branch visit required
Old System OLD
- Logic: Age calculated from PAN DOB
- Minor check: Embedded in personal details SP
- No explicit >100 branch redirect in old code
New System NEW
- Method:
ConfirmPanAsync() calculates age from panDob
- Age <18: Sets
JourneyPath = "MINOR_GUARDIAN_REQUIRED"
- Age >100: DROPPED with
DROP_AGE_OVER_100
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system checked age for minors only, buried in personal details SP. New system has explicit age boundaries: <18 sets a minor/guardian journey path, >100 drops to branch visit. Both checks happen in ConfirmPanAsync() at Stage 4.
4.10 KRA Name Match → ekyc_name Determination
Compare KRA name vs PAN name. The winner becomes the locked ekyc_name used for all subsequent name matching.
BRD / PRD Requirement PRD
BRD Stage 4 — ekyc_name: KRA name vs PAN name comparison:
- Score >=70 → KRA name becomes
ekyc_name
- Score <70 → PAN name becomes
ekyc_name
ekyc_name is locked and used for all subsequent name matching (Aadhaar, bank, etc.)
Old System OLD
- SP:
USP_INSERT_UPDATE_STP_FLAG_SJET with PANSITE_AADHAAR value
- Name matching APIs: Multiple providers — AMAZONAWS, AINXT, SP-based matching
- No single "ekyc_name" concept — name matching was distributed across stages
New System NEW
- Method:
NameMatchService.CalculateScore(kra.KraPrefillName, panName)
- Algorithm: Levenshtein distance-based scoring
- Score >=70:
EkycName = KRA name, EkycNameSource = KRA_NAME
- Score <70:
EkycName = PAN name, EkycNameSource = PAN_NAME
- Storage: Stored on
Lead.EkycName + Lead.EkycNameSource
- File:
Services/Stage4/PanVerificationService.cs, Services/Common/NameMatchService.cs
Key Difference: Old system used multiple name matching providers (AMAZONAWS, AINXT, SP-based) with no single "ekyc_name" concept. New system uses a single NameMatchService with Levenshtein scoring, and the result is stored as Lead.EkycName with a source tag — this name is locked and used for all subsequent matching (Aadhaar, bank, etc.).
4.11 Journey Path Determination (DigiLocker Skip/Required)
Based on KRA status and address usability, determine whether DigiLocker (Stage 5) can be skipped or is required.
BRD / PRD Requirement PRD
BRD Stage 4 — Journey Path:
- KRA_VALIDATED + usable address →
DIGILOCKER_SKIP
- KRA_MOD + usable address →
DIGILOCKER_SKIP
- NON_KRA / API_DOWN →
DIGILOCKER_REQUIRED always
"Usable address" = KRA address JSON contains city + state + pincode.
Old System OLD
- Logic: Journey path determined by KRA status and address validity
- DigiLocker decision: Made within the PAN stage SP
- No explicit "usable address" check — address validation was implicit
New System NEW
- Method:
PanVerificationService.DetermineJourneyPath(kra)
- Address check:
IsKraAddressUsable() — parses KRA address JSON for city + state + pincode
- Storage: Stored on
Lead.JourneyPath
- Values:
DIGILOCKER_SKIP or DIGILOCKER_REQUIRED
- File:
Services/Stage4/PanVerificationService.cs
Key Difference: Old system made the DigiLocker skip/required decision implicitly within the PAN SP. New system has an explicit DetermineJourneyPath() method with IsKraAddressUsable() that programmatically checks for city + state + pincode in the KRA address JSON. The result is stored on Lead.JourneyPath for downstream stages to read.
Stage 5 — DigiLocker / Aadhaar Verification
5.1 DigiLocker Consent & Redirect
Show consent screen, generate redirect URL via AiNXT, and redirect customer to UIDAI portal for Aadhaar verification.
BRD / PRD Requirement PRD
BRD Stage 5 — DigiLocker Initiation:
- Consent screen is mandatory — customer must explicitly tap to consent
- AiNXT generates redirect URL + session token
- Customer is redirected to UIDAI portal
- Maximum 3 attempts allowed
Old System OLD
- SP:
USP_INSERT_AINXT_DIGILOCKER_REQUEST_SJET — logged the DigiLocker request
- API:
LivePhotoAPI.AINXT_Digilocker_GetLinkGeneratedDetails() — generated DigiLocker link
- Controller:
DigilockerAPI handled the OAuth flow
- File:
ExternalAPI/LivePhoto/LivePhotoAPI.cs
New System NEW
- Method:
AadhaarVerificationService.InitiateDigilockerAsync()
- Consent: Creates
Consent record (type = DIGILOCKER, versioned)
- Provider: Calls
AinxtDigilockerProvider.GenerateLinkAsync()
- Fallback: Direct DigiLocker OAuth URL if AiNXT fails
- Max attempts: 3
- File:
Services/Stage5/AadhaarVerificationService.cs
Key Difference: Old system logged DigiLocker requests in a separate SP and generated links via LivePhotoAPI. New system creates a versioned Consent record, uses an abstracted AinxtDigilockerProvider, has a fallback to direct DigiLocker OAuth URL, and enforces a max of 3 attempts.
5.2 Aadhaar XML Extraction (DigiLocker Callback)
Process the AiNXT callback containing signed Aadhaar XML. Extract name, DOB, address, gender, photo, father name. Mask Aadhaar number.
BRD / PRD Requirement PRD
BRD Stage 5 — XML Extraction: AiNXT callback receives signed XML. Extract:
- Name, DOB, address, gender, photo, father name
- Mask Aadhaar number: first 4 digits + last 4 digits, middle masked
Old System OLD
- SP:
USP_INSERT_AINXT_DIGILOCKER_RESPONSE_SJET — stored the DigiLocker response
- SP:
USP_CHECKPANNO_DIGILOCKER_SJET — compared PAN from DigiLocker
- Method:
SAVE_DIGILOCKER_DOCUMENTS_RESPONSE_DETAILS_AINXT() — parsed the XML
- File:
ExternalAPI/LivePhoto/LivePhotoAPI.cs
New System NEW
- Method:
AadhaarVerificationService.ProcessDigilockerCallbackAsync()
- Extraction methods:
ExtractNameFromDigilockerResponse()
ExtractGenderFromDigilockerResponse()
ExtractFatherNameFromDigilockerResponse()
ExtractFieldFromResponse() — generic field extractor
- Masking:
GenerateMaskedAadhaar() creates masked Aadhaar number
- Storage: Creates
DocumentPathInfo records for file tracking
- File:
Services/Stage5/AadhaarVerificationService.cs
Key Difference: Old system used two SPs for storing and comparing DigiLocker data, with XML parsing in LivePhotoAPI. New system has dedicated extraction methods per field, generates masked Aadhaar via GenerateMaskedAadhaar(), and creates DocumentPathInfo records for file lifecycle tracking.
5.3 Address Language Conversion
Convert regional language address text (Hindi, Telugu, Tamil, etc.) to English via translation API.
BRD / PRD Requirement PRD
BRD Stage 5 — Address Translation: Regional language address text → English via AiNXT translation API. Required for address matching and storage.
Old System OLD
- API: Google Services via AiNXT for Hindi/Telugu/Tamil conversion
- Dual provider: Used both Google Translate and AiNXT
New System NEW
- Method:
AadhaarVerificationService calls _digilockerProvider.TranslateAddressAsync()
- Provider: AiNXT only — no Google (per Product team decision)
- Detection:
ContainsNonLatinText() detects regional script characters
- Fallback: If translation fails → raw text used +
NON_STP flag set
- File:
Services/Stage5/AadhaarVerificationService.cs
Key Difference: Old system used both Google Translate and AiNXT for address translation. New system uses AiNXT only (Google removed per Product team). ContainsNonLatinText() detects regional scripts, and if translation fails, the raw address is kept with a NON_STP flag for manual review.
5.4 Aadhaar Name Match
Compare the Aadhaar name against the locked ekyc_name. Score determines STP, NON_STP, or permanent drop.
BRD / PRD Requirement PRD
BRD Stage 5 — Aadhaar Name Match: Aadhaar name vs ekyc_name:
- Score 70–100 = STP (straight-through processing)
- Score 1–69 = NON_STP (manual review required)
- Score 0 = DROP_DL_NAME_FAIL (permanent drop)
Old System OLD
- APIs: Name matching via AMAZONAWS / AINXT APIs
- SP:
USP_INSERT_UPDATE_STP_FLAG_SJET — stored PANSITE_AADHAAR result
- Multiple providers: Fallback across AMAZONAWS, AINXT, and SP-based matching
New System NEW
- Method:
NameMatchService.CalculateScore(extractedName, lead.EkycName)
- Storage:
AadhaarNameMatchScore and StpAadhaarFlag on AadhaarVerification entity
- Score mapping:
| Score Range | Result | Action |
| 70–100 | STP | Straight-through processing |
| 1–69 | NON_STP | Manual review required |
| 0 | DROP_DL_NAME_FAIL | Permanent drop |
- File:
Services/Stage5/AadhaarVerificationService.cs, Services/Common/NameMatchService.cs
Key Difference: Old system used multiple name matching providers (AMAZONAWS, AINXT, SP-based) and stored results via USP_INSERT_UPDATE_STP_FLAG_SJET. New system uses a single NameMatchService.CalculateScore() and stores the score + STP flag directly on the AadhaarVerification entity. Same scoring thresholds (70/1/0) but single provider.
5.5 Aadhaar Upload Fallback
When DigiLocker is unreachable, OTP fails 3 times, or customer chooses manual upload. OCR extracts data from uploaded Aadhaar document.
BRD / PRD Requirement PRD
BRD Stage 5 — Aadhaar Upload: Fallback path when DigiLocker is unavailable:
- Triggers: DigiLocker unreachable, 3 failed OTP attempts, or customer chooses upload
- Accepted formats: JPG, PNG, PDF
- Max file size: 5 MB
- AiNXT OCR extracts Aadhaar data from uploaded document
Old System OLD
- OCR: Amazon Textract OCR via
USP_INSERT_OCR_AMAZON_AADHAR_RESPONSE
- Upload path: Manual Aadhaar proof upload path
- File:
ExternalAPI/LivePhoto/LivePhotoAPI.cs
New System NEW
- Method:
AadhaarVerificationService.ProcessAadhaarUploadAsync()
- Validation: File type (JPG/PNG/PDF) + file size (5 MB max)
- OCR:
ExtractNameFromOcrAsync() calls AinxtDigilockerProvider.OcrExtractAadhaarAsync()
- Fallback: If OCR fails → falls back to
lead.RegistrationName
- Name matching: Same scoring logic as DigiLocker path (5.4)
- File:
Services/Stage5/AadhaarVerificationService.cs
Key Difference: Old system used Amazon Textract for OCR. New system uses AiNXT OCR (OcrExtractAadhaarAsync()), validates file type/size upfront, and shares the same NameMatchService scoring as the DigiLocker path. Falls back to registration name if OCR fails.
5.6 UIDAI Compliance (XML Deletion, Photo Deletion, Masking)
Regulatory compliance: delete Aadhaar XML within 24 hours, delete photo after face match, never store full Aadhaar number, require explicit consent.
BRD / PRD Requirement PRD
BRD Stage 5 — UIDAI Compliance:
- XML deleted within 24 hours (regulatory requirement)
- Photo deleted after Stage 7 face match
- Aadhaar number never stored in full — only masked format
- Explicit consent required before Aadhaar access
Old System OLD
- Deletion: File-based deletion (manual or scheduled)
- SP:
USP_UPDATE_AADHAR_XML_STATUS_SJET — tracked XML deletion status
- Masking: Masking logic in old code was unclear / inconsistent
New System NEW
- XML deletion schedule:
AadhaarXmlDeletionScheduledAt = UtcNow.AddHours(24) set on AadhaarVerification entity
- File tracking:
DocumentPathInfo records created for each file (XML, photo)
- Masking:
GenerateMaskedAadhaar() creates first4 + **** + last4 format
- Deletion job: Scheduled background job queries
document_path_info table for files past deletion deadline
- File:
Services/Stage5/AadhaarVerificationService.cs
Key Difference: Old system tracked XML status via a separate SP with unclear masking. New system sets an explicit 24-hour deletion schedule on the entity, tracks all files via DocumentPathInfo, uses GenerateMaskedAadhaar() for consistent masking (first4+****+last4), and a background job handles automated deletion.
5.7 Downstream Events (DigiLocker Done)
After DigiLocker/Aadhaar verification completes, publish events to CleverTap, Zoho CRM, CDP, and Datalake.
BRD / PRD Requirement PRD
BRD Stage 5 — Downstream Events: Publish to CleverTap, Zoho CRM, CDP, Datalake on DigiLocker completion.
Old System OLD
- Stage update: Inline during processing (synchronous)
- CRM: Zoho / LSQ updated synchronously during DigiLocker processing
- No event-based architecture — direct API calls to each downstream system
New System NEW
- Events: 4
DownstreamEvent records created with EventType = DIGILOCKER_DONE
- Targets: CleverTap, Zoho CRM, CDP, Datalake
- Payload includes:
DigilockerMethod (DIGILOCKER or UPLOAD)
AadhaarName
NameMatchScore
StpAadhaarFlag
City, State, Pincode
XmlDeletionScheduledAt
- Async: Events queued in DB, processed by background worker
- File:
Services/Stage5/AadhaarVerificationService.cs
Key Difference: Old system called Zoho/LSQ synchronously inline during processing. New system publishes 4 async DownstreamEvent records (DIGILOCKER_DONE) with rich payload including method, name match score, STP flag, address fields, and XML deletion schedule. Events are non-blocking — downstream failure does not block the journey.