@trustchex/react-native-sdk 1.409.0 → 1.464.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
  2. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +59 -1
  3. package/ios/Camera/TrustchexCameraView.swift +9 -1
  4. package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
  5. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +1 -4
  6. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +17 -4
  7. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +102 -23
  8. package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
  9. package/lib/module/Screens/Dynamic/VideoCallScreen.js +3 -1
  10. package/lib/module/Screens/Static/ResultScreen.js +128 -22
  11. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +8 -0
  12. package/lib/module/Shared/Animations/recording.json +1 -0
  13. package/lib/module/Shared/Components/DebugNavigationPanel.js +69 -71
  14. package/lib/module/Shared/Components/EIDScanner.js +212 -108
  15. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
  16. package/lib/module/Shared/Components/IdentityDocumentCamera.js +53 -36
  17. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
  18. package/lib/module/Shared/Components/NavigationManager.js +24 -16
  19. package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
  20. package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
  21. package/lib/module/Shared/EIDReader/bacKey.js +16 -2
  22. package/lib/module/Shared/EIDReader/eidReader.js +354 -13
  23. package/lib/module/Shared/EIDReader/eidService.js +25 -1
  24. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
  25. package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
  26. package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
  27. package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
  28. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
  29. package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
  30. package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
  31. package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
  32. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
  33. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
  34. package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
  35. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  36. package/lib/module/Shared/Libs/contains.js +1 -40
  37. package/lib/module/Shared/Libs/country-display.utils.js +34 -0
  38. package/lib/module/Shared/Libs/demo.utils.js +8 -0
  39. package/lib/module/Shared/Libs/mrz.utils.js +3 -2
  40. package/lib/module/Shared/Libs/status-bar.utils.js +4 -2
  41. package/lib/module/Shared/Types/analytics.types.js +2 -0
  42. package/lib/module/Translation/Resources/en.js +41 -2
  43. package/lib/module/Translation/Resources/tr.js +41 -2
  44. package/lib/module/Trustchex.js +54 -20
  45. package/lib/module/version.js +1 -1
  46. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
  47. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
  48. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  49. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  50. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  51. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
  52. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
  53. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -1
  54. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  55. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  57. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  58. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  59. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  60. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  61. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
  62. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  63. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  64. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
  65. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
  66. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
  67. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
  68. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
  69. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
  70. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  71. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
  72. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
  73. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  74. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
  75. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
  76. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
  77. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
  78. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
  79. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
  80. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
  81. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
  82. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
  83. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
  84. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
  85. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
  86. package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
  87. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  88. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
  89. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
  90. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  91. package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
  92. package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
  93. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
  94. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
  95. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  96. package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
  97. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  98. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  99. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -1
  100. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  101. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  102. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +10 -1
  103. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  104. package/lib/typescript/src/Translation/Resources/en.d.ts +40 -1
  105. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  106. package/lib/typescript/src/Translation/Resources/tr.d.ts +40 -1
  107. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  108. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  109. package/lib/typescript/src/version.d.ts +1 -1
  110. package/package.json +7 -4
  111. package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
  112. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -4
  113. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +21 -4
  114. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +124 -23
  115. package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
  116. package/src/Screens/Dynamic/VideoCallScreen.tsx +3 -1
  117. package/src/Screens/Static/ResultScreen.tsx +183 -31
  118. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +9 -0
  119. package/src/Shared/Animations/recording.json +1 -0
  120. package/src/Shared/Components/DebugNavigationPanel.tsx +73 -48
  121. package/src/Shared/Components/EIDScanner.tsx +222 -111
  122. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
  123. package/src/Shared/Components/IdentityDocumentCamera.tsx +199 -184
  124. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
  125. package/src/Shared/Components/NavigationManager.tsx +27 -18
  126. package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
  127. package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
  128. package/src/Shared/EIDReader/bacKey.ts +24 -8
  129. package/src/Shared/EIDReader/eidReader.ts +398 -12
  130. package/src/Shared/EIDReader/eidService.ts +49 -1
  131. package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
  132. package/src/Shared/EIDReader/paceInfo.ts +159 -0
  133. package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
  134. package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
  135. package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
  136. package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
  137. package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
  138. package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
  139. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
  140. package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
  141. package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
  142. package/src/Shared/Libs/analytics.utils.ts +4 -0
  143. package/src/Shared/Libs/contains.ts +0 -53
  144. package/src/Shared/Libs/country-display.utils.ts +55 -0
  145. package/src/Shared/Libs/crypto.utils.ts +2 -2
  146. package/src/Shared/Libs/demo.utils.ts +10 -0
  147. package/src/Shared/Libs/http-client.ts +12 -4
  148. package/src/Shared/Libs/mrz.utils.ts +3 -2
  149. package/src/Shared/Libs/status-bar.utils.ts +4 -2
  150. package/src/Shared/Services/VideoSessionService.ts +1 -1
  151. package/src/Shared/Types/analytics.types.ts +2 -0
  152. package/src/Shared/Types/identificationInfo.ts +11 -0
  153. package/src/Translation/Resources/en.ts +63 -3
  154. package/src/Translation/Resources/tr.ts +62 -3
  155. package/src/Trustchex.tsx +53 -17
  156. package/src/version.ts +1 -1
