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