🎯 Learning Path:
Welcome to the REL-ID IDV Post-Login KYC Flow codelab! This tutorial teaches you how to implement comprehensive Know Your Customer (KYC) identity verification for activated customers using document scanning, facial recognition, and biometric authentication.
In this codelab, you'll implement a complete post-login KYC workflow for activated customers:
This codelab demonstrates post-login KYC for activated customers, where identity verification occurs after user login for compliance purposes:
Pre-Login IDV Flow (Challenge Mode 8):
Activation Code → IDV (Document → Selfie → Consent) → Password Setup → Dashboard
Post-Login KYC Flow (Challenge Mode 9 - This Codelab):
Login → Dashboard → Navigate to KYC → IDV (Document → Selfie → Consent) → Completion
Critical Differences:
Understanding the complete flow is crucial before implementation:
1. User Login (Activated Customer)
↓ (Existing credentials authenticated)
2. Dashboard → Open Drawer Menu
↓ (User navigates to KYC option)
3. 🆕 Activated Customer KYC Screen
├─ Display KYC information and requirements
├─ User clicks "Start KYC Verification"
├─ Screen-level event handler registered (persistent)
├─ initiateActivatedCustomerKYC("Post Login KYC")
│ • workflow: 6 (RDNA_IDV_POSTLOGIN_KYC)
│ • challengeMode: 9 (post-login)
└─ SDK initiates post-login KYC workflow
↓
4. 📄 IDV Document Scan Process
├─ getIDVDocumentScanProcessStartConfirmation event
│ • idvWorkflow: 6 (RDNA_IDV_POSTLOGIN_KYC)
├─ setIDVDocumentScanProcessStartConfirmation(true, 6)
├─ Native Document Capture (Regula SDK)
├─ getIDVConfirmDocumentDetails event
│ • challengeMode: 9
│ • response_data: OCR-extracted document fields
│ • Close button: cancel API (mode 9) → triggers onActivatedCustomerKYCResponse
└─ setIDVConfirmDocumentDetails(true, 9)
↓
5. 🤳 IDV Selfie Capture Process
├─ getIDVSelfieProcessStartConfirmation event
│ • idvWorkflow: 6 (RDNA_IDV_POSTLOGIN_KYC)
├─ setIDVSelfieProcessStartConfirmation(true, false, 6)
├─ Native Selfie Capture (Liveness Detection)
├─ getIDVSelfieConfirmation event
│ • challengeMode: 9
│ • response_data: Biometric match results
│ • Close button: navigate directly (mode 9) → no cancel API
└─ setIDVSelfieConfirmation("true", 9)
↓
6. 🔐 IDV Biometric Consent
├─ getIDVBiometricOptInConsent event
│ • challengeMode: 9
│ • status at root level (no challengeResponse for mode 9)
│ • Close button: navigate directly (mode 9)
└─ setIDVBiometricOptInConsent(true, 9)
↓
7. ✅ KYC Completion
├─ onActivatedCustomerKYCResponse event triggered
├─ Screen-level handler receives event (even if on different screen)
├─ Handler navigates back to ActivatedCustomerKYCScreen
├─ Dual validation performed:
│ • Check error.longErrorCode (0 = OK, 240 = User Cancelled)
│ • Check status.statusCode (100 or 0 = success)
├─ Success/Error alert shown
└─ User clicks OK → Navigate to Dashboard
For post-login KYC flow, you'll use these specific values:
Parameter | Value | Description |
idvWorkflow |
|
|
challengeMode |
| Post-login challenge mode |
Comparison with Pre-Login Flow:
Aspect | Pre-Login (Activation) | Post-Login (KYC) |
Workflow | 0-5 (Activation workflows) | 6 (RDNA_IDV_POSTLOGIN_KYC) |
Challenge Mode | 8 | 9 |
Entry Point | ActivationCodeScreen | ActivatedCustomerKYCScreen (drawer menu) |
Close Button (Document Confirm) | resetAuthState() | cancel API → triggers event |
Close Button (Selfie Confirm) | resetAuthState() | Direct navigation |
Close Button (Biometric Consent) | resetAuthState() | Direct navigation |
Response Structure | challengeResponse.status | status at root level |
Event Handler | Global provider only | Screen-level + global provider |
Ensure you have completed these requirements:
# Required tools
Node.js >= 16
npm or yarn
Cordova CLI
Xcode (for iOS) or Android Studio (for Android)
The code to get started can be found in a GitHub repository.
You can clone the repository using the following command:
git clone https://github.com/uniken-team/relid-codelab-cordova.git
Navigate to the relid-IDV-post-login-kyc folder in the repository you cloned earlier:
cd relid-codelab-cordova/relid-IDV-post-login-kyc
The post-login KYC codelab follows this structure:
relid-IDV-post-login-kyc/
├── www/
│ ├── src/
│ │ ├── tutorial/
│ │ │ ├── screens/
│ │ │ │ ├── idv/ # IDV Screens
│ │ │ │ │ ├── activatedCustomerKyc/ # 🆕 KYC Entry Point
│ │ │ │ │ │ └── ActivatedCustomerKYCScreen.js # Post-login KYC initiation
│ │ │ │ │ ├── IDVDocumentProcessStartConfirmationScreen.js
│ │ │ │ │ ├── IDVConfirmDocumentDetailsScreen.js
│ │ │ │ │ ├── IDVSelfieProcessStartConfirmationScreen.js
│ │ │ │ │ ├── IDVSelfieConfirmationScreen.js
│ │ │ │ │ └── IDVBiometricOptInConsentScreen.js
│ │ │ │ ├── mfa/ # MFA Screens
│ │ │ │ │ ├── DashboardScreen.js
│ │ │ │ │ └── ...
│ │ │ └── navigation/
│ │ │ └── NavigationService.js # 🆕 KYC route added
│ │ └── uniken/
│ │ ├── services/
│ │ │ └── idv/
│ │ │ ├── rdnaIDVService.js # 🆕 initiateActivatedCustomerKYC()
│ │ │ └── rdnaIDVEventManager.js # 🆕 onActivatedCustomerKYCResponse event
│ │ └── utils/
│ │ └── connectionProfileParser.js # File loading with cordova-plugin-file
│ ├── index.html # 🆕 KYC screen template added
│ └── js/
│ └── app.js # deviceready pattern
├── plugins/
│ └── RdnaClient/ # LOCAL Cordova plugin
└── config.xml
The project uses an automated Cordova hook to configure IDV assets for document verification and biometric capture. Unlike React Native which requires manual asset placement, Cordova handles this automatically during the build process.
To use this automated hook in your own Cordova projects, you need the following structure at your project root:
your-cordova-project/
├── idv-native-resources/ # ⚠️ Required folder at project root
│ ├── common/ # Shared files (copied to both platforms)
│ │ ├── regula.license # License file (shared)
│ │ └── db.dat # 110.5MB document database (shared)
│ ├── ios/ # iOS-only files
│ │ └── Certificates.bundle/
│ │ └── icaopkd-002-complete-000325.ldif
│ └── android/ # Android-only files
│ └── assets/
│ └── Regula/
│ └── certificates/
│ └── icaopkd-002-complete-000325.ldif
├── hooks/
│ └── after_prepare/
│ └── copy_idv_native_resources.js # ⚠️ Automated hook script
├── platforms/
├── www/
└── config.xml
Key Design: The common/ folder holds shared resources (db.dat, regula.license) used by both platforms, while ios/ and android/ folders contain only platform-specific files.
Hook Script: hooks/after_prepare/copy_idv_native_resources.js
When you run cordova prepare or cordova build, this hook automatically copies assets from idv-native-resources/ to platform-specific directories:
iOS Destinations (Automated by Hook):
common/regula.license → platforms/ios//Resources/regula.license common/db.dat → platforms/ios//Resources/db.dat ios/Certificates.bundle/ → platforms/ios//Resources/Certificates.bundle/ Android Destinations (Automated by Hook):
common/regula.license → platforms/android/app/src/main/res/raw/regula.licensecommon/db.dat → platforms/android/app/src/main/assets/Regula/db.datandroid/assets/Regula/certificates/ → platforms/android/app/src/main/assets/Regula/certificates/Important:
idv-native-resources/ folder must exist at your project root with the exact structure shown abovecopy_idv_native_resources.js must be in hooks/after_prepare/ directorycommon/ folderios/ or android/ folderscordova prepare - no manual setup neededInstall the required plugins:
# Install local RdnaClient plugin
cordova plugin add ./RdnaClient
# Install file plugin for agent_info.json loading
cordova plugin add cordova-plugin-file
# Install device plugin
cordova plugin add cordova-plugin-device
Before implementing the KYC screen, you need to add the initiateActivatedCustomerKYC() API method to the IDV service layer. This method initiates the post-login KYC workflow and returns a promise.
Add the following method to rdnaIDVService.js:
// www/src/uniken/services/idv/rdnaIDVService.js
/**
* Initiate Activated Customer KYC
*
* Initiates post-login KYC workflow for already activated customers.
* This API triggers the complete IDV flow with workflow 6 (RDNA_IDV_POSTLOGIN_KYC)
* and challenge mode 9 (post-login).
*
* @param {string} reason - Reason for KYC initiation (e.g., "Post Login KYC", "Compliance Verification")
* @returns {Promise<Object>} Promise that resolves with sync response on successful initiation
*
* Flow:
* 1. SDK initiates post-login KYC workflow (workflow 6, mode 9)
* 2. SDK automatically triggers IDV workflow events:
* - getIDVDocumentScanProcessStartConfirmation
* - getIDVConfirmDocumentDetails
* - getIDVSelfieProcessStartConfirmation
* - getIDVSelfieConfirmation
* - getIDVBiometricOptInConsent
* 3. After completion or cancellation, SDK triggers:
* - onActivatedCustomerKYCResponse event
*
* Success Response:
* - error.longErrorCode = 0
*
* Error Responses:
* - Sync error: error.longErrorCode !== 0
* - Backend error: Received in onActivatedCustomerKYCResponse event
*/
async initiateActivatedCustomerKYC(reason) {
return new Promise((resolve, reject) => {
console.log('RdnaIDVService - Initiating activated customer KYC:', JSON.stringify({
reason
}, null, 2));
com.uniken.rdnaplugin.RdnaClient.initiateActivatedCustomerKYC(
(response) => {
console.log('RdnaIDVService - InitiateActivatedCustomerKYC sync callback received');
const result = JSON.parse(response);
console.log('RdnaIDVService - initiateActivatedCustomerKYC sync response:', JSON.stringify({
longErrorCode: result.error?.longErrorCode,
shortErrorCode: result.error?.shortErrorCode,
errorString: result.error?.errorString
}, null, 2));
// Check sync error
if (result.error && result.error.longErrorCode === 0) {
console.log('RdnaIDVService - Activated customer KYC initiated successfully');
console.log('RdnaIDVService - SDK will trigger IDV workflow events');
console.log('RdnaIDVService - Workflow: 6 (RDNA_IDV_POSTLOGIN_KYC)');
console.log('RdnaIDVService - Challenge Mode: 9 (post-login)');
resolve(result);
} else {
console.error('RdnaIDVService - Activated customer KYC initiation failed');
console.error('RdnaIDVService - Error Code:', result.error?.longErrorCode);
console.error('RdnaIDVService - Error String:', result.error?.errorString);
reject(result);
}
},
(error) => {
console.error('RdnaIDVService - initiateActivatedCustomerKYC error callback');
const result = JSON.parse(error);
reject(result);
},
[reason]
);
});
}
RDNA_IDV_POSTLOGIN_KYC workflowonActivatedCustomerKYCResponse eventcom.uniken.rdnaplugin.RdnaClient (no imports needed)Unlike React Native which uses ES6 imports, Cordova requires manual service initialization. The service and event manager use an auto-initialization pattern to ensure they're ready before any SDK events fire.
Auto-Initialization Pattern:
// rdnaIDVService.js
class RdnaIDVService {
constructor() {
if (RdnaIDVService.instance) {
return RdnaIDVService.instance;
}
console.log('RdnaIDVService - Initializing service (auto-initialization on module load)');
this.eventManager = rdnaIDVEventManager;
RdnaIDVService.instance = this;
}
static getInstance() {
if (!RdnaIDVService.instance) {
RdnaIDVService.instance = new RdnaIDVService();
}
return RdnaIDVService.instance;
}
}
// Auto-instantiate singleton when script loads
const rdnaIDVService = RdnaIDVService.getInstance();
Why Auto-Initialize?
Cordova uses synchronous script loading, so order is CRITICAL. Load scripts in the wrong order and you'll get ReferenceError: rdnaIDVService is not defined.
Correct Loading Order:
<!-- CRITICAL: Load in this exact order! -->
<!-- 1. Core Services (must load FIRST) -->
<script type="text/javascript" src="src/uniken/services/idv/rdnaIDVEventManager.js"></script>
<script type="text/javascript" src="src/uniken/services/idv/rdnaIDVService.js"></script>
<!-- 2. Global Providers (depends on services) -->
<script type="text/javascript" src="src/uniken/providers/idv/SDKIDVEventProvider.js"></script>
<!-- 3. App Initializer (initializes providers - MUST load before screens) -->
<script type="text/javascript" src="src/uniken/AppInitializer.js"></script>
<!-- 4. Screens (load LAST - they use services) -->
<script type="text/javascript" src="src/tutorial/screens/idv/activatedCustomerKyc/ActivatedCustomerKYCScreen.js"></script>
<script type="text/javascript" src="src/tutorial/screens/idv/IDVDocumentProcessStartConfirmationScreen.js"></script>
<!-- ... other IDV screens ... -->
Loading Order Rationale:
1. rdnaIDVEventManager.js loads
→ Registers document.addEventListener listeners
→ Auto-creates singleton instance
2. rdnaIDVService.js loads
→ Gets reference to event manager
→ Wraps SDK APIs in Promise-based methods
→ Auto-creates singleton instance
3. SDKIDVEventProvider.js loads
→ Accesses rdnaIDVService (already loaded)
→ Registers global event handlers
→ Auto-creates singleton instance
4. AppInitializer.js loads
→ Calls SDKIDVEventProvider.initialize()
→ Sets up navigation service references
5. Screen files load
→ Access rdnaIDVService (safe - already loaded)
→ Register screen-level event handlers
→ Export to window object
Implement the event listener and handler for onActivatedCustomerKYCResponse in the IDV event manager. This handles the native event from the SDK and invokes the registered callback.
Update rdnaIDVEventManager.js to add the KYC response event:
// www/src/uniken/services/idv/rdnaIDVEventManager.js
class RdnaIDVEventManager {
constructor() {
// ... existing constructor code
// 🆕 Add handler property
this.activatedCustomerKYCResponseHandler = null;
// Auto-register listeners on construction
this.registerEventListeners();
}
/**
* Set handler for activated customer KYC response event
*
* This handler will be invoked when the SDK triggers onActivatedCustomerKYCResponse event.
*
* @param {Function} callback - Callback function to handle KYC response event
*/
setActivatedCustomerKYCResponseHandler(callback) {
console.log('RdnaIDVEventManager - Setting activated customer KYC response handler');
this.activatedCustomerKYCResponseHandler = callback;
}
/**
* Handle onActivatedCustomerKYCResponse event from SDK
*
* This event is triggered when:
* 1. KYC workflow completes successfully (all IDV steps completed)
* 2. User cancels during IDV flow (error code 240 - User Cancelled)
* 3. Backend returns an error during KYC initiation or processing
*
* Event Flow:
* - SDK → Native Bridge → JavaScript Event → This Handler → Registered Callback
*
* Handler Responsibilities:
* 1. Parse JSON response from native SDK
* 2. Log key fields (userID, sessionID, error, status)
* 3. Invoke registered callback with parsed data
* 4. Warn if no callback registered
*
* @param {Object} event - Event object containing response data from native SDK
*/
onActivatedCustomerKYCResponse(event) {
console.log('RdnaIDVEventManager - Activated customer KYC response event received');
try {
const response = event.response || event.detail || event;
console.log('RdnaIDVEventManager - onActivatedCustomerKYCResponse response:', JSON.stringify(response, null, 2));
const data = typeof response === 'string' ? JSON.parse(response) : response;
// Log event data for debugging
console.log('RdnaIDVEventManager - User ID:', data.userID);
console.log('RdnaIDVEventManager - Session ID:', data.sessionID);
console.log('RdnaIDVEventManager - Reason:', data.reason);
console.log('RdnaIDVEventManager - Error Code:', data.error?.longErrorCode);
console.log('RdnaIDVEventManager - Error String:', data.error?.errorString);
console.log('RdnaIDVEventManager - Status Code:', data.status?.statusCode);
console.log('RdnaIDVEventManager - Status Message:', data.status?.statusMessage);
// Invoke registered callback if exists
if (this.activatedCustomerKYCResponseHandler) {
console.log('RdnaIDVEventManager - Invoking activated customer KYC response handler');
this.activatedCustomerKYCResponseHandler(data);
} else {
console.warn('RdnaIDVEventManager - No handler registered for activated customer KYC response');
console.warn('RdnaIDVEventManager - Event data will be lost if not handled');
}
} catch (error) {
console.error('RdnaIDVEventManager - Failed to parse activated customer KYC response:', error);
}
}
/**
* Register all IDV event listeners
*
* Subscribes to native SDK events via document.addEventListener.
*/
registerEventListeners() {
if (this._initialized) {
console.log('RdnaIDVEventManager - Already initialized, skipping event listener registration');
return;
}
console.log('RdnaIDVEventManager - Registering native IDV event listeners');
// ... existing event listeners (document scan, selfie, consent)
// 🆕 Activated Customer KYC Response Event
document.addEventListener(
'onActivatedCustomerKYCResponse',
this.onActivatedCustomerKYCResponse.bind(this),
false
);
this._initialized = true;
console.log('RdnaIDVEventManager - Native IDV event listeners registered');
}
/**
* Cleanup all event listeners
*/
cleanup() {
console.log('RdnaIDVEventManager - Cleaning up event listeners and handlers');
// ... existing cleanup code
// 🆕 Remove KYC response listener
document.removeEventListener('onActivatedCustomerKYCResponse', this.onActivatedCustomerKYCResponse.bind(this), false);
// 🆕 Clear KYC response handler
this.activatedCustomerKYCResponseHandler = null;
this._initialized = false;
console.log('RdnaIDVEventManager - Cleanup completed');
}
}
// Auto-instantiate singleton
const rdnaIDVEventManager = RdnaIDVEventManager.getInstance();
onActivatedCustomerKYCResponse native eventdocument.addEventListener() instead of NativeEventEmitterThe Cordova app uses a persistent drawer menu in index.html. Add the KYC menu item to the drawer content.
Add the KYC menu item to the drawer navigation list:
<!-- www/index.html -->
<!-- Drawer Menu (slides in from left) -->
<nav id="drawer-menu" class="drawer-menu">
<div class="drawer-header">
<h2 class="drawer-title">REL-ID MFA</h2>
<button id="drawer-close-btn" class="drawer-close-btn">✕</button>
</div>
<div class="drawer-content">
<div id="drawer-user-info" class="drawer-user-info" style="display: none;">
<p class="drawer-user-label">Logged in as:</p>
<p id="drawer-username" class="drawer-username">-</p>
</div>
<ul class="drawer-nav">
<li><a href="#" id="drawer-dashboard-link" class="drawer-link">🏠 Dashboard</a></li>
<li>
<a href="#" id="drawer-language-link" class="drawer-link drawer-language-link">
<span class="drawer-language-main">🌐 Change Language</span>
<span id="drawer-current-language" class="drawer-language-current">English</span>
</a>
</li>
<li><a href="#" id="drawer-notifications-link" class="drawer-link">🔔 Get Notifications</a></li>
<li><a href="#" id="drawer-notification-history-link" class="drawer-link">📜 Notification History</a></li>
<li><a href="#" id="drawer-device-mgmt-link" class="drawer-link">📱 Device Management</a></li>
<li><a href="#" id="drawer-lda-toggling-link" class="drawer-link">🔐 LDA Toggling</a></li>
<li><a href="#" id="drawer-data-signing-link" class="drawer-link">🔏 Data Signing</a></li>
<li id="drawer-update-password-item" style="display: none;">
<a href="#" id="drawer-update-password-link" class="drawer-link">🔑 Update Password</a>
</li>
<!-- 🆕 Add KYC menu item -->
<li><a href="#" id="drawer-kyc-link" class="drawer-link">🆔 Activated Customer KYC</a></li>
<li><a href="#" id="drawer-logout-link" class="drawer-link logout-link">🚪 Log Out</a></li>
</ul>
</div>
</nav>
Add the HTML template for the Activated Customer KYC screen:
<!-- www/index.html -->
<!-- Activated Customer KYC Screen Template -->
<template id="ActivatedCustomerKYC-template">
<div class="screen-container">
<!-- Header with Menu Button -->
<div class="dashboard-header">
<button id="kyc-menu-btn" class="menu-button">☰</button>
<h2 class="dashboard-title">Activated Customer KYC</h2>
<div class="header-spacer"></div>
</div>
<div class="kyc-content">
<!-- KYC Information Card -->
<div class="card">
<h2 class="card-title">Identity Verification</h2>
<p class="card-description">
Complete your identity verification by scanning a government-issued ID and taking a selfie.
This process helps us verify your identity and comply with KYC (Know Your Customer) regulations.
</p>
<!-- What You'll Need -->
<div class="kyc-requirements">
<h3 class="requirements-title">What You'll Need:</h3>
<ul class="requirements-list">
<li>📄 Government-issued ID (Passport, Driver's License, National ID, etc.)</li>
<li>📱 Good lighting for clear document capture</li>
<li>🤳 Front-facing camera for selfie verification</li>
<li>⏱️ Approximately 2-3 minutes to complete</li>
</ul>
</div>
<!-- Process Steps -->
<div class="kyc-process">
<h3 class="requirements-title">Verification Process:</h3>
<ul class="requirements-list">
<li><strong>Step 1 - Document Scan:</strong> Capture clear photos of your government-issued ID</li>
<li><strong>Step 2 - Review Details:</strong> Verify the information extracted from your document</li>
<li><strong>Step 3 - Selfie Capture:</strong> Take a selfie for biometric matching</li>
<li><strong>Step 4 - Biometric Consent:</strong> Provide consent for secure biometric template storage</li>
</ul>
</div>
</div>
<!-- Status Banner (hidden by default) -->
<div id="kyc-status-banner" class="status-banner" style="display: none;">
<p id="kyc-status-message" class="status-message"></p>
</div>
<!-- Start KYC Button -->
<div class="button-container">
<button id="start-kyc-btn" class="primary-button">
Start KYC Verification
</button>
</div>
<!-- Privacy & Security Info -->
<div class="card info-card">
<h3 class="info-card-title">🔒 Privacy & Security</h3>
<p class="info-card-text">
Your identity documents and biometric data are encrypted and securely transmitted using industry-standard encryption protocols.
We comply with all applicable data protection regulations including GDPR and CCPA. Your data is never shared with third parties
without your explicit consent.
</p>
</div>
<!-- Additional Help -->
<div class="card info-card">
<h3 class="info-card-title">💡 Tips for Best Results</h3>
<ul class="info-card-list">
<li>Ensure your ID is not expired</li>
<li>Capture photos in a well-lit area</li>
<li>Avoid glare or shadows on the document</li>
<li>Remove glasses for selfie capture</li>
<li>Look directly at the camera</li>
</ul>
</div>
</div>
</div>
</template>
Add the screen script to index.html after other IDV screens:
<!-- www/index.html -->
<!-- IDV Screens -->
<script type="text/javascript" src="src/tutorial/screens/idv/IDVDocumentProcessStartConfirmationScreen.js"></script>
<script type="text/javascript" src="src/tutorial/screens/idv/IDVConfirmDocumentDetailsScreen.js"></script>
<script type="text/javascript" src="src/tutorial/screens/idv/IDVSelfieProcessStartConfirmationScreen.js"></script>
<script type="text/javascript" src="src/tutorial/screens/idv/IDVSelfieConfirmationScreen.js"></script>
<script type="text/javascript" src="src/tutorial/screens/idv/IDVBiometricOptInConsentScreen.js"></script>
<!-- 🆕 Add KYC Screen -->
<script type="text/javascript" src="src/tutorial/screens/idv/activatedCustomerKyc/ActivatedCustomerKYCScreen.js"></script>
Test the complete drawer menu integration:
Test After KYC Flow:

The ActivatedCustomerKYCScreen is the entry point for post-login KYC verification. It demonstrates the persistent event handler pattern and dual validation.
initiateActivatedCustomerKYC() API with workflow 6onContentLoaded() method called by NavigationServiceCreate the KYC screen module:
// www/src/tutorial/screens/idv/activatedCustomerKyc/ActivatedCustomerKYCScreen.js
/**
* Activated Customer KYC Screen - SPA Module
*
* Entry point for post-login KYC (Know Your Customer) verification.
* Allows logged-in users to complete identity verification using the IDV SDK.
*
* Features:
* - KYC information and requirements display
* - "Start KYC Verification" button to initiate IDV workflow
* - Success/error status display after KYC completion
* - Screen-level event handler for KYC completion
*
* SDK Integration:
* - Calls rdnaIDVService.initiateActivatedCustomerKYC(reason)
* - Registers event handler for onActivatedCustomerKYCResponse
* - Receives KYC completion status via event
* - Navigates through standard IDV workflow (document → selfie → consent)
*
* IDV Workflow:
* 1. User clicks "Start KYC Verification"
* 2. initiateActivatedCustomerKYC('Post Login KYC') called
* 3. SDK fires onActivatedCustomerKYCResponse (first time - workflow initiated)
* 4. SDK auto-triggers IDV events: document scan → selfie → consent
* 5. After IDV completion, SDK fires onActivatedCustomerKYCResponse (second time - final result)
* 6. Screen handler navigates back to this screen with result
* 7. Success/error alert displayed
*
* SPA Pattern:
* - onContentLoaded(params) called when navigated to
* - setupEventListeners() attaches DOM handlers and event handler
* - handleStartKYC() initiates KYC workflow
* - handleActivatedCustomerKYCResponse() processes completion event
* - No page reload, just content swap
*/
const ActivatedCustomerKYCScreen = {
/**
* Current state (replaces React useState)
*/
state: {
userID: '',
sessionID: '',
isProcessing: false,
kycResult: null
},
/**
* Called when screen content is loaded into #app-content
*
* @param {Object} params - Navigation parameters
* @param {string} params.userID - User identifier
* @param {string} params.sessionID - Session ID from SDK
* @param {Object} params.kycResult - KYC completion result (if returning from workflow)
*/
onContentLoaded(params) {
console.log('ActivatedCustomerKYCScreen - Content loaded with params:', JSON.stringify(params, null, 2));
// Store state from params
this.state = {
userID: params.userID || '',
sessionID: params.sessionID || '',
isProcessing: false,
kycResult: params.kycResult || null
};
// Setup DOM event listeners and SDK event handler
this.setupEventListeners();
// If returning with KYC result, display it
if (this.state.kycResult) {
this.displayKYCResult(this.state.kycResult);
}
},
/**
* Attach event listeners to DOM elements and register SDK event handler
*/
setupEventListeners() {
const menuBtn = document.getElementById('kyc-menu-btn');
const startKYCBtn = document.getElementById('start-kyc-btn');
if (menuBtn) {
menuBtn.onclick = () => {
console.log('ActivatedCustomerKYCScreen - Menu button clicked, opening drawer');
NavigationService.openDrawer();
};
}
if (startKYCBtn) {
startKYCBtn.onclick = () => {
this.handleStartKYC();
};
}
// CRITICAL: Register screen-level event handler for KYC completion
// This handler persists across the entire IDV workflow and receives the final result
const eventManager = rdnaIDVService.getEventManager();
eventManager.setActivatedCustomerKYCResponseHandler(
this.handleActivatedCustomerKYCResponse.bind(this)
);
console.log('ActivatedCustomerKYCScreen - Event handler registered for onIDVActivatedCustomerKYCResponse');
},
/**
* Handle "Start KYC Verification" button click
*
* Initiates the post-login KYC workflow by calling initiateActivatedCustomerKYC.
* This triggers the IDV flow: document scan → selfie → biometric consent.
*/
async handleStartKYC() {
if (this.state.isProcessing) {
console.log('ActivatedCustomerKYCScreen - KYC already in progress, ignoring button click');
return;
}
console.log('ActivatedCustomerKYCScreen - Starting KYC verification');
this.setProcessing(true);
this.hideStatusBanner();
try {
const syncResponse = await rdnaIDVService.initiateActivatedCustomerKYC('Post Login KYC');
console.log('ActivatedCustomerKYCScreen - initiateActivatedCustomerKYC sync response:', JSON.stringify({
longErrorCode: syncResponse.error?.longErrorCode,
shortErrorCode: syncResponse.error?.shortErrorCode,
errorString: syncResponse.error?.errorString
}, null, 2));
// Sync response successful - SDK will trigger IDV events
console.log('ActivatedCustomerKYCScreen - KYC initiated, IDV workflow will begin automatically');
// Note: The actual result will come via onIDVActivatedCustomerKYCResponse event
// after the entire IDV workflow (document + selfie + consent) completes
} catch (error) {
console.error('ActivatedCustomerKYCScreen - initiateActivatedCustomerKYC error:', error);
this.setProcessing(false);
const errorMessage = error.error?.errorString || 'Failed to initiate KYC verification';
this.showStatusBanner(errorMessage, 'error');
}
},
/**
* Handle onIDVActivatedCustomerKYCResponse event
*
* This event is fired TWICE during the flow:
* 1. First fire: After initiateActivatedCustomerKYC is called (workflow initiated)
* 2. Second fire: After the complete IDV workflow finishes (final result)
*
* We only navigate back and display results on the SECOND fire (when we have final status).
*
* @param {Object} data - KYC response event data
* @param {string} data.userID - User identifier
* @param {string} data.sessionID - Session ID
* @param {string} data.reason - KYC reason
* @param {Object} data.status - Status object with statusCode and statusMessage
* @param {Object} data.error - Error object with longErrorCode and errorString
*/
handleActivatedCustomerKYCResponse(data) {
console.log('ActivatedCustomerKYCScreen - onIDVActivatedCustomerKYCResponse event received:', JSON.stringify(data, null, 2));
// First fire: Workflow initiated (no status object yet, just error code validation)
// We log this but don't navigate or display anything
if (!data.status || (!data.status.statusCode && data.error?.longErrorCode === 0)) {
console.log('ActivatedCustomerKYCScreen - KYC workflow initiated (first event), IDV flow will continue');
return;
}
// Second fire: Final result (has status object with statusCode)
console.log('ActivatedCustomerKYCScreen - Final KYC result received, navigating back to screen');
// Navigate back to this screen with the result
// This will trigger onContentLoaded with kycResult in params
NavigationService.navigate('ActivatedCustomerKYC', {
userID: this.state.userID,
sessionID: this.state.sessionID,
kycResult: data
});
},
/**
* Display KYC result (success or error) with alert dialog
*
* Shows an alert dialog matching Cordova's pattern:
* - Error checking priority:
* 1. First check error.longErrorCode - if not 0, it's a sync error
* 2. Then check status.statusCode - if exists and NOT 100 or 0, it's a backend error
* 3. Otherwise it's success
*
* @param {Object} data - KYC result data from event
*/
displayKYCResult(data) {
console.log('ActivatedCustomerKYCScreen - Displaying KYC result');
this.setProcessing(false);
const errorCode = data.error?.longErrorCode;
const errorString = data.error?.errorString;
const statusCode = data.status?.statusCode;
const statusMessage = data.status?.statusMessage;
console.log('ActivatedCustomerKYCScreen - Response validation:', JSON.stringify({
errorCode,
errorString,
statusCode,
statusMessage
}, null, 2));
// Small delay to ensure navigation completes before showing alert
setTimeout(() => {
// Check for sync error first
if (errorCode !== 0) {
// Sync error case
console.error('ActivatedCustomerKYCScreen - Sync error detected:', errorCode);
alert(
'Error\n\n' +
`KYC initiation failed: ${errorString || 'Failed to initiate KYC verification'} (${errorCode})\n\n` +
'Click OK to go to Dashboard.'
);
console.log('ActivatedCustomerKYCScreen - Navigating to Dashboard after sync error');
NavigationService.navigate('Dashboard', this.state);
} else if (statusCode && statusCode !== 100 && statusCode !== 0) {
// Backend error case (status code exists and is not 100 or 0)
console.error('ActivatedCustomerKYCScreen - Backend error detected:', statusCode);
alert(
'Error\n\n' +
`KYC initiation failed: ${statusMessage || 'Failed to initiate KYC verification'} (${statusCode})\n\n` +
'Click OK to go to Dashboard.'
);
console.log('ActivatedCustomerKYCScreen - Navigating to Dashboard after backend error');
NavigationService.navigate('Dashboard', this.state);
} else {
// Success case
console.log('ActivatedCustomerKYCScreen - The KYC process has been completed successfully');
alert(
'Success\n\n' +
`The KYC process has been completed successfully. ${statusMessage || ''}\n\n` +
'Click OK to go to Dashboard.'
);
console.log('ActivatedCustomerKYCScreen - Navigating to Dashboard');
NavigationService.navigate('Dashboard', this.state);
}
}, 300);
// Clear kycResult from state after displaying
this.state.kycResult = null;
},
/**
* Show status banner with message
*
* @param {string} message - Message to display
* @param {string} type - Banner type ('success' or 'error')
*/
showStatusBanner(message, type) {
const statusBanner = document.getElementById('kyc-status-banner');
const statusMessage = document.getElementById('kyc-status-message');
if (statusBanner && statusMessage) {
statusMessage.textContent = message;
// Remove existing type classes
statusBanner.classList.remove('status-banner-success', 'status-banner-error');
// Add appropriate type class
if (type === 'success') {
statusBanner.classList.add('status-banner-success');
} else if (type === 'error') {
statusBanner.classList.add('status-banner-error');
}
statusBanner.style.display = 'block';
}
},
/**
* Hide status banner
*/
hideStatusBanner() {
const statusBanner = document.getElementById('kyc-status-banner');
if (statusBanner) {
statusBanner.style.display = 'none';
}
},
/**
* Set processing state
*
* @param {boolean} isProcessing - Whether KYC is processing
*/
setProcessing(isProcessing) {
this.state.isProcessing = isProcessing;
// Disable/enable start button during processing
const startKYCBtn = document.getElementById('start-kyc-btn');
if (startKYCBtn) {
startKYCBtn.disabled = isProcessing;
startKYCBtn.textContent = isProcessing ? 'Processing...' : 'Start KYC Verification';
startKYCBtn.style.opacity = isProcessing ? '0.6' : '1';
}
}
};
// Expose to global scope for NavigationService
window.ActivatedCustomerKYCScreen = ActivatedCustomerKYCScreen;
// Registered in setupEventListeners(), stays active across navigation
setupEventListeners() {
const eventManager = rdnaIDVService.getEventManager();
eventManager.setActivatedCustomerKYCResponseHandler(
this.handleActivatedCustomerKYCResponse.bind(this)
);
}
// Called by NavigationService when screen becomes active
onContentLoaded(params) {
// Initialize state from params
this.state = { ...params };
// Setup DOM handlers and SDK event handlers
this.setupEventListeners();
// Display result if returning with kycResult
if (this.state.kycResult) {
this.displayKYCResult(this.state.kycResult);
}
}
// 1. Check sync error first
if (errorCode !== 0) {
// Show sync error
}
// 2. Check backend status code
else if (statusCode && statusCode !== 100 && statusCode !== 0) {
// Show backend error
}
// 3. Success
else {
// Show success
}

Verify the implementation:
Test Success Flow:
Test Cancellation Flow:
The document scan process for post-login KYC uses workflow 6 (RDNA_IDV_POSTLOGIN_KYC) and challenge mode 9. The close button behavior is different from pre-login flow.
When SDK triggers getIDVDocumentScanProcessStartConfirmation event:
// Event handler receives workflow 6
const idvWorkflow = this.state.idvWorkflow;
// idvWorkflow = 6 for post-login KYC
// User confirms to start document scan
async handleStartDocumentScan() {
try {
console.log('IDVDocumentProcessStart - Starting document scan with workflow:', idvWorkflow);
// Pass workflow 6 to SDK
await rdnaIDVService.setIDVDocumentScanProcessStartConfirmation(
true, // isConfirm
idvWorkflow // 6 for post-login KYC
);
console.log('IDVDocumentProcessStart - Document scan initiated');
// SDK will open native camera for document capture
} catch (error) {
console.error('IDVDocumentProcessStart - Error:', error);
}
}
// Close button behavior: resetAuthState for pre-login, cancel API for post-login
async handleClose() {
try {
if (idvWorkflow === 0 || idvWorkflow === 5) {
// Pre-login flow: Call resetAuthState
await rdnaService.resetAuthState();
} else {
// Post-login flow (workflow 6): Call cancel API
console.log('IDVDocumentProcessStart - Post-login flow, calling cancel API');
await rdnaIDVService.setIDVDocumentScanProcessStartConfirmation(
false, // isConfirm = false
idvWorkflow // 6
);
// This triggers onActivatedCustomerKYCResponse event with error 240 (User Cancelled)
}
} catch (error) {
console.error('IDVDocumentProcessStart - Close error:', error);
}
}
After document capture, SDK triggers getIDVConfirmDocumentDetails event with challenge mode 9:
// Event handler receives challenge mode 9
const challengeMode = this.state.challengeMode;
// challengeMode = 9 for post-login KYC
// User confirms document details are correct
async handleConfirmDocument() {
try {
console.log('IDVConfirmDocumentDetails - Confirming document with mode:', challengeMode);
await rdnaIDVService.setIDVConfirmDocumentDetails(
true, // isConfirm
challengeMode // 9 for post-login
);
console.log('IDVConfirmDocumentDetails - Document confirmed, waiting for selfie event');
} catch (error) {
console.error('IDVConfirmDocumentDetails - Error:', error);
}
}
// Close button: Challenge mode-based navigation
async handleClose() {
if (!challengeMode && challengeMode !== 0) {
this.setError('Invalid challenge mode. Unable to close.');
return;
}
this.setProcessing(true);
this.setError('');
try {
if (challengeMode === 8) {
// Pre-login flow: Call resetAuthState
console.log('IDVConfirmDocumentDetails - Pre-login flow, calling resetAuthState');
await rdnaService.resetAuthState();
} else {
// Post-login flow (mode 9): Call cancel API
console.log('IDVConfirmDocumentDetails - Post-login flow, calling cancel API');
await rdnaIDVService.setIDVConfirmDocumentDetails(
false, // isConfirm = false
challengeMode // 9
);
// This triggers onActivatedCustomerKYCResponse event
// Persistent handler navigates back to ActivatedCustomerKYC screen
}
} catch (error) {
console.error('IDVConfirmDocumentDetails - Close error:', error);
const errorMessage = error.error?.errorString || 'Failed to close screen';
this.setError(errorMessage);
} finally {
this.setProcessing(false);
}
}
Test the document scan process:
Test Close Button:
onActivatedCustomerKYCResponse event with error 240The selfie capture process for post-login KYC also uses workflow 6 and challenge mode 9, with different close button navigation patterns.
When SDK triggers getIDVSelfieProcessStartConfirmation event:
// Event handler receives workflow 6
const idvWorkflow = this.state.idvWorkflow;
// idvWorkflow = 6 for post-login KYC
// User confirms to start selfie capture
async handleStartSelfieCapture() {
try {
console.log('IDVSelfieProcessStart - Starting selfie capture with workflow:', idvWorkflow);
await rdnaIDVService.setIDVSelfieProcessStartConfirmation(
true, // isConfirm
false, // useDeviceBackCamera (false = front camera for selfies)
idvWorkflow // 6 for post-login KYC
);
console.log('IDVSelfieProcessStart - Selfie capture initiated');
// SDK will open native camera for selfie with liveness detection
} catch (error) {
console.error('IDVSelfieProcessStart - Error:', error);
}
}
// Close button behavior based on workflow
async handleClose() {
try {
if (idvWorkflow === 0 || idvWorkflow === 5) {
// Pre-login flow: Call resetAuthState
await rdnaService.resetAuthState();
} else {
// Post-login flow (workflow 6): Call cancel API
console.log('IDVSelfieProcessStart - Post-login flow, calling cancel API');
await rdnaIDVService.setIDVSelfieProcessStartConfirmation(
false, // isConfirm = false
false,
idvWorkflow // 6
);
// This triggers onActivatedCustomerKYCResponse event with error 240
}
} catch (error) {
console.error('IDVSelfieProcessStart - Close error:', error);
}
}
After selfie capture, SDK triggers getIDVSelfieConfirmation event with challenge mode 9:
// Event handler receives challenge mode 9
const challengeMode = this.state.challengeMode;
// challengeMode = 9 for post-login KYC
// User confirms selfie biometric results
async handleConfirmSelfie() {
try {
console.log('IDVSelfieConfirmation - Confirming selfie with mode:', challengeMode);
await rdnaIDVService.setIDVSelfieConfirmation(
"true", // action (string value)
challengeMode // 9 for post-login
);
console.log('IDVSelfieConfirmation - Selfie confirmed, waiting for consent event');
} catch (error) {
console.error('IDVSelfieConfirmation - Error:', error);
}
}
// Close button: Challenge mode-based navigation
// Important: No cancel API available for selfie confirmation!
async handleClose() {
if (!challengeMode && challengeMode !== 0) {
this.setError('Invalid challenge mode. Unable to close.');
return;
}
this.setProcessing(true);
this.setError('');
try {
if (challengeMode === 8) {
// Pre-login flow: Call resetAuthState
console.log('IDVSelfieConfirmation - Pre-login flow, calling resetAuthState');
await rdnaService.resetAuthState();
console.log('IDVSelfieConfirmation - ResetAuthState successful');
} else {
// Post-login flow (mode 9): Navigate directly to ActivatedCustomerKYC
// No cancel API available for selfie confirmation
console.log('IDVSelfieConfirmation - Post-login flow, navigating to ActivatedCustomerKYC');
NavigationService.navigate('ActivatedCustomerKYC', this.state);
console.log('IDVSelfieConfirmation - Navigated to ActivatedCustomerKYC, button will be auto-enabled');
}
} catch (error) {
console.error('IDVSelfieConfirmation - Close error:', error);
const errorMessage = error.error?.errorString || 'Failed to close screen';
this.setError(errorMessage);
} finally {
this.setProcessing(false);
}
}
Test the selfie capture process:
Test Close Button:
The biometric consent for post-login KYC uses challenge mode 9 with a different response structure compared to pre-login flow.
Aspect | Pre-Login (Mode 8) | Post-Login (Mode 9) |
challengeResponse | Present | Optional/Absent |
status location |
| Root level |
status access |
|
|
When SDK triggers getIDVBiometricOptInConsent event with challenge mode 9:
// Event handler receives challenge mode 9
const challengeMode = this.state.challengeMode;
// challengeMode = 9 for post-login KYC
// User accepts or rejects biometric template storage
async handleConsentAction(isOptIn) {
if (!challengeMode && challengeMode !== 0) {
this.setError('Invalid challenge mode. Unable to process consent.');
return;
}
this.setProcessing(true);
this.setError('');
try {
console.log('IDVBiometricOptInConsent - Processing consent with mode:', challengeMode, 'isOptIn:', isOptIn);
await rdnaIDVService.setIDVBiometricOptInConsent(
isOptIn, // true = accept, false = reject
challengeMode // 9 for post-login
);
console.log('IDVBiometricOptInConsent - Consent processed successfully');
// SDK will trigger onActivatedCustomerKYCResponse event with completion status
} catch (error) {
console.error('IDVBiometricOptInConsent - Error:', error);
const errorMessage = error.error?.errorString || 'Failed to process consent';
this.setError(errorMessage);
} finally {
this.setProcessing(false);
}
}
// Close button: Challenge mode-based navigation
async handleClose() {
if (this.state.isProcessing) return;
if (!challengeMode && challengeMode !== 0) {
this.setError('Invalid challenge mode. Unable to close.');
return;
}
this.setProcessing(true);
this.setError('');
try {
if (challengeMode === 8) {
// Pre-login flow: Call resetAuthState
console.log('IDVBiometricOptInConsent - Pre-login flow, calling resetAuthState');
await rdnaService.resetAuthState();
console.log('IDVBiometricOptInConsent - ResetAuthState successful');
} else {
// Post-login flow (mode 9): Navigate directly to ActivatedCustomerKYC
console.log('IDVBiometricOptInConsent - Post-login flow, navigating to ActivatedCustomerKYC');
NavigationService.navigate('ActivatedCustomerKYC', this.state);
console.log('IDVBiometricOptInConsent - Navigated to ActivatedCustomerKYC, button will be auto-enabled');
}
} catch (error) {
console.error('IDVBiometricOptInConsent - Close error:', error);
const errorMessage = error.error?.errorString || 'Failed to close screen';
this.setError(errorMessage);
} finally {
this.setProcessing(false);
}
}
In the IDV event manager, use optional chaining to handle both structures:
onGetBiometricOptInConsent(event) {
console.log('RdnaIDVEventManager - Get biometric opt-in consent event received');
try {
const response = event.response || event.detail || event;
const data = typeof response === 'string' ? JSON.parse(response) : response;
// Log both potential status locations
console.log('RdnaIDVEventManager - Status Code (root):', data.status?.statusCode);
console.log('RdnaIDVEventManager - Status Code (challengeResponse):', data.challengeResponse?.status?.statusCode);
if (this.getBiometricOptInConsentHandler) {
this.getBiometricOptInConsentHandler(data);
}
} catch (error) {
console.error('RdnaIDVEventManager - Failed to parse biometric opt-in consent:', error);
}
}
Test the biometric consent process:
Test Reject Consent:
Test Close Button:
The complete post-login KYC flow integrates all components: drawer menu, KYC screen, document scan, selfie capture, and biometric consent.
Verify all components are properly integrated:
rdnaIDVService.initiateActivatedCustomerKYC() implementedrdnaIDVEventManager.onActivatedCustomerKYCResponse() implementeddocument.addEventListener()setActivatedCustomerKYCResponseHandler()cleanup() removes listenerActivatedCustomerKYC-templateActivatedCustomerKYCScreen.jsonContentLoaded() lifecycle methodsetupEventListeners() registers DOM and SDK handlershandleStartKYC() initiates KYC workflowhandleActivatedCustomerKYCResponse() processes eventdisplayKYCResult() shows success/error alertsTest the complete KYC flow from start to finish:
Comprehensive testing ensures the post-login KYC flow works correctly across all scenarios.
# Build and run on iOS
cordova build ios
cordova run ios --device
# Build and run on Android
cordova build android
cordova run android --device
Objective: Verify end-to-end KYC completion with all steps succeeding.
Steps:
Expected Result:
Console Verification:
RdnaIDVService - Activated customer KYC initiated successfully
RdnaIDVService - Workflow: 6 (RDNA_IDV_POSTLOGIN_KYC)
RdnaIDVService - Challenge Mode: 9 (post-login)
...
RdnaIDVEventManager - Activated customer KYC response event received
RdnaIDVEventManager - Error Code: 0
RdnaIDVEventManager - Status Code: 100
ActivatedCustomerKYCScreen - The KYC process has been completed successfully
Objective: Verify proper handling when user cancels during document scan.
Steps:
Expected Result:
setIDVDocumentScanProcessStartConfirmation(false, 6)Console Verification:
IDVDocumentProcessStart - Post-login flow, calling cancel API
RdnaIDVEventManager - Error Code: 240
ActivatedCustomerKYCScreen - Sync error detected: 240
Objective: Verify proper handling when user closes selfie confirmation.
Steps:
Expected Result:
Console Verification:
IDVSelfieConfirmation - Post-login flow, navigating to ActivatedCustomerKYC
ActivatedCustomerKYCScreen - Content loaded with params
Objective: Verify KYC completes successfully even when user rejects biometric consent.
Steps:
Expected Result:
setIDVBiometricOptInConsent(false, 9) calledConsole Verification:
IDVBiometricOptInConsent - Processing consent with mode: 9 isOptIn: false
IDVBiometricOptInConsent - Consent processed successfully
ActivatedCustomerKYCScreen - The KYC process has been completed successfully
For each test scenario, verify:
setupEventListeners()onActivatedCustomerKYCResponse eventerror.longErrorCode first (0 = success, 240 = cancelled, others = error)status.statusCode second (100 or 0 = success, others = backend error)Symptoms: KYC completes but screen doesn't navigate back, no alert shown.
Solution:
rdnaIDVEventManager.registerEventListeners()useFocusEffect for handler registrationonActivatedCustomerKYCResponseSymptoms: Alert shown but navigation to Dashboard fails.
Solution:
navigate('Dashboard', params)userID and sessionIDDashboard-template in index.htmlDashboardScreen.js in index.htmlSymptoms: Clicking close button does nothing or shows error.
Solution:
onContentLoaded()Congratulations! You've successfully implemented a complete post-login KYC (Know Your Customer) identity verification flow for activated customers using REL-ID SDK in Cordova.
Throughout this codelab, you've learned:
✅ Post-Login KYC Architecture
✅ Service Layer Implementation
initiateActivatedCustomerKYC() API with promise pattern✅ Event Management
onActivatedCustomerKYCResponse event handling✅ Navigation Integration
✅ Screen Implementation
onContentLoaded()✅ IDV Flow Adaptation
The implementation follows clean separation of concerns:
┌─────────────────────────────────────────────┐
│ Drawer Menu (index.html) │
│ 🆔 Activated Customer KYC │
└──────────────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ ActivatedCustomerKYCScreen.js (SPA) │
│ - Persistent event handler registration │
│ - initiateActivatedCustomerKYC() call │
│ - Dual validation pattern │
│ - Success/error alert display │
└──────────────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ rdnaIDVService.js (Service) │
│ - initiateActivatedCustomerKYC(reason) │
│ - Workflow 6, Mode 9 handling │
│ - Promise-based API wrapper │
└──────────────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ rdnaIDVEventManager.js (Events) │
│ - onActivatedCustomerKYCResponse handler │
│ - Event listener registration │
│ - Handler invocation and cleanup │
└──────────────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ IDV Workflow (SDK Driven) │
│ Document Scan → Selfie → Biometric │
│ (Workflow 6, Challenge Mode 9) │
└─────────────────────────────────────────────┘
This implementation demonstrates several important patterns:
onContentLoaded() for initializationcom.uniken.rdnaplugin.RdnaClientThank you for completing the REL-ID IDV Post-Login KYC Flow codelab! You now have the skills to implement secure, compliant identity verification for your Cordova applications.
Happy Coding! 🎉