react-native-kalapa-ekyc
v1.3.2
Published
React Native SDK for Kalapa eKYC integration
Readme
Kalapa eKYC React Native SDK Documentation
Complete guide for integrating Kalapa eKYC functionality into your React Native applications.
Changelog
1.3.2
- iOS: Re-enable QR Code step
1.3.1
- Android: Optimize QR-code step
1.3.0
- TypeScript: Fix default export being inferred as
any. Spreading the native module (...NativeModules.KalapaEkyc) into the exported object previously erased all typing, soKalapaEkyc.start()and its result fell back toanyin consumer code. The module is now declared via aKalapaEkycModuleinterface, sostart()is fully typed and returnsPromise<KalapaEkycResponse>({ kalapa_result: KalapaResult; session?: string }) — no casting needed to accesskalapa_resultor its methods. - Android: Update to latest version of Kalapa eKYC to use latest version of Android Camera X 1.6.1.
1.2.10
- Android: Fix switch fall-through in
onErrorcallback — prevents Promise being rejected multiple times - Android: Fix
getCurrentActivity() == nullcase now rejects Promise withACTIVITY_NULLinstead of leaving it pending forever - Android: Fix native crash when optional config keys are missing — all boolean/int fields now use safe
hasKeyguards - Android: SDK now runs on UI thread to safely open Activity
- Android: Bump
compileSdkVersionandtargetSdkVersionto 35 - iOS: Fix
handleResultcrash whenocr_data.data.fieldsis nil (e.g.nfc_onlyflow) - iOS: Fix
qr_coderesponse format — now correctly wrapped as{ data: { decoded_text } }to match TypeScript parser - iOS: Unify
EventSessionExpirederror code toEXPIRED(wasUNAUTHORIZED) - TypeScript:
configparameter is now fully typed (KalapaEkycConfig), no moreany - TypeScript:
flowparameter is now typed asKalapaEkycFlowunion - TypeScript: Added
KalapaEkycErrorCodeunion type for all error codes - TypeScript: Native module linkage guard — throws descriptive error with fix instructions if autolinking fails
- TypeScript:
nfc_dataparser now handles camelCase field names from Android (idNumber,dateOfBirth, etc.)
1.2.9
- React-Native: Add
customer_languageinto config. - Android: Fix obfuscation error
1.2.8
- Android: Update stable version for NFC step.
1.2.7
- Android: Update config to scan QR code or not.
- React-Native: Introduce config
require_qrin AppConfig
1.2.6
- Android: Minor update, UI / UX optimize. Stable version
1.2.5
- Android: Fix NFC step error UNKNOWN rarely happens on some device like Samsung Note 10
1.2.4
- Android: Optimize UI / UX in NFC step
- React-Native: show to skip / show confirm screen.
1.2.3
- Android: Optimize UI / UX in NFC step
1.2.2
- iOS: Maintain UI config
- Android: Maintain / Optimize NFC step
1.2.1
- successColor / failureColor: Now can be configured via sdkConfig
1.2.0
- Documentation: Complete overhaul of KalapaResult class documentation
1.1.3
- Android: Removed GIF dependencies to satisfy Google Play requirement (16KB pages size)
- Stable version
1.1.2
- Android: Removed unnecessary jitpack.io repository
- Android: Updated animation to avoid GIF usage
1.1.1
- Android: Removed unnecessary jcenter repository
1.1.0
- Android: Support target SDK version 35 & edge-to-edge display
- Android: Support base64 library
- UI & UX: General optimization improvements
1.0.7
- General: Applied new flow for both Android & iOS platforms
- iOS: UI & UX optimization
- Android: UI & UX optimization
Requirements
Version Matrix
| Dependency | Version |
|---|---|
| React Native | >= 0.66 |
| Android minSdkVersion | 24 |
| Android compileSdkVersion | 35 |
| Android targetSdkVersion | 35 |
| iOS Deployment Target | >= 13.0 |
| Kalapa Android SDK | 2.12.0 |
Android
Minimum SDK Requirements:
minSdkVersion = 24compileSdkVersion = 35targetSdkVersion = 35android.useAndroidX = trueKotlin = 1.8.0+
Compile Options:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}iOS
- iOS Deployment Target >= 13.0
Getting Started
Install the SDK
Using Yarn
yarn add react-native-kalapa-ekycUsing npm
npm install react-native-kalapa-ekycManual Installation
Add to your package.json:
"dependencies": {
"react-native-kalapa-ekyc": "^1.2.10"
}Configuration
Android
ProGuard / R8 (minifyEnabled true)
If you enable code shrinking for your release build, add the ProGuard rules below to prevent the SDK from being obfuscated or stripped — otherwise the eKYC steps (OCR, NFC, liveness) may crash at runtime.
app/proguard-rules.pro
# Retain generic signatures and exception types for reflection
-keepattributes Signature, InnerClasses, EnclosingMethod, Exceptions, *Annotation*
# Prevent obfuscation of Retrofit annotations and method structures
-keep class retrofit2.** { *; }
-dontwarn retrofit2.**
# Keep interfaces containing Retrofit HTTP annotations
-keepclasseswithmembers class * {
@retrofit2.http.* <methods>;
}
# Keep the platform-specific call adapters from being stripped
-keep class retrofit2.Platform$* { *; }
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
}iOS
Declare Camera and NFC Permissions
Update your Info.plist with the following:
<key>NFCReaderUsageDescription</key>
<string>This eKYC app needs NFC reader to scan chip</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A0000002471001</string>
<string>A0000002472001</string>
<string>00000000000000</string>
</array>
<key>NSCameraUsageDescription</key>
<string>This eKYC app needs to use camera to scan document</string>Enable NFC Capability
Add the Near Field Communication Tag Reading capability to your project target in Xcode.
View Xcode capability documentation
Implementation Guide
1. Initialize a Session
Each eKYC profile requires a unique session ID (JWT access token). The SDK uses this session ID to perform all eKYC steps.
Important Notes:
- Failure to provide a valid session ID results in an
EXPIREDerror - Sessions are valid for 10 minutes by default (adjustable)
- Each session ID should be used for a single eKYC flow
Check the API documentation for creating a new session.
2. Define SDK Configuration
TypeScript Interface
import KalapaEkyc, { KalapaEkycConfig, KalapaEkycFlow } from 'react-native-kalapa-ekyc';
const configInfo: KalapaEkycConfig = {
domain: 'https://ekyc-sdk.kalapa.vn',
main_color: '#1F69E6',
main_text_color: '#000000',
btn_text_color: '#FFFFFF',
background_color: '#FFFFFF',
success_color: '#388E3C',
failure_color: '#F44336',
language: 'vi',
liveness_version: 3,
with_confirm_screen: true,
require_qr: false,
allow_mrz_rescan_on_nfc_mismatch: true,
};Configuration Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| domain | string | https://ekyc-sdk.kalapa.vn | Base URL for SDK API requests |
| main_color | string | #1F69E6 | Hex color for buttons and primary UI elements |
| main_text_color | string | #000000 | Hex color for main text |
| btn_text_color | string | #FFFFFF | Hex color for text inside filled buttons |
| background_color | string | #FFFFFF | Hex color for SDK background |
| success_color | string | #388E3C | Hex color for success state indicators |
| failure_color | string | #F44336 | Hex color for failure state indicators |
| language | 'vi' | 'en' | 'en' | SDK UI language |
| customer_language | string | '' | Custom language key — contact Kalapa to enable |
| liveness_version | 1 | 2 | 3 | 0 | Liveness detection version: 3 = 3 random actions, 2 = move face toward camera, 1 = no action |
| face_data | string | — | Base64 face data. If valid, skips liveness step |
| mrz | string | — | Pre-filled MRZ. If valid, skips MRZ scan. Invalid input throws MRZ_INVALID |
| qr_code | string | — | Pre-filled QR code. If valid, skips QR scan |
| allow_mrz_rescan_on_nfc_mismatch | boolean | false | Allow user to rescan MRZ when NFC chip data doesn't match |
| with_confirm_screen | boolean | false | Show confirm screen before submitting |
| require_qr | boolean | false | Require QR code scan — may reduce the MRZ step in NFC flow |
| session_id | string | — | Resume a previous leftover session |
3. Define the SDK Flow
Flow Types
| Flow | Scan Document | Scan Face | Scan NFC Chip |
|------|:---:|:---:|:---:|
| ekyc | ✓ | ✓ | ✗ |
| nfc_ekyc | ✓ | ✓ | ✓ |
| nfc_only | ✗ | ✓ | ✓ |
Flow Selection Guidelines
The SDK flow typically matches the flow set during session creation. However, there are special use cases:
Example — nfc_only retry:
If a user completed ekyc but failed the NFC scan, you can reuse the same session ID with nfc_only to allow NFC scanning without re-scanning the document and face.
Best Practice: Use separate sessions for different flows to simplify data management.
4. Start the SDK
import KalapaEkyc, { KalapaResult, KalapaEkycConfig, KalapaEkycFlow } from 'react-native-kalapa-ekyc';
const configInfo: KalapaEkycConfig = {
domain: 'https://ekyc-sdk.kalapa.vn',
language: 'vi',
liveness_version: 3,
with_confirm_screen: true,
};
const flow: KalapaEkycFlow = 'nfc_ekyc';
KalapaEkyc.start(sessionId, flow, configInfo)
.then(({ kalapa_result }) => {
const result: KalapaResult = kalapa_result;
if (result.isApproved()) {
// Store or display non-sensitive fields
saveVerificationResult({
decision: result.decision,
session: result.session,
idNumberMasked: result.id_number.replace(/.(?=.{4})/g, '*'),
});
} else if (result.isManualReview()) {
// Notify backend for manual review
}
})
.catch((error) => {
switch (error.code) {
case 'EXPIRED':
// Prompt user to restart — session timed out
break;
case 'CANCELED':
// User backed out — no action needed
break;
case 'PERMISSION_DENIED':
// Guide user to grant camera/NFC permission in Settings
break;
case 'DEVICE_NOT_ACCEPTABLE':
// Block flow — emulator, rooted, or jailbroken device
break;
case 'MRZ_INVALID':
// Invalid pre-filled MRZ was passed in config
break;
case 'NFC_NOT_MATCH':
// NFC chip data doesn't match scanned document
break;
case 'UNSUPPORTED':
// Device doesn't support NFC
break;
case 'CONFIG_ERROR':
// Invalid domain, session, or SDK config
break;
case 'ACTIVITY_NULL':
// Android only — app was backgrounded when SDK launched
break;
case 'OTHER':
default:
break;
}
});Exception Handling
Error Codes
| Error Code | Platform | Description |
|------------|----------|-------------|
| EXPIRED | Android, iOS | Session expired (default 10-minute timeout) |
| CANCELED | Android, iOS | User canceled the eKYC process |
| PERMISSION_DENIED | Android | Camera or NFC permission denied |
| DEVICE_NOT_ACCEPTABLE | Android | Emulator, rooted, or virtual camera detected |
| MRZ_INVALID | Android | Pre-filled MRZ input is invalid |
| NFC_NOT_MATCH | Android | MRZ does not match NFC chip data |
| UNSUPPORTED | Android, iOS | Device doesn't support required features (NFC) |
| CONFIG_ERROR | Android, iOS | Domain, session, or SDK configuration error |
| ACTIVITY_NULL | Android | App Activity was null when SDK tried to launch |
| OTHER | Android, iOS | Unexpected error |
Result Objects
KalapaResult Class
The SDK returns a KalapaResult instance in the resolved value of start(). No manual parsing is needed.
import KalapaEkyc, { KalapaResult } from 'react-native-kalapa-ekyc';
const { kalapa_result } = await KalapaEkyc.start(sessionId, flow, config);
// kalapa_result is already a KalapaResult instanceSecurity note:
KalapaResultcontains sensitive personal data — CCCD number, date of birth, MRZ, and a base64 face image from the NFC chip. Do not log these fields directly. Useresult.id_number.replace(/.(?=.{4})/g, '*')or equivalent masking before any logging. Do not storenfc_data.face_imageunless legally required and properly secured.
Core Properties
| Property | Type | Description |
|----------|------|-------------|
| decision | DecisionType | "APPROVED", "MANUAL", "REJECTED", or "UNKNOWN" |
| session | string | Session JWT token |
| selfie_data | SelfieData | Face matching results |
| nfc_data | NfcData | NFC chip data |
| mrz_data | MrzData \| null | Parsed MRZ fields |
| qr_code | QrCode \| null | QR code data from document |
| rawResult | any | Original unprocessed response |
Root-Level Personal Information
| Property | Type | Description |
|----------|------|-------------|
| name | string | Full name |
| id_number | string | ID card number |
| birthday | string | Birth date |
| gender | string | Gender |
| country | string | Country |
| national | string | Nationality |
| ethnicity | string | Ethnicity |
| religion | string | Religion |
| features | string | Personal identification marks |
| poi | string | Place of issue |
| doe | string | Date of expiry |
| doi | string | Date of issuance |
| home | string | Home address (full text) |
| home_entities | AddressEntities | Parsed home address |
| resident | string | Residential address (full text) |
| resident_entities | AddressEntities | Parsed residential address |
| type | string | Document type |
SelfieData Object
| Property | Type | Description |
|----------|------|-------------|
| is_matched | boolean | Whether face matched the document |
| matching_score | number | Matching confidence score (0–100) |
NfcData Object
Security note:
face_imageis a base64-encoded portrait from the NFC chip. Treat it as biometric data — do not log, cache, or transmit without explicit user consent and appropriate security controls.
Personal Information
| Property | Type | Description |
|----------|------|-------------|
| name | string | Full name |
| id_number | string | ID card number |
| old_id_number | string | Previous ID number (if any) |
| date_of_birth | string | Birth date (DD/MM/YYYY) |
| date_of_expiry | string | Expiry date (DD/MM/YYYY) |
| date_of_issuance | string | Issuance date (DD/MM/YYYY) |
| gender | string | Gender |
| nationality | string | Nationality |
| nation | string | Nation/ethnicity |
Address Information
| Property | Type | Description |
|----------|------|-------------|
| address | string | Current residential address |
| hometown | string | Hometown/place of origin |
Family Information
| Property | Type | Description |
|----------|------|-------------|
| father_name | string | Father's full name |
| mother_name | string | Mother's full name |
| spouse_name | string | Spouse's full name |
Additional Information
| Property | Type | Description |
|----------|------|-------------|
| religion | string | Religion |
| personal_identification | string | Personal identification marks |
| face_image | string | Base64 face image from NFC chip — handle as biometric data |
| mrz | string | Raw MRZ string |
AddressEntities Object
| Property | Type | Description |
|----------|------|-------------|
| district | string | District name |
| ward | string | Ward name |
| province | string | Province name |
| unknown | string | Unparsed address components |
MrzData Object
| Property | Type | Description |
|----------|------|-------------|
| data.fields.birthday | string | undefined | Birth date from MRZ |
| data.fields.doe | string | undefined | Date of expiry from MRZ |
| data.fields.gender | string | undefined | Gender from MRZ |
| data.fields.id_number | string | undefined | ID number from MRZ |
| data.fields.name | string | undefined | Name from MRZ |
| data.raw_mrz | string | undefined | Raw MRZ string |
| error.code | number | undefined | Error code if parsing failed |
| error.message | string | undefined | Error message if parsing failed |
QrCode Object
| Property | Type | Description |
|----------|------|-------------|
| data.decoded_text | string | undefined | Decoded QR code text |
| data.stage | number | undefined | Processing stage |
| error.code | number | undefined | Error code if decoding failed |
| error.message | string | undefined | Error message if decoding failed |
Convenience Methods
Decision Methods
| Method | Returns | Description |
|--------|---------|-------------|
| isApproved() | boolean | true if decision is "APPROVED" |
| isManualReview() | boolean | true if decision is "MANUAL" |
| isRejected() | boolean | true if decision is "REJECTED" |
Face Matching Methods
| Method | Returns | Description |
|--------|---------|-------------|
| isFaceMatched() | boolean | true if face matched successfully |
| getFaceMatchingScore() | number | Face matching confidence score |
Formatted Getters
| Method | Returns | Description |
|--------|---------|-------------|
| getDisplayName() | string | Name from nfc_data, or "N/A" |
| getIdNumber() | string | ID number from nfc_data, or "N/A" |
| getDateOfBirth() | string | Birth date from nfc_data, or "N/A" |
| getFaceImage() | string | Base64 face image from NFC chip |
Utility Methods
| Method | Returns | Description |
|--------|---------|-------------|
| toJSON() | object | Plain object suitable for storage or network transmission |
| fromRawResult(raw) | KalapaResult | Static factory — creates a KalapaResult from a raw object |
Complete Usage Example
import KalapaEkyc, { KalapaResult, KalapaEkycConfig, KalapaEkycFlow } from 'react-native-kalapa-ekyc';
const runEkyc = async (sessionId: string) => {
const config: KalapaEkycConfig = {
domain: 'https://ekyc-sdk.kalapa.vn',
language: 'vi',
liveness_version: 3,
with_confirm_screen: true,
allow_mrz_rescan_on_nfc_mismatch: true,
};
const flow: KalapaEkycFlow = 'nfc_ekyc';
try {
const { kalapa_result: result } = await KalapaEkyc.start(sessionId, flow, config);
// Decision
if (result.isApproved()) {
console.log('Approved');
} else if (result.isManualReview()) {
console.log('Manual review required');
} else if (result.isRejected()) {
console.log('Rejected');
}
// Face matching
if (result.isFaceMatched()) {
console.log(`Face matched — score: ${result.getFaceMatchingScore()}`);
}
// Address entities
if (result.home_entities) {
const { province, district, ward } = result.home_entities;
console.log(`Home: ${ward}, ${district}, ${province}`);
}
// MRZ data
if (result.mrz_data?.data?.fields) {
const { id_number, name } = result.mrz_data.data.fields;
console.log(`MRZ parsed — name: ${name}`); // avoid logging id_number directly
}
// QR code data
if (result.qr_code?.data?.decoded_text) {
console.log('QR decoded');
}
// Export for backend — avoid logging the full object as it contains PII
const payload = result.toJSON();
await submitToBackend(payload);
} catch (error: any) {
switch (error.code) {
case 'EXPIRED':
showAlert('Session expired', 'Please start over.');
break;
case 'CANCELED':
// User backed out — no UI needed
break;
case 'PERMISSION_DENIED':
showAlert('Permission required', 'Enable camera and NFC in Settings.');
break;
case 'DEVICE_NOT_ACCEPTABLE':
showAlert('Unsupported device', 'Please use a physical device.');
break;
case 'NFC_NOT_MATCH':
showAlert('NFC mismatch', 'Please rescan your document.');
break;
case 'ACTIVITY_NULL':
// Android: app was backgrounded — ask user to retry
break;
default:
showAlert('Error', error.message ?? 'An unexpected error occurred.');
}
}
};Summary
The Kalapa eKYC React Native SDK provides identity verification with document scanning, face matching, and NFC chip reading. Key features:
- Three flexible flow types (
ekyc,nfc_ekyc,nfc_only) - Fully typed TypeScript API —
KalapaEkycConfig,KalapaEkycFlow,KalapaEkycErrorCode - Customizable UI (colors, language, confirm screen)
- Three liveness detection versions
- Type-safe
KalapaResultwith convenience methods - Unified error codes across Android and iOS
For API documentation and session creation, refer to the Kalapa API documentation.