@@ -6,7 +6,7 @@
6
6
  * KEY DISTINGUISHING FEATURES:
7
7
  * =============================
8
8
  * PASSPORT: Has MRZ on FRONT with pattern P<XXX (e.g., P<TUR, P<USA)
9
- * ID CARD: Has NO MRZ on front; MRZ only on BACK with code 'I'
9
+ * ID CARD: Has NO MRZ on front; MRZ only on BACK with code starting with I, A, or C
10
10
  *
11
11
  * IMPORTANT: Both passports and ID cards have signature sections!
12
12
  * Signature presence is NOT a distinguishing feature between document types.
@@ -31,7 +31,7 @@
31
31
  import type { Face } from './IdentityDocumentCamera.types';
32
32
  import type { MRZFields } from '../Types/mrzFields';
33
33
  import { PASSPORT_MRZ_PATTERN } from './IdentityDocumentCamera.constants';
34
- import { debugLog, isDebugEnabled } from '../Libs/debug.utils';
34
+ import { isIDCardDocumentCode } from './IdentityDocumentCamera.utils';
35
35
 
36
36
  /**
37
37
  * After this many retry attempts for ID_FRONT flow, proceed with face detection alone
@@ -222,10 +222,13 @@ export function handleIDFrontFlow(
222
222
  }
223
223
 
224
224
  // ============================================================================
225
- // STEP 4: Final MRZ code check (if present, must be 'I' not 'P')
225
+ // STEP 4: Final MRZ code check (if present, must be an ID card code, not P)
226
226
  // ============================================================================
227
227
  // ID cards should NOT have MRZ on front, but if detected, verify it's not passport
228
- if (mrzFields?.documentCode && mrzFields.documentCode !== 'I') {
228
+ if (
229
+ mrzFields?.documentCode &&
230
+ !isIDCardDocumentCode(mrzFields.documentCode)
231
+ ) {
229
232
  return {
230
233
  shouldProceed: false,
231
234
  reason: `MRZ shows unexpected document code: ${mrzFields.documentCode}`,
@@ -382,7 +382,9 @@ const IdentityDocumentCamera = ({
382
382
  } else {
383
383
  // Guide screen with white background - use dark icons
384
384
  StatusBar.setBarStyle('dark-content', true);
385
- StatusBar.setBackgroundColor('#ffffff', true);
385
+ if (Platform.OS === 'android') {
386
+ StatusBar.setBackgroundColor('#ffffff', true);
387
+ }
386
388
  }
387
389
  }, [hasGuideShown]);
388
390
 
@@ -2251,42 +2253,18 @@ const IdentityDocumentCamera = ({
2251
2253
  []
2252
2254
  );
2253
2255
 
2254
- if (!permissionsRequested) {
2256
+ // Show the guide immediately — the guide is pure UI and does not need
2257
+ // camera access. Permission is requested in the background (useEffect) so
2258
+ // it will usually be resolved by the time the user taps "Let's Go".
2259
+ // This avoids a white/blank screen while the async permission check runs.
2260
+ if (!hasGuideShown) {
2255
2261
  return (
2256
- <SafeAreaView style={styles.permissionContainer}>
2257
- <StatusBar barStyle="dark-content" />
2258
- <ActivityIndicator size="large" color={theme.colors.primary} />
2259
- </SafeAreaView>
2260
- );
2261
- }
2262
-
2263
- if (!hasPermission) {
2264
- return (
2265
- <SafeAreaView style={styles.permissionContainer}>
2266
- <StatusBar barStyle="dark-content" />
2267
- <TextView style={styles.permissionText}>
2268
- {t('general.noCameraPermissionGiven')}
2269
- </TextView>
2270
- <StyledButton
2271
- mode="contained"
2272
- onPress={() => {
2273
- Linking.openSettings();
2274
- }}
2275
- >
2276
- {t('general.openSettings')}
2277
- </StyledButton>
2278
- </SafeAreaView>
2279
- );
2280
- }
2281
-
2282
- return (
2283
- <View style={StyleSheet.absoluteFill}>
2284
- <StatusBar
2285
- barStyle={hasGuideShown ? 'light-content' : 'dark-content'}
2286
- backgroundColor={hasGuideShown ? 'transparent' : '#ffffff'}
2287
- translucent
2288
- />
2289
- {!hasGuideShown ? (
2262
+ <View style={StyleSheet.absoluteFill}>
2263
+ <StatusBar
2264
+ barStyle="dark-content"
2265
+ backgroundColor="#ffffff"
2266
+ translucent
2267
+ />
2290
2268
  <SafeAreaView style={styles.guide}>
2291
2269
  <LottieView
2292
2270
  source={require('../../Shared/Animations/id-or-passport.json')}
@@ -2320,163 +2298,200 @@ const IdentityDocumentCamera = ({
2320
2298
  {t('general.letsGo')}
2321
2299
  </StyledButton>
2322
2300
  </SafeAreaView>
2323
- ) : (
2324
- <>
2325
- <TrustchexCamera
2326
- ref={cameraRef}
2327
- style={StyleSheet.absoluteFill as ViewStyle}
2328
- cameraType="back"
2329
- enableFrameProcessing={isActive}
2330
- enableFaceDetection={isActive && faceDetectionEnabled}
2331
- enableTextRecognition={isActive}
2332
- enableBarcodeScanning={isActive && nextStep === 'SCAN_ID_BACK'}
2333
- includeBase64={isActive}
2334
- targetFps={10}
2335
- torchEnabled={isTorchOn}
2336
- onFrameAvailable={handleFrame}
2337
- onCameraReady={handleCameraReady}
2338
- onCameraError={handleCameraError}
2339
- />
2340
- <View style={[styles.topZone, { paddingTop: insets.top }]}>
2341
- {nextStep !== 'COMPLETED' &&
2342
- status !== 'SCANNED' &&
2343
- detectedDocumentType !== 'UNKNOWN' && (
2344
- <TextView style={styles.stepIndicator}>
2345
- {nextStep === 'SCAN_ID_FRONT_OR_PASSPORT'
2346
- ? `${t('identityDocumentCamera.frontSide')} • ${t(
2301
+ </View>
2302
+ );
2303
+ }
2304
+
2305
+ if (!permissionsRequested) {
2306
+ return (
2307
+ <SafeAreaView style={styles.permissionContainer}>
2308
+ <StatusBar barStyle="dark-content" />
2309
+ <ActivityIndicator size="large" color={theme.colors.primary} />
2310
+ </SafeAreaView>
2311
+ );
2312
+ }
2313
+
2314
+ if (!hasPermission) {
2315
+ return (
2316
+ <SafeAreaView style={styles.permissionContainer}>
2317
+ <StatusBar barStyle="dark-content" />
2318
+ <TextView style={styles.permissionText}>
2319
+ {t('general.noCameraPermissionGiven')}
2320
+ </TextView>
2321
+ <StyledButton
2322
+ mode="contained"
2323
+ onPress={() => {
2324
+ Linking.openSettings();
2325
+ }}
2326
+ >
2327
+ {t('general.openSettings')}
2328
+ </StyledButton>
2329
+ </SafeAreaView>
2330
+ );
2331
+ }
2332
+
2333
+ return (
2334
+ <View style={StyleSheet.absoluteFill}>
2335
+ <StatusBar
2336
+ barStyle="light-content"
2337
+ backgroundColor="transparent"
2338
+ translucent
2339
+ />
2340
+ <>
2341
+ <TrustchexCamera
2342
+ ref={cameraRef}
2343
+ style={StyleSheet.absoluteFill as ViewStyle}
2344
+ cameraType="back"
2345
+ enableFrameProcessing={isActive}
2346
+ enableFaceDetection={isActive && faceDetectionEnabled}
2347
+ enableTextRecognition={isActive}
2348
+ enableBarcodeScanning={isActive && nextStep === 'SCAN_ID_BACK'}
2349
+ includeBase64={isActive}
2350
+ targetFps={10}
2351
+ torchEnabled={isTorchOn}
2352
+ onFrameAvailable={handleFrame}
2353
+ onCameraReady={handleCameraReady}
2354
+ onCameraError={handleCameraError}
2355
+ />
2356
+ <View style={[styles.topZone, { paddingTop: insets.top }]}>
2357
+ {nextStep !== 'COMPLETED' &&
2358
+ status !== 'SCANNED' &&
2359
+ detectedDocumentType !== 'UNKNOWN' && (
2360
+ <TextView style={styles.stepIndicator}>
2361
+ {nextStep === 'SCAN_ID_FRONT_OR_PASSPORT'
2362
+ ? `${t('identityDocumentCamera.frontSide')} • ${t(
2363
+ 'identityDocumentCamera.stepProgress',
2364
+ {
2365
+ current: 1,
2366
+ total: onlyMRZScan
2367
+ ? detectedDocumentType === 'PASSPORT'
2368
+ ? 1
2369
+ : 2
2370
+ : detectedDocumentType === 'PASSPORT'
2371
+ ? 2
2372
+ : 3,
2373
+ }
2374
+ )}`
2375
+ : nextStep === 'SCAN_HOLOGRAM'
2376
+ ? `${t('identityDocumentCamera.hologramCheck')} • ${t(
2347
2377
  'identityDocumentCamera.stepProgress',
2348
2378
  {
2349
- current: 1,
2350
- total: onlyMRZScan
2351
- ? detectedDocumentType === 'PASSPORT'
2352
- ? 1
2353
- : 2
2354
- : detectedDocumentType === 'PASSPORT'
2355
- ? 2
2356
- : 3,
2379
+ current: 2,
2380
+ total: detectedDocumentType === 'PASSPORT' ? 2 : 3,
2357
2381
  }
2358
2382
  )}`
2359
- : nextStep === 'SCAN_HOLOGRAM'
2360
- ? `${t('identityDocumentCamera.hologramCheck')} • ${t(
2361
- 'identityDocumentCamera.stepProgress',
2362
- {
2363
- current: 2,
2364
- total: detectedDocumentType === 'PASSPORT' ? 2 : 3,
2365
- }
2366
- )}`
2367
- : nextStep === 'SCAN_ID_BACK'
2368
- ? `${t('identityDocumentCamera.backSide')} • ${t('identityDocumentCamera.stepProgress', { current: 3, total: 3 })}`
2369
- : ''}
2370
- </TextView>
2371
- )}
2372
-
2373
- <AnimatedText
2374
- style={[
2375
- styles.topZoneText,
2376
- // Priority order for coloring (later styles override earlier ones)
2377
- // 1. Success (green) - scan completed
2378
- nextStep === 'COMPLETED' && styles.topZoneTextSuccess,
2379
- // 2. Error (red) - wrong side - with flash opacity
2380
- status === 'INCORRECT' && styles.topZoneTextError,
2381
- status === 'INCORRECT' && {
2382
- opacity: errorFlashAnim,
2383
- },
2384
- // 3. Warning (yellow) - quality issues
2385
- (isBrightnessLow || isFrameBlurry) && styles.topZoneTextWarning,
2386
- // 4. Scanning (green) - all elements detected AND inside scan area
2387
- status === 'SCANNING' &&
2388
- allElementsDetected &&
2389
- elementsOutsideScanArea.length === 0 &&
2390
- !isBrightnessLow &&
2391
- !isFrameBlurry &&
2392
- styles.topZoneTextScanning,
2393
- // 5. Default (white) - aligning (not all detected OR elements outside scan area)
2394
- ]}
2395
- >
2396
- {getStatusMessage(
2397
- nextStep,
2398
- status,
2399
- detectedDocumentType,
2400
- isBrightnessLow,
2401
- isFrameBlurry,
2402
- allElementsDetected,
2403
- elementsOutsideScanArea,
2404
- t
2405
- )}
2406
- </AnimatedText>
2407
- </View>
2408
- <View style={styles.leftZone} />
2409
- <View style={styles.rightZone} />
2410
- <View style={styles.bottomZone} />
2411
- <View
2383
+ : nextStep === 'SCAN_ID_BACK'
2384
+ ? `${t('identityDocumentCamera.backSide')} • ${t('identityDocumentCamera.stepProgress', { current: 3, total: 3 })}`
2385
+ : ''}
2386
+ </TextView>
2387
+ )}
2388
+
2389
+ <AnimatedText
2412
2390
  style={[
2413
- styles.scanArea,
2414
- {
2415
- borderColor:
2416
- nextStep === 'COMPLETED'
2417
- ? '#4CAF50'
2418
- : status === 'INCORRECT'
2419
- ? '#f44336'
2420
- : status === 'SCANNING'
2421
- ? '#2196F3'
2422
- : isBrightnessLow || isFrameBlurry
2423
- ? '#FFC107'
2424
- : 'white',
2425
- borderWidth: status === 'SCANNING' ? 3 : 2,
2391
+ styles.topZoneText,
2392
+ // Priority order for coloring (later styles override earlier ones)
2393
+ // 1. Success (green) - scan completed
2394
+ nextStep === 'COMPLETED' && styles.topZoneTextSuccess,
2395
+ // 2. Error (red) - wrong side - with flash opacity
2396
+ status === 'INCORRECT' && styles.topZoneTextError,
2397
+ status === 'INCORRECT' && {
2398
+ opacity: errorFlashAnim,
2426
2399
  },
2400
+ // 3. Warning (yellow) - quality issues
2401
+ (isBrightnessLow || isFrameBlurry) && styles.topZoneTextWarning,
2402
+ // 4. Scanning (green) - all elements detected AND inside scan area
2403
+ status === 'SCANNING' &&
2404
+ allElementsDetected &&
2405
+ elementsOutsideScanArea.length === 0 &&
2406
+ !isBrightnessLow &&
2407
+ !isFrameBlurry &&
2408
+ styles.topZoneTextScanning,
2409
+ // 5. Default (white) - aligning (not all detected OR elements outside scan area)
2427
2410
  ]}
2428
2411
  >
2429
- {nextStep === 'COMPLETED' ? (
2430
- <LottieView
2431
- source={require('../../Shared/Animations/success.json')}
2432
- style={styles.animation}
2433
- loop={false}
2434
- autoPlay
2435
- />
2436
- ) : isBrightnessLow ? (
2437
- <LottieView
2438
- source={require('../../Shared/Animations/light.json')}
2439
- style={styles.animation}
2440
- loop={true}
2441
- autoPlay
2442
- />
2443
- ) : nextStep === 'SCAN_HOLOGRAM' && isTorchOn ? (
2444
- <LottieView
2445
- source={require('../../Shared/Animations/hologram-scan.json')}
2446
- style={styles.animation}
2447
- loop={true}
2448
- autoPlay
2449
- />
2450
- ) : null}
2451
- </View>
2452
- {isDebugEnabled() && (
2453
- <DebugOverlay
2454
- nextStep={nextStep}
2455
- status={status}
2456
- detectedDocumentType={detectedDocumentType}
2457
- isBrightnessLow={isBrightnessLow}
2458
- isFrameBlurry={isFrameBlurry}
2459
- isTorchOn={isTorchOn}
2460
- documentPlaneBounds={documentPlaneBounds}
2461
- secondaryFaceBounds={secondaryFaceBounds}
2462
- barcodeBounds={barcodeBounds}
2463
- mrzBounds={mrzBounds}
2464
- signatureBounds={signatureBounds}
2465
- currentFaceImage={currentFaceImage}
2466
- currentSecondaryFaceImage={currentSecondaryFaceImage}
2467
- currentHologramImage={currentHologramImage}
2468
- currentHologramMaskImage={_currentHologramMaskImage}
2469
- latestHologramFaceImage={latestHologramFaceImage}
2470
- hologramImageCount={hologramImageCount}
2471
- allElementsDetected={allElementsDetected}
2472
- elementsOutsideScanArea={elementsOutsideScanArea}
2412
+ {getStatusMessage(
2413
+ nextStep,
2414
+ status,
2415
+ detectedDocumentType,
2416
+ isBrightnessLow,
2417
+ isFrameBlurry,
2418
+ allElementsDetected,
2419
+ elementsOutsideScanArea,
2420
+ t
2421
+ )}
2422
+ </AnimatedText>
2423
+ </View>
2424
+ <View style={styles.leftZone} />
2425
+ <View style={styles.rightZone} />
2426
+ <View style={styles.bottomZone} />
2427
+ <View
2428
+ style={[
2429
+ styles.scanArea,
2430
+ {
2431
+ borderColor:
2432
+ nextStep === 'COMPLETED'
2433
+ ? '#4CAF50'
2434
+ : status === 'INCORRECT'
2435
+ ? '#f44336'
2436
+ : status === 'SCANNING'
2437
+ ? '#2196F3'
2438
+ : isBrightnessLow || isFrameBlurry
2439
+ ? '#FFC107'
2440
+ : 'white',
2441
+ borderWidth: status === 'SCANNING' ? 3 : 2,
2442
+ },
2443
+ ]}
2444
+ >
2445
+ {nextStep === 'COMPLETED' ? (
2446
+ <LottieView
2447
+ source={require('../../Shared/Animations/success.json')}
2448
+ style={styles.animation}
2449
+ loop={false}
2450
+ autoPlay
2451
+ />
2452
+ ) : isBrightnessLow ? (
2453
+ <LottieView
2454
+ source={require('../../Shared/Animations/light.json')}
2455
+ style={styles.animation}
2456
+ loop={true}
2457
+ autoPlay
2473
2458
  />
2474
- )}
2475
- {testMode && testModeData && (
2476
- <TestModePanel mrzText={testModeData.mrzText} />
2477
- )}
2478
- </>
2479
- )}
2459
+ ) : nextStep === 'SCAN_HOLOGRAM' && isTorchOn ? (
2460
+ <LottieView
2461
+ source={require('../../Shared/Animations/hologram-scan.json')}
2462
+ style={styles.animation}
2463
+ loop={true}
2464
+ autoPlay
2465
+ />
2466
+ ) : null}
2467
+ </View>
2468
+ {isDebugEnabled() && (
2469
+ <DebugOverlay
2470
+ nextStep={nextStep}
2471
+ status={status}
2472
+ detectedDocumentType={detectedDocumentType}
2473
+ isBrightnessLow={isBrightnessLow}
2474
+ isFrameBlurry={isFrameBlurry}
2475
+ isTorchOn={isTorchOn}
2476
+ documentPlaneBounds={documentPlaneBounds}
2477
+ secondaryFaceBounds={secondaryFaceBounds}
2478
+ barcodeBounds={barcodeBounds}
2479
+ mrzBounds={mrzBounds}
2480
+ signatureBounds={signatureBounds}
2481
+ currentFaceImage={currentFaceImage}
2482
+ currentSecondaryFaceImage={currentSecondaryFaceImage}
2483
+ currentHologramImage={currentHologramImage}
2484
+ currentHologramMaskImage={_currentHologramMaskImage}
2485
+ latestHologramFaceImage={latestHologramFaceImage}
2486
+ hologramImageCount={hologramImageCount}
2487
+ allElementsDetected={allElementsDetected}
2488
+ elementsOutsideScanArea={elementsOutsideScanArea}
2489
+ />
2490
+ )}
2491
+ {testMode && testModeData && (
2492
+ <TestModePanel mrzText={testModeData.mrzText} />
2493
+ )}
2494
+ </>
2480
2495
  </View>
2481
2496
  );
2482
2497
  };
@@ -12,6 +12,15 @@ import {
12
12
  } from './IdentityDocumentCamera.constants';
13
13
  import { debugLog, isDebugEnabled } from '../Libs/debug.utils';
14
14
 
15
+ /**
16
+ * Checks if a document code represents an ID card (TD1/TD2).
17
+ * Per ICAO 9303, valid ID card codes start with 'I', 'A', or 'C'.
18
+ */
19
+ export function isIDCardDocumentCode(code: string | null | undefined): boolean {
20
+ if (!code) return false;
21
+ return code.startsWith('I') || code.startsWith('A') || code.startsWith('C');
22
+ }
23
+
15
24
  /**
16
25
  * Frame-to-screen coordinate transform using FILL_CENTER scaling
17
26
  */
