@trustchex/react-native-sdk 1.409.0 → 1.472.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 (171) 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 +102 -4
  3. package/ios/Camera/TrustchexCameraView.swift +151 -18
  4. package/ios/Permission/PermissionModule.m +22 -0
  5. package/ios/Permission/PermissionModule.swift +67 -0
  6. package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
  7. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +1 -4
  8. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +17 -4
  9. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +125 -25
  10. package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
  11. package/lib/module/Screens/Dynamic/VideoCallScreen.js +10 -13
  12. package/lib/module/Screens/Static/ResultScreen.js +131 -22
  13. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +8 -0
  14. package/lib/module/Shared/Animations/recording.json +1 -0
  15. package/lib/module/Shared/Components/DebugNavigationPanel.js +69 -71
  16. package/lib/module/Shared/Components/EIDScanner.js +222 -115
  17. package/lib/module/Shared/Components/FaceCamera.js +14 -8
  18. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
  19. package/lib/module/Shared/Components/IdentityDocumentCamera.js +60 -42
  20. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
  21. package/lib/module/Shared/Components/NavigationManager.js +24 -16
  22. package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
  23. package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
  24. package/lib/module/Shared/EIDReader/bacKey.js +16 -2
  25. package/lib/module/Shared/EIDReader/eidReader.js +354 -13
  26. package/lib/module/Shared/EIDReader/eidService.js +25 -1
  27. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
  28. package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
  29. package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
  30. package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
  31. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
  32. package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
  33. package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
  34. package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
  35. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
  36. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
  37. package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
  38. package/lib/module/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
  39. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  40. package/lib/module/Shared/Libs/contains.js +1 -40
  41. package/lib/module/Shared/Libs/country-display.utils.js +34 -0
  42. package/lib/module/Shared/Libs/demo.utils.js +8 -0
  43. package/lib/module/Shared/Libs/index.js +20 -0
  44. package/lib/module/Shared/Libs/mrz.utils.js +3 -2
  45. package/lib/module/Shared/Libs/permissions.utils.js +199 -0
  46. package/lib/module/Shared/Libs/status-bar.utils.js +4 -2
  47. package/lib/module/Shared/Types/analytics.types.js +2 -0
  48. package/lib/module/Translation/Resources/en.js +42 -2
  49. package/lib/module/Translation/Resources/tr.js +42 -2
  50. package/lib/module/Trustchex.js +54 -20
  51. package/lib/module/version.js +1 -1
  52. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
  53. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
  54. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  55. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  57. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
  58. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
  59. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -1
  60. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  61. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  62. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  63. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  64. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  65. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  66. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  67. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  68. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
  69. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  70. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  71. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
  72. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
  73. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
  74. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
  75. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
  76. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
  77. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  78. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
  79. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
  82. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
  83. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
  84. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
  85. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
  86. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
  87. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
  88. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
  89. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
  90. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
  91. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
  92. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
  93. package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
  94. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  95. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
  96. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
  97. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  98. package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
  99. package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
  100. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
  101. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
  102. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  103. package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
  104. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  105. package/lib/typescript/src/Shared/Libs/index.d.ts +20 -0
  106. package/lib/typescript/src/Shared/Libs/index.d.ts.map +1 -0
  107. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  108. package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts +58 -0
  109. package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts.map +1 -0
  110. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -1
  111. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  112. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  113. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +10 -1
  114. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  115. package/lib/typescript/src/Translation/Resources/en.d.ts +41 -1
  116. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  117. package/lib/typescript/src/Translation/Resources/tr.d.ts +41 -1
  118. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  119. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  120. package/lib/typescript/src/version.d.ts +1 -1
  121. package/package.json +7 -4
  122. package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
  123. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -4
  124. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +21 -4
  125. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +165 -24
  126. package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
  127. package/src/Screens/Dynamic/VideoCallScreen.tsx +11 -20
  128. package/src/Screens/Static/ResultScreen.tsx +187 -31
  129. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +9 -0
  130. package/src/Shared/Animations/recording.json +1 -0
  131. package/src/Shared/Components/DebugNavigationPanel.tsx +73 -48
  132. package/src/Shared/Components/EIDScanner.tsx +280 -119
  133. package/src/Shared/Components/FaceCamera.tsx +19 -16
  134. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
  135. package/src/Shared/Components/IdentityDocumentCamera.tsx +206 -191
  136. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
  137. package/src/Shared/Components/NavigationManager.tsx +27 -18
  138. package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
  139. package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
  140. package/src/Shared/EIDReader/bacKey.ts +24 -8
  141. package/src/Shared/EIDReader/eidReader.ts +398 -12
  142. package/src/Shared/EIDReader/eidService.ts +49 -1
  143. package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
  144. package/src/Shared/EIDReader/paceInfo.ts +159 -0
  145. package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
  146. package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
  147. package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
  148. package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
  149. package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
  150. package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
  151. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
  152. package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
  153. package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
  154. package/src/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
  155. package/src/Shared/Libs/analytics.utils.ts +4 -0
  156. package/src/Shared/Libs/contains.ts +0 -53
  157. package/src/Shared/Libs/country-display.utils.ts +55 -0
  158. package/src/Shared/Libs/crypto.utils.ts +2 -2
  159. package/src/Shared/Libs/demo.utils.ts +10 -0
  160. package/src/Shared/Libs/http-client.ts +12 -4
  161. package/src/Shared/Libs/index.ts +63 -0
  162. package/src/Shared/Libs/mrz.utils.ts +3 -2
  163. package/src/Shared/Libs/permissions.utils.ts +251 -0
  164. package/src/Shared/Libs/status-bar.utils.ts +4 -2
  165. package/src/Shared/Services/VideoSessionService.ts +1 -1
  166. package/src/Shared/Types/analytics.types.ts +2 -0
  167. package/src/Shared/Types/identificationInfo.ts +11 -0
  168. package/src/Translation/Resources/en.ts +64 -3
  169. package/src/Translation/Resources/tr.ts +63 -3
  170. package/src/Trustchex.tsx +53 -17
  171. package/src/version.ts +1 -1
