@trustchex/react-native-sdk 1.495.9 → 1.495.11

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 (41) hide show
  1. package/android/build.gradle +10 -0
  2. package/android/libs/jj2000-5.2.jar +0 -0
  3. package/android/src/main/java/com/trustchex/reactnativesdk/ImageDecoderModule.kt +88 -0
  4. package/android/src/main/java/com/trustchex/reactnativesdk/Jp2PgxDecoder.kt +135 -0
  5. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKPackage.kt +12 -0
  6. package/android/src/test/java/com/trustchex/reactnativesdk/Jp2PgxDecoderTest.kt +143 -0
  7. package/android/src/test/resources/jp2/gray.jp2 +0 -0
  8. package/android/src/test/resources/jp2/solid_color.j2k +0 -0
  9. package/android/src/test/resources/jp2/solid_color.jp2 +0 -0
  10. package/lib/module/Shared/Components/EIDScanner.js +8 -5
  11. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +19 -16
  12. package/lib/module/Shared/Components/IdentityDocumentCamera.js +43 -4
  13. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +22 -0
  14. package/lib/module/Shared/EIDReader/eidReader.js +11 -9
  15. package/lib/module/Shared/Libs/jp2Decode.js +12 -11
  16. package/lib/module/Shared/Services/DataUploadService.js +16 -4
  17. package/lib/module/version.js +1 -1
  18. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  19. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  20. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  21. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  22. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts +21 -0
  23. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts.map +1 -1
  24. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +15 -0
  25. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  26. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  27. package/lib/typescript/src/Shared/Libs/jp2Decode.d.ts +4 -3
  28. package/lib/typescript/src/Shared/Libs/jp2Decode.d.ts.map +1 -1
  29. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -1
  30. package/lib/typescript/src/version.d.ts +1 -1
  31. package/lib/typescript/src/version.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/Shared/Components/EIDScanner.tsx +7 -5
  34. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +29 -18
  35. package/src/Shared/Components/IdentityDocumentCamera.tsx +48 -3
  36. package/src/Shared/Components/IdentityDocumentCamera.types.ts +21 -0
  37. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +24 -0
  38. package/src/Shared/EIDReader/eidReader.ts +11 -9
  39. package/src/Shared/Libs/jp2Decode.ts +15 -11
  40. package/src/Shared/Services/DataUploadService.ts +41 -8
  41. package/src/version.ts +1 -1
@@ -6,26 +6,27 @@ import { NativeModules, Platform } from 'react-native';
6
6
  * Native JPEG2000 decoder bridge.
7
7
  *
8
8
  * The passport chip portrait (DG2) is frequently stored as JPEG2000
9
- * (image/jp2). React Native's <Image> renders a JP2 data URI on Android (its
10
- * Fresco/Skia pipeline decodes it) but NOT on iOS (UIImage-backed <Image>
11
- * refuses the data URI), so the chip photo showed as a placeholder on iOS.
12
- *
13
- * iOS's ImageIO *can* decode JPEG2000, so the native `ImageDecoderModule`
14
- * round-trips the JP2 to JPEG. We only need this on iOS; on Android the raw
15
- * JP2 already renders, so there is no native counterpart and we no-op.
9
+ * (image/jp2). React Native's <Image> CANNOT render a JP2 data URI on EITHER
10
+ * platform iOS's UIImage-backed <Image> refuses it, and Android's Fresco/Skia
11
+ * pipeline has no JPEG2000 decoder (the chip photo showed as a blank/white
12
+ * placeholder). Both platforms therefore need a native round-trip to JPEG:
13
+ * - iOS: ImageIO decodes JPEG2000 natively.
14
+ * - Android: the bundled pure-Java jj2000 decoder (no ImageIO/NDK).
15
+ * Both are exposed as the `ImageDecoderModule.decodeJp2ToJpeg` native method.
16
16
  */
17
17
 
18
18
  const ImageDecoder = NativeModules.ImageDecoderModule;
19
19
 
20
20
  /**
21
- * Decode a JPEG2000 buffer to JPEG using the native iOS decoder.
21
+ * Decode a JPEG2000 buffer to JPEG using the native decoder (iOS ImageIO /
22
+ * Android jj2000).
22
23
  *
23
24
  * Returns the JPEG bytes + mime type on success, or null when native decode is
24
- * unavailable (Android, or the module isn't linked) or fails — callers should
25
- * fall back to their existing behaviour (keep raw JP2).
25
+ * unavailable (the module isn't linked) or fails — callers should fall back to
26
+ * their existing behaviour (keep raw JP2).
26
27
  */
