@finos_sdk/sdk-ekyc 1.4.1 → 1.4.4

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 CHANGED
@@ -5,31 +5,30 @@
5
5
 
6
6
  React Native SDK for eKYC (electronic Know Your Customer) and eSign. Features include Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, C06 residence verification, SMS OTP verification, and Electronic Signature (eSign) capabilities.
7
7
 
8
- **Version**: 1.4.1
8
+ **Version**: 1.4.2
9
9
 
10
- ## 🚀 Features
10
+ ## Features
11
11
 
12
- - **Unified eKYC Flow** - Complete verification flow with single method call
13
- - **NFC Reading** - Read Vietnamese CCCD cards with NFC chips
14
- - **OCR Processing** - Extract data from ID card images
15
- - **Liveness Detection** - Verify user presence with selfie (Active/Passive modes)
16
- - **Face Matching** - Compare selfie with ID card photo
17
- - **C06 Verification** - Residence information verification
18
- - **eSign** - Electronic signature support (PDF signing, Remote signing)
19
- - **SMS OTP** - Integrated SMS OTP verification
20
- - **Flexible Configuration** - Custom styling and retry options
21
- - **TypeScript Support** - Full type definitions included
12
+ - **Unified eKYC Flow** - Complete verification flow with single method call
13
+ - **NFC Reading** - Read Vietnamese CCCD cards with NFC chips
14
+ - **OCR Processing** - Extract data from ID card images
15
+ - **Liveness Detection** - Verify user presence with selfie (Active/Passive modes)
16
+ - **Face Matching** - Compare selfie with ID card photo
17
+ - **C06 Verification** - Residence information verification
18
+ - **eSign** - Electronic signature support (PDF signing, Remote signing)
19
+ - **SMS OTP** - Integrated SMS OTP verification
20
+ - **Flexible Configuration** - Custom styling, capture button colors, and retry options
21
+ - **TypeScript Support** - Full type definitions included
22
22
 
23
+ ## Requirements
23
24
 
25
+ - React Native 0.70.0+
26
+ - Android 24+ / API Level 24+
27
+ - iOS 14.0+ (Swift 5.7+)
28
+ - NFC and/or Camera hardware depending on modules used
29
+ - Runtime permissions for Camera and/or NFC
24
30
 
25
- ## � Requirements
26
-
27
- - React Native 0.77.0+
28
- - Android 24+ / API Level 24+ (Android platform only)
29
- - NFC and/or Camera hardware depending on modules used
30
- - Runtime permissions for Camera and/or NFC
31
-
32
- ## �🛠️ Installation
31
+ ## Installation
33
32
 
34
33
  ```bash
35
34
  npm install @finos_sdk/sdk-ekyc
@@ -37,12 +36,22 @@ npm install @finos_sdk/sdk-ekyc
37
36
  yarn add @finos_sdk/sdk-ekyc
38
37
  ```
39
38
 
40
- ## 📖 Quick Start
39
+ ---
40
+
41
+ ## Quick Start
41
42
 
42
43
  ### 1. Import the SDK
43
44
 
44
45
  ```typescript
45
- import { FinosEKYC, FinosESign, getEkycError } from '@finos_sdk/sdk-ekyc';
46
+ import {
47
+ FinosEKYC,
48
+ FinosESign,
49
+ getEkycError,
50
+ SDKFaceDetectStatus,
51
+ SDKFlowType,
52
+ AppIDType,
53
+ } from '@finos_sdk/sdk-ekyc';
54
+
46
55
  import type {
47
56
  NfcConfig,
48
57
  C06Config,
@@ -50,108 +59,600 @@ import type {
50
59
  LivenessConfig,
51
60
  FaceServiceConfig,
52
61
  SmsOtpConfig,
53
- SDKFlowType
62
+ StartEkycUIResult,
63
+ EKYCError,
54
64
  } from '@finos_sdk/sdk-ekyc';
55
65
  ```
56
66
 
