@finos_sdk/sdk-ekyc 1.4.5 → 1.4.6

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.
@@ -65,7 +65,7 @@ dependencies {
65
65
  implementation 'com.facebook.react:react-android'
66
66
 
67
67
  // Finos eKYC SDK dependencies from GitHub Packages Maven repository
68
- def sdkVersion = "1.4.5.2"
68
+ def sdkVersion = "1.4.6.1"
69
69
  implementation("finos.sdk.ekyc:ekyc:$sdkVersion")
70
70
  implementation("finos.sdk.ekyc:ekycui:$sdkVersion")
71
71
  implementation("finos.sdk.ekyc:nfc:$sdkVersion")
@@ -27,6 +27,7 @@ import finos.sdk.core.model.sdk.config.AppKeyConfig
27
27
  import finos.sdk.core.model.sdk.config.C06Config
28
28
  import finos.sdk.core.model.sdk.config.EKYCConfigSDK
29
29
  import finos.sdk.core.model.sdk.config.FaceServiceConfig
30
+ import finos.sdk.core.model.sdk.config.LivenessBackConfirmConfig
30
31
  import finos.sdk.core.model.sdk.config.LivenessConfig
31
32
  import finos.sdk.core.model.sdk.config.NfcConfig
32
33
  import finos.sdk.core.model.sdk.config.OcrConfig
@@ -235,7 +236,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
235
236
  val eventMap =
236
237
  Arguments.createMap().apply {
237
238
  putString("event", event.name.toString())
238
- putString("data", data.toString())
239
+ putString("data", Gson().toJson(data))
239
240
  }
240
241
  sendEvent("onNfcScanStart", eventMap)
241
242
  }
@@ -260,7 +261,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
260
261
  val eventMap =
261
262
  Arguments.createMap().apply {
262
263
  putString("event", event.name.toString())
263
- putString("data", data.toString())
264
+ putString("data", Gson().toJson(data))
264
265
  }
265
266
  sendEvent("onNfcEvent", eventMap)
266
267
  }
@@ -314,18 +315,19 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
314
315
  ekycConfigSDK = ekycConfig,
315
316
  callbackSuccess = { event, data ->
316
317
  Log.d(TAG, "✅ checkC06() success")
318
+ val c06Json = Gson().toJson((data as? SDKEkycResult)?.checkC06Response)
317
319
 
318
320
  // Create separate maps for event and promise
319
321
  val eventMap =
320
322
  Arguments.createMap().apply {
321
323
  putString("event", event.name.toString())
322
- putString("data", data.toString())
324
+ putString("data", c06Json)
323
325
  }
324
326
 
325
327
  val promiseMap =
326
328
  Arguments.createMap().apply {
327
329
  putString("event", event.name.toString())
328
- putString("data", data.toString())
330
+ putString("data", c06Json)
329
331
  }
330
332
  sendEvent("onC06Success", eventMap)
331
333
  promise.resolve(promiseMap)
@@ -389,7 +391,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
389
391
  Log.d(TAG, "✅ startOcr() success")
390
392
  val (eventMap, promiseMap) = createSeparateMaps { map ->
391
393
  map.putString("event", event.name.toString())
392
- map.putString("data", (data as SDKEkycResult).ocrResponse.toString())
394
+ map.putString("data", Gson().toJson((data as SDKEkycResult).ocrResponse))
393
395
  }
394
396
  sendEvent("onOcrSuccess", eventMap)
395
397
  promise.resolve(promiseMap)
@@ -428,6 +430,8 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
428
430
  activeActionCount: Int?,
429
431
  forceCaptureTimeout: Double?,
430
432
  isActiveLivenessColor: Boolean?,
433
+ isShowBackConfirmation: Boolean?,
434
+ backConfirmConfigJson: String?,
431
435
  promise: Promise
432
436
  ) {
433
437
  Log.d(TAG, "▶️ startLiveness() called")
@@ -461,6 +465,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
461
465
  null
462
466
  }
463
467
 
468
+ val backConfirmConfig = parseBackConfirmConfig(backConfirmConfigJson)
464
469
  val livenessConfig =