@@ -5,12 +5,15 @@ import { Alert, View, Text, Image, StyleSheet, Animated } from 'react-native';
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  import NFCManager from 'react-native-nfc-manager';
7
7
  import DeviceInfo from 'react-native-device-info';
8
+ import PermissionManager from "../Libs/permissions.utils.js";
9
+ import { debugError } from "../Libs/debug.utils.js";
8
10
  import { eidReader } from "../EIDReader/eidReader.js";
9
11
  import NativeProgressBar from "./NativeProgressBar.js";
10
12
  import { useTranslation } from 'react-i18next';
11
13
  import AppContext from "../Contexts/AppContext.js";
12
14
  import StyledButton from "./StyledButton.js";
13
15
  import LottieView from 'lottie-react-native';
16
+ import { getLocalizedCountryName } from "../Libs/country-display.utils.js";
14
17
  import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
15
18
  import { speak, resetLastMessage } from "../Libs/tts.utils.js";
16
19
  import { trackEIDScanStart, trackEIDScanComplete, trackEIDScanFailed } from "../Libs/analytics.utils.js";
@@ -68,9 +71,32 @@ const EIDScanner = ({
68
71
  const [isScanned, setIsScanned] = React.useState(false);
69
72
  const [hasGuideShown, setHasGuideShown] = React.useState(false);
70
73
  const {
71
- t
74
+ t,
75
+ i18n
72
76
  } = useTranslation();
73
77
  const appContext = React.useContext(AppContext);
78
+ const formatGender = useCallback(code => {
79
+ switch (code?.toUpperCase()) {
80
+ case 'M':
81
+ return t('eidScannerScreen.genderMale', 'Male');
82
+ case 'F':
83
+ return t('eidScannerScreen.genderFemale', 'Female');
84
+ default:
85
+ return t('eidScannerScreen.genderUnspecified', 'Unspecified');
86
+ }
87
+ }, [t]);
88
+ const formatDocumentType = useCallback(code => {
89
+ if (!code) return '';
90
+ const upper = code.toUpperCase();
91
+ if (upper.startsWith('P')) return t('eidScannerScreen.docTypePassport', 'Passport');
92
+ if (upper.startsWith('I')) return t('eidScannerScreen.docTypeID', 'ID Card');
93
+ if (upper.startsWith('A')) return t('eidScannerScreen.docTypeResidence', 'Residence Permit');
94
+ if (upper.startsWith('V')) return t('eidScannerScreen.docTypeVisa', 'Visa');
95
+ return code;
96
+ }, [t]);
97
+ const formatNationality = useCallback(code => {
98
+ return getLocalizedCountryName(code, i18n.language);
99
+ }, [i18n.language]);
74
100
  const [voiceGuidanceMessage, setVoiceGuidanceMessage] = useState();
75
101
  const [attemptNumber, setAttemptNumber] = useState(0);
76
102
  const readNFC = useCallback(async () => {
@@ -83,6 +109,15 @@ const EIDScanner = ({
83
109
  setProgressStage('');
84
110
  resetLastMessage();
85
111
 
112
+ // Check NFC permissions before attempting to scan
113
+ const hasNFCPermission = await PermissionManager.requestPermission('nfc');
114
+ if (!hasNFCPermission) {
115
+ debugError('EIDScanner', 'NFC permission denied');
116
+ setIsScanning(false);
117
+ Alert.alert(t('eidScannerScreen.error'), t('eidScannerScreen.nfcPermissionDenied', 'NFC permission is required to scan your document'));
118
+ return;
119
+ }
120
+
86
121
  // Track EID scan start using analytics helper
87
122
  const docType = documentType || 'UNKNOWN';
88
123
 
@@ -296,102 +331,82 @@ const EIDScanner = ({
296
331
  children: t('eidScannerScreen.startScanning')
297
332
  })]
298
333
  }) : /*#__PURE__*/_jsxs(_Fragment, {
299
- children: [isScanned && documentFaceImage && documentFaceImageMimeType === 'image/jpeg' && /*#__PURE__*/_jsx(Image, {
300
- source: {
301
- uri: `data:${documentFaceImageMimeType};base64,${documentFaceImage}`
302
- },
303
- style: styles.faceImage
304
- }), isScanned && documentFaceImage &&
305
- // TODO Add support for jp2 images
306
- documentFaceImageMimeType === 'image/jp2' && /*#__PURE__*/_jsx(Text, {
307
- style: styles.mainText,
308
- children: t('eidScannerScreen.imageCannotBeShown')
309
- }), isScanned && !documentFaceImage && /*#__PURE__*/_jsx(Text, {
310
- style: styles.mainText,
311
- children: t('eidScannerScreen.faceImageNotFound')
312
- }), isScanned && documentMRZInfo && /*#__PURE__*/_jsxs(View, {
313
- style: styles.mrzInfo,
334
+ children: [isScanned && documentMRZInfo && /*#__PURE__*/_jsxs(View, {
335
+ style: styles.idCardWrapper,
314
336
  children: [/*#__PURE__*/_jsxs(View, {
315
- style: styles.mrzInfoItem,
316
- children: [/*#__PURE__*/_jsxs(Text, {
317
- style: styles.mrzInfoLabel,
318
- children: [t('eidScannerScreen.documentCode'), ":"]
319
- }), /*#__PURE__*/_jsx(Text, {
320
- style: styles.mrzInfoText,
321
- children: documentMRZInfo.getDocumentCode()
322
- })]
323
- }), /*#__PURE__*/_jsxs(View, {
324
- style: styles.mrzInfoItem,
325
- children: [/*#__PURE__*/_jsxs(Text, {
326
- style: styles.mrzInfoLabel,
327
- children: [t('eidScannerScreen.nationality'), ":"]
328
- }), /*#__PURE__*/_jsx(Text, {
329
- style: styles.mrzInfoText,
330
- children: documentMRZInfo.getNationality()
331
- })]
332
- }), /*#__PURE__*/_jsxs(View, {
333
- style: styles.mrzInfoItem,
334
- children: [/*#__PURE__*/_jsxs(Text, {
335
- style: styles.mrzInfoLabel,
336
- children: [t('eidScannerScreen.personalNumber'), ":"]
337
- }), /*#__PURE__*/_jsx(Text, {
338
- style: styles.mrzInfoText,
339
- children: documentMRZInfo.getOptionalData1()
340
- })]
341
- }), /*#__PURE__*/_jsxs(View, {
342
- style: styles.mrzInfoItem,
343
- children: [/*#__PURE__*/_jsxs(Text, {
344
- style: styles.mrzInfoLabel,
345
- children: [t('eidScannerScreen.documentNumber'), ":"]
346
- }), /*#__PURE__*/_jsx(Text, {
347
- style: styles.mrzInfoText,
348
- children: documentMRZInfo.getDocumentNumber()
349
- })]
350
- }), /*#__PURE__*/_jsxs(View, {
351
- style: styles.mrzInfoItem,
352
- children: [/*#__PURE__*/_jsxs(Text, {
353
- style: styles.mrzInfoLabel,
354
- children: [t('eidScannerScreen.name'), ":"]
355
- }), /*#__PURE__*/_jsx(Text, {
356
- style: styles.mrzInfoText,
357
- children: documentMRZInfo.getSecondaryIdentifier()
358
- })]
359
- }), /*#__PURE__*/_jsxs(View, {
360
- style: styles.mrzInfoItem,
361
- children: [/*#__PURE__*/_jsxs(Text, {
362
- style: styles.mrzInfoLabel,
363
- children: [t('eidScannerScreen.surname'), ":"]
364
- }), /*#__PURE__*/_jsx(Text, {
365
- style: styles.mrzInfoText,
366
- children: documentMRZInfo.getPrimaryIdentifier()
367
- })]
368
- }), /*#__PURE__*/_jsxs(View, {
369
- style: styles.mrzInfoItem,
370
- children: [/*#__PURE__*/_jsxs(Text, {
371
- style: styles.mrzInfoLabel,
372
- children: [t('eidScannerScreen.birthDate'), ":"]
373
- }), /*#__PURE__*/_jsx(Text, {
374
- style: styles.mrzInfoText,
375
- children: formatDate(documentMRZInfo.getDateOfBirth())
376
- })]
377
- }), /*#__PURE__*/_jsxs(View, {
378
- style: styles.mrzInfoItem,
379
- children: [/*#__PURE__*/_jsxs(Text, {
380
- style: styles.mrzInfoLabel,
381
- children: [t('eidScannerScreen.gender'), ":"]
382
- }), /*#__PURE__*/_jsx(Text, {
383
- style: styles.mrzInfoText,
384
- children: documentMRZInfo.getGender().getStrCode()
385
- })]
386
- }), /*#__PURE__*/_jsxs(View, {
387
- style: styles.mrzInfoItem,
388
- children: [/*#__PURE__*/_jsxs(Text, {
389
- style: styles.mrzInfoLabel,
390
- children: [t('eidScannerScreen.expirationDate'), ":"]
391
- }), /*#__PURE__*/_jsx(Text, {
392
- style: styles.mrzInfoText,
393
- children: formatDate(documentMRZInfo.getDateOfExpiry())
337
+ style: styles.idCard,
338
+ children: [/*#__PURE__*/_jsx(View, {
339
+ style: [styles.idCardStrip, {
340
+ backgroundColor: appContext.branding.primaryColor
341
+ }],
342
+ children: /*#__PURE__*/_jsx(Text, {
343
+ style: styles.idCardStripDocType,
344
+ children: formatDocumentType(documentMRZInfo.getDocumentCode())
345
+ })
346
+ }), /*#__PURE__*/_jsxs(View, {
347
+ style: styles.idCardBody,
348
+ children: [/*#__PURE__*/_jsx(View, {
349
+ style: styles.idCardPhotoContainer,
350
+ children: /*#__PURE__*/_jsx(View, {
351
+ style: styles.idCardPhotoFrame,
352
+ children: documentFaceImage && (documentFaceImageMimeType === 'image/jpeg' || documentFaceImageMimeType === 'image/png') ? /*#__PURE__*/_jsx(Image, {
353
+ source: {
354
+ uri: `data:${documentFaceImageMimeType};base64,${documentFaceImage}`
355
+ },
356
+ style: styles.idCardPhoto,
357
+ resizeMode: "cover"
358
+ }) : /*#__PURE__*/_jsx(View, {
359
+ style: styles.idCardPhotoPlaceholder,
360
+ children: /*#__PURE__*/_jsx(Text, {
361
+ style: styles.idCardPhotoPlaceholderText,
362
+ children: "?"
363
+ })
364
+ })
365
+ })
366
+ }), /*#__PURE__*/_jsxs(View, {
367
+ style: styles.idCardFields,
368
+ children: [/*#__PURE__*/_jsx(IDCardField, {
369
+ label: t('eidScannerScreen.surname'),
370
+ value: documentMRZInfo.getPrimaryIdentifier()
371
+ }), /*#__PURE__*/_jsx(IDCardField, {
372
+ label: t('eidScannerScreen.name'),
373
+ value: documentMRZInfo.getSecondaryIdentifier()
374
+ }), /*#__PURE__*/_jsx(IDCardField, {
375
+ label: t('eidScannerScreen.documentNumber'),
376
+ value: documentMRZInfo.getDocumentNumber()
377
+ }), /*#__PURE__*/_jsx(IDCardField, {
378
+ label: t('eidScannerScreen.birthDate'),
379
+ value: formatDate(documentMRZInfo.getDateOfBirth())
380
+ }), /*#__PURE__*/_jsx(IDCardField, {
381
+ label: t('eidScannerScreen.expirationDate'),
382
+ value: formatDate(documentMRZInfo.getDateOfExpiry())
383
+ }), /*#__PURE__*/_jsx(IDCardField, {
384
+ label: t('eidScannerScreen.gender'),
385
+ value: formatGender(documentMRZInfo.getGender().getStrCode())
386
+ }), /*#__PURE__*/_jsx(IDCardField, {
387
+ label: t('eidScannerScreen.nationality'),
388
+ value: formatNationality(documentMRZInfo.getNationality())
389
+ }), /*#__PURE__*/_jsx(IDCardField, {
390
+ label: t('eidScannerScreen.issuingState'),
391
+ value: formatNationality(documentMRZInfo.getIssuingState())
392
+ }), !!(documentMRZInfo.getPersonalNumber() || documentMRZInfo.getOptionalData1()) && /*#__PURE__*/_jsx(IDCardField, {
393
+ label: t('eidScannerScreen.personalNumber'),
394
+ value: documentMRZInfo.getPersonalNumber() || documentMRZInfo.getOptionalData1() || ''
395
+ })]
396
+ })]
394
397
  })]
398
+ }), hasNfc && isEnabled && isScanned && /*#__PURE__*/_jsx(View, {
399
+ style: styles.buttonsContainer,
400
+ children: /*#__PURE__*/_jsx(StyledButton, {
401
+ mode: "contained",
402
+ disabled: !documentFaceImage || !documentMRZInfo,
403
+ onPress: () => {
404
+ if (onScanSuccess && documentMRZInfo && documentFaceImage && documentFaceImageMimeType) {
405
+ onScanSuccess(getFieldsFromMRZ(documentMRZInfo), documentFaceImage, documentFaceImageMimeType);
406
+ }
407
+ },
408
+ children: t('eidScannerScreen.approveAndContinue')
409
+ })
395
410
  })]
396
411
  }), !hasNfc && /*#__PURE__*/_jsx(Text, {
397
412
  style: styles.mainText,
@@ -423,7 +438,14 @@ const EIDScanner = ({
423
438
  }) || documentType === 'UNKNOWN' && /*#__PURE__*/_jsx(Text, {
424
439
  style: styles.mainText,
425
440
  children: t('eidScannerScreen.placeDocumentOnNFC')
426
- })), hasNfc && isEnabled && isScanned && /*#__PURE__*/_jsx(View, {
441
+ })), hasNfc && isEnabled && !isScanning && !isScanned && /*#__PURE__*/_jsx(View, {
442
+ style: styles.buttonsContainer,
443
+ children: /*#__PURE__*/_jsx(StyledButton, {
444
+ mode: "contained",
445
+ onPress: readNFC,
446
+ children: t('eidScannerScreen.startScanning')
447
+ })
448
+ }), hasNfc && isEnabled && isScanned && !documentMRZInfo && /*#__PURE__*/_jsx(View, {
427
449
  style: styles.buttonsContainer,
428
450
  children: /*#__PURE__*/_jsx(StyledButton, {
429
451
  mode: "contained",
@@ -508,31 +530,102 @@ const styles = StyleSheet.create({
508
530
  color: 'black',
509
531
  textAlign: 'center'
510
532
  },
511
- faceImage: {
512
- width: 150,
513
- height: 150,
514
- borderRadius: 150,
515
- borderColor: 'lightgray',
516
- borderWidth: 1,
517
- alignSelf: 'center'
533
+ // ── ID Card result layout ──────────────────────────────────────────────
534
+ idCardWrapper: {
535
+ gap: 10
518
536
  },
519
- mrzInfo: {
520
- flexDirection: 'column',
521
- width: '100%'
537
+ idCard: {
538
+ backgroundColor: '#fff',
539
+ borderRadius: 14,
540
+ overflow: 'hidden',
541
+ shadowColor: '#000',
542
+ shadowOffset: {
543
+ width: 0,
544
+ height: 3
545
+ },
546
+ shadowOpacity: 0.12,
547
+ shadowRadius: 10,
548
+ elevation: 4,
549
+ borderWidth: 1,
550
+ borderColor: '#e8e8e8'
522
551
  },
523
- mrzInfoItem: {
552
+ idCardStrip: {
524
553
  flexDirection: 'row',
525
554
  justifyContent: 'space-between',
526
- borderBottomColor: 'lightgray',
527
- borderBottomWidth: 1,
528
- paddingVertical: 5
555
+ alignItems: 'center',
556
+ paddingHorizontal: 12,
557
+ paddingVertical: 6
529
558
  },
530
- mrzInfoLabel: {
531
- color: 'black',
532
- fontWeight: 'bold'
559
+ idCardStripDocType: {
560
+ color: '#fff',
561
+ fontSize: 12,
562
+ fontWeight: '700',
563
+ letterSpacing: 0.5,
564
+ textTransform: 'uppercase'
565
+ },
566
+ idCardStripIssuer: {
567
+ color: 'rgba(255,255,255,0.85)',
568
+ fontSize: 12,
569
+ fontWeight: '600',
570
+ letterSpacing: 0.3
533
571
  },
534
- mrzInfoText: {
535
- color: 'black'
572
+ idCardBody: {
573
+ flexDirection: 'row',
574
+ alignItems: 'stretch'
575
+ },
576
+ idCardPhotoContainer: {
577
+ width: 110,
578
+ padding: 8,
579
+ alignSelf: 'stretch'
580
+ },
581
+ idCardPhotoFrame: {
582
+ flex: 1,
583
+ borderRadius: 10,
584
+ overflow: 'hidden',
585
+ borderWidth: 1,
586
+ borderColor: 'rgba(0,0,0,0.12)'
587
+ },
588
+ idCardPhoto: {
589
+ flex: 1
590
+ },
591
+ idCardPhotoPlaceholder: {
592
+ flex: 1,
593
+ borderRadius: 10,
594
+ backgroundColor: '#f5f5f5',
595
+ alignItems: 'center',
596
+ justifyContent: 'center'
597
+ },
598
+ idCardPhotoPlaceholderText: {
599
+ fontSize: 30,
600
+ color: '#ccc'
601
+ },
602
+ idCardFields: {
603
+ flex: 1,
604
+ flexDirection: 'row',
605
+ flexWrap: 'wrap',
606
+ gap: 6,
607
+ alignContent: 'flex-start',
608
+ padding: 10
609
+ },
610
+ idCardFieldItem: {
611
+ flexGrow: 1,
612
+ flexShrink: 1,
613
+ flexBasis: '45%',
614
+ minWidth: 0
615
+ },
616
+ idCardFieldLabel: {
617
+ fontSize: 8,
618
+ fontWeight: '600',
619
+ color: '#999',
620
+ letterSpacing: 0.3,
621
+ marginBottom: 1
622
+ },
623
+ idCardFieldValue: {
624
+ fontSize: 11,
625
+ fontWeight: '600',
626
+ color: '#111',
627
+ flexShrink: 1,
628
+ flexWrap: 'wrap'
536
629
  },
537
630
  guide: {
538
631
  flex: 1,
@@ -563,4 +656,18 @@ const styles = StyleSheet.create({
563
656
  fontSize: 14
564
657
  }
565
658
  });
659
+ const IDCardField = ({
660
+ label,
661
+ value
662
+ }) => /*#__PURE__*/_jsxs(View, {
663
+ style: styles.idCardFieldItem,
664
+ children: [/*#__PURE__*/_jsx(Text, {
665
+ style: styles.idCardFieldLabel,
666
+ numberOfLines: 1,
667
+ children: label
668
+ }), /*#__PURE__*/_jsx(Text, {
669
+ style: styles.idCardFieldValue,
670
+ children: value || '—'
671
+ })]
672
+ });
566
673
  export default EIDScanner;
@@ -3,8 +3,9 @@
3
3
  import { useIsFocused } from '@react-navigation/native';
4
4
  import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
5
5
  import { debugLog, debugWarn, debugError, logError } from "../Libs/debug.utils.js";
6
+ import PermissionManager from "../Libs/permissions.utils.js";
6
7
  import React, { useEffect, useState, useRef, useCallback } from 'react';
7
- import { StyleSheet, Text, View, Platform, Linking, ActivityIndicator, NativeModules, PermissionsAndroid } from 'react-native';
8
+ import { StyleSheet, Text, View, Linking, ActivityIndicator, NativeModules } from 'react-native';
8
9
  import { TrustchexCamera } from "./TrustchexCamera.js";
9
10
  import { useTranslation } from 'react-i18next';
10
11
  import StyledButton from "./StyledButton.js";
@@ -41,13 +42,18 @@ const FaceCamera = ({
41
42
  } = useTranslation();
42
43
  useEffect(() => {
43
44
  const requestPermissions = async () => {
44
- if (Platform.OS === 'android') {
45
- const cameraGranted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA);
46
- const audioGranted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO);
47
- setHasPermission(cameraGranted === PermissionsAndroid.RESULTS.GRANTED && audioGranted === PermissionsAndroid.RESULTS.GRANTED);
48
- } else {
49
- // iOS permissions handled by native camera
50
- setHasPermission(true);
45
+ try {
46
+ const permissions = await PermissionManager.requestPermissions(['camera', 'microphone']);
47
+ const hasCamera = permissions.get('camera') ?? false;
48
+ const hasMicrophone = permissions.get('microphone') ?? false;
49
+ debugLog('FaceCamera', 'Permissions requested:', {
50
+ camera: hasCamera,
51
+ microphone: hasMicrophone
52
+ });
53
+ setHasPermission(hasCamera && hasMicrophone);
54
+ } catch (error) {
55
+ debugError('FaceCamera', 'Permission request failed:', error);
56
+ setHasPermission(false);
51
57
  }
52
58
  setPermissionsRequested(true);
53
59
  };
@@ -8,7 +8,7 @@
8
8
  * KEY DISTINGUISHING FEATURES:
9
9
  * =============================
10
10
  * PASSPORT: Has MRZ on FRONT with pattern P<XXX (e.g., P<TUR, P<USA)
11
- * ID CARD: Has NO MRZ on front; MRZ only on BACK with code 'I'
11
+ * ID CARD: Has NO MRZ on front; MRZ only on BACK with code starting with I, A, or C
12
12
  *
13
13
  * IMPORTANT: Both passports and ID cards have signature sections!
14
14
  * Signature presence is NOT a distinguishing feature between document types.
@@ -31,6 +31,8 @@
31
31
  */
32
32
 
33
33
  import { PASSPORT_MRZ_PATTERN } from "./IdentityDocumentCamera.constants.js";
34
+ import { isIDCardDocumentCode } from "./IdentityDocumentCamera.utils.js";
35
+
34
36
  /**
35
37
  * After this many retry attempts for ID_FRONT flow, proceed with face detection alone
36
38
  * even if signature text OCR fails. This prevents blocking users due to OCR limitations.
@@ -178,10 +180,10 @@ export function handleIDFrontFlow(faces, text, mrzText, mrzFields, retryCount) {
178
180
  }
179
181
 
180
182
  // ============================================================================
181
- // STEP 4: Final MRZ code check (if present, must be 'I' not 'P')
183
+ // STEP 4: Final MRZ code check (if present, must be an ID card code, not P)
182
184
  // ============================================================================
183
185
  // ID cards should NOT have MRZ on front, but if detected, verify it's not passport
184
- if (mrzFields?.documentCode && mrzFields.documentCode !== 'I') {
186
+ if (mrzFields?.documentCode && !isIDCardDocumentCode(mrzFields.documentCode)) {
185
187
  return {
186
188
  shouldProceed: false,
187
189
  reason: `MRZ shows unexpected document code: ${mrzFields.documentCode}`,
@@ -2,10 +2,11 @@
2
2
 
3
3
  /* eslint-disable react-native/no-inline-styles */
4
4
  import React, { useEffect, useState, useRef, useCallback } from 'react';
5
- import { View, StyleSheet, Text as TextView, Platform, StatusBar, Vibration, Linking, ActivityIndicator, PermissionsAndroid, Animated } from 'react-native';
5
+ import { View, StyleSheet, Text as TextView, Platform, StatusBar, Vibration, Linking, ActivityIndicator, Animated } from 'react-native';
6
6
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
7
7
  import { TrustchexCamera } from "./TrustchexCamera.js";
8
8
  import { NativeModules } from 'react-native';
9
+ import PermissionManager from "../Libs/permissions.utils.js";
9
10
  import mrzUtils from "../Libs/mrz.utils.js";
10
11
  import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
11
12
  import { useIsFocused } from '@react-navigation/native';
@@ -120,11 +121,11 @@ const IdentityDocumentCamera = ({
120
121
  } = useTranslation();
121
122
  useEffect(() => {
122
123
  const requestPermissions = async () => {
123
- if (Platform.OS === 'android') {
124
- const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA);
125
- setHasPermission(granted === PermissionsAndroid.RESULTS.GRANTED);
126
- } else {
127
- setHasPermission(true);
124
+ try {
125
+ const hasCameraPermission = await PermissionManager.requestPermission('camera');
126
+ setHasPermission(hasCameraPermission);
127
+ } catch (error) {
128
+ setHasPermission(false);
128
129
  }
129
130
  setPermissionsRequested(true);
130
131
  };
@@ -224,7 +225,9 @@ const IdentityDocumentCamera = ({
224
225
  } else {
225
226
  // Guide screen with white background - use dark icons
226
227
  StatusBar.setBarStyle('dark-content', true);
227
- StatusBar.setBackgroundColor('#ffffff', true);
228
+ if (Platform.OS === 'android') {
229
+ StatusBar.setBackgroundColor('#ffffff', true);
230
+ }
228
231
  }
229
232
  }, [hasGuideShown]);
230
233
 
@@ -1483,6 +1486,53 @@ const IdentityDocumentCamera = ({
1483
1486
  const handleCameraError = useCallback(event => {
1484
1487
  console.error('Camera error:', event.nativeEvent.error);
1485
1488
  }, []);
1489
+
1490
+ // Show the guide immediately — the guide is pure UI and does not need
1491
+ // camera access. Permission is requested in the background (useEffect) so
1492
+ // it will usually be resolved by the time the user taps "Let's Go".
1493
+ // This avoids a white/blank screen while the async permission check runs.
1494
+ if (!hasGuideShown) {
1495
+ return /*#__PURE__*/_jsxs(View, {
1496
+ style: StyleSheet.absoluteFill,
1497
+ children: [/*#__PURE__*/_jsx(StatusBar, {
1498
+ barStyle: "dark-content",
1499
+ backgroundColor: "#ffffff",
1500
+ translucent: true
1501
+ }), /*#__PURE__*/_jsxs(SafeAreaView, {
1502
+ style: styles.guide,
1503
+ children: [/*#__PURE__*/_jsx(LottieView, {
1504
+ source: require('../../Shared/Animations/id-or-passport.json'),
1505
+ style: styles.guideAnimation,
1506
+ loop: true,
1507
+ autoPlay: true
1508
+ }), /*#__PURE__*/_jsx(TextView, {
1509
+ style: styles.guideHeader,
1510
+ children: t('identityDocumentCamera.guideHeader')
1511
+ }), /*#__PURE__*/_jsxs(View, {
1512
+ style: styles.guidePoints,
1513
+ children: [/*#__PURE__*/_jsx(TextView, {
1514
+ style: styles.guideText,
1515
+ children: t('identityDocumentCamera.guideText')
1516
+ }), /*#__PURE__*/_jsxs(TextView, {
1517
+ style: styles.guideText,
1518
+ children: ["\u2022 ", t('identityDocumentCamera.guidePoint1')]
1519
+ }), /*#__PURE__*/_jsxs(TextView, {
1520
+ style: styles.guideText,
1521
+ children: ["\u2022 ", t('identityDocumentCamera.guidePoint2')]
1522
+ }), /*#__PURE__*/_jsxs(TextView, {
1523
+ style: styles.guideText,
1524
+ children: ["\u2022 ", t('identityDocumentCamera.guidePoint3')]
1525
+ })]
1526
+ }), /*#__PURE__*/_jsx(StyledButton, {
1527
+ mode: "contained",
1528
+ onPress: () => {
1529
+ setHasGuideShown(true);
1530
+ },
1531
+ children: t('general.letsGo')
1532
+ })]
1533
+ })]
1534
+ });
1535
+ }
1486
1536
  if (!permissionsRequested) {
1487
1537
  return /*#__PURE__*/_jsxs(SafeAreaView, {
1488
1538
  style: styles.permissionContainer,
@@ -1514,42 +1564,10 @@ const IdentityDocumentCamera = ({
1514
1564
  return /*#__PURE__*/_jsxs(View, {
1515
1565
  style: StyleSheet.absoluteFill,
1516
1566
  children: [/*#__PURE__*/_jsx(StatusBar, {
1517
- barStyle: hasGuideShown ? 'light-content' : 'dark-content',
1518
- backgroundColor: hasGuideShown ? 'transparent' : '#ffffff',
1567
+ barStyle: "light-content",
1568
+ backgroundColor: "transparent",
1519
1569
  translucent: true
1520
- }), !hasGuideShown ? /*#__PURE__*/_jsxs(SafeAreaView, {
1521
- style: styles.guide,
1522
- children: [/*#__PURE__*/_jsx(LottieView, {
1523
- source: require('../../Shared/Animations/id-or-passport.json'),
1524
- style: styles.guideAnimation,
1525
- loop: true,
1526
- autoPlay: true
1527
- }), /*#__PURE__*/_jsx(TextView, {
1528
- style: styles.guideHeader,
1529
- children: t('identityDocumentCamera.guideHeader')
1530
- }), /*#__PURE__*/_jsxs(View, {
1531
- style: styles.guidePoints,
1532
- children: [/*#__PURE__*/_jsx(TextView, {
1533
- style: styles.guideText,
1534
- children: t('identityDocumentCamera.guideText')
1535
- }), /*#__PURE__*/_jsxs(TextView, {
1536
- style: styles.guideText,
1537
- children: ["\u2022 ", t('identityDocumentCamera.guidePoint1')]
1538
- }), /*#__PURE__*/_jsxs(TextView, {
1539
- style: styles.guideText,
1540
- children: ["\u2022 ", t('identityDocumentCamera.guidePoint2')]
1541
- }), /*#__PURE__*/_jsxs(TextView, {
1542
- style: styles.guideText,
1543
- children: ["\u2022 ", t('identityDocumentCamera.guidePoint3')]
1544
- })]
1545
- }), /*#__PURE__*/_jsx(StyledButton, {
1546
- mode: "contained",
1547
- onPress: () => {
1548
- setHasGuideShown(true);
1549
- },
1550
- children: t('general.letsGo')
1551
- })]
1552
- }) : /*#__PURE__*/_jsxs(_Fragment, {
1570
+ }), /*#__PURE__*/_jsxs(_Fragment, {
1553
1571
  children: [/*#__PURE__*/_jsx(TrustchexCamera, {
1554
1572
  ref: cameraRef,
1555
1573
  style: StyleSheet.absoluteFill,
@@ -4,6 +4,15 @@ import { Dimensions } from 'react-native';
4
4
  import { SIGNATURE_REGEX, PASSPORT_MRZ_PATTERN } from "./IdentityDocumentCamera.constants.js";
5
5
  import { debugLog, isDebugEnabled } from "../Libs/debug.utils.js";
6
6
 
7
+ /**
8
+ * Checks if a document code represents an ID card (TD1/TD2).
9
+ * Per ICAO 9303, valid ID card codes start with 'I', 'A', or 'C'.
10
+ */
11
+ export function isIDCardDocumentCode(code) {
12
+ if (!code) return false;
13
+ return code.startsWith('I') || code.startsWith('A') || code.startsWith('C');
14
+ }
15
+
7
16
  /**
8
17
  * Frame-to-screen coordinate transform using FILL_CENTER scaling
9
18
  */
@@ -175,8 +184,8 @@ export function detectDocumentType(faces, ocrText, mrzFields, frameWidth, mrzTex
175
184
  });
176
185
  }
177
186
 
178
- // ID Back: no face + ID MRZ
179
- if (faces.length === 0 && mrzFields?.documentCode === 'I') {
187
+ // ID Back: no face + ID MRZ (codes starting with I, A, or C per ICAO 9303)
188
+ if (faces.length === 0 && isIDCardDocumentCode(mrzFields?.documentCode)) {
180
189
  return 'ID_BACK';
181
190
  }
182
191
 
@@ -205,8 +214,8 @@ export function detectDocumentType(faces, ocrText, mrzFields, frameWidth, mrzTex
205
214
  // Filter to card-sized faces only (min 5% of frame width to exclude tiny background faces)
206
215
  const cardSizedFaces = frameWidth ? faces.filter(face => face.bounds.width >= frameWidth * 0.05 && face.bounds.height >= frameWidth * 0.05) : faces;
207
216
  if (cardSizedFaces.length > 0) {
208
- // If we have MRZ code 'I', it's definitely an ID card
209
- if (mrzFields?.documentCode === 'I') {
217
+ // If we have an ID card MRZ code (I, A, or C), it's definitely an ID card
218
+ if (isIDCardDocumentCode(mrzFields?.documentCode)) {
210
219
  return 'ID_FRONT';
211
220
  }
212
221