57
- ### 2. Complete eKYC Flow (Recommended)
67
+ ### 2. Initialize SDK
68
+
69
+ ```typescript
70
+ // Initialize the SDK (required before using any method)
71
+ await FinosEKYC.initialize();
72
+
73
+ // Check SDK info
74
+ const info = await FinosEKYC.getSDKInfo();
75
+ console.log(info); // { name, version, buildNumber, platform, isInitialized }
76
+
77
+ // Set transaction ID (optional)
78
+ await FinosEKYC.setTransactionId('your-transaction-id');
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Complete eKYC Flow (startEkycUI)
84
+
85
+ The recommended way to use the SDK. Handles the complete verification flow with a single method call.
86
+
87
+ ### Method Signature
88
+
89
+ ```typescript
90
+ FinosEKYC.startEkycUI(
91
+ appKey: string,
92
+ flowSDK: SDKFlowType[],
93
+ language: string,
94
+ transactionId: string,
95
+ appKeyConfig: AppKeyConfig,
96
+ optionConfig?: OptionConfig,
97
+ styleConfig?: StyleConfig,
98
+ ): Promise<StartEkycUIResult>
99
+ ```
100
+
101
+ ### Full Example with All Configs
102
+
103
+ ```typescript
104
+ const result = await FinosEKYC.startEkycUI(
105
+ 'your-main-app-key',
106
+
107
+ // --- flowSDK: verification steps to execute in order ---
108
+ ['OCR', 'NFC', 'LIVENESS'],
109
+
110
+ // --- language ---
111
+ 'vi', // 'vi' | 'en'
112
+
113
+ // --- transactionId ---
114
+ 'txn-20260319-001',
115
+
116
+ // --- appKeyConfig: API keys for each module ---
117
+ {
118
+ appKey: 'your-main-app-key',
119
+ appKeyNfc: 'your-nfc-key',
120
+ appKeyOcr: 'your-ocr-key',
121
+ appKeyLiveness: 'your-liveness-key',
122
+ appKeyC06: 'your-c06-key',
123
+ appKeyFaceService: 'your-face-key',
124
+ },
125
+
126
+ // --- optionConfig: SDK behavior settings ---
127
+ {
128
+ baseUrl: 'https://api.example.com', // Custom API base URL
129
+ countMaxRetry: 3, // Max retry attempts (default: 3)
130
+ language: 'vi', // Override language
131
+ switchFrontCamera: true, // Use front camera (default: false)
132
+ isActiveLiveness: true, // Active liveness mode (default: true)
133
+ autoCapture: true, // Auto capture (default: true)
134
+ forceCaptureTimeout: 30, // Force capture timeout in seconds
135
+ isShowCameraFont: true, // Show camera guide text
136
+ customActions: [ // Custom liveness actions
137
+ SDKFaceDetectStatus.LEFT,
138
+ SDKFaceDetectStatus.RIGHT,
139
+ SDKFaceDetectStatus.STRAIGHT,
140
+ ],
141
+ activeActionCount: 3, // Random action count 1-10 (if customActions is null)
142
+ appIDType: AppIDType.VIKKI, // App ID type: NONE | HD_BANK | VIKKI
143
+ },
144
+
145
+ // --- styleConfig: UI customization ---
146
+ {
147
+ // Global defaults
148
+ textSize: 14, // Default text size (sp)
149
+ textFont: '', // Font resource name (e.g. 'roboto_medium')
150
+ textColor: 0xFF333333, // Default text color (ARGB int)
151
+ statusBarBackground: undefined, // Status bar drawable resource ID
152
+ backIcon: undefined, // Back button drawable resource ID
153
+
154
+ // Per-element text styles (override global defaults)
155
+ titleStyle: { // Screen title ("CHUP MAT TRUOC", "Chup chan dung")
156
+ textSize: 20,
157
+ textFont: 'roboto_bold',
158
+ textColor: 0xFF142630,
159
+ },
160
+ toolbarStyle: { // Toolbar/navigation bar title
161
+ textSize: 18,
162
+ textFont: '',
163
+ textColor: 0xFF007AFF,
164
+ },
165
+ instructionStyle: { // User instruction text
166
+ textSize: 16,
167
+ textFont: '',
168
+ textColor: 0xFF666666,
169
+ },
170
+ errorStyle: { // Error message text
171
+ textSize: 14,
172
+ textFont: '',
173
+ textColor: 0xFFFF3B30,
174
+ },
175
+ successStyle: { // Success message text
176
+ textSize: 14,
177
+ textFont: '',
178
+ textColor: 0xFF34C759,
179
+ },
180
+ warningStyle: { // Warning message text
181
+ textSize: 14,
182
+ textFont: '',
183
+ textColor: 0xFFFF9500,
184
+ },
185
+
186
+ // Capture button colors (direct color int, ARGB)
187
+ captureButtonColor: 0xFF00A86F, // Capture button color when enabled
188
+ captureButtonDisabledColor: 0xFFCCCCCC, // Capture button color when disabled
189
+ },
190
+ );
191
+ ```
192
+
193
+ ### Result
194
+
195
+ ```typescript
196
+ interface StartEkycUIResult {
197
+ status: 'success';
198
+ event: string;
199
+ data?: string;
200
+ transactionId?: string;
201
+ imageFace?: string; // Base64 selfie/liveness image
202
+ imageOcrFront?: string; // Base64 OCR front image
203
+ imageOcrBack?: string; // Base64 OCR back image
204
+ imageFacePath?: string; // Absolute path to selfie file
205
+ imageOcrFrontPath?: string; // Absolute path to OCR front file
206
+ imageOcrBackPath?: string; // Absolute path to OCR back file
207
+ }
208
+ ```
209
+
210
+ ---
211
+
212
+ ## Configuration Reference
213
+
214
+ ### AppKeyConfig
58
215
 
59
- The SDK provides a unified method to handle the complete eKYC flow:
216
+ API keys for each eKYC module.
217
+
218
+ | Field | Type | Required | Description |
219
+ |-------|------|----------|-------------|
220
+ | `appKey` | `string` | Yes | Main app key |
221
+ | `appKeyNfc` | `string` | Yes | NFC module key |
222
+ | `appKeyOcr` | `string` | Yes | OCR module key |
223
+ | `appKeyLiveness` | `string` | Yes | Liveness module key |
224
+ | `appKeyC06` | `string` | Yes | C06 module key |
225
+ | `appKeyFaceService` | `string` | Yes | Face service module key |
60
226
 
61
227
  ```typescript
