Stage 6 — Bank Account Verification
6.1 Bank Verification Methods (RPD / PD-Hyperverge / PD-Perfios)
Verify customer's bank account via Reverse Penny Drop (RPD) or Penny Drop (PD). HV SDK presents both on same screen; Perfios is fallback when customer exits SDK.
BRD / PRD Requirement PRD
BRD Stage 6 — BANK_VERIFIED: HV SDK presents RPD + PD on the same screen. Customer exits SDK → Perfios fallback for Penny Drop. Rs.1 debit for RPD (refunded within 48 hours by Hyperverge).
- RPD: Customer initiates from HV SDK, Rs.1 debited from customer's account, refunded within 48 hours
- PD-Hyperverge: Penny Drop via Hyperverge SDK flow
- PD-Perfios: Fallback when customer exits HV SDK before completing verification
- All methods must validate account number + IFSC + account holder name
Old System OLD
- Method:
BankAPI.STDALONE_PENNYDROP() — orchestrated YesBank IMPS and Karza verification
- YesBank IMPS:
CallYESBANKIMPSSeparateHosting() — Rs.1 penny drop via YesBank IMPS API
- Karza Fallback:
KARZA_BANK_AC_VERIFICATION() — bank account verification via Karza API
- Vendor Selection:
GET_SETTINGVALUE_PENNYDROP_SJET — SP-based config to select which vendor to use
- File:
ExternalAPI/Bank/BankAPI.cs (1126 lines)
- Tables:
TBL_YESBANK_REQUEST_RESPONSE_LOG, TBL_YESBANK_REQUEST_RESPONSE_ERROR
New System NEW
- Service:
BankVerificationService.VerifyBankAccountAsync() — unified entry point for all verification methods
- Method parameter:
method=RPD|HYPERVERGE_PD|PERFIOS_PD — determines which provider to invoke
- Provider Interface:
IBankVerificationProvider — implemented by HypervergeProvider, PerfiosProvider
- Vendor Resolution:
ResolveProviderFromConfigAsync() — reads from provider_configurations table (not hardcoded SP)
- Entity:
BankVerificationAttempt tracks each attempt with method, provider, result
- File:
Services/Stage6/BankVerificationService.cs
Key Difference: Old system had a single monolithic 1126-line BankAPI.cs with inline vendor logic and YesBank/Karza calls. New system uses strategy pattern with IBankVerificationProvider interface. Vendor selection moved from SP config to provider_configurations table. RPD/PD distinction is explicit in the method parameter.
6.2 IFSC Lookup with Cache
Validate IFSC code format and return bank name, branch, MICR code. Cached in new system.
BRD / PRD Requirement PRD
BRD Stage 6 — IFSC Validation: IFSC validates format (11 alphanumeric, 5th char is 0), returns bank name / branch / MICR. Used for both RPD and PD flows.
- Format validation: 11 characters, first 4 alpha, 5th is '0', last 6 alphanumeric
- Returns: bank name, branch name, MICR code, city, state
- Invalid IFSC → error shown to customer, retry allowed
Old System OLD
- SP:
USP_GETBANKLIST_BY_IFSC_SJET — queried VW_RBIBANK_MASTER_ALL view + BANKMASTER table
- Bandhan Bank:
USP_CHECK_BANB_IFSC_SJET — separate SP specifically for Bandhan Bank IFSC validation
- Tables:
VW_RBIBANK_MASTER_ALL (RBI master view), BANKMASTER
- No caching — every IFSC lookup hit the database directly
New System NEW
- Method:
BankVerificationService.LookupIfscAsync()
- Cache: Redis cache key
ekyc3:ifsc:{ifsc} with 7-day TTL
- Primary Source:
bank_ifsc_master table
- Fallback: In-memory
BankCodeMap (20 major banks) for offline resolution
- File:
Services/Stage6/BankVerificationService.cs
Key Difference: Old system hit the database on every IFSC lookup with no caching. Had a special SP for Bandhan Bank. New system uses Redis cache (7-day TTL) + in-memory BankCodeMap fallback for 20 major banks. Single table bank_ifsc_master replaces RBI master view.
6.3 Bank Account Dedup (Post-eSign)
Detect if the same bank account is already linked to a lead that has passed eSign stage. Prevents duplicate account usage.
BRD / PRD Requirement PRD
BRD Stage 6 — Account Dedup: Bank account hash linked to a post-eSign lead must be blocked. Prevents same account from being used across multiple applications that have progressed past eSign.
- Account number hashed (SHA-256) for comparison
- Only blocks if existing lead has reached eSign stage or beyond
- Pre-eSign leads with same account are allowed (customer may be retrying)
Old System OLD
- Logic: Checked in bank details tables during PD flow
- Tables: Bank details stored across multiple tables; dedup logic embedded within PD stored procedures
- No explicit hash-based dedup — account comparison was done inline during penny drop processing
New System NEW
- Query: Joins
BankAccounts with Leads WHERE stage >= Esign by BankAccountHash
- Hash: SHA-256 of account number stored in
BankAccount.BankAccountHash
- Entity:
BankAccount entity with BankAccountHash field
- File:
Services/Stage6/BankVerificationService.cs
Key Difference: Old system had no formal hash-based dedup for bank accounts. New system uses SHA-256 hash on account number, joined against leads that have passed eSign stage. Explicit, queryable dedup rather than inline checks buried in SP logic.
6.4 Penny Drop Rate Limiting
Limit the number of penny drop attempts per bank account to prevent abuse. Max 10 attempts.
BRD / PRD Requirement PRD
BRD Stage 6 — Rate Limiting: Maximum 10 penny drop attempts per account (derived from old USP_ALLOW_PENNYDROP_SJET logic). Exceeding limit blocks further verification attempts for that account.
- Count includes both successful and failed attempts
- Limit is per account hash, not per lead
- Exceeding limit → permanent block for that account number
Old System OLD
- SP:
USP_ALLOW_PENNYDROP_SJET
- Logic: Counted rows in
TBL_YESBANK_REQUEST_RESPONSE_LOG + TBL_YESBANK_REQUEST_RESPONSE_ERROR
- Threshold: Allow if count < 10
- Tables:
TBL_YESBANK_REQUEST_RESPONSE_LOG, TBL_YESBANK_REQUEST_RESPONSE_ERROR
New System NEW
- Method:
VerifyBankAccountAsync() counts BankVerificationAttempts for same accountHash
- Threshold: If count >= 10 → returns error
BE_BANK_RATE_LIMIT
- Entity:
BankVerificationAttempt — each attempt is a record regardless of success/failure
- File:
Services/Stage6/BankVerificationService.cs
Key Difference: Old system counted across two separate log/error tables via SP. New system counts from a single BankVerificationAttempts entity table. Same threshold (10), cleaner implementation. Error code BE_BANK_RATE_LIMIT is explicit.
6.5 Bank Name Match Scoring
Compare bank account holder name against eKYC name. Score determines STP/NON_STP/RETRY/DROPPED outcome.
BRD / PRD Requirement PRD
BRD Stage 6 — Name Match: Score 70-100 = STP (straight-through processing), 1-69 = NON_STP (manual review), 0 on attempt 1 or 2 = RETRY (customer can try different account), 0 on attempt 3 = DROPPED. One "attempt" = one distinct bank account tried.
- Score range: 0-100
- 70-100: STP — auto-approved, proceeds to next stage
- 1-69: NON_STP — manual review required by operations
- 0 (attempt 1-2): RETRY — customer can enter a different bank account
- 0 (attempt 3): DROPPED — application dropped permanently
Old System OLD
- Entry:
BankAPI.STDALONE_PENNYDROP() called name matching after bank verification
- Name Match Methods:
NameMatchBySP() — SP-based name match
getNamematchResponse() — AMAZONAWS external API
getNamematchResponse_AINXT() — AiNXT name matching API
- SP:
USP_INSERT_UPDATE_STP_FLAG_SJET — stored the STP flag result
- Table:
TBL_NAMEMATCHING_STP_FLAG tracked STP flags per lead
- File:
ExternalAPI/Bank/BankAPI.cs
New System NEW
- Scoring:
NameMatchService.CalculateScore(bankHolderName, ekycName) — Levenshtein distance-based scoring (no external API call)
- Decision:
EvaluateNameMatchResult() — returns STP / NON_STP / RETRY / DROPPED based on score + attempt number
- Tracking:
BankVerificationAttempt entity tracks each attempt with score and result
- STP Flag: Stored on
BankAccount.StpBankFlag
- File:
Services/Stage6/BankVerificationService.cs, Services/Common/NameMatchService.cs
Key Difference: Old system used 3 different external name match services (SP, AMAZONAWS, AiNXT). New system uses local Levenshtein algorithm — no external API dependency. STP flag stored on BankAccount entity instead of separate TBL_NAMEMATCHING_STP_FLAG table. Decision logic in EvaluateNameMatchResult() is explicit with attempt-aware branching.
6.6 Bank Data Cleanup on Retry
When customer retries bank verification with a different account, cleanup previous bank data to avoid stale records.
BRD / PRD Requirement PRD
BRD Stage 6 — Cleanup: When customer retries with a different bank account, previous bank data must be cleaned up. Ensures only the latest verified bank account is associated with the lead.
- Triggered when customer submits a new bank account after a failed/retry attempt
- Previous bank records must not interfere with new verification
Old System OLD
- SP:
USP_DELETE_BANK_DATA_NAMEMISMATCH_SJET — deleted previous bank records on name mismatch retry
- Action: Hard delete of previous bank data rows from bank detail tables
New System NEW
- Method:
CleanupPreviousBankDataAsync()
- Action: Resets
StpBankFlag on previous BankAccount records (soft cleanup, not hard delete)
- File:
Services/Stage6/BankVerificationService.cs
Key Difference: Old system hard-deleted previous bank records via SP. New system performs soft cleanup by resetting the StpBankFlag on previous BankAccount records — preserving audit trail while preventing stale data from affecting the current verification.
6.7 RPD Refund Tracking
Track the Rs.1 refund status for Reverse Penny Drop transactions. Hyperverge refunds within 48 hours.
BRD / PRD Requirement PRD
BRD Stage 6 — RPD Refund: Rs.1 debited during RPD is refunded within 48 hours by Hyperverge. System should track refund status for reconciliation and customer support.
- Refund initiated automatically by Hyperverge after RPD completes
- Expected refund timeline: within 48 hours
- Status tracking for customer support queries
Old System OLD
- No explicit refund tracking in old code
- YesBank IMPS penny drop did not have a formal refund tracking mechanism
- Refund status was not stored or queryable
New System NEW
- Fields:
BankVerificationAttempt.RpdRefundStatus (PENDING / REFUNDED / FAILED) + RpdRefundAt timestamp
- Initial State: Set to
PENDING when RPD method is used
- Update: Status updated via callback or reconciliation job
- File:
Services/Stage6/BankVerificationService.cs
Key Difference: Old system had no refund tracking at all. New system explicitly tracks refund status (PENDING/REFUNDED/FAILED) with timestamp on the BankVerificationAttempt entity. Enables customer support to answer refund queries and reconciliation reporting.
6.8 Annual Income Collection
Collect customer's annual income range at Stage 6 as part of the bank verification flow.
BRD / PRD Requirement PRD
BRD Stage 6 — Income Collection: Annual income range collected at Stage 6 alongside bank verification. Required for regulatory compliance and risk assessment.
- Income range is a dropdown selection (predefined ranges)
- Collected at the bank verification screen
- Used downstream for account opening form and compliance
Old System OLD
- Collection Point: Income range collected in personal details stored procedure (different stage)
- Not co-located with bank verification flow
New System NEW
- Field:
BankAccount.AnnualIncomeRange
- Command: Passed via
SaveBankAccountCommand.AnnualIncomeRange
- File:
Services/Stage6/BankVerificationService.cs
Key Difference: Old system collected income at a different stage (personal details SP). New system co-locates income collection with bank verification at Stage 6, stored directly on the BankAccount entity. Simplifies the flow — customer provides bank + income on the same screen.
Stage 7 — Liveness Check & Photo Match
7.1 Geolocation Check (Within India)
Validate customer is physically within India. Outside India → permanent drop. Can reuse Stage 0 location if same session.
BRD / PRD Requirement PRD
BRD Stage 7 — Location Check: Customer must be within India. Outside → DROP_LOCATION_OUTSIDE_INDIA (permanent). If same session as Stage 0, reuse that location. PAN whitelist allows bypass for exceptions.
- Location must be within India's geographic boundaries
- Outside India → permanent drop (DROP_LOCATION_OUTSIDE_INDIA)
- Same session location from Stage 0 can be reused
- PAN-based whitelist allows specific customers to bypass location check
- City validated against master for CRM/analytics
Old System OLD
- SP:
USP_AINXT_LIVEPHOTO_RESPONSE_DETAILS_SJET — validated location as part of live photo response processing
- PAN Whitelist:
TBL_LIVE_PHOTO_LOCATION_EXCEPTION — PANs allowed to bypass location check
- City Validation:
TBL_PINCODE_MASTER + TBL_LIVEPHOTO_LOCATION_DETAILS_CITYLIST
- Tables:
TBL_LIVE_PHOTO_LOCATION_EXCEPTION, TBL_PINCODE_MASTER, TBL_LIVEPHOTO_LOCATION_DETAILS_CITYLIST
New System NEW
- Method:
LivenessService.ResolveGeolocationAsync()
- Bounding Box: Coordinate check — latitude 6–37, longitude 68–98 (India boundaries)
- Geocoding: Provider fallback for reverse geocoding
- City Validation: Validated against
city_master table
- Whitelist:
LocationExceptionWhitelist entity queried by PAN hash for bypass
- File:
Services/Stage7/LivenessService.cs
Key Difference: Old system validated location inside the live photo response SP with separate tables for pincode/city. New system uses a coordinate bounding box (lat 6-37, lng 68-98) as primary check with geocoding provider fallback. City validated against unified city_master instead of separate pincode/citylist tables. PAN whitelist uses hash-based lookup.
7.2 Liveness SDK (Hyperverge Primary, AiNXT Fallback)
Liveness detection via SDK. Hyperverge is primary vendor for first 2 attempts; AiNXT is fallback. Max 3 attempts total.
BRD / PRD Requirement PRD
BRD Stage 7 — Liveness: Max 3 attempts. Attempt 3 failure → CS Journey CS_LIVENESS_DOWN. Vendor selection: attempts < 2 = Hyperverge, else AiNXT fallback.
- Attempt 1-2: Hyperverge liveness SDK
- Attempt 3: AiNXT liveness SDK (fallback)
- All 3 fail → route to CS Journey (CS_LIVENESS_DOWN)
- Liveness pass required before face match can proceed
Old System OLD
- Vendor Selection:
USP_GET_FACE_MATCH_SERVICE_SJET — SP determined which vendor to use
- AiNXT:
LivePhotoAPI.AINXTRequestAPI()
- Hyperverge:
LivePhotoAPI.HypervergeRequestAPI()
- Logging:
USP_EKYC_HV_LIVEPHOTO_LOG_SJET logged each liveness entry
- File:
ExternalAPI/LivePhoto/LivePhotoAPI.cs
New System NEW
- Initiation:
LivenessService.InitiateLivenessAsync()
- Vendor Logic:
DetermineLivenessVendor() — if attempts < 2 → Hyperverge, else → AiNXT
- Callback:
ProcessLivenessCallbackAsync() handles pass/fail from SDK callback
- CS Journey: After 3 failures → routes to CS Journey (CS_LIVENESS_DOWN)
- Entity:
LivenessVerification tracks each attempt
- File:
Services/Stage7/LivenessService.cs
Key Difference: Old system selected vendor via SP (USP_GET_FACE_MATCH_SERVICE_SJET) and had separate API methods for each vendor. New system uses DetermineLivenessVendor() with explicit attempt-based logic (HV if <2, AiNXT otherwise). Callback-based architecture replaces synchronous request/response. CS Journey routing is built-in after 3 failures.
7.3 Face Match (Selfie vs Aadhaar Photo)
Compare selfie from liveness check against Aadhaar photo. Score determines STP/NON_STP/RETRY/DROPPED outcome.
BRD / PRD Requirement PRD
BRD Stage 7 — Face Match: Score 70-100 = STP, 1-69 = NON_STP, 0 on attempt 1 = RETRY (full liveness repeats), 0 on attempt 2 = DROPPED. No Aadhaar photo available → NON_STP automatically.
- Score range: 0-100
- 70-100: STP — auto-approved
- 1-69: NON_STP — manual review
- 0 (attempt 1): RETRY — full liveness + selfie repeats
- 0 (attempt 2): DROPPED
- No Aadhaar photo on file → NON_STP (cannot perform face match)
Old System OLD
- Face Match:
LivePhotoAPI.AINXTRequestGetImageAPI() — retrieved face match result from AiNXT
- Score: Extracted from
sub_actions_face_match_status in response
- Storage:
USP_SAVE_LIVEPHOTO_RESPONSE_SJET stored SmartLink response
- File:
ExternalAPI/LivePhoto/LivePhotoAPI.cs
New System NEW
- Method:
PerformFaceMatchAsync()
- Provider: Calls
IFaceMatchProvider.MatchFacesAsync(selfieBase64, aadhaarPhotoBase64)
- No Photo Fallback: If no Aadhaar photo available →
StpFaceFlag = NON_STP automatically
- Name Fallback: Name-based fallback if face match provider fails
- Entity:
LivenessVerification.StpFaceFlag stores result
- File:
Services/Stage7/LivenessService.cs
Key Difference: Old system relied solely on AiNXT for face match. New system uses IFaceMatchProvider interface (pluggable vendors) with explicit handling for missing Aadhaar photo (auto NON_STP) and name-based fallback if provider fails. Attempt-aware decision logic (attempt 1 = RETRY, attempt 2 = DROP) is built into PerformFaceMatchAsync().
7.4 Aadhaar Photo Deletion (UIDAI Compliance)
Delete Aadhaar photo from storage immediately after face match completes. Required for UIDAI regulatory compliance.
BRD / PRD Requirement PRD
BRD Stage 7 — Aadhaar Photo Deletion: Aadhaar photo must be deleted from storage immediately after face match is completed. Deletion timestamp tracked for audit compliance.
- Photo deleted immediately after face match (pass or fail)
- Deletion timestamp recorded for UIDAI audit trail
- No Aadhaar photo should remain in any storage after face match
Old System OLD
- SP:
USP_UPDATE_FACE_PROOF_SJET — handled file path updates
- Storage: S3/file lifecycle management was unclear in old codebase
- No explicit deletion timestamp tracked for compliance
New System NEW
- Method:
DeleteAadhaarPhotoFromS3() — called immediately after face match completes
- Timestamps: Sets
AadhaarVerification.AadhaarPhotoDeletedAt + LivenessVerification.AadhaarPhotoDeletedAt
- Dual tracking: Deletion recorded on both Aadhaar and Liveness entities for audit completeness
- File:
Services/Stage7/LivenessService.cs
Key Difference: Old system had unclear S3/file lifecycle for Aadhaar photos. New system has explicit DeleteAadhaarPhotoFromS3() with dual timestamp tracking on both AadhaarVerification and LivenessVerification entities. Ensures UIDAI compliance with verifiable deletion audit trail.
7.5 Selfie Storage for AOF
Store the liveness selfie securely for inclusion in the Account Opening Form (AOF).
BRD / PRD Requirement PRD
BRD Stage 7 — Selfie Storage: Selfie captured during liveness check must be stored securely for inclusion in the Account Opening Form (AOF). Used as photo proof and IPV (In-Person Verification) evidence.
- Selfie stored in S3 with secure access controls
- Referenced in AOF document generation
- Used as IPV evidence for regulatory compliance
Old System OLD
- SP:
USP_UPDATE_FACE_PROOF_SJET — stored file references
- Tables:
TBL_CLIENT_PROOFUPLOAD + TBL_CLIENT_PROOF_PATHINFO with virtual/physical paths
- IPV:
TBL_IPV_DETAILS for In-Person Verification tracking
- Storage: File paths referenced virtual + physical locations
New System NEW
- Method:
StoreSelfieToS3()
- S3 Path:
selfies/{leadId}/{timestamp}_{guid}.jpg
- Entity:
LivenessVerification.SelfieS3Key stores the S3 object key
- Document Tracking:
DocumentPathInfo entity tracks the file metadata
- File:
Services/Stage7/LivenessService.cs
Key Difference: Old system used virtual/physical file paths across TBL_CLIENT_PROOFUPLOAD and TBL_CLIENT_PROOF_PATHINFO with separate IPV table. New system uses S3 with structured paths (selfies/{leadId}/{timestamp}_{guid}.jpg). Single SelfieS3Key field on LivenessVerification entity replaces multiple proof tables.
7.6 Location Exception Whitelist
Allow specific PANs to bypass the geolocation check. Used for known exceptions approved by operations.
BRD / PRD Requirement PRD
BRD Stage 7 — Location Whitelist: Specific PANs are allowed to bypass the location check. Managed by operations team for known exceptions (e.g., NRI customers with India PAN, border area customers).
- PAN-based whitelist maintained by operations
- Whitelisted PANs bypass the "within India" check
- Whitelist checked before location validation
Old System OLD
- Table:
TBL_LIVE_PHOTO_LOCATION_EXCEPTION
- Logic: Checked PAN against exception table before blocking on location failure
- PAN stored in plaintext in the exception table
New System NEW
- Entity:
LocationExceptionWhitelist
- Lookup: Queried by PAN hash in
InitiateLivenessAsync()
- Effect: If whitelisted → overrides
IsWithinIndia to true
- File:
Services/Stage7/LivenessService.cs
Key Difference: Old system stored PAN in plaintext in TBL_LIVE_PHOTO_LOCATION_EXCEPTION. New system uses PAN hash for lookup in LocationExceptionWhitelist entity — PAN is never stored in plaintext. Whitelist check integrated into InitiateLivenessAsync() flow.
7.7 Assisted Journey Handoff Link
FRANCHISE/BRANCH only: RM shares a link via WhatsApp/Copy so customer opens directly on the selfie screen. No OTP/login required.
BRD / PRD Requirement PRD
BRD Stage 7 — Handoff Link: For FRANCHISE/BRANCH journeys only. RM shares a link via WhatsApp or copy-to-clipboard. Customer opens link and lands directly on the selfie screen without OTP or login. WhatsApp templates available for Stage 7 and Stage 8.
- Available for FRANCHISE and BRANCH journey types only
- Link contains URL-safe token for identification
- Customer bypasses OTP/login when opening handoff link
- WhatsApp templates for sharing at Stage 7 and Stage 8
Old System OLD
- No handoff link mechanism in old system
- Assisted journeys required customer to be physically present with RM throughout
- No WhatsApp-based link sharing capability
New System NEW
- Service:
HandoffLinkService.GenerateLinkAsync()
- Entity:
HandoffLink with URL-safe token
- WhatsApp: Templates for Stage 7 + Stage 8 handoff
- Controller:
HandoffController with generate / validate / complete endpoints
- File:
Services/Common/HandoffLinkService.cs, Controllers/HandoffController.cs
Key Difference: Entirely new feature in the new system. Old system had no remote handoff — assisted journeys required physical co-presence. New system enables RM to share a URL-safe token link via WhatsApp, letting the customer complete liveness/selfie independently. HandoffController provides generate/validate/complete API endpoints.
7.8 Downstream Events (Liveness Done)
Publish completion events to CleverTap, Zoho CRM, CDP, and Datalake after liveness and face match complete.
BRD / PRD Requirement PRD
BRD Stage 7 — Downstream Events: On liveness completion, publish events to 4 downstream systems: CleverTap, Zoho CRM, CDP, Datalake. Events carry liveness result, face match score, STP flag, city, selfie status, and Aadhaar photo deletion status.
- CleverTap: Liveness passed/failed event for marketing automation
- Zoho CRM: Update lead with liveness status and face match result
- CDP: Customer data platform enrichment
- Datalake: Raw event for analytics and reporting
Old System OLD
- Updates: Inline updates to various tables during live photo processing
- Table:
TBL_CLIENT_LIVEPHOTO_RESPONSE_DETAILS stored raw response
- No async event publishing — downstream updates were synchronous and tightly coupled
New System NEW
- Events: 4
DownstreamEvent records created (one per target system)
- Payload: LivenessPassed, FaceMatchScore, StpFaceFlag, City, Country, SelfieStored, AadhaarPhotoDeleted
- Targets: CLEVERTAP, ZOHO_CRM, CDP, DATALAKE
- Processing: Async — events queued in DB, processed by background worker
- File:
Services/Stage7/LivenessService.cs
Key Difference: Old system made inline synchronous updates to TBL_CLIENT_LIVEPHOTO_RESPONSE_DETAILS with no formal event publishing. New system creates 4 async DownstreamEvent records with structured payload including all liveness outcomes. Non-blocking — downstream failures don't affect the liveness flow.