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.
Files changed (40) hide show
  1. package/README.md +271 -273
  2. package/android/build.gradle +3 -3
  3. package/android/src/main/java/com/reactnativekalapaekyc/KalapaEkycModule.java +53 -43
  4. package/ios/KalapaEkyc.m +33 -15
  5. package/lib/typescript/src/KalapaResult.d.ts +107 -0
  6. package/lib/typescript/src/index.d.ts +26 -0
  7. package/package.json +7 -7
  8. package/src/KalapaResult.ts +10 -19
  9. package/src/index.tsx +66 -34
  10. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  11. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  12. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  13. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  14. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  15. package/android/.gradle/8.9/gc.properties +0 -0
  16. package/android/.gradle/9.0-milestone-1/checksums/checksums.lock +0 -0
  17. package/android/.gradle/9.0-milestone-1/checksums/sha1-checksums.bin +0 -0
  18. package/android/.gradle/9.0-milestone-1/fileChanges/last-build.bin +0 -0
  19. package/android/.gradle/9.0-milestone-1/fileHashes/fileHashes.lock +0 -0
  20. package/android/.gradle/9.0-milestone-1/gc.properties +0 -0
  21. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  22. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  23. package/android/.gradle/config.properties +0 -2
  24. package/android/.gradle/vcs-1/gc.properties +0 -0
  25. package/android/.idea/AndroidProjectSystem.xml +0 -6
  26. package/android/.idea/caches/deviceStreaming.xml +0 -1510
  27. package/android/.idea/gradle.xml +0 -12
  28. package/android/.idea/material_theme_project_new.xml +0 -10
  29. package/android/.idea/migrations.xml +0 -10
  30. package/android/.idea/misc.xml +0 -10
  31. package/android/.idea/runConfigurations.xml +0 -17
  32. package/android/.idea/vcs.xml +0 -6
  33. package/android/local.properties +0 -8
  34. package/android/src/main/java/com/reactnativekalapaekyc/kUtils.java +0 -66
  35. package/ios/KalapaEkyc.xcodeproj/project.pbxproj +0 -167
  36. package/ios/KalapaEkyc.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -4
  37. package/ios/KalapaEkyc.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  38. package/ios/KalapaEkyc.xcodeproj/project.xcworkspace/xcuserdata/iosdev.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  39. package/ios/KalapaEkyc.xcodeproj/project.xcworkspace/xcuserdata/leo.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  40. 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
- ### React Native
83
+ ### Version Matrix
62
84
 
63
- ```json
64
- "react": "17.0.2"
65
- "react-native": "0.66.3"
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
- - `targetSdkVersion = 33`
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 Development Target >= 13.4
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 `app/package.json`:
135
+ Add to your `package.json`:
109
136
 
110
137
  ```json
