react-native-kalapa-ekyc 1.2.9 → 1.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +271 -273
- package/android/build.gradle +3 -3
- package/android/src/main/java/com/reactnativekalapaekyc/KalapaEkycModule.java +53 -43
- package/ios/KalapaEkyc.m +33 -15
- package/lib/typescript/src/KalapaResult.d.ts +107 -0
- package/lib/typescript/src/index.d.ts +26 -0
- package/package.json +7 -7
- package/src/KalapaResult.ts +10 -19
- package/src/index.tsx +66 -34
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/9.0-milestone-1/checksums/checksums.lock +0 -0
- package/android/.gradle/9.0-milestone-1/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/9.0-milestone-1/fileChanges/last-build.bin +0 -0
- package/android/.gradle/9.0-milestone-1/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/9.0-milestone-1/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/config.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/.idea/AndroidProjectSystem.xml +0 -6
- package/android/.idea/caches/deviceStreaming.xml +0 -1510
- package/android/.idea/gradle.xml +0 -12
- package/android/.idea/material_theme_project_new.xml +0 -10
- package/android/.idea/migrations.xml +0 -10
- package/android/.idea/misc.xml +0 -10
- package/android/.idea/runConfigurations.xml +0 -17
- package/android/.idea/vcs.xml +0 -6
- package/android/local.properties +0 -8
- package/android/src/main/java/com/reactnativekalapaekyc/kUtils.java +0 -66
- package/ios/KalapaEkyc.xcodeproj/project.pbxproj +0 -167
- package/ios/KalapaEkyc.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -4
- package/ios/KalapaEkyc.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/KalapaEkyc.xcodeproj/project.xcworkspace/xcuserdata/iosdev.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/KalapaEkyc.xcodeproj/project.xcworkspace/xcuserdata/leo.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/KalapaEkyc.xcodeproj/xcuserdata/mac.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
package/README.md
CHANGED
|
@@ -1,37 +1,59 @@
|
|
|
1
1
|
# Kalapa eKYC React Native SDK Documentation
|
|
2
2
|
|
|
3
3
|
Complete guide for integrating Kalapa eKYC functionality into your React Native applications.
|
|
4
|
+
|
|
4
5
|
---
|
|
6
|
+
|
|
5
7
|
## Changelog
|
|
8
|
+
|
|
9
|
+
### 1.2.10
|
|
10
|
+
- **Android**: Fix switch fall-through in `onError` callback — prevents Promise being rejected multiple times
|
|
11
|
+
- **Android**: Fix `getCurrentActivity() == null` case now rejects Promise with `ACTIVITY_NULL` instead of leaving it pending forever
|
|
12
|
+
- **Android**: Fix native crash when optional config keys are missing — all boolean/int fields now use safe `hasKey` guards
|
|
13
|
+
- **Android**: SDK now runs on UI thread to safely open Activity
|
|
14
|
+
- **Android**: Bump `compileSdkVersion` and `targetSdkVersion` to 35
|
|
15
|
+
- **iOS**: Fix `handleResult` crash when `ocr_data.data.fields` is nil (e.g. `nfc_only` flow)
|
|
16
|
+
- **iOS**: Fix `qr_code` response format — now correctly wrapped as `{ data: { decoded_text } }` to match TypeScript parser
|
|
17
|
+
- **iOS**: Unify `EventSessionExpired` error code to `EXPIRED` (was `UNAUTHORIZED`)
|
|
18
|
+
- **TypeScript**: `config` parameter is now fully typed (`KalapaEkycConfig`), no more `any`
|
|
19
|
+
- **TypeScript**: `flow` parameter is now typed as `KalapaEkycFlow` union
|
|
20
|
+
- **TypeScript**: Added `KalapaEkycErrorCode` union type for all error codes
|
|
21
|
+
- **TypeScript**: Native module linkage guard — throws descriptive error with fix instructions if autolinking fails
|
|
22
|
+
- **TypeScript**: `nfc_data` parser now handles camelCase field names from Android (`idNumber`, `dateOfBirth`, etc.)
|
|
23
|
+
|
|
6
24
|
### 1.2.9
|
|
7
25
|
- **React-Native**: Add `customer_language` into config.
|
|
8
|
-
- **Android**: Fix obfuscation error
|
|
26
|
+
- **Android**: Fix obfuscation error
|
|
27
|
+
|
|
9
28
|
### 1.2.8
|
|
10
29
|
- **Android**: Update stable version for NFC step.
|
|
30
|
+
|
|
11
31
|
### 1.2.7
|
|
12
32
|
- **Android**: Update config to scan QR code or not.
|
|
13
|
-
- **React-Native**: Introduce config require_qr in AppConfig
|
|
33
|
+
- **React-Native**: Introduce config `require_qr` in AppConfig
|
|
34
|
+
|
|
14
35
|
### 1.2.6
|
|
15
36
|
- **Android**: Minor update, UI / UX optimize. Stable version
|
|
37
|
+
|
|
16
38
|
### 1.2.5
|
|
17
39
|
- **Android**: Fix NFC step error UNKNOWN rarely happens on some device like Samsung Note 10
|
|
40
|
+
|
|
18
41
|
### 1.2.4
|
|
19
42
|
- **Android**: Optimize UI / UX in NFC step
|
|
20
|
-
- **React-Native**: show to skip / show confirm screen.
|
|
43
|
+
- **React-Native**: show to skip / show confirm screen.
|
|
44
|
+
|
|
21
45
|
### 1.2.3
|
|
22
46
|
- **Android**: Optimize UI / UX in NFC step
|
|
47
|
+
|
|
23
48
|
### 1.2.2
|
|
24
49
|
- **iOS**: Maintain UI config
|
|
25
50
|
- **Android**: Maintain / Optimize NFC step
|
|
51
|
+
|
|
26
52
|
### 1.2.1
|
|
27
53
|
- **successColor / failureColor**: Now can be configured via sdkConfig
|
|
54
|
+
|
|
28
55
|
### 1.2.0
|
|
29
56
|
- **Documentation**: Complete overhaul of KalapaResult class documentation
|
|
30
|
-
- **Documentation**: Added comprehensive property documentation for all root-level fields
|
|
31
|
-
- **Documentation**: Added AddressEntities, MrzData, and QrCode object documentation
|
|
32
|
-
- **Documentation**: Enhanced usage examples with all available properties
|
|
33
|
-
- **Documentation**: Fixed package name references throughout documentation
|
|
34
|
-
- **Documentation**: Added examples for accessing MRZ data, QR code data, and address entities
|
|
35
57
|
|
|
36
58
|
### 1.1.3
|
|
37
59
|
- **Android**: Removed GIF dependencies to satisfy Google Play requirement (16KB pages size)
|
|
@@ -58,18 +80,23 @@ Complete guide for integrating Kalapa eKYC functionality into your React Native
|
|
|
58
80
|
|
|
59
81
|
## Requirements
|
|
60
82
|
|
|
61
|
-
###
|
|
83
|
+
### Version Matrix
|
|
62
84
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
| Dependency | Version |
|
|
86
|
+
|---|---|
|
|
87
|
+
| React Native | >= 0.66 |
|
|
88
|
+
| Android `minSdkVersion` | 24 |
|
|
89
|
+
| Android `compileSdkVersion` | 35 |
|
|
90
|
+
| Android `targetSdkVersion` | 35 |
|
|
91
|
+
| iOS Deployment Target | >= 13.0 |
|
|
92
|
+
| Kalapa Android SDK | 2.11.8 |
|
|
67
93
|
|
|
68
94
|
### Android
|
|
69
95
|
|
|
70
96
|
**Minimum SDK Requirements:**
|
|
71
97
|
- `minSdkVersion = 24`
|
|
72
|
-
- `
|
|
98
|
+
- `compileSdkVersion = 35`
|
|
99
|
+
- `targetSdkVersion = 35`
|
|
73
100
|
- `android.useAndroidX = true`
|
|
74
101
|
- `Kotlin = 1.8.0+`
|
|
75
102
|
|
|
@@ -83,7 +110,7 @@ compileOptions {
|
|
|
83
110
|
|
|
84
111
|
### iOS
|
|
85
112
|
|
|
86
|
-
- iOS
|
|
113
|
+
- iOS Deployment Target >= 13.0
|
|
87
114
|
|
|
88
115
|
---
|
|
89
116
|
|
|
@@ -105,11 +132,11 @@ npm install react-native-kalapa-ekyc
|
|
|
105
132
|
|
|
106
133
|
#### Manual Installation
|
|
107
134
|
|
|
108
|
-
Add to your `
|
|
135
|
+
Add to your `package.json`:
|
|
109
136
|
|
|
110
137
|
```json
|
|
111
138
|
"dependencies": {
|
|
112
|
-
"react-native-kalapa-ekyc": "^1.2.
|
|
139
|
+
"react-native-kalapa-ekyc": "^1.2.10"
|
|
113
140
|
}
|
|
114
141
|
```
|
|
115
142
|
|
|
@@ -151,7 +178,7 @@ Add the **Near Field Communication Tag Reading** capability to your project targ
|
|
|
151
178
|
Each eKYC profile requires a unique session ID (JWT access token). The SDK uses this session ID to perform all eKYC steps.
|
|
152
179
|
|
|
153
180
|
**Important Notes:**
|
|
154
|
-
- Failure to provide a valid session ID results in an
|
|
181
|
+
- Failure to provide a valid session ID results in an `EXPIRED` error
|
|
155
182
|
- Sessions are valid for 10 minutes by default (adjustable)
|
|
156
183
|
- Each session ID should be used for a single eKYC flow
|
|
157
184
|
|
|
@@ -161,63 +188,57 @@ Check the [API documentation](https://www.notion.so/Init-session-820a772b8402499
|
|
|
161
188
|
|
|
162
189
|
### 2. Define SDK Configuration
|
|
163
190
|
|
|
164
|
-
####
|
|
165
|
-
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
customer_language: <CUSTOMER_LANGUAGE>
|
|
184
|
-
}
|
|
191
|
+
#### TypeScript Interface
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import KalapaEkyc, { KalapaEkycConfig, KalapaEkycFlow } from 'react-native-kalapa-ekyc';
|
|
195
|
+
|
|
196
|
+
const configInfo: KalapaEkycConfig = {
|
|
197
|
+
domain: 'https://ekyc-sdk.kalapa.vn',
|
|
198
|
+
main_color: '#1F69E6',
|
|
199
|
+
main_text_color: '#000000',
|
|
200
|
+
btn_text_color: '#FFFFFF',
|
|
201
|
+
background_color: '#FFFFFF',
|
|
202
|
+
success_color: '#388E3C',
|
|
203
|
+
failure_color: '#F44336',
|
|
204
|
+
language: 'vi',
|
|
205
|
+
liveness_version: 3,
|
|
206
|
+
with_confirm_screen: true,
|
|
207
|
+
require_qr: false,
|
|
208
|
+
allow_mrz_rescan_on_nfc_mismatch: true,
|
|
209
|
+
};
|
|
185
210
|
```
|
|
186
211
|
|
|
187
212
|
#### Configuration Parameters
|
|
188
213
|
|
|
189
|
-
| Parameter | Type | Description |
|
|
190
|
-
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
193
|
-
| `
|
|
194
|
-
| `
|
|
195
|
-
| `
|
|
196
|
-
| `
|
|
197
|
-
| `
|
|
198
|
-
| `
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
| `
|
|
203
|
-
| `
|
|
204
|
-
| `
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
214
|
+
| Parameter | Type | Default | Description |
|
|
215
|
+
|-----------|------|---------|-------------|
|
|
216
|
+
| `domain` | string | `https://ekyc-sdk.kalapa.vn` | Base URL for SDK API requests |
|
|
217
|
+
| `main_color` | string | `#1F69E6` | Hex color for buttons and primary UI elements |
|
|
218
|
+
| `main_text_color` | string | `#000000` | Hex color for main text |
|
|
219
|
+
| `btn_text_color` | string | `#FFFFFF` | Hex color for text inside filled buttons |
|
|
220
|
+
| `background_color` | string | `#FFFFFF` | Hex color for SDK background |
|
|
221
|
+
| `success_color` | string | `#388E3C` | Hex color for success state indicators |
|
|
222
|
+
| `failure_color` | string | `#F44336` | Hex color for failure state indicators |
|
|
223
|
+
| `language` | `'vi'` \| `'en'` | `'en'` | SDK UI language |
|
|
224
|
+
| `customer_language` | string | `''` | Custom language key — contact Kalapa to enable |
|
|
225
|
+
| `liveness_version` | `1` \| `2` \| `3` | `0` | Liveness detection version: `3` = 3 random actions, `2` = move face toward camera, `1` = no action |
|
|
226
|
+
| `face_data` | string | — | Base64 face data. If valid, skips liveness step |
|
|
227
|
+
| `mrz` | string | — | Pre-filled MRZ. If valid, skips MRZ scan. Invalid input throws `MRZ_INVALID` |
|
|
228
|
+
| `qr_code` | string | — | Pre-filled QR code. If valid, skips QR scan |
|
|
229
|
+
| `allow_mrz_rescan_on_nfc_mismatch` | boolean | `false` | Allow user to rescan MRZ when NFC chip data doesn't match |
|
|
230
|
+
| `with_confirm_screen` | boolean | `false` | Show confirm screen before submitting |
|
|
231
|
+
| `require_qr` | boolean | `false` | Require QR code scan — may reduce the MRZ step in NFC flow |
|
|
232
|
+
| `session_id` | string | — | Resume a previous leftover session |
|
|
233
|
+
|
|
207
234
|
---
|
|
208
235
|
|
|
209
236
|
### 3. Define the SDK Flow
|
|
210
237
|
|
|
211
|
-
#### Available eKYC Steps
|
|
212
|
-
|
|
213
|
-
1. Scan the document
|
|
214
|
-
2. Scan the face
|
|
215
|
-
3. Scan the NFC chip
|
|
216
|
-
|
|
217
238
|
#### Flow Types
|
|
218
239
|
|
|
219
|
-
| Flow
|
|
220
|
-
|
|
240
|
+
| Flow | Scan Document | Scan Face | Scan NFC Chip |
|
|
241
|
+
|------|:---:|:---:|:---:|
|
|
221
242
|
| `ekyc` | ✓ | ✓ | ✗ |
|
|
222
243
|
| `nfc_ekyc` | ✓ | ✓ | ✓ |
|
|
223
244
|
| `nfc_only` | ✗ | ✓ | ✓ |
|
|
@@ -226,11 +247,8 @@ let configInfo = {
|
|
|
226
247
|
|
|
227
248
|
The SDK flow typically matches the flow set during session creation. However, there are special use cases:
|
|
228
249
|
|
|
229
|
-
**Example
|
|
230
|
-
If
|
|
231
|
-
- Reuse the same session ID
|
|
232
|
-
- Set flow to `nfc_only`
|
|
233
|
-
- Allow NFC chip scanning without re-scanning document and face
|
|
250
|
+
**Example — `nfc_only` retry:**
|
|
251
|
+
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.
|
|
234
252
|
|
|
235
253
|
**Best Practice:** Use separate sessions for different flows to simplify data management.
|
|
236
254
|
|
|
@@ -238,53 +256,64 @@ If users completed the basic `ekyc` flow but failed to scan the NFC chip, you ca
|
|
|
238
256
|
|
|
239
257
|
### 4. Start the SDK
|
|
240
258
|
|
|
241
|
-
```
|
|
242
|
-
import KalapaEkyc, { KalapaResult } from 'react-native-kalapa-ekyc';
|
|
259
|
+
```typescript
|
|
260
|
+
import KalapaEkyc, { KalapaResult, KalapaEkycConfig, KalapaEkycFlow } from 'react-native-kalapa-ekyc';
|
|
261
|
+
|
|
262
|
+
const configInfo: KalapaEkycConfig = {
|
|
263
|
+
domain: 'https://ekyc-sdk.kalapa.vn',
|
|
264
|
+
language: 'vi',
|
|
265
|
+
liveness_version: 3,
|
|
266
|
+
with_confirm_screen: true,
|
|
267
|
+
};
|
|
243
268
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
269
|
+
const flow: KalapaEkycFlow = 'nfc_ekyc';
|
|
270
|
+
|
|
271
|
+
KalapaEkyc.start(sessionId, flow, configInfo)
|
|
272
|
+
.then(({ kalapa_result }) => {
|
|
273
|
+
const result: KalapaResult = kalapa_result;
|
|
274
|
+
|
|
275
|
+
if (result.isApproved()) {
|
|
276
|
+
// Store or display non-sensitive fields
|
|
277
|
+
saveVerificationResult({
|
|
278
|
+
decision: result.decision,
|
|
279
|
+
session: result.session,
|
|
280
|
+
idNumberMasked: result.id_number.replace(/.(?=.{4})/g, '*'),
|
|
281
|
+
});
|
|
282
|
+
} else if (result.isManualReview()) {
|
|
283
|
+
// Notify backend for manual review
|
|
257
284
|
}
|
|
258
285
|
})
|
|
259
286
|
.catch((error) => {
|
|
260
287
|
switch (error.code) {
|
|
261
|
-
case
|
|
262
|
-
|
|
288
|
+
case 'EXPIRED':
|
|
289
|
+
// Prompt user to restart — session timed out
|
|
290
|
+
break;
|
|
291
|
+
case 'CANCELED':
|
|
292
|
+
// User backed out — no action needed
|
|
263
293
|
break;
|
|
264
|
-
case
|
|
265
|
-
|
|
294
|
+
case 'PERMISSION_DENIED':
|
|
295
|
+
// Guide user to grant camera/NFC permission in Settings
|
|
266
296
|
break;
|
|
267
|
-
case
|
|
268
|
-
|
|
297
|
+
case 'DEVICE_NOT_ACCEPTABLE':
|
|
298
|
+
// Block flow — emulator, rooted, or jailbroken device
|
|
269
299
|
break;
|
|
270
|
-
case
|
|
271
|
-
|
|
300
|
+
case 'MRZ_INVALID':
|
|
301
|
+
// Invalid pre-filled MRZ was passed in config
|
|
272
302
|
break;
|
|
273
|
-
case
|
|
274
|
-
|
|
303
|
+
case 'NFC_NOT_MATCH':
|
|
304
|
+
// NFC chip data doesn't match scanned document
|
|
275
305
|
break;
|
|
276
|
-
case
|
|
277
|
-
|
|
306
|
+
case 'UNSUPPORTED':
|
|
307
|
+
// Device doesn't support NFC
|
|
278
308
|
break;
|
|
279
|
-
case
|
|
280
|
-
|
|
309
|
+
case 'CONFIG_ERROR':
|
|
310
|
+
// Invalid domain, session, or SDK config
|
|
281
311
|
break;
|
|
282
|
-
case
|
|
283
|
-
|
|
312
|
+
case 'ACTIVITY_NULL':
|
|
313
|
+
// Android only — app was backgrounded when SDK launched
|
|
284
314
|
break;
|
|
285
|
-
case
|
|
315
|
+
case 'OTHER':
|
|
286
316
|
default:
|
|
287
|
-
console.error("Unknown error:", error.message);
|
|
288
317
|
break;
|
|
289
318
|
}
|
|
290
319
|
});
|
|
@@ -296,17 +325,18 @@ KalapaEkyc.start(sessionId, sdkFlow, configInfo)
|
|
|
296
325
|
|
|
297
326
|
### Error Codes
|
|
298
327
|
|
|
299
|
-
| Error Code | Description |
|
|
300
|
-
|
|
301
|
-
| `EXPIRED` | Session expired (10
|
|
302
|
-
| `CANCELED` | User canceled the eKYC process |
|
|
303
|
-
| `PERMISSION_DENIED` | Camera or NFC permission denied |
|
|
304
|
-
| `DEVICE_NOT_ACCEPTABLE` | Emulator, rooted, or
|
|
305
|
-
| `MRZ_INVALID` |
|
|
306
|
-
| `NFC_NOT_MATCH` | MRZ
|
|
307
|
-
| `UNSUPPORTED` | Device doesn't support required features (
|
|
308
|
-
| `CONFIG_ERROR` |
|
|
309
|
-
| `
|
|
328
|
+
| Error Code | Platform | Description |
|
|
329
|
+
|------------|----------|-------------|
|
|
330
|
+
| `EXPIRED` | Android, iOS | Session expired (default 10-minute timeout) |
|
|
331
|
+
| `CANCELED` | Android, iOS | User canceled the eKYC process |
|
|
332
|
+
| `PERMISSION_DENIED` | Android | Camera or NFC permission denied |
|
|
333
|
+
| `DEVICE_NOT_ACCEPTABLE` | Android | Emulator, rooted, or virtual camera detected |
|
|
334
|
+
| `MRZ_INVALID` | Android | Pre-filled MRZ input is invalid |
|
|
335
|
+
| `NFC_NOT_MATCH` | Android | MRZ does not match NFC chip data |
|
|
336
|
+
| `UNSUPPORTED` | Android, iOS | Device doesn't support required features (NFC) |
|
|
337
|
+
| `CONFIG_ERROR` | Android, iOS | Domain, session, or SDK configuration error |
|
|
338
|
+
| `ACTIVITY_NULL` | Android | App Activity was null when SDK tried to launch |
|
|
339
|
+
| `OTHER` | Android, iOS | Unexpected error |
|
|
310
340
|
|
|
311
341
|
---
|
|
312
342
|
|
|
@@ -314,103 +344,79 @@ KalapaEkyc.start(sessionId, sdkFlow, configInfo)
|
|
|
314
344
|
|
|
315
345
|
### KalapaResult Class
|
|
316
346
|
|
|
317
|
-
The SDK
|
|
347
|
+
The SDK returns a `KalapaResult` instance in the resolved value of `start()`. No manual parsing is needed.
|
|
318
348
|
|
|
319
349
|
```typescript
|
|
320
|
-
import { KalapaResult } from 'react-native-kalapa-ekyc';
|
|
321
|
-
|
|
322
|
-
// Create instance using static method (recommended)
|
|
323
|
-
const result = KalapaResult.fromRawResult(rawResult);
|
|
350
|
+
import KalapaEkyc, { KalapaResult } from 'react-native-kalapa-ekyc';
|
|
324
351
|
|
|
325
|
-
|
|
326
|
-
|
|
352
|
+
const { kalapa_result } = await KalapaEkyc.start(sessionId, flow, config);
|
|
353
|
+
// kalapa_result is already a KalapaResult instance
|
|
327
354
|
```
|
|
328
355
|
|
|
356
|
+
> **Security note:** `KalapaResult` contains sensitive personal data — CCCD number, date of birth, MRZ, and a base64 face image from the NFC chip. Do **not** log these fields directly. Use `result.id_number.replace(/.(?=.{4})/g, '*')` or equivalent masking before any logging. Do not store `nfc_data.face_image` unless legally required and properly secured.
|
|
357
|
+
|
|
329
358
|
#### Core Properties
|
|
330
359
|
|
|
331
360
|
| Property | Type | Description |
|
|
332
361
|
|----------|------|-------------|
|
|
333
|
-
| `decision` | DecisionType |
|
|
334
|
-
| `selfie_data` | SelfieData | Face matching results |
|
|
335
|
-
| `nfc_data` | NfcData | NFC chip data |
|
|
362
|
+
| `decision` | `DecisionType` | `"APPROVED"`, `"MANUAL"`, `"REJECTED"`, or `"UNKNOWN"` |
|
|
336
363
|
| `session` | string | Session JWT token |
|
|
364
|
+
| `selfie_data` | `SelfieData` | Face matching results |
|
|
365
|
+
| `nfc_data` | `NfcData` | NFC chip data |
|
|
366
|
+
| `mrz_data` | `MrzData \| null` | Parsed MRZ fields |
|
|
367
|
+
| `qr_code` | `QrCode \| null` | QR code data from document |
|
|
337
368
|
| `rawResult` | any | Original unprocessed response |
|
|
338
369
|
|
|
339
370
|
#### Root-Level Personal Information
|
|
340
371
|
|
|
341
372
|
| Property | Type | Description |
|
|
342
373
|
|----------|------|-------------|
|
|
343
|
-
| `name` | string | Full name
|
|
374
|
+
| `name` | string | Full name |
|
|
344
375
|
| `id_number` | string | ID card number |
|
|
345
376
|
| `birthday` | string | Birth date |
|
|
346
|
-
| `gender` | string | Gender
|
|
347
|
-
| `country` | string | Country
|
|
377
|
+
| `gender` | string | Gender |
|
|
378
|
+
| `country` | string | Country |
|
|
348
379
|
| `national` | string | Nationality |
|
|
349
|
-
| `ethnicity` | string | Ethnicity
|
|
350
|
-
| `religion` | string |
|
|
351
|
-
| `features` | string | Personal identification marks
|
|
380
|
+
| `ethnicity` | string | Ethnicity |
|
|
381
|
+
| `religion` | string | Religion |
|
|
382
|
+
| `features` | string | Personal identification marks |
|
|
352
383
|
| `poi` | string | Place of issue |
|
|
353
|
-
|
|
354
|
-
#### Date Information
|
|
355
|
-
|
|
356
|
-
| Property | Type | Description |
|
|
357
|
-
|----------|------|-------------|
|
|
358
384
|
| `doe` | string | Date of expiry |
|
|
359
385
|
| `doi` | string | Date of issuance |
|
|
360
|
-
|
|
361
|
-
#### Address Information
|
|
362
|
-
|
|
363
|
-
| Property | Type | Description |
|
|
364
|
-
|----------|------|-------------|
|
|
365
386
|
| `home` | string | Home address (full text) |
|
|
366
|
-
| `home_entities` | AddressEntities | Parsed home address
|
|
387
|
+
| `home_entities` | `AddressEntities` | Parsed home address |
|
|
367
388
|
| `resident` | string | Residential address (full text) |
|
|
368
|
-
| `resident_entities` | AddressEntities | Parsed residential address
|
|
369
|
-
|
|
370
|
-
#### Document Data
|
|
371
|
-
|
|
372
|
-
| Property | Type | Description |
|
|
373
|
-
|----------|------|-------------|
|
|
374
|
-
| `mrz_data` | MrzData \| null | Machine Readable Zone data with parsed fields |
|
|
375
|
-
| `qr_code` | QrCode \| null | QR code data from document |
|
|
389
|
+
| `resident_entities` | `AddressEntities` | Parsed residential address |
|
|
376
390
|
| `type` | string | Document type |
|
|
377
391
|
|
|
378
392
|
---
|
|
379
393
|
|
|
380
394
|
### SelfieData Object
|
|
381
395
|
|
|
382
|
-
#### Properties
|
|
383
|
-
|
|
384
396
|
| Property | Type | Description |
|
|
385
397
|
|----------|------|-------------|
|
|
386
|
-
| `is_matched` | boolean |
|
|
387
|
-
| `matching_score` | number | Matching confidence score (0
|
|
388
|
-
|
|
389
|
-
#### Usage Example
|
|
390
|
-
|
|
391
|
-
```typescript
|
|
392
|
-
if (result.selfie_data.is_matched) {
|
|
393
|
-
console.log(`Face matched with ${result.selfie_data.matching_score}% confidence`);
|
|
394
|
-
}
|
|
395
|
-
```
|
|
398
|
+
| `is_matched` | boolean | Whether face matched the document |
|
|
399
|
+
| `matching_score` | number | Matching confidence score (0–100) |
|
|
396
400
|
|
|
397
401
|
---
|
|
398
402
|
|
|
399
403
|
### NfcData Object
|
|
400
404
|
|
|
405
|
+
> **Security note:** `face_image` is 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.
|
|
406
|
+
|
|
401
407
|
#### Personal Information
|
|
402
408
|
|
|
403
409
|
| Property | Type | Description |
|
|
404
410
|
|----------|------|-------------|
|
|
405
|
-
| `name` | string | Full name
|
|
411
|
+
| `name` | string | Full name |
|
|
406
412
|
| `id_number` | string | ID card number |
|
|
407
413
|
| `old_id_number` | string | Previous ID number (if any) |
|
|
408
|
-
| `date_of_birth` | string | Birth date
|
|
409
|
-
| `date_of_expiry` | string |
|
|
410
|
-
| `date_of_issuance` | string |
|
|
411
|
-
| `gender` | string | Gender
|
|
414
|
+
| `date_of_birth` | string | Birth date (DD/MM/YYYY) |
|
|
415
|
+
| `date_of_expiry` | string | Expiry date (DD/MM/YYYY) |
|
|
416
|
+
| `date_of_issuance` | string | Issuance date (DD/MM/YYYY) |
|
|
417
|
+
| `gender` | string | Gender |
|
|
412
418
|
| `nationality` | string | Nationality |
|
|
413
|
-
| `nation` | string | Nation/ethnicity
|
|
419
|
+
| `nation` | string | Nation/ethnicity |
|
|
414
420
|
|
|
415
421
|
#### Address Information
|
|
416
422
|
|
|
@@ -431,22 +437,15 @@ if (result.selfie_data.is_matched) {
|
|
|
431
437
|
|
|
432
438
|
| Property | Type | Description |
|
|
433
439
|
|----------|------|-------------|
|
|
434
|
-
| `religion` | string |
|
|
435
|
-
| `personal_identification` | string | Personal identification marks
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
| Property | Type | Description |
|
|
440
|
-
|----------|------|-------------|
|
|
441
|
-
| `face_image` | string | Base64 encoded face image from NFC chip |
|
|
442
|
-
| `mrz` | string | Machine Readable Zone data (raw string) |
|
|
440
|
+
| `religion` | string | Religion |
|
|
441
|
+
| `personal_identification` | string | Personal identification marks |
|
|
442
|
+
| `face_image` | string | Base64 face image from NFC chip — handle as biometric data |
|
|
443
|
+
| `mrz` | string | Raw MRZ string |
|
|
443
444
|
|
|
444
445
|
---
|
|
445
446
|
|
|
446
447
|
### AddressEntities Object
|
|
447
448
|
|
|
448
|
-
Represents parsed address components:
|
|
449
|
-
|
|
450
449
|
| Property | Type | Description |
|
|
451
450
|
|----------|------|-------------|
|
|
452
451
|
| `district` | string | District name |
|
|
@@ -458,48 +457,27 @@ Represents parsed address components:
|
|
|
458
457
|
|
|
459
458
|
### MrzData Object
|
|
460
459
|
|
|
461
|
-
Contains MRZ (Machine Readable Zone) information:
|
|
462
|
-
|
|
463
|
-
| Property | Type | Description |
|
|
464
|
-
|----------|------|-------------|
|
|
465
|
-
| `data` | MrzDataFields \| undefined | Parsed MRZ fields |
|
|
466
|
-
| `error` | MrzDataError \| undefined | Error information if parsing failed |
|
|
467
|
-
|
|
468
|
-
#### MrzDataFields
|
|
469
|
-
|
|
470
460
|
| Property | Type | Description |
|
|
471
461
|
|----------|------|-------------|
|
|
472
|
-
| `birthday` | string \| undefined | Birth date from MRZ |
|
|
473
|
-
| `doe` | string \| undefined | Date of expiry from MRZ |
|
|
474
|
-
| `gender` | string \| undefined | Gender from MRZ |
|
|
475
|
-
| `id_number` | string \| undefined | ID number from MRZ |
|
|
476
|
-
| `name` | string \| undefined | Name from MRZ |
|
|
477
|
-
| `raw_mrz` | string \| undefined | Raw MRZ string |
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
| Property | Type | Description |
|
|
482
|
-
|----------|------|-------------|
|
|
483
|
-
| `code` | number | Error code |
|
|
484
|
-
| `message` | string | Error message |
|
|
462
|
+
| `data.fields.birthday` | string \| undefined | Birth date from MRZ |
|
|
463
|
+
| `data.fields.doe` | string \| undefined | Date of expiry from MRZ |
|
|
464
|
+
| `data.fields.gender` | string \| undefined | Gender from MRZ |
|
|
465
|
+
| `data.fields.id_number` | string \| undefined | ID number from MRZ |
|
|
466
|
+
| `data.fields.name` | string \| undefined | Name from MRZ |
|
|
467
|
+
| `data.raw_mrz` | string \| undefined | Raw MRZ string |
|
|
468
|
+
| `error.code` | number \| undefined | Error code if parsing failed |
|
|
469
|
+
| `error.message` | string \| undefined | Error message if parsing failed |
|
|
485
470
|
|
|
486
471
|
---
|
|
487
472
|
|
|
488
473
|
### QrCode Object
|
|
489
474
|
|
|
490
|
-
Contains QR code information:
|
|
491
|
-
|
|
492
475
|
| Property | Type | Description |
|
|
493
476
|
|----------|------|-------------|
|
|
494
|
-
| `data` |
|
|
495
|
-
| `
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
| Property | Type | Description |
|
|
500
|
-
|----------|------|-------------|
|
|
501
|
-
| `decoded_text` | string \| undefined | Decoded QR code text |
|
|
502
|
-
| `stage` | number \| undefined | Processing stage |
|
|
477
|
+
| `data.decoded_text` | string \| undefined | Decoded QR code text |
|
|
478
|
+
| `data.stage` | number \| undefined | Processing stage |
|
|
479
|
+
| `error.code` | number \| undefined | Error code if decoding failed |
|
|
480
|
+
| `error.message` | string \| undefined | Error message if decoding failed |
|
|
503
481
|
|
|
504
482
|
---
|
|
505
483
|
|
|
@@ -509,92 +487,112 @@ Contains QR code information:
|
|
|
509
487
|
|
|
510
488
|
| Method | Returns | Description |
|
|
511
489
|
|--------|---------|-------------|
|
|
512
|
-
| `isApproved()` | boolean |
|
|
513
|
-
| `isManualReview()` | boolean |
|
|
514
|
-
| `isRejected()` | boolean |
|
|
490
|
+
| `isApproved()` | boolean | `true` if decision is `"APPROVED"` |
|
|
491
|
+
| `isManualReview()` | boolean | `true` if decision is `"MANUAL"` |
|
|
492
|
+
| `isRejected()` | boolean | `true` if decision is `"REJECTED"` |
|
|
515
493
|
|
|
516
494
|
### Face Matching Methods
|
|
517
495
|
|
|
518
496
|
| Method | Returns | Description |
|
|
519
497
|
|--------|---------|-------------|
|
|
520
|
-
| `isFaceMatched()` | boolean |
|
|
521
|
-
| `getFaceMatchingScore()` | number |
|
|
498
|
+
| `isFaceMatched()` | boolean | `true` if face matched successfully |
|
|
499
|
+
| `getFaceMatchingScore()` | number | Face matching confidence score |
|
|
522
500
|
|
|
523
501
|
### Formatted Getters
|
|
524
502
|
|
|
525
503
|
| Method | Returns | Description |
|
|
526
504
|
|--------|---------|-------------|
|
|
527
|
-
| `getDisplayName()` | string |
|
|
528
|
-
| `getIdNumber()` | string |
|
|
529
|
-
| `getDateOfBirth()` | string |
|
|
530
|
-
| `getFaceImage()` | string |
|
|
505
|
+
| `getDisplayName()` | string | Name from `nfc_data`, or `"N/A"` |
|
|
506
|
+
| `getIdNumber()` | string | ID number from `nfc_data`, or `"N/A"` |
|
|
507
|
+
| `getDateOfBirth()` | string | Birth date from `nfc_data`, or `"N/A"` |
|
|
508
|
+
| `getFaceImage()` | string | Base64 face image from NFC chip |
|
|
531
509
|
|
|
532
510
|
### Utility Methods
|
|
533
511
|
|
|
534
512
|
| Method | Returns | Description |
|
|
535
513
|
|--------|---------|-------------|
|
|
536
|
-
| `toJSON()` | object |
|
|
537
|
-
| `fromRawResult(
|
|
514
|
+
| `toJSON()` | object | Plain object suitable for storage or network transmission |
|
|
515
|
+
| `fromRawResult(raw)` | KalapaResult | Static factory — creates a `KalapaResult` from a raw object |
|
|
538
516
|
|
|
539
517
|
---
|
|
540
518
|
|
|
541
519
|
## Complete Usage Example
|
|
542
520
|
|
|
543
521
|
```typescript
|
|
544
|
-
import KalapaEkyc, { KalapaResult } from 'react-native-kalapa-ekyc';
|
|
522
|
+
import KalapaEkyc, { KalapaResult, KalapaEkycConfig, KalapaEkycFlow } from 'react-native-kalapa-ekyc';
|
|
523
|
+
|
|
524
|
+
const runEkyc = async (sessionId: string) => {
|
|
525
|
+
const config: KalapaEkycConfig = {
|
|
526
|
+
domain: 'https://ekyc-sdk.kalapa.vn',
|
|
527
|
+
language: 'vi',
|
|
528
|
+
liveness_version: 3,
|
|
529
|
+
with_confirm_screen: true,
|
|
530
|
+
allow_mrz_rescan_on_nfc_mismatch: true,
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
const flow: KalapaEkycFlow = 'nfc_ekyc';
|
|
545
534
|
|
|
546
|
-
const handleEkyc = async () => {
|
|
547
535
|
try {
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
// Check decision
|
|
536
|
+
const { kalapa_result: result } = await KalapaEkyc.start(sessionId, flow, config);
|
|
537
|
+
|
|
538
|
+
// Decision
|
|
552
539
|
if (result.isApproved()) {
|
|
553
|
-
console.log(
|
|
540
|
+
console.log('Approved');
|
|
554
541
|
} else if (result.isManualReview()) {
|
|
555
|
-
console.log(
|
|
542
|
+
console.log('Manual review required');
|
|
543
|
+
} else if (result.isRejected()) {
|
|
544
|
+
console.log('Rejected');
|
|
556
545
|
}
|
|
557
|
-
|
|
558
|
-
//
|
|
546
|
+
|
|
547
|
+
// Face matching
|
|
559
548
|
if (result.isFaceMatched()) {
|
|
560
|
-
console.log(`Face matched: ${result.getFaceMatchingScore()}
|
|
549
|
+
console.log(`Face matched — score: ${result.getFaceMatchingScore()}`);
|
|
561
550
|
}
|
|
562
|
-
|
|
563
|
-
//
|
|
564
|
-
console.log(`Name: ${result.name}`);
|
|
565
|
-
console.log(`ID Number: ${result.id_number}`);
|
|
566
|
-
console.log(`Birthday: ${result.birthday}`);
|
|
567
|
-
|
|
568
|
-
// Access NFC data
|
|
569
|
-
const userData = {
|
|
570
|
-
name: result.nfc_data.name,
|
|
571
|
-
idNumber: result.nfc_data.id_number,
|
|
572
|
-
birthDate: result.nfc_data.date_of_birth,
|
|
573
|
-
faceImage: result.nfc_data.face_image
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
// Access address entities
|
|
551
|
+
|
|
552
|
+
// Address entities
|
|
577
553
|
if (result.home_entities) {
|
|
578
|
-
|
|
554
|
+
const { province, district, ward } = result.home_entities;
|
|
555
|
+
console.log(`Home: ${ward}, ${district}, ${province}`);
|
|
579
556
|
}
|
|
580
|
-
|
|
581
|
-
//
|
|
582
|
-
if (result.mrz_data?.data) {
|
|
583
|
-
|
|
584
|
-
console.log(`
|
|
557
|
+
|
|
558
|
+
// MRZ data
|
|
559
|
+
if (result.mrz_data?.data?.fields) {
|
|
560
|
+
const { id_number, name } = result.mrz_data.data.fields;
|
|
561
|
+
console.log(`MRZ parsed — name: ${name}`); // avoid logging id_number directly
|
|
585
562
|
}
|
|
586
|
-
|
|
587
|
-
//
|
|
588
|
-
if (result.qr_code?.data) {
|
|
589
|
-
console.log(
|
|
563
|
+
|
|
564
|
+
// QR code data
|
|
565
|
+
if (result.qr_code?.data?.decoded_text) {
|
|
566
|
+
console.log('QR decoded');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Export for backend — avoid logging the full object as it contains PII
|
|
570
|
+
const payload = result.toJSON();
|
|
571
|
+
await submitToBackend(payload);
|
|
572
|
+
|
|
573
|
+
} catch (error: any) {
|
|
574
|
+
switch (error.code) {
|
|
575
|
+
case 'EXPIRED':
|
|
576
|
+
showAlert('Session expired', 'Please start over.');
|
|
577
|
+
break;
|
|
578
|
+
case 'CANCELED':
|
|
579
|
+
// User backed out — no UI needed
|
|
580
|
+
break;
|
|
581
|
+
case 'PERMISSION_DENIED':
|
|
582
|
+
showAlert('Permission required', 'Enable camera and NFC in Settings.');
|
|
583
|
+
break;
|
|
584
|
+
case 'DEVICE_NOT_ACCEPTABLE':
|
|
585
|
+
showAlert('Unsupported device', 'Please use a physical device.');
|
|
586
|
+
break;
|
|
587
|
+
case 'NFC_NOT_MATCH':
|
|
588
|
+
showAlert('NFC mismatch', 'Please rescan your document.');
|
|
589
|
+
break;
|
|
590
|
+
case 'ACTIVITY_NULL':
|
|
591
|
+
// Android: app was backgrounded — ask user to retry
|
|
592
|
+
break;
|
|
593
|
+
default:
|
|
594
|
+
showAlert('Error', error.message ?? 'An unexpected error occurred.');
|
|
590
595
|
}
|
|
591
|
-
|
|
592
|
-
// Export to JSON for storage/transmission
|
|
593
|
-
const jsonData = result.toJSON();
|
|
594
|
-
await saveUserData(jsonData);
|
|
595
|
-
|
|
596
|
-
} catch (error) {
|
|
597
|
-
handleEkycError(error);
|
|
598
596
|
}
|
|
599
597
|
};
|
|
600
598
|
```
|
|
@@ -603,13 +601,13 @@ const handleEkyc = async () => {
|
|
|
603
601
|
|
|
604
602
|
## Summary
|
|
605
603
|
|
|
606
|
-
The Kalapa eKYC React Native SDK provides
|
|
604
|
+
The Kalapa eKYC React Native SDK provides identity verification with document scanning, face matching, and NFC chip reading. Key features:
|
|
607
605
|
|
|
608
|
-
- Three flexible flow types
|
|
609
|
-
-
|
|
610
|
-
-
|
|
611
|
-
-
|
|
612
|
-
-
|
|
613
|
-
-
|
|
606
|
+
- Three flexible flow types (`ekyc`, `nfc_ekyc`, `nfc_only`)
|
|
607
|
+
- Fully typed TypeScript API — `KalapaEkycConfig`, `KalapaEkycFlow`, `KalapaEkycErrorCode`
|
|
608
|
+
- Customizable UI (colors, language, confirm screen)
|
|
609
|
+
- Three liveness detection versions
|
|
610
|
+
- Type-safe `KalapaResult` with convenience methods
|
|
611
|
+
- Unified error codes across Android and iOS
|
|
614
612
|
|
|
615
|
-
For API documentation and session creation, refer to the [Kalapa API documentation](https://kalapa-no.notion.site/eKYC-API-SDK-document-2a2a4ccfa1184a789cb4513fade351e4?pvs=74)
|
|
613
|
+
For API documentation and session creation, refer to the [Kalapa API documentation](https://kalapa-no.notion.site/eKYC-API-SDK-document-2a2a4ccfa1184a789cb4513fade351e4?pvs=74).
|