465
470
  LivenessConfig(
466
471
  isActiveLiveness = isActiveLiveness ?: false,
@@ -475,7 +480,9 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
475
480
  activeActionCount = activeActionCount ?: 2,
476
481
  forceCaptureTimeout = (forceCaptureTimeout ?: 0.0).toLong(),
477
482
  selfieImage = imageFile,
478
- transactionId = transactionId
483
+ transactionId = transactionId,
484
+ isShowBackConfirmation = isShowBackConfirmation ?: false,
485
+ backConfirmConfig = backConfirmConfig,
479
486
  )
480
487
  val ekycConfig =
481
488
  EKYCConfigSDK(
@@ -486,9 +493,9 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
486
493
  eKYCFinOSLiveness.startEkyc(
487
494
  ekycConfigSDK = ekycConfig,
488
495
  callbackSuccess = { event, data ->
489
- // LOG_SUCCESS = internal submit result event (v1.4.5), don't resolve promise
496
+ // LOG_SUCCESS = internal submit result event (v1.4.6.1), don't resolve promise
490
497
  if (event == EKYCEvent.LOG_SUCCESS) {
491
- val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: data?.toString() ?: "null"
498
+ val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: (data as? SDKEkycResult)?.checkLivenessResponse?.let { Gson().toJson(it) } ?: Gson().toJson(data)
492
499
  Log.d(TAG, "LOG_SUCCESS [Liveness]: $logDataStr")
493
500
  val logMap = Arguments.createMap().apply {
494
501
  putString("event", event.name.toString())
@@ -500,7 +507,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
500
507
  Log.d(TAG, "✅ startLiveness() success")
501
508
  val (eventMap, promiseMap) = createSeparateMaps { map ->
502
509
  map.putString("event", event.name.toString())
503
- map.putString("data", (data as SDKEkycResult).checkLivenessResponse.toString())
510
+ map.putString("data", Gson().toJson((data as SDKEkycResult).checkLivenessResponse))
504
511
  }
505
512
  sendEvent("onLivenessSuccess", eventMap)
506
513
  promise.resolve(promiseMap)
@@ -570,9 +577,10 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
570
577
  eKYCFinOSFaceService.startEkyc(
571
578
  ekycConfigSDK = ekycConfig,
572
579
  callbackSuccess = { event, data ->
573
- // LOG_SUCCESS = internal submit result event (v1.4.5), don't resolve promise
580
+ // LOG_SUCCESS = internal submit result event (v1.4.6.1), don't resolve promise
574
581
  if (event == EKYCEvent.LOG_SUCCESS) {
575
- val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: data?.toString() ?: "null"
582
+ val faceResponse = (data as? SDKEkycResult)?.checkFaceResponse
583
+ val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: faceResponse?.let { Gson().toJson(it) } ?: "null"
576
584
  Log.d(TAG, "LOG_SUCCESS [FaceCompare]: $logDataStr")
577
585
  val logMap = Arguments.createMap().apply {
578
586
  putString("event", event.name.toString())
@@ -582,9 +590,24 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
582
590
  return@startEkyc
583
591
  }
584
592
  Log.d(TAG, "✅ startFaceCompare() success")
593
+ val faceResponse = (data as? SDKEkycResult)?.checkFaceResponse
585
594
  val (eventMap, promiseMap) = createSeparateMaps { map ->
586
595
  map.putString("event", event.name.toString())
587
- map.putString("data", data.toString())
596
+ val resultMap = Arguments.createMap().apply {
597
+ putDouble("conf", faceResponse?.result?.conf?.toString()?.toDoubleOrNull() ?: 0.0)
598
+ putString("match", faceResponse?.result?.match?.toString())
599
+ putInt("matchScore", faceResponse?.result?.matchScore?.toString()?.toIntOrNull() ?: 0)
600
+ putString("toBeReviewed", faceResponse?.result?.toBeReviewed?.toString())
601
+ }
602
+ val faceMap = Arguments.createMap().apply {
603
+ putString("requestId", faceResponse?.requestId?.toString())
604
+ putMap("result", resultMap)
605
+ putString("status", faceResponse?.status?.toString())
606
+ putInt("statusCode", faceResponse?.statusCode?.toString()?.toIntOrNull() ?: 0)
607
+ putString("error", faceResponse?.error?.toString())
608
+ putString("type", faceResponse?.type?.toString())
609
+ }
610
+ map.putMap("data", faceMap)
588
611
  }
589
612
  sendEvent("onFaceCompareSuccess", eventMap)
590
613
  promise.resolve(promiseMap)
@@ -783,6 +806,9 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
783
806
  else -> AppIDType.NONE
784
807
  }
785
808
 
809
+ val isShowBackConfirmation = extractBooleanValue(optionConfigJson, "isShowBackConfirmation") ?: false
810
+ val backConfirmConfig = parseBackConfirmConfig(extractObjectJson(optionConfigJson, "backConfirmConfig"))
811
+
786
812
  val livenessConfig = if (finalFlow.contains(SDKType.LIVENESS)) {
787
813
  LivenessConfig(
788
814
  isActiveLiveness = isActiveLiveness,
@@ -790,6 +816,8 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
790
816
  isShowCameraFont = switchFrontCamera,
791
817
  customActions = customActionsList?.takeIf { it.isNotEmpty() },
792
818
  activeActionCount = activeActionCount,
819
+ isShowBackConfirmation = isShowBackConfirmation,
820
+ backConfirmConfig = backConfirmConfig,
793
821
  )
794
822
  } else null
795
823
 
@@ -809,7 +837,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
809
837
  callbackSuccess = { event, data ->
810
838
  // LOG_SUCCESS = internal submit result event (v1.4.5), emit event only, don't resolve promise
811
839
  if (event == EKYCEvent.LOG_SUCCESS) {
812
- val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: data?.toString() ?: "null"
840
+ val logDataStr = data?.customData?.let { Gson().toJson(it) } ?: Gson().toJson(data)
813
841
  Log.d(TAG, "LOG_SUCCESS [EkycUI]: $logDataStr")
814
842
  sendEvent("onEkycUISuccess", Arguments.createMap().apply {
815
843
  putString("status", "log")
@@ -834,7 +862,7 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
834
862
  val (eventMap, promiseMap) = createSeparateMaps { map ->
835
863
  map.putString("status", "success")
836
864
  map.putString("event", event.name.toString())
837
- map.putString("data", data.toString())
865
+ map.putString("data", Gson().toJson(data))
838
866
  map.putString("transactionId", ekycTransactionId)
839
867
  ekycFiles?.imageFace?.absolutePath?.let { map.putString("imageFacePath", it) }
840
868
  ekycFiles?.imageOcrFront?.absolutePath?.let { map.putString("imageOcrFrontPath", it) }
@@ -895,32 +923,42 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
895
923
  private fun parseOptionConfig(optionConfigJson: String): OptionConfig {
896
924
  return try {
897
925
  if (optionConfigJson.isBlank() || optionConfigJson == "{}") {
898
- // Return default values based on demo OptionConfig.kt
899
- OptionConfig(
900
- baseUrl = null,
901
- countMaxRetry = 3,
902
- language = null
903
- )
926
+ OptionConfig()
904
927
  } else {
905
- // Simple JSON parsing (in real implementation, use proper JSON library)
906
928
  val baseUrl = extractStringValue(optionConfigJson, "baseUrl")
907
929
  val countMaxRetry = extractIntValue(optionConfigJson, "countMaxRetry") ?: 3
908
930
  val language = extractStringValue(optionConfigJson, "language")
931
+ val networkTimeoutMs = extractLongValue(optionConfigJson, "networkTimeoutMs") ?: 30_000L
909
932
 
910
933
  OptionConfig(
911
934
  baseUrl = baseUrl,
912
935
  countMaxRetry = countMaxRetry,
913
- language = language
936
+ language = language,
937
+ networkTimeoutMs = networkTimeoutMs,
914
938
  )
915
939
  }
916
940
  } catch (e: Exception) {
917
941
  Log.e(TAG, "Error parsing optionConfig: ${e.message}", e)
918
- // Return default values
919
- OptionConfig(
920
- baseUrl = null,
921
- countMaxRetry = 3,
922
- language = null
942
+ OptionConfig()
943
+ }
944
+ }
945
+
946
+ private fun parseBackConfirmConfig(json: String?): LivenessBackConfirmConfig? {
947
+ if (json.isNullOrBlank() || json == "{}") return null
948
+ return try {
949
+ LivenessBackConfirmConfig(
950
+ titleVi = extractStringValue(json, "titleVi"),
951
+ titleEn = extractStringValue(json, "titleEn"),
952
+ bodyVi = extractStringValue(json, "bodyVi"),
953
+ bodyEn = extractStringValue(json, "bodyEn"),
954
+ confirmButtonVi = extractStringValue(json, "confirmButtonVi"),
955
+ confirmButtonEn = extractStringValue(json, "confirmButtonEn"),
956
+ cancelButtonVi = extractStringValue(json, "cancelButtonVi"),
957
+ cancelButtonEn = extractStringValue(json, "cancelButtonEn"),
923
958
  )
959
+ } catch (e: Exception) {
960
+ Log.e(TAG, "Error parsing backConfirmConfig: ${e.message}", e)
961
+ null
924
962
  }
925
963
  }
926
964
 
@@ -934,6 +972,16 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
934
972
  }
935
973
  }
936
974
 
975
+ private fun extractLongValue(json: String, key: String): Long? {
976
+ return try {
977
+ val pattern = "\"$key\"\\s*:\\s*(-?\\d+)".toRegex()
978
+ val match = pattern.find(json)
979
+ match?.groupValues?.get(1)?.toLong()
980
+ } catch (e: Exception) {
981
+ null
982
+ }
983
+ }
984
+
937
985
  private fun extractBooleanValue(json: String, key: String): Boolean? {
938
986
  return try {
939
987
  val pattern = "\"$key\"\\s*:\\s*(true|false)".toRegex()
@@ -1088,6 +1136,17 @@ class EKYCModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
1088
1136
  }
1089
1137
  }
1090
1138
 
1139
+ /** Extract a nested JSON object `{ ... }` for a given key. Returns the raw JSON string or null. */
1140
+ private fun extractObjectJson(json: String, key: String): String? {
1141
+ return try {
1142
+ val obj = JSONObject(json)
1143
+ val nested = obj.optJSONObject(key) ?: return null
1144
+ nested.toString()
1145
+ } catch (e: Exception) {
1146
+ null
1147
+ }
1148
+ }
1149
+
1091
1150
  // ==================== SMS OTP Methods ====================
1092
1151
 
1093
1152
  @ReactMethod
@@ -148,6 +148,16 @@ declare class SDKeKYC {
148
148
  baseUrl?: string;
149
149
  countMaxRetry?: number;
150
150
  language?: string;
151
+ networkTimeoutMs?: number;
152
+ switchFrontCamera?: boolean;
153
+ isActiveLiveness?: boolean;
154
+ autoCapture?: boolean;
155
+ forceCaptureTimeout?: number;
156
+ customActions?: string[];
157
+ activeActionCount?: number;
158
+ appIDType?: string;
159
+ isShowBackConfirmation?: boolean;
160
+ backConfirmConfig?: import('./src/types/ekycLivenessType').LivenessBackConfirmConfig;
151
161
  }, styleConfig?: {
152
162
  textSize?: number;
153
163
  textFont?: string;
@@ -213,7 +213,10 @@ class SDKeKYC {
213
213
  try {
214
214
  // Convert SDKFaceDetectStatus enum array to string array
215
215
  const customActionsStrings = (_b = (_a = config.customActions) === null || _a === void 0 ? void 0 : _a.map(action => String(action))) !== null && _b !== void 0 ? _b : undefined;
216
- return await nativeModule.startLiveness(config.appKey, config.selfieImage, (_c = config.transactionId) !== null && _c !== void 0 ? _c : '', config.isActiveLiveness, config.isShowCameraFont, customActionsStrings, config.activeActionCount, config.forceCaptureTimeout, config.isActiveLivenessColor);
216
+ const backConfirmConfigJson = config.backConfirmConfig
217
+ ? JSON.stringify(config.backConfirmConfig)
218
+ : undefined;
219
+ return await nativeModule.startLiveness(config.appKey, config.selfieImage, (_c = config.transactionId) !== null && _c !== void 0 ? _c : '', config.isActiveLiveness, config.isShowCameraFont, customActionsStrings, config.activeActionCount, config.forceCaptureTimeout, config.isActiveLivenessColor, config.isShowBackConfirmation, backConfirmConfigJson);
217
220
  }
218
221
  catch (error) {
219
222
  console.error('Liveness Error:', error);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
4
4
  "description": "React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -308,6 +308,8 @@ export declare class FinosEKYCModule {
308
308
  baseUrl?: string;
309
309
  countMaxRetry?: number;
310
310
  language?: string;
311
+ /** Network timeout in milliseconds (default: 30000). callTimeout = networkTimeoutMs * 2 */
312
+ networkTimeoutMs?: number;
311
313
  switchFrontCamera?: boolean;
312
314
  /** LivenessConfig – SDKeKYCActivity (157-169) */
313
315
  isActiveLiveness?: boolean;
@@ -318,6 +320,10 @@ export declare class FinosEKYCModule {
318
320
  activeActionCount?: number;
319
321
  /** AppIDType: NONE | HD_BANK | VIKKI (default: AppIDType.NONE) */
320
322
  appIDType?: AppIDType;
323
+ /** Show confirmation bottom sheet when user presses back in liveness screen (v1.4.6+) */
324
+ isShowBackConfirmation?: boolean;
325
+ /** Text config for the back confirmation bottom sheet (v1.4.6+) */
326
+ backConfirmConfig?: import('../types/ekycLivenessType').LivenessBackConfirmConfig;
321
327
  }, styleConfig?: {
322
328
  textSize?: number;
323
329
  textFont?: string;
@@ -286,6 +286,8 @@ export declare class FinosESignModule {
286
286
  baseUrl?: string;
287
287
  countMaxRetry?: number;
288
288
  language?: string;
289
+ /** Network timeout in milliseconds (default: 30000). callTimeout = networkTimeoutMs * 2 */
290
+ networkTimeoutMs?: number;
289
291
  switchFrontCamera?: boolean;
290
292
  }, styleConfig?: {
291
293
  textSize?: number;
@@ -20,6 +20,17 @@ export declare enum SDKFaceDetectStatus {
20
20
  export declare const SDK_LIVENESS_ACTIONS: readonly SDKFaceDetectStatus[];
21
21
  /** Chuyển customActions enum[] sang string[] khi gửi xuống native. */
22
22
  export declare function customActionsToStrings(actions: SDKFaceDetectStatus[]): string[];
23
+ /** Text config for the back-press confirmation bottom sheet (v1.4.6+). All fields optional – fallback to built-in strings. */
24
+ export interface LivenessBackConfirmConfig {
25
+ titleVi?: string;
26
+ titleEn?: string;
27
+ bodyVi?: string;
28
+ bodyEn?: string;
29
+ confirmButtonVi?: string;
30
+ confirmButtonEn?: string;
31
+ cancelButtonVi?: string;
32
+ cancelButtonEn?: string;
33
+ }
23
34
  export interface LivenessConfig {
24
35
  appKey: string;
25
36
  transactionId?: string;
@@ -30,6 +41,8 @@ export interface LivenessConfig {
30
41
  customActions?: SDKFaceDetectStatus[];
31
42
  activeActionCount?: number;
32
43
  forceCaptureTimeout?: number;
44
+ isShowBackConfirmation?: boolean;
45
+ backConfirmConfig?: LivenessBackConfirmConfig;
33
46
  }
34
47
  export interface CheckLivenessResponse {
35
48
  metadata?: Metadata;
@@ -96,14 +96,14 @@ class EKYCModule: RCTEventEmitter {
96
96
  callbackSuccess: { event, data in
97
97
  switch event {
98
98
  case .SCAN_NFC_START:
99
- self?.emit("onNfcScanStart", body: ["event": event.description, "data": "\(String(describing: data))"])
99
+ self?.emit("onNfcScanStart", body: ["event": event.description, "data": (data as? SDKEkycResult)?.nfcResponse ?? "{}"])
100
100
  case .SCAN_NFC_SUCCESS:
101
101
  let nfcResponse = (data as? SDKEkycResult)?.nfcResponse ?? ""
102
102
  let body: [String: Any] = ["event": event.description, "data": nfcResponse]
103
103
  self?.emit("onNfcScanSuccess", body: body)
104
104
  resolve(body)
105
105
  default:
106
- self?.emit("onNfcEvent", body: ["event": event.description, "data": "\(String(describing: data))"])
106
+ self?.emit("onNfcEvent", body: ["event": event.description, "data": (data as? SDKEkycResult)?.nfcResponse ?? "{}"])
107
107
  }
108
108
  },
109
109
  callbackError: { event, errorResult in
@@ -123,7 +123,8 @@ class EKYCModule: RCTEventEmitter {
123
123
  let ekycConfig = EKYCConfigSDK(appKey: AppKeyConfig(appKey: appKey), c06Config: c06Config)
124
124
  SdkEkycC06.startEkyc(ekycConfigSDK: ekycConfig,
125
125
  callbackSuccess: { [weak self] event, data in
126
- let body: [String: Any] = ["event": event.description, "data": "\(String(describing: data))"]
126
+ let c06Response = (data as? SDKEkycResult)?.checkC06Response?.description ?? "{}"
127
+ let body: [String: Any] = ["event": event.description, "data": c06Response]
127
128
  self?.emit("onC06Success", body: body)
128
129
  resolve(body)
129
130
  },
@@ -210,7 +211,17 @@ class EKYCModule: RCTEventEmitter {
210
211
  DispatchQueue.main.async { [weak self] in
211
212
  SdkEkycFaceService.startEkyc(ekycConfigSDK: ekycConfig,
212
213
  callbackSuccess: { event, data in
213
- let body: [String: Any] = ["event": event.description, "data": "\(String(describing: data))"]
214
+ let faceResponse = (data as? SDKEkycResult)?.checkFaceResponse
215
+ var faceDict: [String: Any] = [:]
216
+ if let v = faceResponse?.requestId { faceDict["requestId"] = v }
217
+ if let r = faceResponse?.result {
218
+ faceDict["result"] = ["conf": r.conf, "match": r.match, "matchScore": r.matchScore, "toBeReviewed": r.toBeReviewed]
219
+ }
220
+ if let v = faceResponse?.status { faceDict["status"] = v }
221
+ if let v = faceResponse?.statusCode { faceDict["statusCode"] = v }
222
+ if let v = faceResponse?.error { faceDict["error"] = v }
223
+ if let v = faceResponse?.type { faceDict["type"] = v }
224
+ let body: [String: Any] = ["event": event.description, "data": faceDict]
214
225
  self?.emit("onFaceCompareSuccess", body: body)
215
226
  resolve(body)
216
227
  },
@@ -291,7 +302,16 @@ class EKYCModule: RCTEventEmitter {
291
302
  return
292
303
  }
293
304
  let files = result?.ekycStateModel?.eKYCFileModel
294
- var body: [String: Any] = ["status": "success", "event": event.description, "data": "\(String(describing: data))", "transactionId": result?.ekycStateModel?.transactionId ?? ""]
305
+ var dataDict: [String: Any] = [:]
306
+ if let v = result?.nfcResponse { dataDict["nfcResponse"] = v }
307
+ if let v = result?.checkC06Response?.description { dataDict["checkC06Response"] = v }
308
+ if let v = result?.ocrResponse?.description { dataDict["ocrResponse"] = v }
309
+ if let v = result?.checkLivenessResponse?.description { dataDict["checkLivenessResponse"] = v }
310
+ if let v = result?.checkFaceResponse?.description { dataDict["checkFaceResponse"] = v }
311
+ if let v = result?.customData { dataDict["customData"] = v }
312
+ let dataJson = (try? JSONSerialization.data(withJSONObject: dataDict))
313
+ .flatMap { String(data: $0, encoding: .utf8) } ?? "{}"
314
+ var body: [String: Any] = ["status": "success", "event": event.description, "data": dataJson, "transactionId": result?.ekycStateModel?.transactionId ?? ""]
295
315
  if let facePath = files?.imageFace { body["imageFacePath"] = facePath.path }
296
316
  if let frontPath = files?.imageOcrFront { body["imageOcrFrontPath"] = frontPath.path }
297
317
  if let backPath = files?.imageOcrBack { body["imageOcrBackPath"] = backPath.path }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos_sdk/sdk-ekyc",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
4
4
  "description": "React Native SDK for eKYC - Vietnamese CCCD NFC reading, OCR, Liveness detection, Face matching, and C06, eSign, SmsOTP residence verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -764,6 +764,8 @@ export class FinosEKYCModule {
764
764
  baseUrl?: string;
765
765
  countMaxRetry?: number;
766
766
  language?: string;
767
+ /** Network timeout in milliseconds (default: 30000). callTimeout = networkTimeoutMs * 2 */
768
+ networkTimeoutMs?: number;
767
769
  switchFrontCamera?: boolean;
768
770
  /** LivenessConfig – SDKeKYCActivity (157-169) */
769
771
  isActiveLiveness?: boolean;
@@ -774,6 +776,10 @@ export class FinosEKYCModule {
774
776
  activeActionCount?: number;
775
777
  /** AppIDType: NONE | HD_BANK | VIKKI (default: AppIDType.NONE) */
776
778
  appIDType?: AppIDType;
779
+ /** Show confirmation bottom sheet when user presses back in liveness screen (v1.4.6+) */
780
+ isShowBackConfirmation?: boolean;
781
+ /** Text config for the back confirmation bottom sheet (v1.4.6+) */
782
+ backConfirmConfig?: import('../types/ekycLivenessType').LivenessBackConfirmConfig;
777
783
  },
778
784
  styleConfig?: {
779
785
  textSize?: number;
@@ -634,6 +634,8 @@ export class FinosESignModule {
634
634
  baseUrl?: string;
635
635
  countMaxRetry?: number;
636
636
  language?: string;
637
+ /** Network timeout in milliseconds (default: 30000). callTimeout = networkTimeoutMs * 2 */
638
+ networkTimeoutMs?: number;
637
639
  switchFrontCamera?: boolean;
638
640
  },
639
641
  styleConfig?: {
@@ -38,11 +38,23 @@ export function customActionsToStrings(actions: SDKFaceDetectStatus[]): string[]
38
38
  return actions.map(a => a as string);
39
39
  }
40
40
 
41
+ /** Text config for the back-press confirmation bottom sheet (v1.4.6+). All fields optional – fallback to built-in strings. */
42
+ export interface LivenessBackConfirmConfig {
43
+ titleVi?: string;
44
+ titleEn?: string;
45
+ bodyVi?: string;
46
+ bodyEn?: string;
47
+ confirmButtonVi?: string;
48
+ confirmButtonEn?: string;
49
+ cancelButtonVi?: string;
50
+ cancelButtonEn?: string;
51
+ }
52
+
41
53
  export interface LivenessConfig {
42
54
  appKey: string;
43
55
  transactionId?: string;
44
56
  // old fields usingRandomAction and isStraight removed in 1.3.1
45
- // usingRandomAction: boolean;
57
+ // usingRandomAction: boolean;
46
58
  // isStraight: boolean;
47
59
  selfieImage: string;
48
60
  // New fields for version 1.3.0
@@ -52,6 +64,9 @@ export interface LivenessConfig {
52
64
  customActions?: SDKFaceDetectStatus[];
53
65
  activeActionCount?: number;
54
66
  forceCaptureTimeout?: number;
67
+ // New fields for version 1.4.6
68
+ isShowBackConfirmation?: boolean;
69
+ backConfirmConfig?: LivenessBackConfirmConfig;
55
70
  }
56
71
 
57
72
  export interface CheckLivenessResponse {