111
138
  "dependencies": {
112
- "react-native-kalapa-ekyc": "^1.2.9"
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 unauthorized error
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
- #### Basic Configuration Structure
165
-
166
- ```jsx
167
- let configInfo = {
168
- domain: <BASE_URL>,
169
- main_color: <MAIN_COLOR>,
170
- main_text_color: <MAIN_TEXT_COLOR>,
171
- btn_text_color: <BTN_TEXT_COLOR>,
172
- background_color: <BG_COLOR>,
173
- success_color: <SUCCESS_COLOR>,
174
- failure_color: <FAILURE_COLOR>,
175
- language: <LANGUAGE>,
176
- liveness_version: <LIVENESS_VERSION>,
177
- face_data: <FACE_DATA>,
178
- mrz: <INPUT_MRZ>,
179
- qr_code: <INPUT_QR_CODE>,
180
- allow_mrz_rescan_on_nfc_mismatch: <ALLOW_MRZ_RESCAN_ON_NFC_MISMATCH>,
181
- with_confirm_screen: <WITH_CONFIRM_SCREEN>,
182
- require_qr: <REQUIRE_QR>,
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
- | `BASE_URL` | String | Base URL for SDK API requests. Example endpoints: `<BASE_URL>/api/kyc/scan-front`, `<BASE_URL>/api/kyc/scan-back` |
192
- | `BG_COLOR` | String | Hex color code for SDK background |
193
- | `MAIN_COLOR` | String | Hex color code for buttons and functional texts |
194
- | `MAIN_TEXT_COLOR` | String | Hex color code for main text elements |
195
- | `BTN_TEXT_COLOR` | String | Hex color code for text inside filled buttons |
196
- | `SUCCESS_COLOR` | String | Hex color code for text and view that indicated for success state |
197
- | `FAILURE_COLOR` | String | Hex color code for text and view that indicated for failure state |
198
- | `LIVENESS_VERSION` | Integer | Liveness detection version:<br/>• `3` - Requires 3 random actions (turn left/right/up/down, tilt left/right)<br/>• `2` - Move face towards camera<br/>• `1` - No action required |
199
- | `LANGUAGE` | String | UI language: `"vi"` (Vietnamese) or `"en"` (English) |
200
- | `FACE_DATA` | String | Base64 face data. If valid, skips liveness step and uses this for comparison with NFC portrait or document portrait |
201
- | `INPUT_MRZ` | String | Pre-filled MRZ data. If valid, skips MRZ scan step. Invalid input throws `INVALID_MRZ` error |
202
- | `INPUT_QR_CODE` | String | Pre-filled QR code. If valid, skips QR scan step. Invalid input throws `INVALID_QR` error |
203
- | `ALLOW_MRZ_RESCAN_ON_NFC_MISMATCH` | Boolean | If `true`, allows user to rescan MRZ when chip data doesn't match input MRZ |
204
- | `WITH_CONFIRM_SCREEN` | Boolean | If `true`, allows user to enter confirm step |
205
- | `REQUIRE_QR` | Boolean | If `true`, SDK will scan for QR Code and may reduce the MRZ step when enter the NFC step. |
206
- | `CUSTOMER_LANGUAGE` | String | Default "", If you want to custom your own language, contact Kalapa in order to use this feature. |
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 Type | Scan Document | Scan Face | Scan NFC Chip |
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 Use Case for `nfc_only`:**
230
- If users completed the basic `ekyc` flow but failed to scan the NFC chip, you can:
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
- ```jsx
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
- let sessionId = yourSessionInitFunc();
245
- let sdkFlow = "<YOUR_SDK_FLOW>"; // ekyc / nfc_ekyc / nfc_only
246
- let configInfo = {...};
247
-
248
- KalapaEkyc.start(sessionId, sdkFlow, configInfo)
249
- .then((rawResult) => {
250
- try {
251
- // You can use either the constructor or static method
252
- const result = KalapaResult.fromRawResult(rawResult);
253
- // Or: const result = new KalapaResult(rawResult);
254
- handleSuccessfulResult(result);
255
- } catch (error) {
256
- console.error("Result parsing error:", error);
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 "EXPIRED":
262
- console.warn("Session expired:", error.message);
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 "CANCELED":
265
- console.warn("User canceled:", error.message);
294
+ case 'PERMISSION_DENIED':
295
+ // Guide user to grant camera/NFC permission in Settings
266
296
  break;
267
- case "PERMISSION_DENIED":
268
- console.error("Permission denied:", error.message);
297
+ case 'DEVICE_NOT_ACCEPTABLE':
298
+ // Block flow — emulator, rooted, or jailbroken device
269
299
  break;
270
- case "DEVICE_NOT_ACCEPTABLE":
271
- console.error("Device not acceptable:", error.message);
300
+ case 'MRZ_INVALID':
301
+ // Invalid pre-filled MRZ was passed in config
272
302
  break;
273
- case "MRZ_INVALID":
274
- console.error("Invalid MRZ:", error.message);
303
+ case 'NFC_NOT_MATCH':
304
+ // NFC chip data doesn't match scanned document
275
305
  break;
276
- case "NFC_NOT_MATCH":
277
- console.error("NFC mismatch:", error.message);
306
+ case 'UNSUPPORTED':
307
+ // Device doesn't support NFC
278
308
  break;
279
- case "UNSUPPORTED":
280
- console.error("Unsupported device:", error.message);
309
+ case 'CONFIG_ERROR':
310
+ // Invalid domain, session, or SDK config
281
311
  break;
282
- case "CONFIG_ERROR":
283
- console.error("Configuration error:", error.message);
312
+ case 'ACTIVITY_NULL':
313
+ // Android only — app was backgrounded when SDK launched
284
314
  break;
285
- case "OTHER":
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 minutes timeout) |
302
- | `CANCELED` | User canceled the eKYC process |
303
- | `PERMISSION_DENIED` | Camera or NFC permission denied |
304
- | `DEVICE_NOT_ACCEPTABLE` | Emulator, rooted, or jailbroken device detected |
305
- | `MRZ_INVALID` | Input MRZ is invalid |
306
- | `NFC_NOT_MATCH` | MRZ doesn't match NFC chip data |
307
- | `UNSUPPORTED` | Device doesn't support required features (usually NFC) |
308
- | `CONFIG_ERROR` | Configuration error |
309
- | `OTHER` | Unexpected error |
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 provides a `KalapaResult` class for type-safe data access:
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
- // Or use constructor directly
326
- const result = new KalapaResult(rawResult);
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 | Decision result: `"APPROVED"`, `"MANUAL"`, `"REJECTED"`, or `"UNKNOWN"` |
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 from the verification result |
374
+ | `name` | string | Full name |
344
375
  | `id_number` | string | ID card number |
345
376
  | `birthday` | string | Birth date |
346
- | `gender` | string | Gender information |
347
- | `country` | string | Country information |
377
+ | `gender` | string | Gender |
378
+ | `country` | string | Country |
348
379
  | `national` | string | Nationality |
349
- | `ethnicity` | string | Ethnicity information |
350
- | `religion` | string | Religious information |
351
- | `features` | string | Personal identification marks or features |
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 entities (district, ward, province, unknown) |
387
+ | `home_entities` | `AddressEntities` | Parsed home address |
367
388
  | `resident` | string | Residential address (full text) |
368
- | `resident_entities` | AddressEntities | Parsed residential address entities (district, ward, province, unknown) |
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 | Face matching result |
387
- | `matching_score` | number | Matching confidence score (0-100) |
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 (0100) |
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 from NFC chip |
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 in DD/MM/YYYY format |
409
- | `date_of_expiry` | string | ID expiry date in DD/MM/YYYY format |
410
- | `date_of_issuance` | string | ID issuance date in DD/MM/YYYY format |
411
- | `gender` | string | Gender information |
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 information |
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 | Religious information |
435
- | `personal_identification` | string | Personal identification marks or features |
436
-
437
- #### Technical Data
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
- #### MrzDataError
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` | QrCodeData \| undefined | Decoded QR code data |
495
- | `error` | { code: number, message: string } \| undefined | Error information if decoding failed |
496
-
497
- #### QrCodeData
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 | Returns `true` if decision is `"APPROVED"` |
513
- | `isManualReview()` | boolean | Returns `true` if decision is `"MANUAL"` |
514
- | `isRejected()` | boolean | Returns `true` if decision is `"REJECTED"` |
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 | Returns `true` if face matched successfully |
521
- | `getFaceMatchingScore()` | number | Returns face matching confidence score |
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 | Returns formatted name or `"N/A"` if empty |
528
- | `getIdNumber()` | string | Returns formatted ID number or `"N/A"` if empty |
529
- | `getDateOfBirth()` | string | Returns formatted birth date or `"N/A"` if empty |
530
- | `getFaceImage()` | string | Returns base64 face image from NFC chip |
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 | Returns clean object for storage/transmission |
537
- | `fromRawResult(rawResult)` | KalapaResult | Static method to create KalapaResult from raw response |
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 rawResult = await KalapaEkyc.start(sessionId, "nfc_only", configInfo);
549
- const result = KalapaResult.fromRawResult(rawResult);
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("eKYC Approved");
540
+ console.log('Approved');
554
541
  } else if (result.isManualReview()) {
555
- console.log("Manual review required");
542
+ console.log('Manual review required');
543
+ } else if (result.isRejected()) {
544
+ console.log('Rejected');
556
545
  }
557
-
558
- // Check face matching
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
- // Access root-level data
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
- console.log(`Home: ${result.home_entities.province}, ${result.home_entities.district}`);
554
+ const { province, district, ward } = result.home_entities;
555
+ console.log(`Home: ${ward}, ${district}, ${province}`);
579
556
  }
580
-
581
- // Access MRZ data if available
582
- if (result.mrz_data?.data) {
583
- console.log(`MRZ Name: ${result.mrz_data.data.name}`);
584
- console.log(`Raw MRZ: ${result.mrz_data.data.raw_mrz}`);
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
- // Access QR code data if available
588
- if (result.qr_code?.data) {
589
- console.log(`QR Code: ${result.qr_code.data.decoded_text}`);
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 comprehensive identity verification with document scanning, face matching, and NFC chip reading capabilities. Key features include:
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 for different verification needs
609
- - Customizable UI with color and language options
610
- - Multiple liveness detection versions
611
- - Type-safe result objects with convenience methods
612
- - Robust error handling with detailed error codes
613
- - Support for pre-filled data to skip certain steps
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).