27
28
  export async function decodeJp2ToJpeg(jp2Buffer) {
28
- if (Platform.OS !== 'ios' || !ImageDecoder?.decodeJp2ToJpeg) {
29
+ if (Platform.OS !== 'ios' && Platform.OS !== 'android' || !ImageDecoder?.decodeJp2ToJpeg) {
29
30
  return null;
30
31
  }
31
32
  try {
@@ -92,7 +92,7 @@ export class DataUploadService {
92
92
  // every entry is deleted in the finally below, whether the upload succeeds
93
93
  // or not.
94
94
  const tempPaths = [];
95
- const writeTempImage = async (fileName, base64Data) => {
95
+ const writeTempImage = async (fileName, base64Data, mimeType = 'image/jpeg') => {
96
96
  const filePath = decodeURIComponent(`${RNFS.TemporaryDirectoryPath}/${fileName}`);
97
97
  await RNFS.writeFile(filePath, base64Data, 'base64');
98
98
  tempPaths.push(filePath);
@@ -100,10 +100,19 @@ export class DataUploadService {
100
100
  name: 'files',
101
101
  filename: fileName,
102
102
  filepath: filePath,
103
- filetype: 'image/jpeg'
103
+ // The filename extension AND this filetype must reflect the REAL format.
104
+ // The backend keys its jp2→jpeg conversion off the .jp2 extension, so a
105
+ // raw JP2 mislabeled as .jpg/image/jpeg reaches face-verification as a
106
+ // corrupt JPEG and is rejected. Pass the actual mimeType for anything
107
+ // that isn't already JPEG (the chip portrait can be jp2/png).
108
+ filetype: mimeType
104
109
  });
105
110
  return filePath;
106
111
  };
112
+
113
+ // Pick a file extension that matches the image's real mime type so the
114
+ // backend handles it correctly (it converts .jp2 → jpeg on receipt).
115
+ const extensionForMime = mimeType => mimeType === 'image/jp2' ? 'jp2' : mimeType === 'image/png' ? 'png' : 'jpg';
107
116
  try {
108
117
  // Add document front image
109
118
  const frontImage = scannedDocument?.frontImage;
@@ -117,10 +126,13 @@ export class DataUploadService {
117
126
  await writeTempImage('DOCUMENT_BACK_IMAGE.jpg', backImage);
118
127
  }
119
128
 
120
- // Add face image from document
129
+ // Add face image from document. The NFC chip portrait (DG2) can be JPEG,
130
+ // PNG (JS-decoded), or — when native JP2 decode was unavailable — raw JP2,
131
+ // so the extension/filetype must track the real mime type, not assume JPEG.
121
132
  const faceImage = scannedDocument?.faceImage;
122
133
  if (faceImage && faceImage !== '') {
123
- await writeTempImage('FACE_IMAGE.jpg', faceImage);
134
+ const faceMime = scannedDocument?.faceImageMimeType || 'image/jpeg';
135
+ await writeTempImage(`FACE_IMAGE.${extensionForMime(faceMime)}`, faceImage, faceMime);
124
136
  }
125
137
 
126
138
  // Add secondary face image from document (optional)
@@ -2,4 +2,4 @@
2
2
 
3
3
  // This file is auto-generated. Do not edit manually.
4
4
  // Version is synced from package.json during build.
5
- export const SDK_VERSION = '1.495.9';
5
+ export const SDK_VERSION = '1.495.11';
@@ -1 +1 @@
1
- {"version":3,"file":"EIDScanner.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/EIDScanner.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAwBpD,UAAU,eAAe;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CACd,OAAO,EAAE,SAAS,EAClB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,KACtB,IAAI,CAAC;IACV,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED,QAAA,MAAM,UAAU,GAAI,6FAOjB,eAAe,4CA4yBjB,CAAC;AA4NF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"EIDScanner.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/EIDScanner.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAwBpD,UAAU,eAAe;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,CACd,OAAO,EAAE,SAAS,EAClB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,KACtB,IAAI,CAAC;IACV,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAED,QAAA,MAAM,UAAU,GAAI,6FAOjB,eAAe,4CA8yBjB,CAAC;AA4NF,eAAe,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.tsx"],"names":[],"mappings":"AAyEA,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,2BAA2B,EAK5B,MAAM,gCAAgC,CAAC;AAGxC,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,2BAA2B,EAAE,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAMnE,QAAA,MAAM,sBAAsB,GAAI,uDAI7B,2BAA2B,4CAujF7B,CAAC;AAiIF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"IdentityDocumentCamera.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.tsx"],"names":[],"mappings":"AA0EA,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,2BAA2B,EAK5B,MAAM,gCAAgC,CAAC;AAGxC,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,2BAA2B,EAAE,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAMnE,QAAA,MAAM,sBAAsB,GAAI,uDAI7B,2BAA2B,4CAmmF7B,CAAC;AAiIF,eAAe,sBAAsB,CAAC"}
@@ -105,5 +105,5 @@ export declare function handleIDBackFlow(mrzText: string | null, mrzFields: MRZF
105
105
  * self-corrects: SCAN_ID_BACK simply won't find an ID-back MRZ/barcode and the
106
106
  * user can cancel, which is far better than skipping the back of every real ID.
107
107
  */
108
- export declare function getNextStepAfterHologram(detectedDocumentType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN', currentFrameDocType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN', mrzDocCode: string | undefined): 'COMPLETED' | 'SCAN_ID_BACK';
108
+ export declare function getNextStepAfterHologram(detectedDocumentType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN', currentFrameDocType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN', mrzDocCode: string | undefined, sawPassportSignal?: boolean): 'COMPLETED' | 'SCAN_ID_BACK';
109
109
  //# sourceMappingURL=IdentityDocumentCamera.flows.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.flows.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.flows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAUpD,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EACP,mBAAmB,GACnB,cAAc,GACd,qBAAqB,GACrB,sBAAsB,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EACP,mBAAmB,GACnB,oBAAoB,GACpB,qBAAqB,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,cAAc,GAAG,kBAAkB,GAAG,sBAAsB,CAAC;CAC3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,iBAAiB,EAAE,OAAO,EAC1B,WAAW,EAAE,OAAO,EACpB,iBAAiB,EAAE,OAAO,EAC1B,YAAY,EAAE,OAAO,GACpB,kBAAkB,CAgEpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,UAAU,EAAE,MAAM,GACjB,iBAAiB,CA0EnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,QAAQ,EAAE,OAAO,EACjB,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,EAAE,OAAO,GACnB,gBAAgB,CA2DlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,wBAAwB,CACtC,oBAAoB,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,EACrE,mBAAmB,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,EACpE,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,WAAW,GAAG,cAAc,CAsB9B"}
1
+ {"version":3,"file":"IdentityDocumentCamera.flows.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.flows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAgB,MAAM,gCAAgC,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAcpD,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EACP,mBAAmB,GACnB,cAAc,GACd,qBAAqB,GACrB,sBAAsB,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EACP,mBAAmB,GACnB,oBAAoB,GACpB,qBAAqB,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,cAAc,GAAG,kBAAkB,GAAG,sBAAsB,CAAC;CAC3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,iBAAiB,EAAE,OAAO,EAC1B,WAAW,EAAE,OAAO,EACpB,iBAAiB,EAAE,OAAO,EAC1B,YAAY,EAAE,OAAO,GACpB,kBAAkB,CAgEpB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,UAAU,EAAE,MAAM,GACjB,iBAAiB,CA0EnB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,QAAQ,EAAE,OAAO,EACjB,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,WAAW,EAAE,OAAO,GACnB,gBAAgB,CA2DlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,wBAAwB,CACtC,oBAAoB,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,EACrE,mBAAmB,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,EACpE,UAAU,EAAE,MAAM,GAAG,SAAS,EAM9B,iBAAiB,GAAE,OAAe,GACjC,WAAW,GAAG,cAAc,CAuB9B"}
@@ -102,6 +102,27 @@ export interface Barcode {
102
102
  import type { MRZFields } from '../Types/mrzFields';
103
103
  export type ScanStep = 'SCAN_ID_FRONT_OR_PASSPORT' | 'SCAN_ID_BACK' | 'SCAN_HOLOGRAM' | 'COMPLETED';
104
104
  export type ScanStatus = 'SEARCHING' | 'SCANNING' | 'SCANNED' | 'INCORRECT';
105
+ /**
106
+ * What the camera has classified the document in view as.
107
+ *
108
+ * The single most important property of a document type is whether it has a
109
+ * BACK SIDE that still needs scanning — that is what drives the scan flow:
110
+ *
111
+ * PASSPORT — ICAO TD3 booklet. The MRZ, photo and all data live on ONE page
112
+ * (the data page). There is NO second machine-readable side, so a
113
+ * passport must NEVER be routed to SCAN_ID_BACK. Detected by the
114
+ * TD3 MRZ document code 'P' / the passport MRZ pattern.
115
+ * ID_FRONT — the photo/front side of a TD1/TD2 ID card. The front carries the
116
+ * photo + signature but usually NO MRZ (the MRZ is on the back),
117
+ * so the front alone is NOT enough — it MUST be followed by a back
118
+ * scan (SCAN_ID_BACK).
119
+ * ID_BACK — the back side of a TD1/TD2 ID card. Carries the MRZ (document
120
+ * code 'I'/'A'/'C') and usually a barcode. Terminal side.
121
+ * UNKNOWN — not yet classified, or signals conflict.
122
+ *
123
+ * Use `documentHasBackSide()` / `isPassportDocumentCode()` rather than comparing
124
+ * these strings ad-hoc, so the "is this a passport?" rule lives in one place.
125
+ */
105
126
  export type DocumentType = 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN';
106
127
  export type CompletedStep = 'SCAN_ID_FRONT_OR_PASSPORT' | 'SCAN_ID_BACK' | 'SCAN_HOLOGRAM' | null;
107
128
  export type BoundsWithRotation = {
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.types.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,YAAY,GACZ,SAAS,GACT,UAAU,GACV,eAAe,GACf,WAAW,GACX,eAAe,GACf,KAAK,GACL,cAAc,GACd,WAAW,CAAC;AAEhB,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB,wBAAwB,CAAC,EAAE,eAAe,EAAE,CAAC;IAC7C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,SAAS,CAAC;IACtB,iBAAiB,EAAE,gBAAgB,CAAC;IACpC,KAAK,EAAE,SAAS,CAAC;IACjB,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1D,MAAM,MAAM,SAAS,GAAG;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,gBAAgB,EAAE,gBAAgB;IAClC,QAAQ,EAAE,YAAY;IACtB,SAAS,EAAE,SAAS;IACpB,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE;IAC5B,QAAQ,EAAE,MAAM;CACjB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,mBAAmB,EAAE,gBAAgB;IACrC,YAAY,EAAE,SAAS;IACvB,WAAW,EAAE,MAAM;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EACR,gBAAgB,GAChB,UAAU,GACV,oBAAoB,GACpB,eAAe,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,OAAO,CAAC;IACrB,yBAAyB,EAAE,CAAC,WAAW,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACtE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,YAAY,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,MAAM,QAAQ,GAChB,2BAA2B,GAC3B,cAAc,GACd,eAAe,GACf,WAAW,CAAC;AAEhB,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,CAAC;AAE5E,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3E,MAAM,MAAM,aAAa,GACrB,2BAA2B,GAC3B,cAAc,GACd,eAAe,GACf,IAAI,CAAC;AAET,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3C,CAAC"}
1
+ {"version":3,"file":"IdentityDocumentCamera.types.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.types.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,21 @@ import type { MRZFields } from '../Types/mrzFields';
5
5
  * Per ICAO 9303, valid ID card codes start with 'I', 'A', or 'C'.
6
6
  */
7
7
  export declare function isIDCardDocumentCode(code: string | null | undefined): boolean;
8
+ /**
9
+ * Checks if a document code represents a passport (ICAO TD3).
10
+ * Per ICAO 9303 Part 4, the first character of a passport MRZ document code is
11
+ * 'P' — for the ordinary `P<`, the German `PP`, and the diplomatic/service/
12
+ * official `PD`/`PS`/`PO` variants alike.
13
+ */
14
+ export declare function isPassportDocumentCode(code: string | null | undefined): boolean;
15
+ /**
16
+ * Single source of truth for "does this document have a second side left to
17
+ * scan?". A passport's data all lives on one page, so it has no back; an ID
18
+ * card's MRZ lives on the back, so an ID front always does. Used by the
19
+ * post-front / post-hologram routing so the "ask for the back side?" decision
20
+ * is never re-derived ad-hoc (the bug where passports were sent to SCAN_ID_BACK).
21
+ */
22
+ export declare function documentHasBackSide(documentType: DocumentType): boolean;
8
23
  /**
9
24
  * Frame-to-screen coordinate transform using FILL_CENTER scaling
10
25
  */
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.utils.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"IdentityDocumentCamera.utils.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.utils.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"eidReader.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/EIDReader/eidReader.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAyY7C,QAAA,MAAM,SAAS,GACb,gBAAgB,MAAM,EACtB,aAAa,MAAM,EACnB,cAAc,MAAM,EACpB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,KAC5C,OAAO,CACN;IACE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD,SAAS,CA+XZ,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"eidReader.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/EIDReader/eidReader.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AA2Y7C,QAAA,MAAM,SAAS,GACb,gBAAgB,MAAM,EACtB,aAAa,MAAM,EACnB,cAAc,MAAM,EACpB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,KAC5C,OAAO,CACN;IACE,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD,SAAS,CA+XZ,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -1,10 +1,11 @@
1
1
  import { Buffer } from 'buffer';
2
2
  /**
3
- * Decode a JPEG2000 buffer to JPEG using the native iOS decoder.
3
+ * Decode a JPEG2000 buffer to JPEG using the native decoder (iOS ImageIO /
4
+ * Android jj2000).
4
5
  *
5
6
  * Returns the JPEG bytes + mime type on success, or null when native decode is
6
- * unavailable (Android, or the module isn't linked) or fails — callers should
7
- * fall back to their existing behaviour (keep raw JP2).
7
+ * unavailable (the module isn't linked) or fails — callers should fall back to
8
+ * their existing behaviour (keep raw JP2).
8
9
  */
9
10
  export declare function decodeJp2ToJpeg(jp2Buffer: Buffer): Promise<{
10
11
  base64: string;
@@ -1 +1 @@
1
- {"version":3,"file":"jp2Decode.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/jp2Decode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAsBhC;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAetD"}
1
+ {"version":3,"file":"jp2Decode.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/jp2Decode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAsBhC;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAkBtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"DataUploadService.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Services/DataUploadService.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAQhE;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,MAAM;YAKb,0BAA0B;IAexC;;OAEG;IACG,kBAAkB,CACtB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,uBAAuB,EACxC,UAAU,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,IAAI,CAAC;IA2DhB;;OAEG;IACG,WAAW,CACf,gBAAgB,EAAE,MAAM,EACxB,eAAe,CAAC,EAAE,uBAAuB,EACzC,iBAAiB,CAAC,EAAE,iBAAiB,EACrC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,IAAI,CAAC;IA0JhB;;OAEG;IACG,mBAAmB,CACvB,kBAAkB,EAAE,kBAAkB,EACtC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACvC,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,OAAO,CAAC;IAkGnB,OAAO,CAAC,iBAAiB;CAY1B"}
1
+ {"version":3,"file":"DataUploadService.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Services/DataUploadService.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAQhE;;;GAGG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,MAAM;YAKb,0BAA0B;IAexC;;OAEG;IACG,kBAAkB,CACtB,gBAAgB,EAAE,MAAM,EACxB,eAAe,EAAE,uBAAuB,EACxC,UAAU,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,IAAI,CAAC;IA8DhB;;OAEG;IACG,WAAW,CACf,gBAAgB,EAAE,MAAM,EACxB,eAAe,CAAC,EAAE,uBAAuB,EACzC,iBAAiB,CAAC,EAAE,iBAAiB,EACrC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,IAAI,CAAC;IAmLhB;;OAEG;IACG,mBAAmB,CACvB,kBAAkB,EAAE,kBAAkB,EACtC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACvC,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,OAAO,CAAC;IAuGnB,OAAO,CAAC,iBAAiB;CAY1B"}
@@ -1,2 +1,2 @@
1
- export declare const SDK_VERSION = "1.495.9";
1
+ export declare const SDK_VERSION = "1.495.11";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,YAAY,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustchex/react-native-sdk",
3
- "version": "1.495.9",
3
+ "version": "1.495.11",
4
4
  "description": "Trustchex mobile app react native SDK for android or ios devices",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -643,12 +643,14 @@ const EIDScanner = ({
643
643
  <View style={styles.idCardPhotoContainer}>
644
644
  <View style={styles.idCardPhotoFrame}>
645
645
  {documentFaceImage &&
646
+ // The chip portrait (DG2) is JPEG2000, which RN's <Image>
647
+ // can't render on either platform — the native decoder
648
+ // (iOS ImageIO / Android jj2000) converts it to JPEG (or
649
+ // the small-image JS fallback to PNG) before this point.
650
+ // A raw image/jp2 would render as a blank box, so we only
651
+ // accept the decoded formats and otherwise show '?'.
646
652
  (documentFaceImageMimeType === 'image/jpeg' ||
647
- documentFaceImageMimeType === 'image/png' ||
648
- // Android's <Image> decodes a raw JP2 data URI; on iOS
649
- // the chip JP2 is converted to JPEG before this point
650
- // (native ImageIO), so iOS never reaches here as jp2.
651
- documentFaceImageMimeType === 'image/jp2') ? (
653
+ documentFaceImageMimeType === 'image/png') ? (
652
654
  <Image
653
655
  source={{
654
656
  uri: `data:${documentFaceImageMimeType};base64,${documentFaceImage}`,
@@ -28,10 +28,14 @@
28
28
  * - No face, has MRZ with code 'I'
29
29
  */
30
30
 
31
- import type { Face } from './IdentityDocumentCamera.types';
31
+ import type { Face, DocumentType } from './IdentityDocumentCamera.types';
32
32
  import type { MRZFields } from '../Types/mrzFields';
33
33
  import { PASSPORT_MRZ_PATTERN } from './IdentityDocumentCamera.constants';
34
- import { isIDCardDocumentCode } from './IdentityDocumentCamera.utils';
34
+ import {
35
+ isIDCardDocumentCode,
36
+ isPassportDocumentCode,
37
+ documentHasBackSide,
38
+ } from './IdentityDocumentCamera.utils';
35
39
 
36
40
  /**
37
41
  * After this many retry attempts for ID_FRONT flow, proceed with face detection alone
@@ -350,27 +354,34 @@ export function handleIDBackFlow(
350
354
  export function getNextStepAfterHologram(
351
355
  detectedDocumentType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN',
352
356
  currentFrameDocType: 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN',
353
- mrzDocCode: string | undefined
357
+ mrzDocCode: string | undefined,
358
+ // True if a passport MRZ pattern / 'P' code was seen in ANY front frame this
359
+ // session — not just the accepting one. The passport MRZ is dense and often
360
+ // OCRs a few frames AFTER the face + signature, so a passport can get locked
361
+ // as ID_FRONT before its MRZ is read. Latching the signal across frames is
362
+ // what stops that passport from being wrongly routed to SCAN_ID_BACK here.
363
+ sawPassportSignal: boolean = false
354
364
  ): 'COMPLETED' | 'SCAN_ID_BACK' {
355
- // Any positive passport signal done (no back side).
365
+ // Resolve to a single document type, passport-biased: any positive passport
366
+ // signal wins (a passport must never wait for a back that does not exist),
367
+ // then positive ID-card evidence (a non-'P' MRZ code, or an ID_FRONT
368
+ // classification — an ID front legitimately has no MRZ, so the classification
369
+ // is the only signal we get), else UNKNOWN.
356
370
  const isPassport =
371
+ sawPassportSignal ||
357
372
  detectedDocumentType === 'PASSPORT' ||
358
373
  currentFrameDocType === 'PASSPORT' ||
359
- mrzDocCode === 'P';
360
-
361
- if (isPassport) {
362
- return 'COMPLETED';
363
- }
364
-
365
- // Positive ID-card evidence → scan the back. Either a non-'P' MRZ code, or a
366
- // front classified as ID_FRONT (an ID front legitimately has no MRZ, so the
367
- // classification is the only signal we get).
368
- const hasIdCardMrzCode = !!mrzDocCode && mrzDocCode !== 'P';
374
+ isPassportDocumentCode(mrzDocCode);
375
+ const hasIdCardMrzCode = !!mrzDocCode && !isPassportDocumentCode(mrzDocCode);
369
376
  const isIdFront =
370
377
  detectedDocumentType === 'ID_FRONT' || currentFrameDocType === 'ID_FRONT';
371
- if (hasIdCardMrzCode || isIdFront) {
372
- return 'SCAN_ID_BACK';
373
- }
374
378
 
375
- return 'COMPLETED';
379
+ const resolved: DocumentType = isPassport
380
+ ? 'PASSPORT'
381
+ : hasIdCardMrzCode || isIdFront
382
+ ? 'ID_FRONT'
383
+ : 'UNKNOWN';
384
+
385
+ // Single source of truth for "is there a back side left to scan?".
386
+ return documentHasBackSide(resolved) ? 'SCAN_ID_BACK' : 'COMPLETED';
376
387
  }
@@ -47,6 +47,7 @@ import {
47
47
  areMRZFieldsEqual,
48
48
  hasRequiredMRZFields,
49
49
  validateFacePosition,
50
+ isPassportDocumentCode,
50
51
  } from './IdentityDocumentCamera.utils';
51
52
  import {
52
53
  handlePassportFlow,
@@ -178,6 +179,15 @@ const IdentityDocumentCamera = ({
178
179
  >('UNKNOWN');
179
180
  const consistentDocTypeCount = useRef(0);
180
181
 
182
+ // Latches true the moment a passport MRZ pattern / 'P' document code is seen in
183
+ // ANY front frame this session. The passport MRZ is dense and often OCRs a few
184
+ // frames AFTER the face + signature, so without this a passport can be locked
185
+ // as ID_FRONT (which has no MRZ on its front) and then wrongly asked for a back
186
+ // side that does not exist. Once latched, the front is never accepted as
187
+ // ID_FRONT and the post-hologram routing always completes instead of scanning
188
+ // a back. Reset only when a brand-new scan session starts.
189
+ const sawPassportSignal = useRef(false);
190
+
181
191
  // Frame quality tracking - persist across callbacks
182
192
  const lastFrameQuality = useRef({
183
193
  hasAcceptableQuality: true,
@@ -325,6 +335,7 @@ const IdentityDocumentCamera = ({
325
335
  lastValidMRZText.current = null;
326
336
  lastValidMRZFields.current = null;
327
337
  validMRZConsecutiveCount.current = 0;
338
+ sawPassportSignal.current = false;
328
339
  mrzAggregator.current.reset();
329
340
  setMrzReliable(false);
330
341
  // Start a fresh diagnostics session for the support report.
@@ -666,6 +677,15 @@ const IdentityDocumentCamera = ({
666
677
 
667
678
  // Reset MRZ retry counter for each new step so retries start fresh
668
679
  mrzDetectionCurrentRetryCount.current = 0;
680
+ // A transition back to the initial front step means a brand-new document
681
+ // scan is starting, so the sticky passport signal from any previous
682
+ // document must NOT carry over — otherwise an ID card scanned after a
683
+ // passport (without the camera unfocusing) would be forced into the
684
+ // passport flow and skip its back side. (The focus-based reset above only
685
+ // fires when the camera goes inactive.)
686
+ if (nextStepType === 'SCAN_ID_FRONT_OR_PASSPORT') {
687
+ sawPassportSignal.current = false;
688
+ }
669
689
  // Only clear MRZ text when entering SCAN_ID_BACK (new MRZ expected).
670
690
  // Preserve across SCAN_HOLOGRAM so passport completion has MRZ data.
671
691
  if (nextStepType === 'SCAN_ID_BACK') {
@@ -1764,7 +1784,8 @@ const IdentityDocumentCamera = ({
1764
1784
  const nextStepAfterHologram = getNextStepAfterHologram(
1765
1785
  detectedDocumentType,
1766
1786
  documentType,
1767
- mrzDocCode
1787
+ mrzDocCode,
1788
+ sawPassportSignal.current
1768
1789
  );
1769
1790
 
1770
1791
  transitionStepWithCallback(
@@ -1783,11 +1804,35 @@ const IdentityDocumentCamera = ({
1783
1804
  // INITIAL SCAN STEP - Detect document type and validate
1784
1805
  // ============================================================================
1785
1806
  if (nextStep === 'SCAN_ID_FRONT_OR_PASSPORT') {
1807
+ // Latch ANY passport signal seen in a front frame — the parsed 'P' code
1808
+ // OR the raw passport MRZ pattern (which survives even when full MRZ
1809
+ // parsing fails). The passport MRZ often OCRs a few frames after the
1810
+ // face + signature, so we remember it across frames; once latched, this
1811
+ // front can never be accepted as an ID card / asked for a back side.
1812
+ // Only AUTHORITATIVE passport signals latch — a parsed MRZ document code
1813
+ // of 'P', or a settled PASSPORT classification. We deliberately do NOT
1814
+ // latch on a raw PASSPORT_MRZ_PATTERN substring match against OCR text:
1815
+ // that loose test can fire on ID-card MRZ noise, and because the latch is
1816
+ // sticky and one-directional a single false match would force the
1817
+ // passport flow for the rest of the session and silently skip the ID
1818
+ // card's back side. The parsed 'P' code still covers the lagging-MRZ case
1819
+ // (it just requires the MRZ to have actually parsed first).
1820
+ if (
1821
+ isPassportDocumentCode(parsedMRZData?.fields?.documentCode) ||
1822
+ documentType === 'PASSPORT' ||
1823
+ detectedDocumentType === 'PASSPORT'
1824
+ ) {
1825
+ sawPassportSignal.current = true;
1826
+ }
1827
+
1786
1828
  // Determine which flow handler to use.
1787
1829
  // Current-frame passport detection always takes precedence over a locked
1788
- // ID_FRONT — passport MRZ may not appear until later frames.
1830
+ // ID_FRONT — passport MRZ may not appear until later frames. A latched
1831
+ // passport signal also forces the passport flow, so a passport that was
1832
+ // momentarily locked as ID_FRONT (before its MRZ was read) is corrected
1833
+ // instead of being driven into the ID-card back-side flow.
1789
1834
  const flowDocumentType =
1790
- documentType === 'PASSPORT'
1835
+ documentType === 'PASSPORT' || sawPassportSignal.current
1791
1836
  ? 'PASSPORT'
1792
1837
  : detectedDocumentType !== 'UNKNOWN'
1793
1838
  ? detectedDocumentType
@@ -115,6 +115,27 @@ export type ScanStep =
115
115
 
116
116
  export type ScanStatus = 'SEARCHING' | 'SCANNING' | 'SCANNED' | 'INCORRECT';
117
117
 
118
+ /**
119
+ * What the camera has classified the document in view as.
120
+ *
121
+ * The single most important property of a document type is whether it has a
122
+ * BACK SIDE that still needs scanning — that is what drives the scan flow:
123
+ *
124
+ * PASSPORT — ICAO TD3 booklet. The MRZ, photo and all data live on ONE page
125
+ * (the data page). There is NO second machine-readable side, so a
126
+ * passport must NEVER be routed to SCAN_ID_BACK. Detected by the
127
+ * TD3 MRZ document code 'P' / the passport MRZ pattern.
128
+ * ID_FRONT — the photo/front side of a TD1/TD2 ID card. The front carries the
129
+ * photo + signature but usually NO MRZ (the MRZ is on the back),
130
+ * so the front alone is NOT enough — it MUST be followed by a back
131
+ * scan (SCAN_ID_BACK).
132
+ * ID_BACK — the back side of a TD1/TD2 ID card. Carries the MRZ (document
133
+ * code 'I'/'A'/'C') and usually a barcode. Terminal side.
134
+ * UNKNOWN — not yet classified, or signals conflict.
135
+ *
136
+ * Use `documentHasBackSide()` / `isPassportDocumentCode()` rather than comparing
137
+ * these strings ad-hoc, so the "is this a passport?" rule lives in one place.
138
+ */
118
139
  export type DocumentType = 'ID_FRONT' | 'ID_BACK' | 'PASSPORT' | 'UNKNOWN';
119
140
 
120
141
  export type CompletedStep =
@@ -21,6 +21,30 @@ export function isIDCardDocumentCode(code: string | null | undefined): boolean {
21
21
  return code.startsWith('I') || code.startsWith('A') || code.startsWith('C');
22
22
  }
23
23
 
24
+ /**
25
+ * Checks if a document code represents a passport (ICAO TD3).
26
+ * Per ICAO 9303 Part 4, the first character of a passport MRZ document code is
27
+ * 'P' — for the ordinary `P<`, the German `PP`, and the diplomatic/service/
28
+ * official `PD`/`PS`/`PO` variants alike.
29
+ */
30
+ export function isPassportDocumentCode(
31
+ code: string | null | undefined
32
+ ): boolean {
33
+ if (!code) return false;
34
+ return code.startsWith('P');
35
+ }
36
+
37
+ /**
38
+ * Single source of truth for "does this document have a second side left to
39
+ * scan?". A passport's data all lives on one page, so it has no back; an ID
40
+ * card's MRZ lives on the back, so an ID front always does. Used by the
41
+ * post-front / post-hologram routing so the "ask for the back side?" decision
42
+ * is never re-derived ad-hoc (the bug where passports were sent to SCAN_ID_BACK).
43
+ */
44
+ export function documentHasBackSide(documentType: DocumentType): boolean {
45
+ return documentType === 'ID_FRONT';
46
+ }
47
+
24
48
  /**
25
49
  * Frame-to-screen coordinate transform using FILL_CENTER scaling
26
50
  */
@@ -248,10 +248,11 @@ async function convertJP2IfNeeded(
248
248
  return { base64: imageBuffer.toString('base64'), mimeType };
249
249
  }
250
250
 
251
- // Prefer the native decoder (iOS ImageIO). It decodes JPEG2000 of any size
252
- // without blocking the JS thread, so it both fixes the iOS preview (RN's
253
- // <Image> can't render a raw image/jp2 data URI) and avoids the slow pure-JS
254
- // decoder entirely. Returns null on Android / when unavailable → fall through.
251
+ // Prefer the native decoder (iOS ImageIO / Android jj2000). It decodes
252
+ // JPEG2000 of any size off the JS thread, so it fixes the on-screen preview on
253
+ // BOTH platforms (RN's <Image> can't render a raw image/jp2 data URI on either)
254
+ // and avoids the slow pure-JS decoder entirely. Returns null only when the
255
+ // native module is unavailable/unlinked or it fails → fall through.
255
256
  const native = await decodeJp2ToJpeg(imageBuffer);
256
257
  if (native) {
257
258
  debugLog(
@@ -261,11 +262,12 @@ async function convertJP2IfNeeded(
261
262
  return native;
262
263
  }
263
264
 
264
- // Too large to decode synchronously without freezing the UI keep the raw
265
- // JP2. The face image is still captured (the verification backend / face-match
266
- // accept JPEG2000); only the on-screen preview can't render it. This lets the
267
- // NFC read COMPLETE instead of hanging. (On Android the raw JP2 still renders
268
- // in <Image>; on iOS the native path above normally handles it.)
265
+ // Native decode unavailable AND too large to decode synchronously in JS
266
+ // without freezing the UI — keep the raw JP2. The face image is still captured
267
+ // (the verification backend / face-match accept JPEG2000); only the on-screen
268
+ // preview can't render it. This lets the NFC read COMPLETE instead of hanging.
269
+ // In normal operation the native path above handles this on both platforms; we
270
+ // only reach here if the native module failed or isn't linked.
269
271
  if (imageBuffer.length > MAX_JP2_DECODE_BYTES) {
270
272
  debugLog(
271
273
  'EID',
@@ -5,13 +5,13 @@ import { Buffer } from 'buffer';
5
5
  * Native JPEG2000 decoder bridge.
6
6
  *
7
7
  * The passport chip portrait (DG2) is frequently stored as JPEG2000
8
- * (image/jp2). React Native's <Image> renders a JP2 data URI on Android (its
9
- * Fresco/Skia pipeline decodes it) but NOT on iOS (UIImage-backed <Image>
10
- * refuses the data URI), so the chip photo showed as a placeholder on iOS.
11
- *
12
- * iOS's ImageIO *can* decode JPEG2000, so the native `ImageDecoderModule`
13
- * round-trips the JP2 to JPEG. We only need this on iOS; on Android the raw
14
- * JP2 already renders, so there is no native counterpart and we no-op.
8
+ * (image/jp2). React Native's <Image> CANNOT render a JP2 data URI on EITHER
9
+ * platform iOS's UIImage-backed <Image> refuses it, and Android's Fresco/Skia
10
+ * pipeline has no JPEG2000 decoder (the chip photo showed as a blank/white
11
+ * placeholder). Both platforms therefore need a native round-trip to JPEG:
12
+ * - iOS: ImageIO decodes JPEG2000 natively.
13
+ * - Android: the bundled pure-Java jj2000 decoder (no ImageIO/NDK).
14
+ * Both are exposed as the `ImageDecoderModule.decodeJp2ToJpeg` native method.
15
15
  */
16
16
  interface ImageDecoderNativeModule {
17
17
  decodeJp2ToJpeg(base64Jp2: string): Promise<string>;
@@ -22,16 +22,20 @@ const ImageDecoder = (
22
22
  ).ImageDecoderModule;
23
23
 
24
24
  /**
25
- * Decode a JPEG2000 buffer to JPEG using the native iOS decoder.
25
+ * Decode a JPEG2000 buffer to JPEG using the native decoder (iOS ImageIO /
26
+ * Android jj2000).
26
27
  *
27
28
  * Returns the JPEG bytes + mime type on success, or null when native decode is
28
- * unavailable (Android, or the module isn't linked) or fails — callers should
29
- * fall back to their existing behaviour (keep raw JP2).
29
+ * unavailable (the module isn't linked) or fails — callers should fall back to
30
+ * their existing behaviour (keep raw JP2).
30
31
  */
31
32
  export async function decodeJp2ToJpeg(
32
33
  jp2Buffer: Buffer
33
34
  ): Promise<{ base64: string; mimeType: string } | null> {
34
- if (Platform.OS !== 'ios' || !ImageDecoder?.decodeJp2ToJpeg) {
35
+ if (
36
+ (Platform.OS !== 'ios' && Platform.OS !== 'android') ||
37
+ !ImageDecoder?.decodeJp2ToJpeg
38
+ ) {
35
39
  return null;
36
40
  }
37
41
  try {