react-native-kalapa-ekyc 1.2.8 → 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 +274 -271
- package/android/build.gradle +3 -3
- package/android/src/main/java/com/reactnativekalapaekyc/KalapaEkycModule.java +55 -43
- package/ios/KalapaEkyc.m +39 -17
- 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/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/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
- 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,34 +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
|
|
6
|
-
|
|
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
|
+
|
|
24
|
+
### 1.2.9
|
|
25
|
+
- **React-Native**: Add `customer_language` into config.
|
|
26
|
+
- **Android**: Fix obfuscation error
|
|
27
|
+
|
|
28
|
+
### 1.2.8
|
|
7
29
|
- **Android**: Update stable version for NFC step.
|
|
30
|
+
|
|
8
31
|
### 1.2.7
|
|
9
32
|
- **Android**: Update config to scan QR code or not.
|
|
10
|
-
- **React-Native**: Introduce config require_qr in AppConfig
|
|
33
|
+
- **React-Native**: Introduce config `require_qr` in AppConfig
|
|
34
|
+
|
|
11
35
|
### 1.2.6
|
|
12
36
|
- **Android**: Minor update, UI / UX optimize. Stable version
|
|
37
|
+
|
|
13
38
|
### 1.2.5
|
|
14
39
|
- **Android**: Fix NFC step error UNKNOWN rarely happens on some device like Samsung Note 10
|
|
40
|
+
|
|
15
41
|
### 1.2.4
|
|
16
42
|
- **Android**: Optimize UI / UX in NFC step
|
|
17
|
-
- **React-Native**: show to skip / show confirm screen.
|
|
43
|
+
- **React-Native**: show to skip / show confirm screen.
|
|
44
|
+
|
|
18
45
|
### 1.2.3
|
|
19
46
|
- **Android**: Optimize UI / UX in NFC step
|
|
47
|
+
|
|
20
48
|
### 1.2.2
|
|
21
49
|
- **iOS**: Maintain UI config
|
|
22
50
|
- **Android**: Maintain / Optimize NFC step
|
|
51
|
+
|
|
23
52
|
### 1.2.1
|
|
24
53
|
- **successColor / failureColor**: Now can be configured via sdkConfig
|
|
54
|
+
|
|
25
55
|
### 1.2.0
|
|
26
56
|
- **Documentation**: Complete overhaul of KalapaResult class documentation
|
|
27
|
-
- **Documentation**: Added comprehensive property documentation for all root-level fields
|
|
28
|
-
- **Documentation**: Added AddressEntities, MrzData, and QrCode object documentation
|
|
29
|
-
- **Documentation**: Enhanced usage examples with all available properties
|
|
30
|
-
- **Documentation**: Fixed package name references throughout documentation
|
|
31
|
-
- **Documentation**: Added examples for accessing MRZ data, QR code data, and address entities
|
|
32
57
|
|
|
33
58
|
### 1.1.3
|
|
34
59
|
- **Android**: Removed GIF dependencies to satisfy Google Play requirement (16KB pages size)
|
|
@@ -55,18 +80,23 @@ Complete guide for integrating Kalapa eKYC functionality into your React Native
|
|
|
55
80
|
|
|
56
81
|
## Requirements
|
|
57
82
|
|
|
58
|
-
###
|
|
83
|
+
### Version Matrix
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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 |
|
|
64
93
|
|
|
65
94
|
### Android
|
|
66
95
|
|
|
67
96
|
**Minimum SDK Requirements:**
|
|
68
97
|
- `minSdkVersion = 24`
|
|
69
|
-
- `
|
|
98
|
+
- `compileSdkVersion = 35`
|
|
99
|
+
- `targetSdkVersion = 35`
|
|
70
100
|
- `android.useAndroidX = true`
|
|
71
101
|
- `Kotlin = 1.8.0+`
|
|
72
102
|
|
|
@@ -80,7 +110,7 @@ compileOptions {
|
|
|
80
110
|
|
|
81
111
|
### iOS
|
|
82
112
|
|
|
83
|
-
- iOS
|
|
113
|
+
- iOS Deployment Target >= 13.0
|
|
84
114
|
|
|
85
115
|
---
|
|
86
116
|
|
|
@@ -102,11 +132,11 @@ npm install react-native-kalapa-ekyc
|
|
|
102
132
|
|
|
103
133
|
#### Manual Installation
|
|
104
134
|
|
|
105
|
-
Add to your `
|
|
135
|
+
Add to your `package.json`:
|
|
106
136
|
|
|
107
137
|
```json
|
|
108
138
|
"dependencies": {
|
|
109
|
-
"react-native-kalapa-ekyc": "^1.2.
|
|
139
|
+
"react-native-kalapa-ekyc": "^1.2.10"
|
|
110
140
|
}
|
|
111
141
|
```
|
|
112
142
|
|
|
@@ -148,7 +178,7 @@ Add the **Near Field Communication Tag Reading** capability to your project targ
|
|
|
148
178
|
Each eKYC profile requires a unique session ID (JWT access token). The SDK uses this session ID to perform all eKYC steps.
|
|
149
179
|
|
|
150
180
|
**Important Notes:**
|
|
151
|
-
- Failure to provide a valid session ID results in an
|
|
181
|
+
- Failure to provide a valid session ID results in an `EXPIRED` error
|
|
152
182
|
- Sessions are valid for 10 minutes by default (adjustable)
|
|
153
183
|
- Each session ID should be used for a single eKYC flow
|
|
154
184
|
|
|
@@ -158,61 +188,57 @@ Check the [API documentation](https://www.notion.so/Init-session-820a772b8402499
|
|
|
158
188
|
|
|
159
189
|
### 2. Define SDK Configuration
|
|
160
190
|
|
|
161
|
-
####
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
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
|
+
};
|
|
181
210
|
```
|
|
182
211
|
|
|
183
212
|
#### Configuration Parameters
|
|
184
213
|
|
|
185
|
-
| Parameter | Type | Description |
|
|
186
|
-
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
193
|
-
| `
|
|
194
|
-
| `
|
|
195
|
-
| `
|
|
196
|
-
| `
|
|
197
|
-
| `
|
|
198
|
-
| `
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
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
|
+
|
|
202
234
|
---
|
|
203
235
|
|
|
204
236
|
### 3. Define the SDK Flow
|
|
205
237
|
|
|
206
|
-
#### Available eKYC Steps
|
|
207
|
-
|
|
208
|
-
1. Scan the document
|
|
209
|
-
2. Scan the face
|
|
210
|
-
3. Scan the NFC chip
|
|
211
|
-
|
|
212
238
|
#### Flow Types
|
|
213
239
|
|
|
214
|
-
| Flow
|
|
215
|
-
|
|
240
|
+
| Flow | Scan Document | Scan Face | Scan NFC Chip |
|
|
241
|
+
|------|:---:|:---:|:---:|
|
|
216
242
|
| `ekyc` | ✓ | ✓ | ✗ |
|
|
217
243
|
| `nfc_ekyc` | ✓ | ✓ | ✓ |
|
|
218
244
|
| `nfc_only` | ✗ | ✓ | ✓ |
|
|
@@ -221,11 +247,8 @@ let configInfo = {
|
|
|
221
247
|
|
|
222
248
|
The SDK flow typically matches the flow set during session creation. However, there are special use cases:
|
|
223
249
|
|
|
224
|
-
**Example
|
|
225
|
-
If
|
|
226
|
-
- Reuse the same session ID
|
|
227
|
-
- Set flow to `nfc_only`
|
|
228
|
-
- 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.
|
|
229
252
|
|
|
230
253
|
**Best Practice:** Use separate sessions for different flows to simplify data management.
|
|
231
254
|
|
|
@@ -233,53 +256,64 @@ If users completed the basic `ekyc` flow but failed to scan the NFC chip, you ca
|
|
|
233
256
|
|
|
234
257
|
### 4. Start the SDK
|
|
235
258
|
|
|
236
|
-
```
|
|
237
|
-
import KalapaEkyc, { KalapaResult } from 'react-native-kalapa-ekyc';
|
|
259
|
+
```typescript
|
|
260
|
+
import KalapaEkyc, { KalapaResult, KalapaEkycConfig, KalapaEkycFlow } from 'react-native-kalapa-ekyc';
|
|
238
261
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
262
|
+
const configInfo: KalapaEkycConfig = {
|
|
263
|
+
domain: 'https://ekyc-sdk.kalapa.vn',
|
|
264
|
+
language: 'vi',
|
|
265
|
+
liveness_version: 3,
|
|
266
|
+
with_confirm_screen: true,
|
|
267
|
+
};
|
|
268
|
+
|
|
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
|
|
252
284
|
}
|
|
253
285
|
})
|
|
254
286
|
.catch((error) => {
|
|
255
287
|
switch (error.code) {
|
|
256
|
-
case
|
|
257
|
-
|
|
288
|
+
case 'EXPIRED':
|
|
289
|
+
// Prompt user to restart — session timed out
|
|
258
290
|
break;
|
|
259
|
-
case
|
|
260
|
-
|
|
291
|
+
case 'CANCELED':
|
|
292
|
+
// User backed out — no action needed
|
|
261
293
|
break;
|
|
262
|
-
case
|
|
263
|
-
|
|
294
|
+
case 'PERMISSION_DENIED':
|
|
295
|
+
// Guide user to grant camera/NFC permission in Settings
|
|
264
296
|
break;
|
|
265
|
-
case
|
|
266
|
-
|
|
297
|
+
case 'DEVICE_NOT_ACCEPTABLE':
|
|
298
|
+
// Block flow — emulator, rooted, or jailbroken device
|
|
267
299
|
break;
|
|
268
|
-
case
|
|
269
|
-
|
|
300
|
+
case 'MRZ_INVALID':
|
|
301
|
+
// Invalid pre-filled MRZ was passed in config
|
|
270
302
|
break;
|
|
271
|
-
case
|
|
272
|
-
|
|
303
|
+
case 'NFC_NOT_MATCH':
|
|
304
|
+
// NFC chip data doesn't match scanned document
|
|
273
305
|
break;
|
|
274
|
-
case
|
|
275
|
-
|
|
306
|
+
case 'UNSUPPORTED':
|
|
307
|
+
// Device doesn't support NFC
|
|
276
308
|
break;
|
|
277
|
-
case
|
|
278
|
-
|
|
309
|
+
case 'CONFIG_ERROR':
|
|
310
|
+
// Invalid domain, session, or SDK config
|
|
279
311
|
break;
|
|
280
|
-
case
|
|
312
|
+
case 'ACTIVITY_NULL':
|
|
313
|
+
// Android only — app was backgrounded when SDK launched
|
|
314
|
+
break;
|
|
315
|
+
case 'OTHER':
|
|
281
316
|
default:
|
|
282
|
-
console.error("Unknown error:", error.message);
|
|
283
317
|
break;
|
|
284
318
|
}
|
|
285
319
|
});
|
|
@@ -291,17 +325,18 @@ KalapaEkyc.start(sessionId, sdkFlow, configInfo)
|
|
|
291
325
|
|
|
292
326
|
### Error Codes
|
|
293
327
|
|
|
294
|
-
| Error Code | Description |
|
|
295
|
-
|
|
296
|
-
| `EXPIRED` | Session expired (10
|
|
297
|
-
| `CANCELED` | User canceled the eKYC process |
|
|
298
|
-
| `PERMISSION_DENIED` | Camera or NFC permission denied |
|
|
299
|
-
| `DEVICE_NOT_ACCEPTABLE` | Emulator, rooted, or
|
|
300
|
-
| `MRZ_INVALID` |
|
|
301
|
-
| `NFC_NOT_MATCH` | MRZ
|
|
302
|
-
| `UNSUPPORTED` | Device doesn't support required features (
|
|
303
|
-
| `CONFIG_ERROR` |
|
|
304
|
-
| `
|
|
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 |
|
|
305
340
|
|
|
306
341
|
---
|
|
307
342
|
|
|
@@ -309,103 +344,79 @@ KalapaEkyc.start(sessionId, sdkFlow, configInfo)
|
|
|
309
344
|
|
|
310
345
|
### KalapaResult Class
|
|
311
346
|
|
|
312
|
-
The SDK
|
|
347
|
+
The SDK returns a `KalapaResult` instance in the resolved value of `start()`. No manual parsing is needed.
|
|
313
348
|
|
|
314
349
|
```typescript
|
|
315
|
-
import { KalapaResult } from 'react-native-kalapa-ekyc';
|
|
316
|
-
|
|
317
|
-
// Create instance using static method (recommended)
|
|
318
|
-
const result = KalapaResult.fromRawResult(rawResult);
|
|
350
|
+
import KalapaEkyc, { KalapaResult } from 'react-native-kalapa-ekyc';
|
|
319
351
|
|
|
320
|
-
|
|
321
|
-
|
|
352
|
+
const { kalapa_result } = await KalapaEkyc.start(sessionId, flow, config);
|
|
353
|
+
// kalapa_result is already a KalapaResult instance
|
|
322
354
|
```
|
|
323
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
|
+
|
|
324
358
|
#### Core Properties
|
|
325
359
|
|
|
326
360
|
| Property | Type | Description |
|
|
327
361
|
|----------|------|-------------|
|
|
328
|
-
| `decision` | DecisionType |
|
|
329
|
-
| `selfie_data` | SelfieData | Face matching results |
|
|
330
|
-
| `nfc_data` | NfcData | NFC chip data |
|
|
362
|
+
| `decision` | `DecisionType` | `"APPROVED"`, `"MANUAL"`, `"REJECTED"`, or `"UNKNOWN"` |
|
|
331
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 |
|
|
332
368
|
| `rawResult` | any | Original unprocessed response |
|
|
333
369
|
|
|
334
370
|
#### Root-Level Personal Information
|
|
335
371
|
|
|
336
372
|
| Property | Type | Description |
|
|
337
373
|
|----------|------|-------------|
|
|
338
|
-
| `name` | string | Full name
|
|
374
|
+
| `name` | string | Full name |
|
|
339
375
|
| `id_number` | string | ID card number |
|
|
340
376
|
| `birthday` | string | Birth date |
|
|
341
|
-
| `gender` | string | Gender
|
|
342
|
-
| `country` | string | Country
|
|
377
|
+
| `gender` | string | Gender |
|
|
378
|
+
| `country` | string | Country |
|
|
343
379
|
| `national` | string | Nationality |
|
|
344
|
-
| `ethnicity` | string | Ethnicity
|
|
345
|
-
| `religion` | string |
|
|
346
|
-
| `features` | string | Personal identification marks
|
|
380
|
+
| `ethnicity` | string | Ethnicity |
|
|
381
|
+
| `religion` | string | Religion |
|
|
382
|
+
| `features` | string | Personal identification marks |
|
|
347
383
|
| `poi` | string | Place of issue |
|
|
348
|
-
|
|
349
|
-
#### Date Information
|
|
350
|
-
|
|
351
|
-
| Property | Type | Description |
|
|
352
|
-
|----------|------|-------------|
|
|
353
384
|
| `doe` | string | Date of expiry |
|
|
354
385
|
| `doi` | string | Date of issuance |
|
|
355
|
-
|
|
356
|
-
#### Address Information
|
|
357
|
-
|
|
358
|
-
| Property | Type | Description |
|
|
359
|
-
|----------|------|-------------|
|
|
360
386
|
| `home` | string | Home address (full text) |
|
|
361
|
-
| `home_entities` | AddressEntities | Parsed home address
|
|
387
|
+
| `home_entities` | `AddressEntities` | Parsed home address |
|
|
362
388
|
| `resident` | string | Residential address (full text) |
|
|
363
|
-
| `resident_entities` | AddressEntities | Parsed residential address
|
|
364
|
-
|
|
365
|
-
#### Document Data
|
|
366
|
-
|
|
367
|
-
| Property | Type | Description |
|
|
368
|
-
|----------|------|-------------|
|
|
369
|
-
| `mrz_data` | MrzData \| null | Machine Readable Zone data with parsed fields |
|
|
370
|
-
| `qr_code` | QrCode \| null | QR code data from document |
|
|
389
|
+
| `resident_entities` | `AddressEntities` | Parsed residential address |
|
|
371
390
|
| `type` | string | Document type |
|
|
372
391
|
|
|
373
392
|
---
|
|
374
393
|
|
|
375
394
|
### SelfieData Object
|
|
376
395
|
|
|
377
|
-
#### Properties
|
|
378
|
-
|
|
379
396
|
| Property | Type | Description |
|
|
380
397
|
|----------|------|-------------|
|
|
381
|
-
| `is_matched` | boolean |
|
|
382
|
-
| `matching_score` | number | Matching confidence score (0
|
|
383
|
-
|
|
384
|
-
#### Usage Example
|
|
385
|
-
|
|
386
|
-
```typescript
|
|
387
|
-
if (result.selfie_data.is_matched) {
|
|
388
|
-
console.log(`Face matched with ${result.selfie_data.matching_score}% confidence`);
|
|
389
|
-
}
|
|
390
|
-
```
|
|
398
|
+
| `is_matched` | boolean | Whether face matched the document |
|
|
399
|
+
| `matching_score` | number | Matching confidence score (0–100) |
|
|
391
400
|
|
|
392
401
|
---
|
|
393
402
|
|
|
394
403
|
### NfcData Object
|
|
395
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
|
+
|
|
396
407
|
#### Personal Information
|
|
397
408
|
|
|
398
409
|
| Property | Type | Description |
|
|
399
410
|
|----------|------|-------------|
|
|
400
|
-
| `name` | string | Full name
|
|
411
|
+
| `name` | string | Full name |
|
|
401
412
|
| `id_number` | string | ID card number |
|
|
402
413
|
| `old_id_number` | string | Previous ID number (if any) |
|
|
403
|
-
| `date_of_birth` | string | Birth date
|
|
404
|
-
| `date_of_expiry` | string |
|
|
405
|
-
| `date_of_issuance` | string |
|
|
406
|
-
| `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 |
|
|
407
418
|
| `nationality` | string | Nationality |
|
|
408
|
-
| `nation` | string | Nation/ethnicity
|
|
419
|
+
| `nation` | string | Nation/ethnicity |
|
|
409
420
|
|
|
410
421
|
#### Address Information
|
|
411
422
|
|
|
@@ -426,22 +437,15 @@ if (result.selfie_data.is_matched) {
|
|
|
426
437
|
|
|
427
438
|
| Property | Type | Description |
|
|
428
439
|
|----------|------|-------------|
|
|
429
|
-
| `religion` | string |
|
|
430
|
-
| `personal_identification` | string | Personal identification marks
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
| Property | Type | Description |
|
|
435
|
-
|----------|------|-------------|
|
|
436
|
-
| `face_image` | string | Base64 encoded face image from NFC chip |
|
|
437
|
-
| `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 |
|
|
438
444
|
|
|
439
445
|
---
|
|
440
446
|
|
|
441
447
|
### AddressEntities Object
|
|
442
448
|
|
|
443
|
-
Represents parsed address components:
|
|
444
|
-
|
|
445
449
|
| Property | Type | Description |
|
|
446
450
|
|----------|------|-------------|
|
|
447
451
|
| `district` | string | District name |
|
|
@@ -453,48 +457,27 @@ Represents parsed address components:
|
|
|
453
457
|
|
|
454
458
|
### MrzData Object
|
|
455
459
|
|
|
456
|
-
Contains MRZ (Machine Readable Zone) information:
|
|
457
|
-
|
|
458
|
-
| Property | Type | Description |
|
|
459
|
-
|----------|------|-------------|
|
|
460
|
-
| `data` | MrzDataFields \| undefined | Parsed MRZ fields |
|
|
461
|
-
| `error` | MrzDataError \| undefined | Error information if parsing failed |
|
|
462
|
-
|
|
463
|
-
#### MrzDataFields
|
|
464
|
-
|
|
465
460
|
| Property | Type | Description |
|
|
466
461
|
|----------|------|-------------|
|
|
467
|
-
| `birthday` | string \| undefined | Birth date from MRZ |
|
|
468
|
-
| `doe` | string \| undefined | Date of expiry from MRZ |
|
|
469
|
-
| `gender` | string \| undefined | Gender from MRZ |
|
|
470
|
-
| `id_number` | string \| undefined | ID number from MRZ |
|
|
471
|
-
| `name` | string \| undefined | Name from MRZ |
|
|
472
|
-
| `raw_mrz` | string \| undefined | Raw MRZ string |
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
| Property | Type | Description |
|
|
477
|
-
|----------|------|-------------|
|
|
478
|
-
| `code` | number | Error code |
|
|
479
|
-
| `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 |
|
|
480
470
|
|
|
481
471
|
---
|
|
482
472
|
|
|
483
473
|
### QrCode Object
|
|
484
474
|
|
|
485
|
-
Contains QR code information:
|
|
486
|
-
|
|
487
475
|
| Property | Type | Description |
|
|
488
476
|
|----------|------|-------------|
|
|
489
|
-
| `data` |
|
|
490
|
-
| `
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
| Property | Type | Description |
|
|
495
|
-
|----------|------|-------------|
|
|
496
|
-
| `decoded_text` | string \| undefined | Decoded QR code text |
|
|
497
|
-
| `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 |
|
|
498
481
|
|
|
499
482
|
---
|
|
500
483
|
|
|
@@ -504,92 +487,112 @@ Contains QR code information:
|
|
|
504
487
|
|
|
505
488
|
| Method | Returns | Description |
|
|
506
489
|
|--------|---------|-------------|
|
|
507
|
-
| `isApproved()` | boolean |
|
|
508
|
-
| `isManualReview()` | boolean |
|
|
509
|
-
| `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"` |
|
|
510
493
|
|
|
511
494
|
### Face Matching Methods
|
|
512
495
|
|
|
513
496
|
| Method | Returns | Description |
|
|
514
497
|
|--------|---------|-------------|
|
|
515
|
-
| `isFaceMatched()` | boolean |
|
|
516
|
-
| `getFaceMatchingScore()` | number |
|
|
498
|
+
| `isFaceMatched()` | boolean | `true` if face matched successfully |
|
|
499
|
+
| `getFaceMatchingScore()` | number | Face matching confidence score |
|
|
517
500
|
|
|
518
501
|
### Formatted Getters
|
|
519
502
|
|
|
520
503
|
| Method | Returns | Description |
|
|
521
504
|
|--------|---------|-------------|
|
|
522
|
-
| `getDisplayName()` | string |
|
|
523
|
-
| `getIdNumber()` | string |
|
|
524
|
-
| `getDateOfBirth()` | string |
|
|
525
|
-
| `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 |
|
|
526
509
|
|
|
527
510
|
### Utility Methods
|
|
528
511
|
|
|
529
512
|
| Method | Returns | Description |
|
|
530
513
|
|--------|---------|-------------|
|
|
531
|
-
| `toJSON()` | object |
|
|
532
|
-
| `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 |
|
|
533
516
|
|
|
534
517
|
---
|
|
535
518
|
|
|
536
519
|
## Complete Usage Example
|
|
537
520
|
|
|
538
521
|
```typescript
|
|
539
|
-
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';
|
|
540
534
|
|
|
541
|
-
const handleEkyc = async () => {
|
|
542
535
|
try {
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
// Check decision
|
|
536
|
+
const { kalapa_result: result } = await KalapaEkyc.start(sessionId, flow, config);
|
|
537
|
+
|
|
538
|
+
// Decision
|
|
547
539
|
if (result.isApproved()) {
|
|
548
|
-
console.log(
|
|
540
|
+
console.log('Approved');
|
|
549
541
|
} else if (result.isManualReview()) {
|
|
550
|
-
console.log(
|
|
542
|
+
console.log('Manual review required');
|
|
543
|
+
} else if (result.isRejected()) {
|
|
544
|
+
console.log('Rejected');
|
|
551
545
|
}
|
|
552
|
-
|
|
553
|
-
//
|
|
546
|
+
|
|
547
|
+
// Face matching
|
|
554
548
|
if (result.isFaceMatched()) {
|
|
555
|
-
console.log(`Face matched: ${result.getFaceMatchingScore()}
|
|
549
|
+
console.log(`Face matched — score: ${result.getFaceMatchingScore()}`);
|
|
556
550
|
}
|
|
557
|
-
|
|
558
|
-
//
|
|
559
|
-
console.log(`Name: ${result.name}`);
|
|
560
|
-
console.log(`ID Number: ${result.id_number}`);
|
|
561
|
-
console.log(`Birthday: ${result.birthday}`);
|
|
562
|
-
|
|
563
|
-
// Access NFC data
|
|
564
|
-
const userData = {
|
|
565
|
-
name: result.nfc_data.name,
|
|
566
|
-
idNumber: result.nfc_data.id_number,
|
|
567
|
-
birthDate: result.nfc_data.date_of_birth,
|
|
568
|
-
faceImage: result.nfc_data.face_image
|
|
569
|
-
};
|
|
570
|
-
|
|
571
|
-
// Access address entities
|
|
551
|
+
|
|
552
|
+
// Address entities
|
|
572
553
|
if (result.home_entities) {
|
|
573
|
-
|
|
554
|
+
const { province, district, ward } = result.home_entities;
|
|
555
|
+
console.log(`Home: ${ward}, ${district}, ${province}`);
|
|
556
|
+
}
|
|
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
|
|
574
562
|
}
|
|
575
|
-
|
|
576
|
-
//
|
|
577
|
-
if (result.
|
|
578
|
-
console.log(
|
|
579
|
-
console.log(`Raw MRZ: ${result.mrz_data.data.raw_mrz}`);
|
|
563
|
+
|
|
564
|
+
// QR code data
|
|
565
|
+
if (result.qr_code?.data?.decoded_text) {
|
|
566
|
+
console.log('QR decoded');
|
|
580
567
|
}
|
|
581
|
-
|
|
582
|
-
//
|
|
583
|
-
|
|
584
|
-
|
|
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.');
|
|
585
595
|
}
|
|
586
|
-
|
|
587
|
-
// Export to JSON for storage/transmission
|
|
588
|
-
const jsonData = result.toJSON();
|
|
589
|
-
await saveUserData(jsonData);
|
|
590
|
-
|
|
591
|
-
} catch (error) {
|
|
592
|
-
handleEkycError(error);
|
|
593
596
|
}
|
|
594
597
|
};
|
|
595
598
|
```
|
|
@@ -598,13 +601,13 @@ const handleEkyc = async () => {
|
|
|
598
601
|
|
|
599
602
|
## Summary
|
|
600
603
|
|
|
601
|
-
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:
|
|
602
605
|
|
|
603
|
-
- Three flexible flow types
|
|
604
|
-
-
|
|
605
|
-
-
|
|
606
|
-
-
|
|
607
|
-
-
|
|
608
|
-
-
|
|
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
|
|
609
612
|
|
|
610
|
-
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).
|