Old System vs New System — Stage 0 (Arrival) & Stage 1 (Registration)

For each check/function: what it does, how old code did it, how new code does it • Collapsible sections

Stage 0 — Customer Arrives on the Platform

0.1 Session Initialization
Create a session when customer lands on the platform. Capture UTM params, device type, referral code.
BRD / PRD Requirement PRD

BRD Stage 0 — SESSION_INIT: Before the customer types anything, the platform silently captures their entry point, creates a session, runs fraud pre-checks, and assigns a journey variant. No customer input required.

  • Capture UTM parameters, referral code, channel code, device type, OS, browser, network type
  • Session token created in Redis (TTL 15 min, resets on interaction)
  • LaunchDarkly assigns journey_variant_id
  • IP velocity check: same IP + >3 leads in 1 hour → SUSPECTED_FRAUD_IP flag (don't block)
  • Location permission mandatory before Stage 1
Old System OLD

No explicit Stage 0 in old code. Session was implicit in .NET server context.

  • Controller: RegistrationController.SignUp() received UTM params as part of the registration payload — no separate session creation step.
  • SP: USP_CLIENT_REGISTRATION_SJET — UTM params (utm_source, utm_medium, utm_campaign, GCLID, etc.) passed as parameters and stored directly in TBL_DEDUPE_DATA_DUMP.
  • Tables: TBL_DEDUPE_DATA_DUMP (UTM columns), TBL_OAO_DETAILS
  • Files: Joruney_imp_code\Registration\RegistrationController.cs, RegistrationRepository.cs
  • No Redis session. No LaunchDarkly. No device fingerprinting.
New System NEW

Dedicated Stage 0 with explicit session creation before registration.

  • Controller: ArrivalController.Init()GET /api/v1/journey/init
  • Service: ArrivalService.InitJourneyAsync()
  • Entity: LeadSession — stores UTM params, device info, location, IP, referral code, firebase token
  • Session: Stored in cache with 15-min TTL, refreshed on interaction
  • Tables: lead_sessions
  • Files: Services/Stage0/ArrivalService.cs, Controllers/Stage0/ArrivalController.cs
Key Difference: Old system had no separate session step — UTM/device data was bundled into registration. New system creates a session BEFORE registration, enabling analytics tracking even for users who don't proceed to register.
0.2 Location Capture & South Tag
Capture customer's geolocation. Tag as SOUTH (Karnataka, TN, Kerala, AP, Telangana) or OTHERS for Zoho CRM routing.
BRD / PRD Requirement PRD

Platform requests location permission immediately on page load. If granted, reverse geocode lat/lng to city/state. If city is in KA/TN/KL/AP/TG → tag SOUTH, else OTHERS. Location denied → re-prompt until granted. Customer cannot proceed to Stage 1 without location.

Old System OLD
  • SP: USP_CAPTURE_SOUTHDETAILS_SJET
  • API: AiNXT reverse geocoding to convert lat/lng to city/state
  • Logic: If state IN (Karnataka, Tamil Nadu, Kerala, Andhra Pradesh, Telangana) → tag = SOUTH
  • Stored in: TBL_OAO_DETAILS and pushed to Zoho CRM directly
  • Controller: RegistrationController.GeoLocation()
New System NEW
  • Service: ArrivalService captures lat/lng in LeadSession
  • Resolution: RegistrationService.ResolveSouthTagFromSessionAsync() — looks up LeadSession.LocationTag
  • Reference: state_master.IsSouth flag
  • Stored in: Lead.SouthTag (SOUTH or OTHERS)
  • Zoho: Via DownstreamEvent to ZOHO_CRM (async, not direct API call)
Key Difference: Old system pushed to Zoho synchronously. New system publishes async downstream event. South tag stored on Lead entity, not separate table.
0.3 Referral Code Validation
Validate referral/BA code against client master. Return referrer name if valid.
BRD / PRD Requirement PRD

If referral code present in URL, validate against referral registry. Valid → store as referral_code. Invalid/expired → referral_code = null. Journey continues. No error shown to customer.

Old System OLD
  • SP: [DIY].[USP_VALIDATE_REFERALCODE_SJET]
  • Table: MOSL_FEED_CLIENT_DETAILS — matched CL_code column
  • Returns: STATUS (1=valid, 0=invalid), REFERALNAME
  • Controller: RegistrationController.VALIDATEREFERALCODE()
  • File: Joruney_imp_code\Common\StoreProcedures.cs line 188
New System NEW
  • Primary: RegistrationService checks referral_codes table (own table)
  • Fallback: BackOfficeCheckService.ValidateReferralCodeViaClientMasterAsync() — queries client_master.ClCode
  • If invalid: lead.ReferralCode = null (cleared silently)
  • File: Services/Stage1/RegistrationService.cs, Services/Common/BackOfficeCheckService.cs
Key Difference: Old system checked MOSL_FEED directly. New system has own referral_codes table first, then falls back to client_master (synced from MOSL_FEED).

Stage 1 — Registration

1.1 Input Validation (Mobile + Name + Consents)
Validate mobile number format, full name, and mandatory consents before processing registration.
BRD / PRD Requirement PRD

Mobile: 10 digits starting 6-9. Name: 2-100 chars alpha+spaces. 3 mandatory consents (Account Opening, Communication/WhatsApp opt-in, T&C). Each consent stored with version, timestamp, IP, platform. Proceed button enabled only when all valid.

Old System OLD
  • Validation: CommonClass.CheckDangerousString() — SQL injection check on all fields
  • Mobile: Validation.chk_PhoneNo() — 10-digit Indian mobile. NRI variant: chk_PhoneNo_NRI()
  • Consents: No explicit consent tracking. No versioning. No per-consent records.
  • File: Joruney_imp_code\Common\Validation.cs, RegistrationController.cs line 142
New System NEW
  • Method: RegistrationService.ValidateInput()
  • Mobile: Regex ^[6-9]\d{9}$ (compiled)
  • Name: Regex ^[a-zA-Z\s]{2,100}$ (alpha + spaces, 2-100 chars)
  • Consents: 3 mandatory booleans checked: ConsentAccountOpening, ConsentCommunication, ConsentTerms
  • Consent Storage: SaveConsentsAsync() creates 3 Consent records with version, IP, platform, timestamp
  • File: Services/Stage1/RegistrationService.cs line 454
Key Difference: Old system had no consent tracking. New system stores 3 versioned consent records per lead — compliance requirement for SEBI/DPDP.
1.2 Restricted Mobile Check
Block registration if mobile number is on the restricted list.
BRD / PRD Requirement PRD

BRD Priority 1: Mobile on MOFSL negative list OR SEBI debarred list → journey ends permanently. Error: "This number is not eligible." DROP_NEGATIVE_LIST.

Old System OLD
  • SP: Embedded inside USP_CLIENT_REGISTRATION_SJET (5,780 lines)
  • Table: Checked against internal restricted list within the massive registration SP
  • Not a separate SP — part of the monolithic registration procedure
New System NEW
  • Service: RegistrationService.SignupAsync() — standalone check BEFORE dedupe
  • Query: _db.Set<RestrictedMobile>().AnyAsync(r => (r.Mobile == mobile || r.MobileHash == mobileHash) && r.IsActive)
  • Table: restricted_mobiles (has both Mobile plain and MobileHash)
  • Error: MOBILE_RESTRICTED
  • File: Services/Stage1/RegistrationService.cs
1.3 Suspicious Contact Flag
Flag mobile/email as suspicious for ops review. Does NOT block the journey.
BRD / PRD Requirement PRD

Device fingerprint check: same device >2 leads in 30 days → flag DEVICE_DEDUP on session. Not blocked at Stage 0, checked again at Stage 1. Suspicious contacts flagged for ops visibility only.

Old System OLD
  • SP: Usp_GetSuspiciousPhoneOrEMailId
  • Called from: LoginRepository.GenerateEmailOTP(), LoginController.GENERATEEMAILOTP()
  • Behavior: Flag only, don't block
New System NEW
  • Query: _db.Set<SuspiciousContact>().AnyAsync(s => s.IdentifierHash == mobileHash && s.IdentifierType == "MOBILE" && s.IsActive)
  • Table: suspicious_contacts
  • Behavior: Logs warning, does NOT block (same as old)
1.4 Offline Client Block (Branch Client)
Block online journey if mobile belongs to an existing branch offline client.
BRD / PRD Requirement PRD

BRD Priority 2: Active Demat account exists in CBOS for this mobile → journey ends. Error: "An active account already exists. Please log in to RIISE." BE_REG_001. Includes check against back-office client master.

Old System OLD
  • SP: [DIY].[USP_Check_Direct_Offline_Client_EXP_SJET]
  • Called from: LoginRepository.AuthenticateLogin()_accountOpening.Check_Direct_Offline_Client_EXP(strUserID)
  • Message: "You are our existing client of {Branch} branch. Kindly get in touch in branch to open your account."
  • File: Joruney_imp_code\Registration\LoginRepository.cs line 163
New System NEW
  • Service: BackOfficeCheckService.CheckOfflineClientAsync(mobile)
  • Query: client_master WHERE Mobile matches AND IsActive AND BranchCode NOT IN ('DIRECT','DAD') AND BranchCode IS NOT NULL
  • Error: BE_REG_OFFLINE_CLIENT — same message as old system
  • File: Services/Common/BackOfficeCheckService.cs, called from RegistrationService.SignupAsync()
1.5 Negative List Check (MOFSL + SEBI)
Check mobile (and IP) against MOFSL blacklist and SEBI debarred list.
BRD / PRD Requirement PRD

Negative list check on mobile + IP. If API unavailable → proceed with NEGATIVE_LIST_CHECK_SKIPPED flag, manual check triggered post-eSign. Non-blocking with flag.

Old System OLD
  • Table: TBL_FFF_NOT_ALLOW — checked inside Proc_DedupeCheck_LeadSquare_LSQ_SJET
  • Scope: Mobile only. No IP check.
  • Fallback: No graceful degradation — if table unavailable, no check performed
New System NEW
  • Method: RegistrationService.CheckNegativeListWithFallbackAsync(mobileHash, ipAddress)
  • Table: negative_list_entries — supports MOBILE, PAN, IP_ADDRESS types with expiry dates
  • Scope: Mobile hash + IP address (BRD requirement)
  • Fallback: If check fails → WasSkipped = true, lead tagged NEGATIVE_LIST_CHECK_SKIPPED, ops review post-eSign
  • Error: DROP_NEGATIVE_LIST
Key Difference: Old system only checked mobile. New system also checks IP address. Old had no graceful degradation; new flags as SKIPPED and continues with ops review.
1.6 CBOS / Back-Office Active Account Check
Check if mobile already belongs to an active Demat account holder in back-office systems.
BRD / PRD Requirement PRD

CBOS dedupe API checks for existing active Demat account. If unavailable → proceed with CBOS_DEDUPE_SKIPPED flag, ops review post-eSign. Run in parallel with negative list and EKYC dedupe.

Old System OLD
  • SP: [DIY].[USP_CHECK_MOBILE_EXISTS_BO_EXP_SJET] (96 lines)
  • Tables checked (UNION):
    • VW_MOSL_FEED_CLIENT_DETAILS — active records (InactiveDate IS NULL)
    • TBL_CLIENT_PERSONALDETAILS + TBL_CLIENT_STAGEDETAILS — in-progress leads
    • TBL_DEDUPE_DATA_DUMP — DedupParam IN (N, EPO, MPO)
    • TBL_DPMASTER — DP master with CM_MOBILE
  • Whitelist: TBL_PAN_MOBILE_EMAIL_WHITELIST — exemptions deleted from temp table before check
  • Output: @IsMobileExists (BIT), @Status, @Message
New System NEW
  • Method: RegistrationService.CheckCbosDedupeAsync(mobile, mobileHash)
  • Two checks:
    1. leads table — WHERE Mobile matches AND IsCompleted AND CurrentState = "ACTIVATED"
    2. client_master — WHERE Mobile matches AND IsActive AND InactiveDate IS NULL
  • Bypass: If active account found, calls BackOfficeCheckService.CheckBypassEligibilityAsync("MOBILE", mobile)
  • Fallback: If CBOS check fails → WasSkipped = true, lead tagged CBOS_DEDUPE_SKIPPED
  • File: Services/Stage1/RegistrationService.cs
Key Difference: Old system did multi-table UNION in single SP. New system checks own leads table + synced client_master separately. Bypass logic is a separate service (not embedded in SP).
1.7 Mobile/Email/PAN Bypass Logic
Allow registration even when mobile/email/PAN exists in back-office — if all records are INACTIVE, PMS-only, or belong to branch/subbroker OWNER.
BRD / PRD Requirement PRD

BRD does not explicitly define bypass logic — this is carried over from old system's business rules. Bypass allows legitimate users whose mobile/email/PAN is in BO (inactive accounts, PMS-only, branch/subbroker owner) to register.

Old System OLD
  • SP: [DIY].[USP_BYPASS_MOBILE_EMAIL_PAN_SJET] (283 lines)
  • Parameters: @TYPE (MOBILE/EMAIL/PAN), @VALUE, @USERID, @ACCOUNTTYPE, @HOLDERTYPE
  • Tables: MOSL_FEED_CLIENT_DETAILS, MOSL_FEED_BRANCH, MOSL_FEED_SUBBROKERS
  • Conditions checked:
    1. INACTIVE — all records have InactiveDate set
    2. PMS — all records have ProductType NOT IN (PMS, RPS)
    3. OWNER (Branch) — mobile in MOSL_FEED_BRANCH, not as active client with different CL_CODE
    4. OWNER (SubBroker) — same for MOSL_FEED_SUBBROKERS
  • Output: @ISBYPASS (1=allowed, 0=blocked)
  • Logging: Inserts into TBL_TRACK_BYPASS_MOBILE_EMAIL_PAN
New System NEW
  • Service: BackOfficeCheckService.CheckBypassEligibilityAsync(type, identifierValue)
  • Routes to: CheckMobileBypassAsync() or CheckEmailBypassAsync()
  • Tables: client_master, branch_master, subbroker_master, duplicate_bypass_whitelist
  • Same 4 conditions + extra:
    1. INACTIVE — all client_master records have InactiveDate set
    2. PMS — all active records have ProductType IN (PMS, RPS)
    3. OWNER_BRANCH — mobile/email in branch_master, no conflicting active client
    4. OWNER_SUBBROKER — mobile/email in subbroker_master
    5. WHITELIST (new) — mobile/email in duplicate_bypass_whitelist
  • Logging: Creates FraudCheck record with CheckType=BYPASS
  • File: Services/Common/BackOfficeCheckService.cs
Key Difference: Old SP checked MOSL_FEED tables directly. New code checks synced tables (client_master, branch_master, subbroker_master). Added WHITELIST bypass (new). Logging uses FraudCheck entity instead of dedicated bypass log table.
1.8 EKYC Dedupe (In-Progress Application Check)
Check if mobile already has an in-progress application within 90 days. Determine resume vs block vs new.
BRD / PRD Requirement PRD

8-priority eligibility table: P1=Negative list, P2=Active Demat, P3=Old platform redirect, P4=Same channel/BA/RM resume, P5=Different channel block, P6=REJECTED/PERMANENTLY_CLOSED → new lead, P7=CS_EXPIRED → archive + new, P8=No application → new lead. Mobile freed on reset.

Old System OLD
  • SPs:
    • Proc_DedupeCheck_LeadSquare_LSQ_SJET (802 lines) — main CRM dedupe
    • USP_CHECK_NINTY_DAY_LOGIC_LSQ_SJET — 90-day check on Tbl_DAD_TO_RETAIL_TRANSFERRED_LEADS
    • Proc_Dedupe_UserIDExistProcess_Revamp_LSQ_SJET — user ID existence + product transition
    • proc_execute_dad_dedupe_discard_LSQ_SJET — discard/archive old lead for re-registration
  • Tables: TBL_DEDUPE_DATA_DUMP, TBL_LSQ_LEADID_STAGEDETAILS, TBL_DAD_TO_RETAIL_TRANSFERRED_LEADS, TBL_FFF_NOT_ALLOW, TBL_ZOHOEKYC_MAPPER
  • DEDUPE_PARAM output: N (new), EPO (email prev owned), MPO (mobile prev owned), C2 (transition)
  • No channel comparison — all in-progress leads were resumed regardless of channel
New System NEW
  • Method: RegistrationService.CheckEkycDedupeAsync(mobile)
  • Query: leads WHERE Mobile matches, NOT completed, CreatedAt >= 90-day cutoff, NOT DROPPED/ARCHIVED
  • Also checks: REJECTED and CS_EXPIRED leads (for archive-and-create-new logic)
  • Decision table: ApplyEligibilityDecisionTable() with 8-priority rules
  • Channel comparison: Same channel/BA → RESUME. Different channel → BLOCKED (new requirement)
  • Multiple lead resolution: BackOfficeCheckService.CountActiveLeadsAsync() + ArchiveOlderDuplicateLeadsAsync()
  • File: Services/Stage1/RegistrationService.cs
Key Difference: Old system had 4 separate SPs for dedupe across 800+ lines. New system is a single method with clear decision table. Old had no channel comparison; new blocks different-channel leads. Old used DEDUPE_PARAM; new archives duplicates automatically.
1.9 Lead Record Creation
Create the primary lead record with all registration data.
BRD / PRD Requirement PRD

Lead created with lead_id (UUID), mobile_hash (SHA-256), registration_name, channel, source, UTM, device_type, location_tag. State = INITIATED. Consent records saved before OTP sent. Lead creation retry: 3x at 2-sec intervals. Consent save must succeed before OTP.

Old System OLD
  • SP: [DIY].[USP_CLIENT_REGISTRATION_SJET]5,780 lines, 200+ parameters
  • Operations:
    1. Insert into TBL_DEDUPE_DATA_DUMP (plain mobile stored)
    2. SSO user creation: MOSL_SSO.dbo.tblUser + USR_ADDITIONAL_INFO
    3. State/city validation: TBL_STATE_CITY_MASTER_LSQ / MST_CITY_STATE
    4. Branch/RM assignment: 500+ lines of campaign-specific routing (Alliance, IDFC, INDUSIND, AUSFBDIRECT, etc.)
    5. Campaign logic: IIBLMICROSITE, AUFSB, CBI, BA Self-Direct
    6. Input tracking: TBL_TRACKINGINPUT_USP_CLIENT_REGISTRATION_SJET
  • File: Joruney_imp_code\Common\StoreProcedures.cs line 186
New System NEW
  • Method: RegistrationService.SaveLeadWithRetryAsync(lead) — 3 retries at 2-sec intervals
  • Entity: Lead — stores Mobile (plain) + MobileHash
  • Consents: SaveConsentsAsync() — 3 Consent records
  • Campaign routing: CampaignRoutingService.AssignRmAndBranchAsync(lead) — INDUSIND, IIBLMICROSITE, AUFSB, CBI
  • Referral validation: Checks referral_codes then client_master.ClCode fallback
  • No SSO user — JWT-based auth (no MOSL_SSO tables)
  • DedupeClassification: Set to "N" on new lead
  • BypassReason: Set if bypass was applied
  • File: Services/Stage1/RegistrationService.cs
Key Difference: Old system was a 5,780-line SP handling everything. New system decomposes into: lead creation (RegistrationService), campaign routing (CampaignRoutingService), bypass logic (BackOfficeCheckService), referral validation (BackOfficeCheckService). No SSO user — JWT-based auth. Lead creation has retry logic.
1.10 OTP Generation & Delivery
Generate 4-digit OTP, store it, and send via SMS to the customer.
BRD / PRD Requirement PRD

4-digit OTP, 5-min validity, stored in memory only (never DB). Max 5 wrong attempts → DROPPED. Max 3 resends in 30 min. 30-sec cooldown. Delivery cascade: SMS → WhatsApp → Push → RCS (only SMS implemented currently). All channels fail → CS Journey CS_OTP_PROVIDER_DOWN.

Old System OLD
  • SP: SEND_RESUME_OTP — generated OTP, stored in database
  • SMS: SingleMessageService SOAP API (Netcore) — SMS only, no cascade
  • Template: SMSTemplate class
  • Logging: [DIY].[USP_INSERT_SMS_SEND_API_LOG_SJET]
  • File: Joruney_imp_code\Registration\LoginRepository.cs
New System NEW
  • Generation: OtpGeneratorService.GenerateOtp(4) — 4-digit cryptographic random
  • Storage: InMemoryOtpStore (ConcurrentDictionary keyed by mobile, TTL 5 min). Never in DB.
  • SMS: NetcoreSmsProvider — MOSL internal SMS service (mosms.motilaloswal.com)
  • Template: SmsTemplateService
  • OTP record: OtpVerification entity tracks attempts/resends (but OTP value only in memory)
  • File: Services/Stage1/RegistrationService.cs, Caching/InMemoryOtpStore.cs
Key Difference: Old system stored OTP in database. New system stores in-memory only (ConcurrentDictionary) — OTP never persisted to any database. Old used SOAP; new uses HTTP. Background cleanup timer removes expired OTPs every 60 seconds.
1.11 CRM Lead Creation (Zoho)
Create a lead record in CRM system for sales tracking.
BRD / PRD Requirement PRD

On lead creation, push to: CleverTap (registration_proceed_en, lead_created_en), Zoho CRM (new lead with mobile_hash, source, location_tag), RIISE App (pre-create customer profile), GCM, Datalake, CDP. All async, non-blocking.

Old System OLD
  • SPs: PROC_DEDUPECHECK_LEADSQUARE_LSQ_SJET, USP_LEADSQUARED_REQUEST_RESPONSE_LOG_SJET, USP_UPDATE_LSQLEADID_SJET
  • APIs: LeadSquare API (create lead, create opportunity, activity log) + Zoho CRM API (PUT /crm/v7/Leads)
  • Both LSQ and Zoho — dual CRM integration based on IsZohoLead flag
  • Sync calls — LSQ/Zoho called inline during registration
  • File: Joruney_imp_code\Registration\RegistrationRepository.cs, ExternalAPI\LeadSquare\LeadSquare.cs, ExternalAPI\Zoho\zohoApi.cs
New System NEW
  • Method: RegistrationService.PublishDownstreamEventsAsync(lead)
  • Pattern: Creates DownstreamEvent records (async, non-blocking)
  • Targets: CLEVERTAP, ZOHO_CRM, DATALAKE, CDP, RIISE, GCM (6 targets)
  • No LeadSquare — removed per Product team. Zoho CRM only.
  • Async — events queued in DB, processed by background worker
  • File: Services/Stage1/RegistrationService.cs
Key Difference: Old system called LSQ + Zoho synchronously inline. New system publishes async downstream events to 6 targets. No LeadSquare — Zoho only. Events are non-blocking — CRM failure doesn't block registration.
1.12 RM-Assisted Zoho Resume (OTP Bypass)
When an RM resumes a dropped-off journey from Zoho CRM, OTP should be bypassed.
BRD / PRD Requirement PRD

Per Product team decision: When RM resumes a dropped-off journey from Zoho CRM, OTP is bypassed. Customer proceeds directly to last completed stage. This applies to RM-assisted Zoho resume only (not franchise users).

Old System OLD
  • SP: [DIY].[USP_INSERT_UPDATE_MOBILE_EMAIL_OTP_BYPASS_SJET]
  • Scope: Bypass for franchise users (not RM-specific)
  • Table: TBL_MOBILE_EMAIL_OTP_BYPASS_DETAILS — ISMOBILEBYPASS / ISEMAILBYPASS flags
New System NEW
  • Detection: RegistrationService.SignupAsync() checks command.Source == "ZOHO_RM_RESUME" || command.IsRmResume
  • Flag: Sets existingLead.IsRmResumeBypass = true
  • Effect: OtpVerificationService.VerifyOtpAsync() — if lead.IsRmResumeBypass, skips OTP entirely, transitions directly to OTP_VERIFIED, fires background checks
  • Audit: State transition logged with TriggerAction = "RM_RESUME_BYPASS"
  • File: Services/Stage1/RegistrationService.cs, Services/Stage2/OtpVerificationService.cs
Key Difference: Old system had generic franchise bypass. New system is specifically for RM-assisted Zoho resume — sets a flag on the lead that OTP verification honors. Audit trail records the bypass action.