@finos_sdk/sdk-ekyc
v1.4.7
Published
React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification
Downloads
686
Maintainers
Readme
@finos_sdk/sdk-ekyc
React Native SDK for eKYC (electronic Know Your Customer) and eSign. Features include Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, C06 residence verification, SMS OTP verification, and Electronic Signature (eSign) capabilities.
Version: 1.4.7
Features
- Unified eKYC Flow - Complete verification flow with single method call
- NFC Reading - Read Vietnamese CCCD cards with NFC chips
- OCR Processing - Extract data from ID card images
- Liveness Detection - Verify user presence with selfie (Active/Passive modes)
- Face Matching - Compare selfie with ID card photo
- C06 Verification - Residence information verification
- eSign - Electronic signature support (PDF signing, Remote signing)
- SMS OTP - Integrated SMS OTP verification
- Flexible Configuration - Custom styling, capture button colors, and retry options
- TypeScript Support - Full type definitions included
Requirements
- React Native 0.70.0+
- Android 24+ / API Level 24+
- iOS 14.0+ (Swift 5.7+)
- NFC and/or Camera hardware depending on modules used
- Runtime permissions for Camera and/or NFC
Installation
npm install @finos_sdk/sdk-ekyc
# or
yarn add @finos_sdk/sdk-ekycQuick Start
1. Import the SDK
import {
FinosEKYC,
FinosESign,
getEkycError,
SDKFaceDetectStatus,
SDKFlowType,
AppIDType,
} from '@finos_sdk/sdk-ekyc';
import type {
NfcConfig,
C06Config,
OcrConfig,
LivenessConfig,
FaceServiceConfig,
SmsOtpConfig,
StartEkycUIResult,
EKYCError,
} from '@finos_sdk/sdk-ekyc';2. Initialize SDK
// Initialize the SDK (required before using any method)
await FinosEKYC.initialize();
// Check SDK info
const info = await FinosEKYC.getSDKInfo();
console.log(info); // { name, version, buildNumber, platform, isInitialized }
// Set transaction ID (optional)
await FinosEKYC.setTransactionId('your-transaction-id');Complete eKYC Flow (startEkycUI)
The recommended way to use the SDK. Handles the complete verification flow with a single method call.
Method Signature
FinosEKYC.startEkycUI(
appKey: string,
flowSDK: SDKFlowType[],
language: string,
transactionId: string,
appKeyConfig: AppKeyConfig,
optionConfig?: OptionConfig,
styleConfig?: StyleConfig,
): Promise<StartEkycUIResult>Full Example with All Configs
const result = await FinosEKYC.startEkycUI(
'your-main-app-key',
// --- flowSDK: verification steps to execute in order ---
['OCR', 'NFC', 'LIVENESS'],
// --- language ---
'vi', // 'vi' | 'en'
// --- transactionId ---
'txn-20260319-001',
// --- appKeyConfig: API keys for each module ---
{
appKey: 'your-main-app-key',
appKeyNfc: 'your-nfc-key',
appKeyOcr: 'your-ocr-key',
appKeyLiveness: 'your-liveness-key',
appKeyC06: 'your-c06-key',
appKeyFaceService: 'your-face-key',
},
// --- optionConfig: SDK behavior settings ---
{
baseUrl: 'https://api.example.com', // Custom API base URL
countMaxRetry: 3, // Max retry attempts (default: 3)
language: 'vi', // Override language
switchFrontCamera: true, // Use front camera (default: false)
isActiveLiveness: true, // Active liveness mode (default: true)
autoCapture: true, // Auto capture (default: true)
forceCaptureTimeout: 30, // Force capture timeout in seconds
isShowCameraFont: true, // Show camera guide text
customActions: [ // Custom liveness actions
SDKFaceDetectStatus.LEFT,
SDKFaceDetectStatus.RIGHT,
SDKFaceDetectStatus.STRAIGHT,
],
activeActionCount: 3, // Random action count 1-10 (if customActions is null)
appIDType: AppIDType.VIKKI, // App ID type: NONE | HD_BANK | VIKKI
},
// --- styleConfig: UI customization ---
{
// Global defaults
textSize: 14, // Default text size (sp)
textFont: '', // Font resource name (e.g. 'roboto_medium')
textColor: 0xFF333333, // Default text color (ARGB int)
statusBarBackground: undefined, // Status bar drawable resource ID
backIcon: undefined, // Back button drawable resource ID
// Per-element text styles (override global defaults)
titleStyle: { // Screen title ("CHUP MAT TRUOC", "Chup chan dung")
textSize: 20,
textFont: 'roboto_bold',
textColor: 0xFF142630,
},
toolbarStyle: { // Toolbar/navigation bar title
textSize: 18,
textFont: '',
textColor: 0xFF007AFF,
},
instructionStyle: { // User instruction text
textSize: 16,
textFont: '',
textColor: 0xFF666666,
},
errorStyle: { // Error message text
textSize: 14,
textFont: '',
textColor: 0xFFFF3B30,
},
successStyle: { // Success message text
textSize: 14,
textFont: '',
textColor: 0xFF34C759,
},
warningStyle: { // Warning message text
textSize: 14,
textFont: '',
textColor: 0xFFFF9500,
},
// Capture button colors (direct color int, ARGB)
captureButtonColor: 0xFF00A86F, // Capture button color when enabled
captureButtonDisabledColor: 0xFFCCCCCC, // Capture button color when disabled
},
);Result
interface StartEkycUIResult {
status: 'success';
event: string;
data?: string;
transactionId?: string;
imageFace?: string; // Base64 selfie/liveness image
imageOcrFront?: string; // Base64 OCR front image
imageOcrBack?: string; // Base64 OCR back image
imageFacePath?: string; // Absolute path to selfie file
imageOcrFrontPath?: string; // Absolute path to OCR front file
imageOcrBackPath?: string; // Absolute path to OCR back file
}Configuration Reference
AppKeyConfig
API keys for each eKYC module.
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| appKey | string | Yes | Main app key |
| appKeyNfc | string | Yes | NFC module key |
| appKeyOcr | string | Yes | OCR module key |
| appKeyLiveness | string | Yes | Liveness module key |
| appKeyC06 | string | Yes | C06 module key |
| appKeyFaceService | string | Yes | Face service module key |
const appKeyConfig = {
appKey: 'your-main-app-key',
appKeyNfc: 'your-nfc-key',
appKeyOcr: 'your-ocr-key',
appKeyLiveness: 'your-liveness-key',
appKeyC06: 'your-c06-key',
appKeyFaceService: 'your-face-key',
};OptionConfig
SDK behavior and runtime settings.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| baseUrl | string | undefined | Custom API base URL |
| countMaxRetry | number | 3 | Max retry attempts on failure |
| language | string | undefined | Override language ('vi' or 'en') |
| switchFrontCamera | boolean | false | Use front camera for liveness/face |
| isActiveLiveness | boolean | true | Enable active liveness detection |
| autoCapture | boolean | true | Auto capture when conditions met |
| forceCaptureTimeout | number | 0 | Force capture timeout in seconds (0 = disabled) |
| isShowCameraFont | boolean | true | Show instruction text on camera screen |
| customActions | SDKFaceDetectStatus[] | undefined | Custom liveness action sequence |
| activeActionCount | number | 2 | Number of random actions (1-10, used when customActions is null) |
| appIDType | AppIDType | NONE | App ID type for branding: NONE, HD_BANK, or VIKKI |
// Minimal
const optionConfig = { countMaxRetry: 3 };
// Full
const optionConfig = {
baseUrl: 'https://api.example.com',
countMaxRetry: 3,
language: 'vi',
switchFrontCamera: true,
isActiveLiveness: true,
autoCapture: true,
forceCaptureTimeout: 30,
isShowCameraFont: true,
customActions: [
SDKFaceDetectStatus.LEFT,
SDKFaceDetectStatus.RIGHT,
SDKFaceDetectStatus.STRAIGHT,
],
activeActionCount: 3,
appIDType: AppIDType.VIKKI,
};AppIDType
Determines the branding/skin of SDK screens.
| Value | Description |
|-------|-------------|
| NONE | Default, no branding |
| HD_BANK | HD Bank branding |
| VIKKI | Vikki branding |
import { AppIDType } from '@finos_sdk/sdk-ekyc';
// In optionConfig
{ appIDType: AppIDType.VIKKI }StyleConfig
UI appearance customization. All color values use ARGB integer format (e.g., 0xFFRRGGBB).
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| textSize | number | 14 | Default text size (sp) |
| textFont | string | '' | Default font resource name (e.g., 'roboto_medium') |
| textColor | number | 0xFF000000 | Default text color (ARGB) |
| statusBarBackground | number | undefined | Status bar background drawable resource ID |
| backIcon | number | undefined | Back button icon drawable resource ID |
| titleStyle | TextStyle | undefined | Style for screen titles |
| toolbarStyle | TextStyle | undefined | Style for toolbar/navigation title |
| instructionStyle | TextStyle | undefined | Style for instruction text |
| errorStyle | TextStyle | undefined | Style for error messages |
| successStyle | TextStyle | undefined | Style for success messages |
| warningStyle | TextStyle | undefined | Style for warning messages |
| captureButtonColor | number | undefined | Capture button color when enabled (ARGB) |
| captureButtonDisabledColor | number | undefined | Capture button color when disabled (ARGB) |
TextStyle (sub-config)
Each text style can override the global defaults. null/undefined fields fall back to the parent StyleConfig defaults.
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| textSize | number | Parent textSize | Text size (sp) |
| textFont | string | Parent textFont | Font resource name |
| textColor | number | Parent textColor | Text color (ARGB) |
// Minimal - just change capture button color
const styleConfig = {
captureButtonColor: 0xFF00A86F,
captureButtonDisabledColor: 0xFFCCCCCC,
};
// Custom theme with all styles
const styleConfig = {
textSize: 14,
textFont: 'roboto_regular',
textColor: 0xFF333333,
titleStyle: {
textSize: 20,
textFont: 'roboto_bold',
textColor: 0xFF142630,
},
toolbarStyle: {
textSize: 18,
textColor: 0xFF007AFF,
},
instructionStyle: {
textSize: 16,
textColor: 0xFF666666,
},
errorStyle: {
textColor: 0xFFFF3B30,
},
successStyle: {
textColor: 0xFF34C759,
},
warningStyle: {
textColor: 0xFFFF9500,
},
captureButtonColor: 0xFF142630,
captureButtonDisabledColor: 0xFFDDDDDD,
};SDKFlowType
Available flow step types for startEkycUI.
| Value | Description |
|-------|-------------|
| 'OCR' | Scan ID card with camera (front/back) |
| 'NFC' | Read NFC chip on CCCD |
| 'LIVENESS' | Liveness detection (selfie) |
// Common flows
const flow1 = ['OCR', 'NFC', 'LIVENESS']; // Full eKYC
const flow2 = ['OCR', 'LIVENESS']; // Without NFC
const flow3 = ['LIVENESS']; // Liveness onlySDKFaceDetectStatus
Available liveness actions for customActions.
| Value | Description |
|-------|-------------|
| LEFT | Turn head left |
| RIGHT | Turn head right |
| UP | Look up |
| DOWN | Look down |
| STRAIGHT | Look straight |
| SMILE | Smile |
| BLINK | Blink eyes |
| TILT_LEFT | Tilt head left |
| TILT_RIGHT | Tilt head right |
| WINK_LEFT | Wink left eye |
| WINK_RIGHT | Wink right eye |
import { SDKFaceDetectStatus } from '@finos_sdk/sdk-ekyc';
const customActions = [
SDKFaceDetectStatus.LEFT,
SDKFaceDetectStatus.RIGHT,
SDKFaceDetectStatus.SMILE,
];Individual Module APIs
NFC Scanning
const config: NfcConfig = {
appKey: 'your-nfc-key', // Required
documentNumber: '012345678012', // Required: CCCD number
birthDate: '01011990', // Required: format DDMMYYYY
expireDate: '01012030', // Required: format DDMMYYYY
transactionId: 'txn-001', // Optional
facePathStorage: '/path/to/face', // Optional: path to save face image
};
const result = await FinosEKYC.startNfcScan(config);NfcConfig
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| appKey | string | Yes | NFC module API key |
| documentNumber | string | Yes | CCCD/CMND number |
| birthDate | string | Yes | Date of birth (DDMMYYYY) |
| expireDate | string | Yes | Expiry date (DDMMYYYY) |
| transactionId | string | No | Transaction ID |
| facePathStorage | string | No | File path to store extracted face image |
OCR Processing
const config: OcrConfig = {
appKey: 'your-ocr-key', // Required
transactionId: 'txn-001', // Required
idImagePath: 'base64_image_data', // Required: Base64 encoded image
expectedDocumentSide: 'front', // Required: 'front' or 'back'
};
const result = await FinosEKYC.startOcr(config);OcrConfig
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| appKey | string | Yes | OCR module API key |
| transactionId | string | Yes | Transaction ID |
| idImagePath | string | Yes | Base64 encoded ID card image |
| expectedDocumentSide | string | Yes | 'front' or 'back' |
Liveness Detection
const config: LivenessConfig = {
appKey: 'your-liveness-key', // Required
selfieImage: 'base64_image', // Required: Base64 encoded selfie
transactionId: 'txn-001', // Optional
switchFrontCamera: true, // Optional: use front camera
isActiveLiveness: true, // Optional: active liveness mode
autoCapture: true, // Optional: auto capture
isShowCameraFont: true, // Optional: show guide text
forceCaptureTimeout: 30, // Optional: force capture timeout (seconds)
customActions: [ // Optional: custom action sequence
SDKFaceDetectStatus.LEFT,
SDKFaceDetectStatus.RIGHT,
SDKFaceDetectStatus.SMILE,
],
activeActionCount: 3, // Optional: random action count (1-10)
};
const result = await FinosEKYC.startLiveness(config);LivenessConfig
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| appKey | string | - | Liveness module API key (required) |
| selfieImage | string | - | Base64 encoded selfie image (required) |
| transactionId | string | undefined | Transaction ID |
| switchFrontCamera | boolean | false | Use front camera |
| isActiveLiveness | boolean | false | Enable active liveness mode |
| autoCapture | boolean | true | Auto capture when conditions met |
| isShowCameraFont | boolean | true | Show camera guide text |
| forceCaptureTimeout | number | 0 | Force capture timeout in seconds |
| customActions | SDKFaceDetectStatus[] | undefined | Custom action sequence (overrides random) |
| activeActionCount | number | 2 | Random actions count 1-10 (when customActions is null) |
Face Comparison
const config: FaceServiceConfig = {
appKey: 'your-face-key', // Required
selfieImage: 'base64_selfie', // Required: Base64 selfie image
idImage: 'base64_id_image', // Required: Base64 ID card image
transactionId: 'txn-001', // Optional
appKeyFaceService: 'alt-key', // Optional: alternative key
};
const result = await FinosEKYC.startFaceCompare(config);FaceServiceConfig
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| appKey | string | Yes | Face service API key |
| selfieImage | string | Yes | Base64 encoded selfie image |
| idImage | string | Yes | Base64 encoded ID card portrait |
| transactionId | string | No | Transaction ID |
| appKeyFaceService | string | No | Alternative face service key |
C06 Verification
const config: C06Config = {
appKey: 'your-c06-key', // Required
sod: 'nfc_sod_data', // Required: SOD data from NFC
idCardNumber: '012345678012', // Required: CCCD number
recentLocation: 'Ha Noi', // Optional: recent location
transactionId: 'txn-001', // Optional
deviceType: 'android', // Optional
};
const result = await FinosEKYC.checkC06(config);C06Config
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| appKey | string | Yes | C06 module API key |
| sod | string | Yes | SOD data from NFC scan |
| idCardNumber | string | Yes | CCCD/CMND number |
| recentLocation | string | No | Recent location for verification |
| transactionId | string | No | Transaction ID |
| deviceType | string | No | Device type identifier |
SMS OTP Integration
const otpConfig: SmsOtpConfig = {
appKey: 'your-api-key', // Required: X-FinOS-Api-Key
phoneNumber: '+84901234567', // Required: with country code
referenceId: 'ref-001', // Required: unique reference
purpose: 'transaction', // Optional (default: 'transaction')
requestId: undefined, // Required for verify/resend (from send response)
};
// 1. Send OTP
const sendResult = await FinosEKYC.sendOtp(otpConfig);
console.log('Request ID:', sendResult.requestId);
// 2. Verify OTP (use requestId from send response)
otpConfig.requestId = sendResult.requestId;
const verifyResult = await FinosEKYC.verifyOtp(otpConfig, '123456');
// 3. Resend OTP
const resendResult = await FinosEKYC.resendOtp(otpConfig);SmsOtpConfig
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| appKey | string | Yes | SMS OTP API key (X-FinOS-Api-Key) |
| phoneNumber | string | Yes | Phone number with country code (e.g., +84901234567) |
| referenceId | string | Yes | Unique reference ID |
| purpose | string | No | Purpose of OTP (default: 'transaction') |
| requestId | string | For verify/resend | Request ID from send response |
eSign Integration
Initialize
// Initialize eSign SDK
await FinosESign.initializeESign('your-finos-token', false); // token, isProd
// Get SDK token
const token = await FinosESign.getSdkToken(
'012345678012', // CCCD number
'Nguyen Van A', // Full name
'device-id-123', // Device ID
);Open Session
const session = await FinosESign.openSessionId(
'access-token', // Access token (or null)
'username', // Username (or null)
true, // Remember me (or null)
);
console.log(session.deviceState, session.code, session.message);Device Registration
const result = await FinosESign.registerDevice(
'12345678', // 8-digit recovery code
'123456', // 6-digit PIN code
'fcm-token', // Optional FCM token
);Certificate Management
// List certificates
const { certs } = await FinosESign.listCerts(1, 10);
// certs: [{ serial, subject, validFrom, validTo }]
// Verify certificate
const verifyResult = await FinosESign.verifyCert('cert-serial');Sign Documents
// Sign a PDF
const signResult = await FinosESign.signPdf(JSON.stringify(signRequest));
// Sign PDF at multiple positions
const multiResult = await FinosESign.signPdfMultiplePositions(JSON.stringify(signRequest));
// Confirm signature
const confirmResult = await FinosESign.confirmSign(
'sign-request-id', // Sign request ID
'123456', // PIN code
'auth-id', // Optional auth ID
'auth-data', // Optional auth data
true, // Confirm (default: true)
);Remote Signing
// Register for remote signing
const regResult = await FinosESign.registerRemoteSigning(JSON.stringify(requestBody));
// Register and Confirm in one step
const compositeResult = await FinosESign.registerAndConfirm(
JSON.stringify(requestBody),
'base64-confirmation-doc',
);
// Send confirmation document
const docResult = await FinosESign.sendConfirmationDocument(JSON.stringify(requestBody));Authorization Management
// Initialize authorize request
await FinosESign.initAuthorize(
'cert-serial', // Certificate serial
10, // Quantity (signing count)
3600, // Time (seconds until expiry)
'Authorization msg',// Message
);
// List authorize requests
const { requests } = await FinosESign.listAuthorize(1, 10, 'ACTIVE');
// Register/Confirm authorize
await FinosESign.registerAuthorize(
'auth-id',
'auth-data',
'authorize-request-id',
'123456', // PIN code
true, // Confirm
);Event Listeners
NFC Events
FinosEKYC.onNfcScanStart((data) => console.log('NFC scan started', data));
FinosEKYC.onNfcScanSuccess((data) => console.log('NFC data:', data));
FinosEKYC.onNfcError((error) => console.error('NFC error:', error.code, error.message));OCR Events
FinosEKYC.onOcrSuccess((data) => console.log('OCR result:', data));
FinosEKYC.onOcrError((error) => console.error('OCR error:', error.code, error.message));Liveness Events
FinosEKYC.onLivenessSuccess((data) => console.log('Liveness result:', data));
FinosEKYC.onLivenessError((error) => console.error('Liveness error:', error.code, error.message));Face Compare Events
FinosEKYC.onFaceCompareSuccess((data) => console.log('Face match:', data));
FinosEKYC.onFaceCompareError((error) => console.error('Face error:', error.code, error.message));C06 Events
FinosEKYC.onC06Success((data) => console.log('C06 result:', data));
FinosEKYC.onC06Error((error) => console.error('C06 error:', error.code, error.message));SMS OTP Events
FinosEKYC.onSmsOtpSendSuccess((data) => console.log('OTP sent:', data));
FinosEKYC.onSmsOtpResendSuccess((data) => console.log('OTP resent:', data));
FinosEKYC.onSmsOtpError((error) => console.error('OTP error:', error.code, error.message));eSign Events
FinosESign.onESignInitSuccess((data) => console.log('eSign init:', data));
FinosESign.onESignOpenSessionSuccess((data) => console.log('Session:', data));
FinosESign.onESignRegisterDeviceSuccess((data) => console.log('Device registered:', data));
FinosESign.onESignListCertsSuccess((data) => console.log('Certs:', data));
FinosESign.onESignSignPdfSuccess((data) => console.log('PDF signed:', data));
FinosESign.onESignError((error) => console.error('eSign error:', error.code, error.message));Cleanup
// Remove all event listeners when done
FinosEKYC.removeAllListeners();
FinosESign.removeAllListeners();Error Handling
All errors follow a unified EKYCError format:
interface EKYCError {
event: string; // Error event name (e.g., 'FACE_ERROR', 'NFC_ERROR')
code: string; // Error code (e.g., '110', '200', '999')
message: string; // Human-readable message
}Usage
import { getEkycError, fromCode, getLocalizedMessage, USER_CANCEL } from '@finos_sdk/sdk-ekyc';
try {
const result = await FinosEKYC.startEkycUI(/* ... */);
} catch (error) {
const err = getEkycError(error);
console.error(`[${err.event}] Code: ${err.code}, Message: ${err.message}`);
// Check for user cancellation
if (err.code === '999' || err.code === USER_CANCEL.code) {
console.log('User cancelled the flow');
return;
}
// Lookup error details by code
const errorInfo = fromCode(err.code);
if (errorInfo) {
console.log('VI:', getLocalizedMessage(errorInfo, 'vi'));
console.log('EN:', getLocalizedMessage(errorInfo, 'en'));
}
}Common Error Codes
| Code | Constant | Description |
|------|----------|-------------|
| 999 | USER_CANCEL | User cancelled the flow |
| 000 | ERROR_UNKNOWN | Unknown error |
| 0 | SDK_START_ERROR | Failed to start SDK (check key) |
| 001 | OCR_UNRECOGNIZED_ID_CARD | Cannot recognize ID card |
| 110 | LIVENESS_ERROR | Liveness detection error |
| 109 | FACE_ERROR | Face comparison failed |
| 200 | SCAN_NFC_ERROR | NFC scan failed |
| 203 | C06_ERROR | C06 verification error |
| 209 | NFC_USER_CANCEL | User cancelled NFC scan |
| 300 | SMS_OTP_ERROR | SMS OTP error |
Lifecycle Management
Handle app lifecycle events for NFC:
import { AppState } from 'react-native';
useEffect(() => {
const subscription = AppState.addEventListener('change', (state) => {
if (state === 'active') {
FinosEKYC.onResume();
} else if (state === 'background') {
FinosEKYC.onPause();
}
});
return () => {
subscription.remove();
FinosEKYC.removeAllListeners();
};
}, []);What's New
v1.4.2
AppIDTypeenum (NONE, HD_BANK, VIKKI) for branding/skin selectioncaptureButtonColorandcaptureButtonDisabledColorin StyleConfigUSER_CANCELerror code (999) for user cancellation detection- User cancellation callback support across all modules
- Android responsive layouts for camera preview screens
- Refactored StyleConfig JSON parsing with proper JSONObject
v1.4.1
- Full iOS native bridge support
- eSign module with PDF signing, remote signing, authorization
- SMS OTP integration
- Active Liveness with custom actions
