Incode Cordova SDK reference
Incode Welcome
Incode Welcome
Incode Welcome provides effortless onboarding where security matters.
Incode Welcome is part of Incode Omnichannel Biometric Identity Platform, that is powered by Incode's world class Face Recognition, Liveness detection and ID Validation models. Organizations can choose to have an optional video conference to additionally verify the customer’s identity.
In this repo you can find an example of onboarding app that uses Cordova Plugin to to enable remote account opening.
Supported Android Version
API 21+ & CompileSDK version 34.
Supported iOS Version
iOS 13+
Additional Steps for Android
Cordova sample app needs to provide a source from where to fetch Android SDK dependency from.
Suggested method would be to add a hook within the config.xml file that will be triggered after platform is added:
</platform>
<hook type="after_platform_add" src="hooks/after_platform_add/add-incode-maven-repo.js" />
</widget>
add-incode-maven-repo.js content that can be reused:
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const repoBlock = `maven {
url = uri("https://maven.pkg.github.com/Incode-Technologies-Example-Repos/android-omni-packages")
credentials {
username = project.findProperty("github_username") ?: System.getenv("GITHUB_USERNAME")
password = project.findProperty("github_token") ?: System.getenv("GITHUB_TOKEN")
}
}`;
function removeIncodeMavenBlocks(block) {
let idx = 0;
while (true) {
const mavenIndex = block.indexOf('maven {', idx);
if (mavenIndex === -1) break;
// Only consider blocks containing the Incode repo URL
const urlIdx = block.indexOf('repo.incode.com/artifactory/libs-incode-welcome', mavenIndex);
if (urlIdx === -1) {
idx = mavenIndex + 7;
continue;
}
// Find the matching closing brace for maven { ... }
let braceCount = 0;
let endIdx = mavenIndex;
let found = false;
for (; endIdx < block.length; endIdx++) {
if (block[endIdx] === '{') braceCount++;
else if (block[endIdx] === '}') braceCount--;
if (braceCount === 0 && endIdx > mavenIndex) {
found = true;
break;
}
}
if (found) {
block = block.slice(0, mavenIndex) + block.slice(endIdx + 1);
idx = mavenIndex; // Continue searching after removed block
} else {
break; // Malformed, stop
}
}
return block;
}
function insertRepoFirst(block) {
block = removeIncodeMavenBlocks(block);
return block.replace(/repositories\s*\{/, match => `${match}\n${repoBlock}\n`);
}
function patchGradle(context) {
const projectRoot = context.opts && context.opts.projectRoot ? context.opts.projectRoot : process.cwd();
const appGradlePath = path.join(projectRoot, 'platforms/android/app/build.gradle');
if (!fs.existsSync(appGradlePath)) {
console.log('app/build.gradle not found:', appGradlePath);
return;
}
let appGradle = fs.readFileSync(appGradlePath, 'utf8');
// Patch buildscript
appGradle = appGradle.replace(/(buildscript\s*\{[\s\S]*?)(\n\s*dependencies\s*\{)/, (match, p1, p2) => {
let beforeDeps = p1;
if (/repositories\s*\{/.test(beforeDeps)) {
beforeDeps = insertRepoFirst(beforeDeps);
} else {
beforeDeps = beforeDeps.replace(/buildscript\s*\{/, m => `${m}\n repositories {\n${repoBlock}\n }\n`);
}
return beforeDeps + p2;
});
// Patch allprojects
appGradle = appGradle.replace(/(allprojects\s*\{[\s\S]*?)(\n\s*task |\n\s*ext |\n\s*android |\n\s*\/\*|$)/, (match, p1, p2) => {
let beforeNext = p1;
if (/repositories\s*\{/.test(beforeNext)) {
beforeNext = insertRepoFirst(beforeNext);
} else {
beforeNext = beforeNext.replace(/allprojects\s*\{/, m => `${m}\n repositories {\n${repoBlock}\n }\n`);
}
return beforeNext + p2;
});
fs.writeFileSync(appGradlePath, appGradle, 'utf8');
}
// Cordova will call as a function during the build, but allow running directly for manual testing
module.exports = patchGradle;
if (require.main === module) {
patchGradle({ opts: { projectRoot: process.cwd() } });
}
Additional Steps for iOS
-
For Cordova
Run "pod install" under ios folder -
For Capacitor/Ionic
a. Copy below lines in the pod file
source 'https://github.com/CocoaPods/Specs.git' source '[email protected]:Incode-Technologies-Example-Repos/IncdDistributionPodspecs.git'b. Run "pod install" under ios/app folder or "pod install --repo-update" under ios/app folder
c. npx cap sync
Documentation
Install the Incode cordova plugin in your app path using command:
cordova plugin add incode-cordova-plugin
OR
cordova plugin add https://github.com/Incode-Technologies-Example-Repos/incode-cordova-plugin.git
OR for nfc version of plugin
cordova plugin add "https://github.com/Incode-Technologies-Example-Repos/incode-cordova-plugin.git#mainNfc"
As our SDK needs camera to capture Id and face, use command:
cordova plugin add cordova-plugin-camera
Same applies for GeoLocation if its required use command:
cordova plugin add cordova-plugin-geolocation
Implementation
Following functions can be used to initialize the SDK with different modules as per your business requirement.
- SDK Initialize - Below function will initialize Incode SDK which is required atleast once in app life cycle.
cordova.exec(function(param) {
console.log("Success: "+param);
// start the incode session here
}, function(err) {
console.log("Error: "+ err);
// handle the error by showing some UI or alert.
}, "Cplugin", "initializeSDK", [apiKey,apiUrl,loggingEnabled,testMode]);
Above function take following mandatory parameters
-
apiKey - String - Pass here API mentioned in your delivery document
-
apiUrl - String - Pass here API URL mentioned in your delivery document.
-
loggingEnabled - String - true/false should be the value to enable or disable logging.
-
testMode - Set to true if running on simulator/emulators, false when running on devices. Default false.
-
Start the session - Below function will start the fresh session when nothing passed in interviewId. Or else reopens the exisiting session. It returns interviewId and token on success callback.
cordova.exec(function(data) {
console.log("OnSession created Success: "+data.interviewId + " Token:"+data.token);
// Start the Incode moudles or sections from here
}, function(err) {
console.log(" startSession Error: "+ err);
// handle the error by showing some UI or alert.
}, "Cplugin", "setupOnboardingSession", [
configId,
externalId,
interviewId,
externalToken,
disableHookCheck,
disableEmulatorDetection,
disableRootDetection,
disableJailbreakDetection,
disableVirtualEnvironmentDetection,
]);
Above function take following mandatory parameters
-
configId - String - This id can be taken from our incode dashboard also called as flow id. This will apply backend settings as per set on dashboard.
-
externalId - String - This is the custom id which client can pass based on their unique identifier of the user. If not used pass null.
-
interviewId - String - This is the session id on our dashboard if you want to restart same session then pass id here. If not used or first time session this should be null.
-
externalToken - String - This is the session token generated by your backend after calling our
omni/startAPI(make sure to add token value in this parameter). configId can be null if this is passed. -
disableHookCheck - String - This is flag to disable hook check. Used only on Android.
-
disableEmulatorDetection - String - This is flag to disable emulator detection. Used only on Android.
-
disableRootDetection - String - This is flag to disable root detection. Used only on Android.
-
disableJailbreakDetection - String - This is flag to disable jailbreak detection. Used only on iOS.
-
disableVirtualEnvironmentDetection - String - This is flag to disable virtual environment detection. Used only on Android.
To Set theme for iOS, use below fucntion. Make sure to call this function before startOnboardingSection.
cordova.exec(function(param) {
}, function(err) {
console.log("Error in set Theme: "+ err);
}, "Cplugin", "setTheme", [jsonTheme]);
- Start the section by clubbing multiple modules. Refer code below to start the section with default settings.
cordova.exec(function(winParam) {
console.log("Phone added Success: "+winParam);
// handle the response here
}, function(err) {
console.log("Error: "+ err);
// handle the error by showing some UI or alert.
}, "Cplugin", "startOnboardingSection", [
{"module":"addPhone"},{"module":"addGeolocation"},
{"module":"addUserConsent","title":"Title","content":"Content"},
{"module":"addMachineLearningConsent","consentType":"US"},
{"module":"addId","showTutorials":"true","waitForTutorials":"false",
"idCategory":"FIRST","enableBackShownAsFrontCheck":"true",
"enableFrontShownAsBackCheck":"true","autocaptureUXMode":"HOLDSTILL"},
{"module":"addSelfieScan","showTutorials":"true","waitForTutorials":"false",
"maskCheckEnabled":"true","lensesCheckEnabled":"true"},
{
module: "addFaceAuthentication",
showTutorials: "false",
autoCaptureTimeout: "3",
captureAttempts: "3",
eyesClosedCheck: "true",
headCoverCheck: "true",
lensesCheck: "true",
faceMaskCheck: "true",
},
{
"module":"addFaceMatch",
"matchType":"idSelfie"
},
{
"module":"addNFC",
"idType": "passport",
"showNFCSymbolConfirmationScreen":"false",
"showInitialDataConfirmationScreen":"false"
"processNFCData":"false"
},
{"module":"addEKYC"},
{"module":"addAntifraud"},
{"module":"addGovernmentValidation"},
{"module":"addDocumentScan","documentType":"ADDRESS_STATEMENT"},
{"module":"addSignature"}]);
Our Cordova plugin supports following UI & Non UI modules:
-
addPhone
-
addGeolocation
-
addId
-
addSelfieScan
-
addFaceMatch
-
addSignature
-
addGovernmentValidation
-
addDocumentScan
-
userScore
-
approve (Non UI)
-
getUserScore (Non UI)
-
faceMatch (Non UI)
-
startFaceLogin
-
finishOnboarding (Non UI)
-
addVideoSelfie
-
addEKYC
-
addAntifraud
-
addNFC
-
addFaceAuthentication
To load individual modules one at a time refer below example. For all the modules checkout our sample code.
cordova.exec(function(winParam) {
console.log("Phone added Success: "+winParam);
//start next module here
}, function(err) {
console.log("Error: "+ err);
// handle the error by showing some UI or alert.
}, "Cplugin", "startOnboardingSection", ["addId"]);
To fetch scores or results once onboarding modules are done.
cordova.exec(function(winParam) {
myObj = JSON.stringify(winParam);
console.log("Score : "+myObj);
}, function(err) {
console.log("Error: "+ err);
}, "Cplugin", "getUserScore", ["fast"]);
To finish the session below code is mandatory after all sections or modules are completed successfully. This should be called only once when all the sections are done.
cordova.exec(function(winParam) {
console.log("finishOnboarding Success: "+winParam);
}, function(err) {
console.log("Error: "+ err);
callbackError('Nothing to echo.' +err);
}, "Cplugin", "finishOnboarding", []);
Make sure to delete local cache data once all all the above steps are over.
cordova.exec(function(winParam) {
console.log("deleteLocalUserData Success: "+winParam);
}, function(err) {
console.log("Error: "+ err);
}, "Cplugin", "deleteUserLocalData", []);
V2 Customization guide
Use IncodeOnboardingSdk.setTheme(theme: theme) method to provide a JSON for your custom theme that will be applied to both Android and iOS V2 UX:
Sample JSON:
{
"displayMode": "light",
"typography": {
"family": {
"text": {
"android": {
"regular": "onboard_sdk_dm_sans_regular",
"bold": "onboard_sdk_dm_sans_bold",
"medium": "onboard_sdk_dm_sans_medium"
},
"ios": {
"regular": "DMSans-Regular",
"bold": "DMSans-Bold",
"medium": "DMSans-Medium"
}
},
"display": {
"android": {
"regular": "onboard_sdk_dm_sans_regular",
"bold": "onboard_sdk_dm_sans_bold",
"medium": "onboard_sdk_dm_sans_medium"
},
"ios": {
"extraBold": "DMSans-ExtraBold"
}
}
},
"letterSpacing": {
"none": 0,
"medium": -0.5,
"large": -1.0,
"extraLarge": -1.5
}
},
"colorPalette": {
"neutral": "#ffffff",
"black": "#000000",
"brand50": "#e5f0ff",
"brand200": "#99c3ff",
"brand300": "#66a6ff",
"brand400": "#3388ff",
"brand500": "#006aff",
"brand600": "#0055cc",
"brand900": "#21273b",
"gray50": "#FCFCFD",
"gray100": "#EBECEF",
"gray200": "#C6C8D2",
"gray300": "#A3A8B8",
"gray500": "#60667C",
"gray700": "#3A3E4B",
"gray800": "#262831",
"gray900": "#14151A",
"brandSecondary50": "#F2E2FE",
"brandSecondary500": "#820AD1",
"negative50": "#FFF0F0",
"negative500": "#FF5A5F",
"negative600": "#E71111",
"negative950": "#240001",
"warning50": "#FFF7EB",
"warning400": "#FFB647",
"warning500": "#FF9900",
"warning950": "#523100",
"positive50": "#E4FBF0",
"positive600": "#189F60",
"positive800": "#0C5030"
}
}V1 Customization guide
To change theme and resources (text, images and videos) on Android platform please look at a guide here.
To change resources on iOS platform please look at a guide here.
To change theme on iOS platform specify json theme configuration and call IncodeOnboardingSdk.setTheme(theme: theme):
Sample JSON to use for iOS platform:
Map<String, dynamic> theme = {
"colors": {
"accent": "#00B2FD",
"primary": "#20263D",
"background": "#FFFFFF",
"secondaryBackground": "#E9E9EB",
"success": "#0CD5A2",
"error": "#FF5C6F",
"warning": "#F3AB3C",
"cancel": "#20263D"
},
"fonts": {
"buttonBig": {
"name": "CircularXXTT-Black",
"size": "20"
},
"buttonMedium": {
"name": "CircularXXTT-Black",
"size": "16"
},
"buttonSmall": {
"name": "CircularXXTT-Black",
"size": "12"
},
"title": {
"name": "CircularXXTT-Black",
"size": "25"
},
"hugeTitle": {
"name": "CircularXXTT-Black",
"size": "40"
},
"subtitle": {
"name": "CircularXXTT-Black",
"size": "18"
},
"boldedSubtitle": {
"name": "CircularXXTT-Bold",
"size": "18"
},
"smallSubtitle": {
"name": "CircularXXTT-Black",
"size": "14"
},
"info": {
"name": "CircularXXTT-Black",
"size": "16"
},
"body": {
"name": "CircularXXTT-Medium",
"size": "14"
},
"boldedBody": {
"name": "CircularXXTT-Bold",
"size": "14"
},
"textFieldBig": {
"name": "CircularXXTT-Black",
"size": "20"
},
"textFieldMedium": {
"name": "CircularXXTT-Black",
"size": "15"
}
},
"buttons": {
"primary": {
"states": {
"normal": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#00B2FD",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 32,
"shadowColor": "#000000",
"shadowOffset": [0,5],
"shadowOpacity": 0.15,
"shadowRadius": 9,
"textColor": "#FFFFFF",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
},
"highlighted": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#20263D",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 32,
"shadowColor": "#000000",
"shadowOffset": [0,5],
"shadowOpacity": 0.15,
"shadowRadius": 9,
"textColor": "#00B2FD",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
},
"disabled": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#E9E9EB",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 32,
"shadowColor": "#000000",
"shadowOffset": [0,5],
"shadowOpacity": 0,
"shadowRadius": 9,
"textColor": "#FFFFFF",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
}
},
"big": {
"height": 64,
"minWidth": 200,
"contentInsets": {
"top": 19,
"left": 32,
"bottom": 19,
"right": 32
},
"kerning": 0
},
"medium": {
"height": 46,
"minWidth": 0,
"contentInsets": {
"top": 12,
"left": 24,
"bottom": 12,
"right": 24
},
"kerning": 0
}
},
"secondary": {
"states": {
"normal": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#FFFFFF",
"borderColor": "#20263D",
"borderWidth": 1,
"cornerRadius": 32,
"shadowColor": "",
"shadowOffset": [0,0],
"shadowOpacity": 0,
"shadowRadius": 0,
"textColor": "#20263D",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
},
"highlighted": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#20263D",
"borderColor": "#20263D",
"borderWidth": 1,
"cornerRadius": 32,
"shadowColor": "",
"shadowOffset": [0,0],
"shadowOpacity": 0,
"shadowRadius": 0,
"textColor": "#00B2FD",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
},
"disabled": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#FFFFFF",
"borderColor": "#E9E9EB",
"borderWidth": 1,
"cornerRadius": 32,
"shadowColor": "",
"shadowOffset": [0,0],
"shadowOpacity": 0,
"shadowRadius": 0,
"textColor": "#E9E9EB",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
}
},
"big": {
"height": 64,
"minWidth": 200,
"contentInsets": {
"top": 19,
"left": 32,
"bottom": 19,
"right": 32
},
"kerning": 0
},
"medium": {
"height": 46,
"minWidth": 0,
"contentInsets": {
"top": 12,
"left": 24,
"bottom": 12,
"right": 24
},
"kerning": 0
}
},
"text": {
"states": {
"normal": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 0,
"shadowColor": "",
"shadowOffset": [0,0],
"shadowOpacity": 0,
"shadowRadius": 0,
"textColor": "#20263D",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
},
"highlighted": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 0,
"shadowColor": "",
"shadowOffset": [0,0],
"shadowOpacity": 0,
"shadowRadius": 0,
"textColor": "#00B2FD",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
},
"disabled": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 0,
"shadowColor": "",
"shadowOffset": [0,0],
"shadowOpacity": 0,
"shadowRadius": 0,
"textColor": "#E9E9EB",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
}
},
"big": {
"height": 40,
"minWidth": 200,
"contentInsets": {
"top": 8,
"left": 16,
"bottom": 8,
"right": 16
},
"kerning": 0
},
"medium": {
"height": 30,
"minWidth": 0,
"contentInsets": {
"top": 12,
"left": 24,
"bottom": 12,
"right": 24
},
"kerning": 0
}
},
"help": {
"states": {
"normal": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#00B2FD",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 32,
"shadowColor": "#000000",
"shadowOffset": [0,5],
"shadowOpacity": 0.15,
"shadowRadius": 9,
"textColor": "#FFFFFF",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
},
"highlighted": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#20263D",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 32,
"shadowColor": "#000000",
"shadowOffset": [0,5],
"shadowOpacity": 0.15,
"shadowRadius": 9,
"textColor": "#00B2FD",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
},
"disabled": {
"animateStateChange": true,
"alpha": 1,
"backgroundColor": "#E9E9EB",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 32,
"shadowColor": "#000000",
"shadowOffset": [0,5],
"shadowOpacity": 0,
"shadowRadius": 9,
"textColor": "#FFFFFF",
"transform": {
"a": 1,
"b": 0,
"c": 0,
"d": 1,
"tx": 0,
"ty": 0
}
}
},
"big": {
"height": 64,
"minWidth": 200,
"contentInsets": {
"top": 19,
"left": 32,
"bottom": 19,
"right": 32
},
"kerning": 0
},
"medium": {
"height": 46,
"minWidth": 0,
"contentInsets": {
"top": 12,
"left": 24,
"bottom": 12,
"right": 24
},
"kerning": 0
}
}
},
"labels": {
"title": {
"textColor": "#20263D",
"kerning": 0
},
"secondaryTitle": {
"textColor": "#FFFFFF",
"kerning": 0
},
"subtitle": {
"textColor": "#20263D",
"kerning": 0
},
"secondarySubtitle": {
"textColor": "#FFFFFF",
"kerning": 0
},
"smallSubtitle": {
"textColor": "#20263D",
"kerning": 0
},
"info": {
"textColor": "#636670",
"kerning": 0
},
"secondaryInfo": {
"textColor": "#FFFFFF",
"kerning": 0
},
"body": {
"textColor": "#20263D",
"kerning": 0
},
"secondaryBody": {
"textColor": "#FFFFFF",
"kerning": 0
},
"code": {
"textColor": "#20263D",
"kerning": 16
}
},
"customComponents": {
"cameraFeedback": {
"alpha": 0.8,
"backgroundColor": "#000000",
"cornerRadius": 20,
"textBackgroundColor": "",
"textColor": "#FFFFFF"
},
"idCaptureHelp": {
"commonIssueLayoutOrientation": "horizontal"
},
"idSideLabel": {
"alpha": 1,
"backgroundColor": "#FFFFFF",
"borderColor": "",
"borderWidth": 0,
"cornerRadius": 5
},
"separator": {
"alpha": 1.0,
"color": "#20263D",
"cornerRadius": 0,
"padding": 24,
"thickness": 1
},
"signature": {
"signatureColor": "#04BD19",
"canvasBorderColor": "#EC03FC"
},
"idAutocaptureCountdown": {
"backgroundColor": "#00B2FD",
"numberColor": "#FFFFFF"
}
},
};
IncodeOnboardingSdk.setTheme(theme: theme);Set UX configuration
To programmatically set UX configuration in runtime, call IncodeSdk.setUXConfig.
const uxConfig = JSON.stringify({
"showFooter": false
});
cordova.exec(function(param) {
}, function(err) {
}, "Cplugin", "setUXConfig", [uxConfig]);Start workflow
Cplugin.startWorkflow starts the workflow defined on Incode's dashboard.
To choose the workflow to start and session options, specify parameters inside the required sessionConfig argument:
configurationId(string, required): The configuration ID of the workflow as defined in the dashboard.region(string): Supported values areALL(all regions),BR(Brazil), andIN(India).queue(string): The queue to which this onboarding session will be attached.interviewId(string): If you have created an onboarding session on the backend, provide itsinterviewIdto start the flow for that session.token(string): If you have created an onboarding session on the backend, provide itstokento start the flow for that session.externalId(string): An external identifier used outside of Incode Omni.externalCustomerId(string): Links the onboarding session to an entity in an external system, such as a prospect or customer ID from your database.customFields(object): Custom key-value data to attach to this onboarding session.
Example usage:
const sessionConfig = {
configurationId: "your-workflow-id",
};
cordova.exec(function(winParam) {
console.log("startWorkflow Success: "+winParam);
}, function(err) {
console.log("Error: "+ err);
}, "Cplugin", "startWorkflow", [sessionConfig]); License
Copyright 2018 Incode Technologies. All rights reserved.
Updated 17 days ago