@@ -225,8 +234,8 @@ export function detectDocumentType(
225
234
  });
226
235
  }
227
236
 
228
- // ID Back: no face + ID MRZ
229
- if (faces.length === 0 && mrzFields?.documentCode === 'I') {
237
+ // ID Back: no face + ID MRZ (codes starting with I, A, or C per ICAO 9303)
238
+ if (faces.length === 0 && isIDCardDocumentCode(mrzFields?.documentCode)) {
230
239
  return 'ID_BACK';
231
240
  }
232
241
 
@@ -265,8 +274,8 @@ export function detectDocumentType(
265
274
  : faces;
266
275
 
267
276
  if (cardSizedFaces.length > 0) {
268
- // If we have MRZ code 'I', it's definitely an ID card
269
- if (mrzFields?.documentCode === 'I') {
277
+ // If we have an ID card MRZ code (I, A, or C), it's definitely an ID card
278
+ if (isIDCardDocumentCode(mrzFields?.documentCode)) {
270
279
  return 'ID_FRONT';
271
280
  }
272
281
 
@@ -18,9 +18,10 @@ import i18n from '../../Translation';
18
18
  import StyledButton from './StyledButton';
19
19
  import { analyticsService } from '../Services/AnalyticsService';
20
20
 
21
- // Simple global navigation lock
22
- let isNavigating = false;
23
- let lastNavigationTime = 0;
21
+ // Navigation lock - use a ref-like pattern so unmounting resets state.
22
+ // The module-level object is shared across all instances but the
23
+ // cleanup effect in the component resets it on unmount.
24
+ const navigationLock = { isNavigating: false, lastNavigationTime: 0 };
24
25
 
25
26
  export type NavigationManagerRef = {
26
27
  navigateToNextStep: () => void;
@@ -45,6 +46,7 @@ const NavigationManager = forwardRef(
45
46
  IDENTITY_DOCUMENT_SCAN: 'IdentityDocumentScanningScreen',
46
47
  IDENTITY_DOCUMENT_EID_SCAN: 'IdentityDocumentEIDScanningScreen',
47
48
  LIVENESS_CHECK: 'LivenessDetectionScreen',
49
+ VERBAL_CONSENT: 'VerbalConsentScreen',
48
50
  VIDEO_CALL: 'VideoCallScreen',
49
51
  },
50
52
  RESULT: 'ResultScreen',
@@ -52,14 +54,14 @@ const NavigationManager = forwardRef(
52
54
 
53
55
  React.useEffect(() => {
54
56
  const handleFocus = () => {
55
- isNavigating = false;
57
+ navigationLock.isNavigating = false;
56
58
  };
57
59
 
58
60
  navigation.addListener('focus', handleFocus);
59
61
 
60
62
  return () => {
61
63
  navigation.removeListener('focus', handleFocus);
62
- isNavigating = false;
64
+ navigationLock.isNavigating = false;
63
65
  };
64
66
  }, [navigation]);
65
67
 
@@ -114,16 +116,22 @@ const NavigationManager = forwardRef(
114
116
  return routes.DYNAMIC_ROUTES.VIDEO_CALL;
115
117
  }
116
118
 
119
+ if (nextStep.type === 'VERBAL_CONSENT') {
120
+ return routes.DYNAMIC_ROUTES.VERBAL_CONSENT;
121
+ }
122
+
117
123
  return routes.VERIFICATION_SESSION_CHECK;
118
124
  },
119
125
  [
120
126
  appContext,
121
127
  routes.VERIFICATION_SESSION_CHECK,
128
+ routes.RESULT,
122
129
  routes.DYNAMIC_ROUTES.CONTRACT_ACCEPTANCE,
123
- routes.DYNAMIC_ROUTES.IDENTITY_DOCUMENT_EID_SCAN,
124
130
  routes.DYNAMIC_ROUTES.IDENTITY_DOCUMENT_SCAN,
131
+ routes.DYNAMIC_ROUTES.IDENTITY_DOCUMENT_EID_SCAN,
125
132
  routes.DYNAMIC_ROUTES.LIVENESS_CHECK,
126
- routes.RESULT,
133
+ routes.DYNAMIC_ROUTES.VIDEO_CALL,
134
+ routes.DYNAMIC_ROUTES.VERBAL_CONSENT,
127
135
  ]
128
136
  );
129
137
 
@@ -132,14 +140,15 @@ const NavigationManager = forwardRef(
132
140
  const minTimeBetweenNavigation = 1000;
133
141
 
134
142
  if (
135
- isNavigating ||
136
- currentTime - lastNavigationTime < minTimeBetweenNavigation
143
+ navigationLock.isNavigating ||
144
+ currentTime - navigationLock.lastNavigationTime <
145
+ minTimeBetweenNavigation
137
146
  ) {
138
147
  return;
139
148
  }
140
149
 
141
- isNavigating = true;
142
- lastNavigationTime = currentTime;
150
+ navigationLock.isNavigating = true;
151
+ navigationLock.lastNavigationTime = currentTime;
143
152
 
144
153
  try {
145
154
  const nextRoute = getNextRoute(
@@ -156,12 +165,12 @@ const NavigationManager = forwardRef(
156
165
  })
157
166
  );
158
167
  } catch (error) {
159
- isNavigating = false;
168
+ navigationLock.isNavigating = false;
160
169
  throw error;
161
170
  }
162
171
 
163
172
  setTimeout(() => {
164
- isNavigating = false;
173
+ navigationLock.isNavigating = false;
165
174
  }, 1000);
166
175
  }, [
167
176
  getNextRoute,
@@ -171,7 +180,7 @@ const NavigationManager = forwardRef(
171
180
  ]);
172
181
 
173
182
  const goToNextRouteWithAlert = useCallback(() => {
174
- if (isNavigating) {
183
+ if (navigationLock.isNavigating) {
175
184
  return;
176
185
  }
177
186
 
@@ -192,11 +201,11 @@ const NavigationManager = forwardRef(
192
201
  }, [goToNextRoute, t]);
193
202
 
194
203
  const reset = useCallback(() => {
195
- if (isNavigating) {
204
+ if (navigationLock.isNavigating) {
196
205
  return;
197
206
  }
198
207
 
199
- isNavigating = true;
208
+ navigationLock.isNavigating = true;
200
209
 
201
210
  try {
202
211
  // Preserve demo session state when resetting
@@ -241,12 +250,12 @@ const NavigationManager = forwardRef(
241
250
  })
242
251
  );
243
252
  } catch (error) {
244
- isNavigating = false;
253
+ navigationLock.isNavigating = false;
245
254
  throw error;
246
255
  }
247
256
 
248
257
  setTimeout(() => {
249
- isNavigating = false;
258
+ navigationLock.isNavigating = false;
250
259
  }, 1000);
251
260
  }, [appContext, navigation, routes.VERIFICATION_SESSION_CHECK]);
252
261