62
- // Configure your app keys
63
- const AppKey = {
228
+ const appKeyConfig = {
64
229
  appKey: 'your-main-app-key',
65
- appKeyNfc: 'your-nfc-app-key',
66
- appKeyC06: 'your-c06-app-key',
67
- appKeyOcr: 'your-ocr-app-key',
68
- appKeyLiveness: 'your-liveness-app-key',
69
- appKeyFaceService: 'your-face-service-app-key',
230
+ appKeyNfc: 'your-nfc-key',
231
+ appKeyOcr: 'your-ocr-key',
232
+ appKeyLiveness: 'your-liveness-key',
233
+ appKeyC06: 'your-c06-key',
234
+ appKeyFaceService: 'your-face-key',
70
235
  };
236
+ ```
71
237
 
72
- const handleEkycFlow = async () => {
73
- try {
74
- // Initialize SDK first
75
- await FinosEKYC.initialize();
76
-
77
- // Start complete eKYC flow
78
- const result = await FinosEKYC.startEkycUI(
79
- AppKey.appKey, // Main app key
80
- ['OCR', 'NFC', 'LIVENESS'], // Flow steps
81
- 'vi', // Language ('vi' | 'en')
82
- 'test-transaction-123', // Transaction ID
83
- AppKey, // All app keys
84
- { countMaxRetry: 3 }, // Option config
85
- { // Style config
86
- textSize: 14,
87
- textColor: 0xff000000,
88
- toolbarStyle: {
89
- textSize: 26,
90
- textColor: 0xff007AFF,
91
- }
92
- }
93
- );
94
-
95
- console.log('eKYC Result:', result);
96
- } catch (error) {
97
- const err = getEkycError(error);
98
- console.error('eKYC Error:', err.customCode, err.customMessage);
99
- }
238
+ ### OptionConfig
239
+
240
+ SDK behavior and runtime settings.
241
+
242
+ | Field | Type | Default | Description |
243
+ |-------|------|---------|-------------|
244
+ | `baseUrl` | `string` | `undefined` | Custom API base URL |
245
+ | `countMaxRetry` | `number` | `3` | Max retry attempts on failure |
246
+ | `language` | `string` | `undefined` | Override language (`'vi'` or `'en'`) |
247
+ | `switchFrontCamera` | `boolean` | `false` | Use front camera for liveness/face |
248
+ | `isActiveLiveness` | `boolean` | `true` | Enable active liveness detection |
249
+ | `autoCapture` | `boolean` | `true` | Auto capture when conditions met |
250
+ | `forceCaptureTimeout` | `number` | `0` | Force capture timeout in seconds (0 = disabled) |
251
+ | `isShowCameraFont` | `boolean` | `true` | Show instruction text on camera screen |
252
+ | `customActions` | `SDKFaceDetectStatus[]` | `undefined` | Custom liveness action sequence |
253
+ | `activeActionCount` | `number` | `2` | Number of random actions (1-10, used when `customActions` is null) |
254
+ | `appIDType` | `AppIDType` | `NONE` | App ID type for branding: `NONE`, `HD_BANK`, or `VIKKI` |
255
+
256
+ ```typescript
257
+ // Minimal
258
+ const optionConfig = { countMaxRetry: 3 };
259
+
260
+ // Full
261
+ const optionConfig = {
262
+ baseUrl: 'https://api.example.com',
263
+ countMaxRetry: 3,
264
+ language: 'vi',
265
+ switchFrontCamera: true,
266
+ isActiveLiveness: true,
267
+ autoCapture: true,
268
+ forceCaptureTimeout: 30,
269
+ isShowCameraFont: true,
270
+ customActions: [
271
+ SDKFaceDetectStatus.LEFT,
272
+ SDKFaceDetectStatus.RIGHT,
273
+ SDKFaceDetectStatus.STRAIGHT,
274
+ ],
275
+ activeActionCount: 3,
276
+ appIDType: AppIDType.VIKKI,
277
+ };
278
+ ```
279
+
280
+ ### AppIDType
281
+
282
+ Determines the branding/skin of SDK screens.
283
+
284
+ | Value | Description |
285
+ |-------|-------------|
286
+ | `NONE` | Default, no branding |
287
+ | `HD_BANK` | HD Bank branding |
288
+ | `VIKKI` | Vikki branding |
289
+
290
+ ```typescript
291
+ import { AppIDType } from '@finos_sdk/sdk-ekyc';
292
+
293
+ // In optionConfig
294
+ { appIDType: AppIDType.VIKKI }
295
+ ```
296
+
297
+ ### StyleConfig
298
+
299
+ UI appearance customization. All color values use ARGB integer format (e.g., `0xFFRRGGBB`).
300
+
301
+ | Field | Type | Default | Description |
302
+ |-------|------|---------|-------------|
303
+ | `textSize` | `number` | `14` | Default text size (sp) |
304
+ | `textFont` | `string` | `''` | Default font resource name (e.g., `'roboto_medium'`) |
305
+ | `textColor` | `number` | `0xFF000000` | Default text color (ARGB) |
306
+ | `statusBarBackground` | `number` | `undefined` | Status bar background drawable resource ID |
307
+ | `backIcon` | `number` | `undefined` | Back button icon drawable resource ID |
308
+ | `titleStyle` | `TextStyle` | `undefined` | Style for screen titles |
309
+ | `toolbarStyle` | `TextStyle` | `undefined` | Style for toolbar/navigation title |
310
+ | `instructionStyle` | `TextStyle` | `undefined` | Style for instruction text |
311
+ | `errorStyle` | `TextStyle` | `undefined` | Style for error messages |
312
+ | `successStyle` | `TextStyle` | `undefined` | Style for success messages |
313
+ | `warningStyle` | `TextStyle` | `undefined` | Style for warning messages |
314
+ | `captureButtonColor` | `number` | `undefined` | Capture button color when enabled (ARGB) |
315
+ | `captureButtonDisabledColor` | `number` | `undefined` | Capture button color when disabled (ARGB) |
316
+
317
+ #### TextStyle (sub-config)
318
+
319
+ Each text style can override the global defaults. `null`/`undefined` fields fall back to the parent `StyleConfig` defaults.
320
+
321
+ | Field | Type | Default | Description |
322
+ |-------|------|---------|-------------|
323
+ | `textSize` | `number` | Parent `textSize` | Text size (sp) |
324
+ | `textFont` | `string` | Parent `textFont` | Font resource name |
325
+ | `textColor` | `number` | Parent `textColor` | Text color (ARGB) |
326
+
327
+ ```typescript
328
+ // Minimal - just change capture button color
329
+ const styleConfig = {
330
+ captureButtonColor: 0xFF00A86F,
331
+ captureButtonDisabledColor: 0xFFCCCCCC,
332
+ };
333
+
334
+ // Custom theme with all styles
335
+ const styleConfig = {
336
+ textSize: 14,
337
+ textFont: 'roboto_regular',
338
+ textColor: 0xFF333333,
339
+ titleStyle: {
340
+ textSize: 20,
341
+ textFont: 'roboto_bold',
342
+ textColor: 0xFF142630,
343
+ },
344
+ toolbarStyle: {
345
+ textSize: 18,
346
+ textColor: 0xFF007AFF,
347
+ },
348
+ instructionStyle: {
349
+ textSize: 16,
350
+ textColor: 0xFF666666,
351
+ },
352
+ errorStyle: {
353
+ textColor: 0xFFFF3B30,
354
+ },
355
+ successStyle: {
356
+ textColor: 0xFF34C759,
357
+ },
358
+ warningStyle: {
359
+ textColor: 0xFFFF9500,
360
+ },
361
+ captureButtonColor: 0xFF142630,
362
+ captureButtonDisabledColor: 0xFFDDDDDD,
363
+ };
364
+ ```
365
+
366
+ ### SDKFlowType
367
+
368
+ Available flow step types for `startEkycUI`.
369
+
370
+ | Value | Description |
371
+ |-------|-------------|
372
+ | `'OCR'` | Scan ID card with camera (front/back) |
373
+ | `'NFC'` | Read NFC chip on CCCD |
374
+ | `'LIVENESS'` | Liveness detection (selfie) |
375
+
376
+ ```typescript
377
+ // Common flows
378
+ const flow1 = ['OCR', 'NFC', 'LIVENESS']; // Full eKYC
379
+ const flow2 = ['OCR', 'LIVENESS']; // Without NFC
380
+ const flow3 = ['LIVENESS']; // Liveness only
381
+ ```
382
+
383
+ ### SDKFaceDetectStatus
384
+
385
+ Available liveness actions for `customActions`.
386
+
387
+ | Value | Description |
388
+ |-------|-------------|
389
+ | `LEFT` | Turn head left |
390
+ | `RIGHT` | Turn head right |
391
+ | `UP` | Look up |
392
+ | `DOWN` | Look down |
393
+ | `STRAIGHT` | Look straight |
394
+ | `SMILE` | Smile |
395
+ | `BLINK` | Blink eyes |
396
+ | `TILT_LEFT` | Tilt head left |
397
+ | `TILT_RIGHT` | Tilt head right |
398
+ | `WINK_LEFT` | Wink left eye |
399
+ | `WINK_RIGHT` | Wink right eye |
400
+
401
+ ```typescript
402
+ import { SDKFaceDetectStatus } from '@finos_sdk/sdk-ekyc';
403
+
404
+ const customActions = [
405
+ SDKFaceDetectStatus.LEFT,
406
+ SDKFaceDetectStatus.RIGHT,
407
+ SDKFaceDetectStatus.SMILE,
408
+ ];
409
+ ```
410
+
411
+ ---
412
+
413
+ ## Individual Module APIs
414
+
415
+ ### NFC Scanning
416
+
417
+ ```typescript
418
+ const config: NfcConfig = {
419
+ appKey: 'your-nfc-key', // Required
420
+ documentNumber: '012345678012', // Required: CCCD number
421
+ birthDate: '01011990', // Required: format DDMMYYYY
422
+ expireDate: '01012030', // Required: format DDMMYYYY
423
+ transactionId: 'txn-001', // Optional
424
+ facePathStorage: '/path/to/face', // Optional: path to save face image
425
+ };
426
+
427
+ const result = await FinosEKYC.startNfcScan(config);
428
+ ```
429
+
430
+ **NfcConfig**
431
+
432
+ | Field | Type | Required | Description |
433
+ |-------|------|----------|-------------|
434
+ | `appKey` | `string` | Yes | NFC module API key |
435
+ | `documentNumber` | `string` | Yes | CCCD/CMND number |
436
+ | `birthDate` | `string` | Yes | Date of birth (DDMMYYYY) |
437
+ | `expireDate` | `string` | Yes | Expiry date (DDMMYYYY) |
438
+ | `transactionId` | `string` | No | Transaction ID |
439
+ | `facePathStorage` | `string` | No | File path to store extracted face image |
440
+
441
+ ### OCR Processing
442
+
443
+ ```typescript
444
+ const config: OcrConfig = {
445
+ appKey: 'your-ocr-key', // Required
446
+ transactionId: 'txn-001', // Required
447
+ idImagePath: 'base64_image_data', // Required: Base64 encoded image
448
+ expectedDocumentSide: 'front', // Required: 'front' or 'back'
449
+ };
450
+
451
+ const result = await FinosEKYC.startOcr(config);
452
+ ```
453
+
454
+ **OcrConfig**
455
+
456
+ | Field | Type | Required | Description |
457
+ |-------|------|----------|-------------|
458
+ | `appKey` | `string` | Yes | OCR module API key |
459
+ | `transactionId` | `string` | Yes | Transaction ID |
460
+ | `idImagePath` | `string` | Yes | Base64 encoded ID card image |
461
+ | `expectedDocumentSide` | `string` | Yes | `'front'` or `'back'` |
462
+
463
+ ### Liveness Detection
464
+
465
+ ```typescript
466
+ const config: LivenessConfig = {
467
+ appKey: 'your-liveness-key', // Required
468
+ selfieImage: 'base64_image', // Required: Base64 encoded selfie
469
+ transactionId: 'txn-001', // Optional
470
+ switchFrontCamera: true, // Optional: use front camera
471
+ isActiveLiveness: true, // Optional: active liveness mode
472
+ autoCapture: true, // Optional: auto capture
473
+ isShowCameraFont: true, // Optional: show guide text
474
+ forceCaptureTimeout: 30, // Optional: force capture timeout (seconds)
475
+ customActions: [ // Optional: custom action sequence
476
+ SDKFaceDetectStatus.LEFT,
477
+ SDKFaceDetectStatus.RIGHT,
478
+ SDKFaceDetectStatus.SMILE,
479
+ ],
480
+ activeActionCount: 3, // Optional: random action count (1-10)
481
+ };
482
+
483
+ const result = await FinosEKYC.startLiveness(config);
484
+ ```
485
+
486
+ **LivenessConfig**
487
+
488
+ | Field | Type | Default | Description |
489
+ |-------|------|---------|-------------|
490
+ | `appKey` | `string` | - | Liveness module API key (required) |
491
+ | `selfieImage` | `string` | - | Base64 encoded selfie image (required) |
492
+ | `transactionId` | `string` | `undefined` | Transaction ID |
493
+ | `switchFrontCamera` | `boolean` | `false` | Use front camera |
494
+ | `isActiveLiveness` | `boolean` | `false` | Enable active liveness mode |
495
+ | `autoCapture` | `boolean` | `true` | Auto capture when conditions met |
496
+ | `isShowCameraFont` | `boolean` | `true` | Show camera guide text |
497
+ | `forceCaptureTimeout` | `number` | `0` | Force capture timeout in seconds |
498
+ | `customActions` | `SDKFaceDetectStatus[]` | `undefined` | Custom action sequence (overrides random) |
499
+ | `activeActionCount` | `number` | `2` | Random actions count 1-10 (when `customActions` is null) |
500
+
501
+ ### Face Comparison
502
+
503
+ ```typescript
504
+ const config: FaceServiceConfig = {
505
+ appKey: 'your-face-key', // Required
506
+ selfieImage: 'base64_selfie', // Required: Base64 selfie image
507
+ idImage: 'base64_id_image', // Required: Base64 ID card image
508
+ transactionId: 'txn-001', // Optional
509
+ appKeyFaceService: 'alt-key', // Optional: alternative key
510
+ };
511
+
512
+ const result = await FinosEKYC.startFaceCompare(config);
513
+ ```
514
+
515
+ **FaceServiceConfig**
516
+
517
+ | Field | Type | Required | Description |
518
+ |-------|------|----------|-------------|
519
+ | `appKey` | `string` | Yes | Face service API key |
520
+ | `selfieImage` | `string` | Yes | Base64 encoded selfie image |
521
+ | `idImage` | `string` | Yes | Base64 encoded ID card portrait |
522
+ | `transactionId` | `string` | No | Transaction ID |
523
+ | `appKeyFaceService` | `string` | No | Alternative face service key |
524
+
525
+ ### C06 Verification
526
+
527
+ ```typescript
528
+ const config: C06Config = {
529
+ appKey: 'your-c06-key', // Required
530
+ sod: 'nfc_sod_data', // Required: SOD data from NFC
531
+ idCardNumber: '012345678012', // Required: CCCD number
532
+ recentLocation: 'Ha Noi', // Optional: recent location
533
+ transactionId: 'txn-001', // Optional
534
+ deviceType: 'android', // Optional
535
+ };
536
+
537
+ const result = await FinosEKYC.checkC06(config);
538
+ ```
539
+
540
+ **C06Config**
541
+
542
+ | Field | Type | Required | Description |
543
+ |-------|------|----------|-------------|
544
+ | `appKey` | `string` | Yes | C06 module API key |
545
+ | `sod` | `string` | Yes | SOD data from NFC scan |
546
+ | `idCardNumber` | `string` | Yes | CCCD/CMND number |
547
+ | `recentLocation` | `string` | No | Recent location for verification |
548
+ | `transactionId` | `string` | No | Transaction ID |
549
+ | `deviceType` | `string` | No | Device type identifier |
550
+
551
+ ---
552
+
553
+ ## SMS OTP Integration
554
+
555
+ ```typescript
556
+ const otpConfig: SmsOtpConfig = {
557
+ appKey: 'your-api-key', // Required: X-FinOS-Api-Key
558
+ phoneNumber: '+84901234567', // Required: with country code
559
+ referenceId: 'ref-001', // Required: unique reference
560
+ purpose: 'transaction', // Optional (default: 'transaction')
561
+ requestId: undefined, // Required for verify/resend (from send response)
100
562
  };
563
+
564
+ // 1. Send OTP
565
+ const sendResult = await FinosEKYC.sendOtp(otpConfig);
566
+ console.log('Request ID:', sendResult.requestId);
567
+
568
+ // 2. Verify OTP (use requestId from send response)
569
+ otpConfig.requestId = sendResult.requestId;
570
+ const verifyResult = await FinosEKYC.verifyOtp(otpConfig, '123456');
571
+
572
+ // 3. Resend OTP
573
+ const resendResult = await FinosEKYC.resendOtp(otpConfig);
101
574
  ```
102
575
 
576
+ **SmsOtpConfig**
103
577
 
578
+ | Field | Type | Required | Description |
579
+ |-------|------|----------|-------------|
580
+ | `appKey` | `string` | Yes | SMS OTP API key (X-FinOS-Api-Key) |
581
+ | `phoneNumber` | `string` | Yes | Phone number with country code (e.g., `+84901234567`) |
582
+ | `referenceId` | `string` | Yes | Unique reference ID |
583
+ | `purpose` | `string` | No | Purpose of OTP (default: `'transaction'`) |
584
+ | `requestId` | `string` | For verify/resend | Request ID from send response |
104
585
 
105
- ## ✍️ eSign Integration
586
+ ---
106
587
 
107
- The eSign module allows for electronic signature operations, including device registration, certificate management, and document signing.
588
+ ## eSign Integration
108
589
 
109
- ### 1. Initialize eSign
590
+ ### Initialize
110
591
 
111
592
  ```typescript
112
- // Initialize eSign (optional: pass token and isProd flag)
113
- await FinosESign.initializeESign("your-access-token", false);
593
+ // Initialize eSign SDK
594
+ await FinosESign.initializeESign('your-finos-token', false); // token, isProd
595
+
596
+ // Get SDK token
597
+ const token = await FinosESign.getSdkToken(
598
+ '012345678012', // CCCD number
599
+ 'Nguyen Van A', // Full name
600
+ 'device-id-123', // Device ID
601
+ );
114
602
  ```
115
603
 
116
- ### 2. Device Registration
604
+ ### Open Session
605
+
606
+ ```typescript
607
+ const session = await FinosESign.openSessionId(
608
+ 'access-token', // Access token (or null)
609
+ 'username', // Username (or null)
610
+ true, // Remember me (or null)
611
+ );
612
+ console.log(session.deviceState, session.code, session.message);
613
+ ```
117
614
 
118
- Register a device to perform signing operations.
615
+ ### Device Registration
119
616
 
120
617
  ```typescript
121
618
  const result = await FinosESign.registerDevice(
122
- "12345678", // 8-digit recovery code
123
- "123456", // 6-digit PIN code
124
- "fcm-token" // Optional FCM token
619
+ '12345678', // 8-digit recovery code
620
+ '123456', // 6-digit PIN code
621
+ 'fcm-token', // Optional FCM token
125
622
  );
126
- console.log(result.message);
127
623
  ```
128
624
 
129
- ### 3. Manage Certificates
625
+ ### Certificate Management
130
626
 
131
627
  ```typescript
132
628
  // List certificates
133
- const certs = await FinosESign.listCerts(1, 10);
629
+ const { certs } = await FinosESign.listCerts(1, 10);
630
+ // certs: [{ serial, subject, validFrom, validTo }]
134
631
 
135
- // Verify specific certificate
136
- const verifyResult = await FinosESign.verifyCert("serial-number");
632
+ // Verify certificate
633
+ const verifyResult = await FinosESign.verifyCert('cert-serial');
137
634
  ```
138
635
 
139
- ### 4. Sign Documents
636
+ ### Sign Documents
140
637
 
141
638
  ```typescript
142
- // Sign a PDF document
639
+ // Sign a PDF
143
640
  const signResult = await FinosESign.signPdf(JSON.stringify(signRequest));
144
641
 
145
- // Sign and Confirm (Composite API)
146
- const result = await FinosESign.confirmSign(
147
- "sign-request-id",
148
- "123456" // PIN code
642
+ // Sign PDF at multiple positions
643
+ const multiResult = await FinosESign.signPdfMultiplePositions(JSON.stringify(signRequest));
644
+
645
+ // Confirm signature
646
+ const confirmResult = await FinosESign.confirmSign(
647
+ 'sign-request-id', // Sign request ID
648
+ '123456', // PIN code
649
+ 'auth-id', // Optional auth ID
650
+ 'auth-data', // Optional auth data
651
+ true, // Confirm (default: true)
149
652
  );
150
653
  ```
151
654
 
152
- ### 5. Remote Signing
153
-
154
- Supported flows for remote signing:
655
+ ### Remote Signing
155
656
 
156
657
  ```typescript
157
658
  // Register for remote signing
@@ -159,111 +660,199 @@ const regResult = await FinosESign.registerRemoteSigning(JSON.stringify(requestB
159
660
 
160
661
  // Register and Confirm in one step
161
662
  const compositeResult = await FinosESign.registerAndConfirm(
162
- JSON.stringify(requestBody),
163
- "base64-confirmation-doc"
663
+ JSON.stringify(requestBody),
664
+ 'base64-confirmation-doc',
665
+ );
666
+
667
+ // Send confirmation document
668
+ const docResult = await FinosESign.sendConfirmationDocument(JSON.stringify(requestBody));
669
+ ```
670
+
671
+ ### Authorization Management
672
+
673
+ ```typescript
674
+ // Initialize authorize request
675
+ await FinosESign.initAuthorize(
676
+ 'cert-serial', // Certificate serial
677
+ 10, // Quantity (signing count)
678
+ 3600, // Time (seconds until expiry)
679
+ 'Authorization msg',// Message
680
+ );
681
+
682
+ // List authorize requests
683
+ const { requests } = await FinosESign.listAuthorize(1, 10, 'ACTIVE');
684
+
685
+ // Register/Confirm authorize
686
+ await FinosESign.registerAuthorize(
687
+ 'auth-id',
688
+ 'auth-data',
689
+ 'authorize-request-id',
690
+ '123456', // PIN code
691
+ true, // Confirm
164
692
  );
165
693
  ```
166
694
 
167
- ## 📨 SMS OTP Integration
695
+ ---
696
+
697
+ ## Event Listeners
168
698
 
169
- Integrated SMS OTP verification for multi-factor authentication.
699
+ ### NFC Events
170
700
 
171
701
  ```typescript
172
- const otpConfig: SmsOtpConfig = {
173
- phone: "0901234567",
174
- // other config params
175
- };
702
+ FinosEKYC.onNfcScanStart((data) => console.log('NFC scan started', data));
703
+ FinosEKYC.onNfcScanSuccess((data) => console.log('NFC data:', data));
704
+ FinosEKYC.onNfcError((error) => console.error('NFC error:', error.code, error.message));
705
+ ```
176
706
 
177
- // 1. Send OTP
178
- const sendResult = await FinosEKYC.sendOtp(otpConfig);
707
+ ### OCR Events
179
708
 
180
- // 2. Verify OTP
181
- const verifyResult = await FinosEKYC.verifyOtp(otpConfig, "123456");
709
+ ```typescript
710
+ FinosEKYC.onOcrSuccess((data) => console.log('OCR result:', data));
711
+ FinosEKYC.onOcrError((error) => console.error('OCR error:', error.code, error.message));
712
+ ```
182
713
 
183
- // 3. Resend OTP
184
- const resendResult = await FinosEKYC.resendOtp(otpConfig);
714
+ ### Liveness Events
715
+
716
+ ```typescript
717
+ FinosEKYC.onLivenessSuccess((data) => console.log('Liveness result:', data));
718
+ FinosEKYC.onLivenessError((error) => console.error('Liveness error:', error.code, error.message));
185
719
  ```
186
720
 
187
- ## 🎯 Individual Module APIs (Advanced)
721
+ ### Face Compare Events
188
722
 
189
- ### Liveness Detection (Updated)
723
+ ```typescript
724
+ FinosEKYC.onFaceCompareSuccess((data) => console.log('Face match:', data));
725
+ FinosEKYC.onFaceCompareError((error) => console.error('Face error:', error.code, error.message));
726
+ ```
190
727
 
191
- Refined configuration options for Active Liveness and Custom Actions.
728
+ ### C06 Events
192
729
 
193
730
  ```typescript
194
- import { FinosEKYC, FinosESign, getEkycError, SDKFaceDetectStatus } from '@finos_sdk/sdk-ekyc';
731
+ FinosEKYC.onC06Success((data) => console.log('C06 result:', data));
732
+ FinosEKYC.onC06Error((error) => console.error('C06 error:', error.code, error.message));
733
+ ```
195
734
 
196
- const config: LivenessConfig = {
197
- appKey: 'your_app_key',
198
- isActiveLiveness: true, // Enable active liveness
199
- autoCapture: true, // Auto capture when conditions met
200
- isShowCameraFont: true, // Show instructions on camera
201
- customActions: [ // Custom action sequence using Enum
202
- SDKFaceDetectStatus.LEFT,
203
- SDKFaceDetectStatus.RIGHT,
204
- SDKFaceDetectStatus.SMILE
205
- ],
206
- // activeActionCount: 2, // Or specify number of random actions
207
- switchFrontCamera: true // Use front camera
208
- };
735
+ ### SMS OTP Events
209
736
 
210
- await FinosEKYC.startLiveness(config);
737
+ ```typescript
738
+ FinosEKYC.onSmsOtpSendSuccess((data) => console.log('OTP sent:', data));
739
+ FinosEKYC.onSmsOtpResendSuccess((data) => console.log('OTP resent:', data));
740
+ FinosEKYC.onSmsOtpError((error) => console.error('OTP error:', error.code, error.message));
211
741
  ```
212
742
 
213
- ### OCR Processing
743
+ ### eSign Events
744
+
214
745
  ```typescript
215
- const config: OcrConfig = {
216
- appKey: 'your_app_key',
217
- idImagePath: 'base64_image_data',
218
- expectedDocumentSide: 'front',
219
- };
746
+ FinosESign.onESignInitSuccess((data) => console.log('eSign init:', data));
747
+ FinosESign.onESignOpenSessionSuccess((data) => console.log('Session:', data));
748
+ FinosESign.onESignRegisterDeviceSuccess((data) => console.log('Device registered:', data));
749
+ FinosESign.onESignListCertsSuccess((data) => console.log('Certs:', data));
750
+ FinosESign.onESignSignPdfSuccess((data) => console.log('PDF signed:', data));
751
+ FinosESign.onESignError((error) => console.error('eSign error:', error.code, error.message));
752
+ ```
220
753
 
221
- await FinosEKYC.startOcr(config);
754
+ ### Cleanup
755
+
756
+ ```typescript
757
+ // Remove all event listeners when done
758
+ FinosEKYC.removeAllListeners();
759
+ FinosESign.removeAllListeners();
222
760
  ```
223
761
 
224
- ### NFC Scanning
762
+ ---
763
+
764
+ ## Error Handling
765
+
766
+ All errors follow a unified `EKYCError` format:
767
+
225
768
  ```typescript
226
- const config: NfcConfig = {
227
- appKey: 'your_app_key',
228
- documentNumber: '123456789',
229
- birthDate: '01011990',
230
- expireDate: '01012030',
231
- };
769
+ interface EKYCError {
770
+ event: string; // Error event name (e.g., 'FACE_ERROR', 'NFC_ERROR')
771
+ code: string; // Error code (e.g., '110', '200', '999')
772
+ message: string; // Human-readable message
773
+ }
774
+ ```
775
+
776
+ ### Usage
232
777
 
233
- await FinosEKYC.startNfcScan(config);
778
+ ```typescript
779
+ import { getEkycError, fromCode, getLocalizedMessage, USER_CANCEL } from '@finos_sdk/sdk-ekyc';
780
+
781
+ try {
782
+ const result = await FinosEKYC.startEkycUI(/* ... */);
783
+ } catch (error) {
784
+ const err = getEkycError(error);
785
+ console.error(`[${err.event}] Code: ${err.code}, Message: ${err.message}`);
786
+
787
+ // Check for user cancellation
788
+ if (err.code === '999' || err.code === USER_CANCEL.code) {
789
+ console.log('User cancelled the flow');
790
+ return;
791
+ }
792
+
793
+ // Lookup error details by code
794
+ const errorInfo = fromCode(err.code);
795
+ if (errorInfo) {
796
+ console.log('VI:', getLocalizedMessage(errorInfo, 'vi'));
797
+ console.log('EN:', getLocalizedMessage(errorInfo, 'en'));
798
+ }
799
+ }
234
800
  ```
235
801
 
236
- ## ⚙️ Configuration Options
802
+ ### Common Error Codes
237
803
 
238
- ### Style Configuration
804
+ | Code | Constant | Description |
805
+ |------|----------|-------------|
806
+ | `999` | `USER_CANCEL` | User cancelled the flow |
807
+ | `000` | `ERROR_UNKNOWN` | Unknown error |
808
+ | `0` | `SDK_START_ERROR` | Failed to start SDK (check key) |
809
+ | `001` | `OCR_UNRECOGNIZED_ID_CARD` | Cannot recognize ID card |
810
+ | `110` | `LIVENESS_ERROR` | Liveness detection error |
811
+ | `109` | `FACE_ERROR` | Face comparison failed |
812
+ | `200` | `SCAN_NFC_ERROR` | NFC scan failed |
813
+ | `203` | `C06_ERROR` | C06 verification error |
814
+ | `209` | `NFC_USER_CANCEL` | User cancelled NFC scan |
815
+ | `300` | `SMS_OTP_ERROR` | SMS OTP error |
239
816
 
240
- Customize the UI appearance:
817
+ ---
818
+
819
+ ## Lifecycle Management
820
+
821
+ Handle app lifecycle events for NFC:
241
822
 
242
823
  ```typescript
243
- const styleConfig = {
244
- textSize: 14,
245
- textColor: 0xff000000,
246
- statusBarBackground: 0,
247
- titleStyle: {
248
- textSize: 26,
249
- textColor: 0xff34C759,
250
- },
251
- instructionStyle: {
252
- textSize: 24,
253
- textColor: 0xffFF6B35,
254
- },
255
- // New styles for error/success/warning states
256
- errorStyle: { textColor: 0xffFF0000 },
257
- successStyle: { textColor: 0xff00FF00 }
258
- };
824
+ import { AppState } from 'react-native';
825
+
826
+ useEffect(() => {
827
+ const subscription = AppState.addEventListener('change', (state) => {
828
+ if (state === 'active') {
829
+ FinosEKYC.onResume();
830
+ } else if (state === 'background') {
831
+ FinosEKYC.onPause();
832
+ }
833
+ });
834
+
835
+ return () => {
836
+ subscription.remove();
837
+ FinosEKYC.removeAllListeners();
838
+ };
839
+ }, []);
259
840
  ```
260
841
 
842
+ ---
261
843
 
844
+ ## What's New
262
845
 
263
- ## 🆕 What's New in v1.3.8
846
+ ### v1.4.2
847
+ - `AppIDType` enum (NONE, HD_BANK, VIKKI) for branding/skin selection
848
+ - `captureButtonColor` and `captureButtonDisabledColor` in StyleConfig
849
+ - `USER_CANCEL` error code (999) for user cancellation detection
850
+ - User cancellation callback support across all modules
851
+ - Android responsive layouts for camera preview screens
852
+ - Refactored StyleConfig JSON parsing with proper JSONObject
264
853
 
265
- - ✨ **eSign Module**: Complete electronic signature support (Sign, Verify, Certs)
266
- - 📱 **SMS OTP**: Integrated SMS OTP verification
267
- - 🔄 **Composite APIs**: `registerAndConfirm` for streamlined remote signing
268
- - 👁️ **Enhanced Liveness**: improved Active Liveness configuration with custom actions
269
- - 🛠️ **Bug Fixes**: Improved error handling and stability improvements
854
+ ### v1.4.1
855
+ - Full iOS native bridge support
856
+ - eSign module with PDF signing, remote signing, authorization
857
+ - SMS OTP integration
858
+ - Active Liveness with custom actions