@trustchex/react-native-sdk 1.495.11 → 1.495.12

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.
@@ -17,6 +17,15 @@ export const REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS = 3;
17
17
  export const SIGNATURE_REGEX = /s[gi]g?n[au]?t[u]?r|imz[a]s?/i;
18
18
  export const SIGNATURE_TEXT_REGEX = /signature|imza|İmza/i;
19
19
  export const MRZ_BLOCK_PATTERN = /[A-Z0-9<]{8,}.*</i;
20
+ // A DENSE MRZ run — a contiguous block of MRZ characters that contains a chevron
21
+ // FILLER pair (`<<`). Real machine-readable zones are padded with `<<…`; a card
22
+ // FACE (ID front) has no MRZ, and the occasional stray single `<` OCR'd from a
23
+ // guilloche/chevron glyph will NOT contain a `<<` filler run. Used to decide
24
+ // "is a real MRZ visible on this front?" without the false positives of
25
+ // MRZ_BLOCK_PATTERN (whose `.*<` lets a lone detached `<` anywhere in the frame
26
+ // match) — that looseness could otherwise wedge a legitimate ID front that
27
+ // happens to OCR a stray `<`.
28
+ export const MRZ_DENSE_PATTERN = /[A-Z0-9<]{6,}<<[A-Z0-9<]*/i;
20
29
  // Matches the line-1 start of ANY ICAO TD3 passport MRZ, so the camera routes
21
30
  // every passport variant into the passport flow (not the ID-card back-side
22
31
  // flow). Per ICAO 9303 Part 4: `P` + a type char (filler `<` or a subtype
@@ -30,7 +30,7 @@
30
30
  * - No face, has MRZ with code 'I'
31
31
  */
32
32
 
33
- import { PASSPORT_MRZ_PATTERN } from "./IdentityDocumentCamera.constants.js";
33
+ import { PASSPORT_MRZ_PATTERN, MRZ_DENSE_PATTERN } from "./IdentityDocumentCamera.constants.js";
34
34
  import { isIDCardDocumentCode, isPassportDocumentCode, documentHasBackSide } from "./IdentityDocumentCamera.utils.js";
35
35
 
36
36
  /**
@@ -142,8 +142,15 @@ export function handleIDFrontFlow(faces, text, mrzText, mrzFields, retryCount) {
142
142
  nextAction: 'REJECT_AS_PASSPORT'
143
143
  };
144
144
  }
145
- if (mrzText && PASSPORT_MRZ_PATTERN.test(mrzText)) {
146
- // Passport MRZ pattern visible even if not fully parsed
145
+
146
+ // Passport MRZ pattern visible even if not fully parsed. Check BOTH the
147
+ // parsed `mrzText` (null until the MRZ validates) AND the raw OCR `text` — a
148
+ // passport's MRZ is dense and frequently OCRs a few frames after its face +
149
+ // signature, so on the accepting frame `mrzText` is often still null while the
150
+ // raw `P<XXX...` run is already visible. Testing only `mrzText` (the old bug)
151
+ // let such a passport pass as an ID front and get routed to a back side that
152
+ // does not exist.
153
+ if (mrzText && PASSPORT_MRZ_PATTERN.test(mrzText) || PASSPORT_MRZ_PATTERN.test(text)) {
147
154
  return {
148
155
  shouldProceed: false,
149
156
  reason: 'Passport MRZ pattern visible - not an ID card',
@@ -163,6 +170,34 @@ export function handleIDFrontFlow(faces, text, mrzText, mrzFields, retryCount) {
163
170
  };
164
171
  }
165
172
 
173
+ // ============================================================================
174
+ // STEP 2b: An ID-card FRONT has NO MRZ (the MRZ is on the back). So if a real
175
+ // MRZ is currently visible on this "front", it is almost certainly a passport
176
+ // data page whose 'P' code we just haven't parsed yet — do NOT accept it as an
177
+ // ID front; wait for the MRZ to resolve (→ a 'P' code routes it to the
178
+ // passport flow). This is the guard that stops a passport (face + signature +
179
+ // not-yet-parsed MRZ) being accepted as ID_FRONT and then getting stuck at
180
+ // SCAN_ID_BACK, which rejects the passport's photo page because it has a face.
181
+ //
182
+ // We use MRZ_DENSE_PATTERN (requires a `<<` filler run), NOT the loose
183
+ // MRZ_BLOCK_PATTERN — a card face has no MRZ, but a stray single `<` OCR'd
184
+ // from a guilloche could match the loose pattern and wedge a real ID front.
185
+ // We ALSO honor the signature retry threshold: a real ID front genuinely has
186
+ // no MRZ, so if a "dense MRZ" keeps matching frame after frame past the
187
+ // threshold it is OCR noise, not a passport — fall through and let STEP 3's
188
+ // face-only acceptance proceed rather than waiting forever.
189
+ const mrzVisibleOnFront = !!mrzText || MRZ_DENSE_PATTERN.test(text);
190
+ const stillWaitingForMrz = retryCount <= SIGNATURE_RETRY_THRESHOLD;
191
+ if (mrzVisibleOnFront && stillWaitingForMrz && !isIDCardDocumentCode(mrzFields?.documentCode)) {
192
+ // An ID-card MRZ code (I/A/C) is allowed to proceed (handled in STEP 4); any
193
+ // other visible MRZ → keep waiting for it to parse and reveal a 'P' code.
194
+ return {
195
+ shouldProceed: false,
196
+ reason: 'MRZ block visible on front - waiting to confirm it is not a passport',
197
+ nextAction: 'WAIT_FOR_MRZ'
198
+ };
199
+ }
200
+
166
201
  // ============================================================================
167
202
  // STEP 3: Check for signature (optional front side confirmation)
168
203
  // ============================================================================
@@ -23,7 +23,7 @@ import { useTheme } from "../Contexts/ThemeContext.js";
23
23
  import DebugOverlay, { TestModePanel } from "./DebugOverlay.js";
24
24
  import { getStatusMessage, getFrameToScreenTransform, transformBoundsToScreen, getScanAreaBounds, angleBetweenPoints, detectDocumentType, determineDocumentTypeToSet, areMRZFieldsEqual, hasRequiredMRZFields, validateFacePosition, isPassportDocumentCode } from "./IdentityDocumentCamera.utils.js";
25
25
  import { handlePassportFlow, handleIDFrontFlow, handleIDBackFlow, getNextStepAfterHologram } from "./IdentityDocumentCamera.flows.js";
26
- import { HOLOGRAM_IMAGE_COUNT, HOLOGRAM_DETECTION_THRESHOLD, HOLOGRAM_DETECTION_RETRY_COUNT, HOLOGRAM_CAPTURE_INTERVAL, HOLOGRAM_MAX_FRAMES_WITHOUT_FACE, MIN_BRIGHTNESS_THRESHOLD, MAX_BRIGHTNESS_THRESHOLD, FACE_EDGE_MARGIN_PERCENT, MAX_CONSECUTIVE_QUALITY_FAILURES, REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS, SIGNATURE_TEXT_REGEX, MRZ_BLOCK_PATTERN, PASSPORT_MRZ_PATTERN, MIN_CARD_FACE_SIZE_PERCENT } from "./IdentityDocumentCamera.constants.js";
26
+ import { HOLOGRAM_IMAGE_COUNT, HOLOGRAM_DETECTION_THRESHOLD, HOLOGRAM_DETECTION_RETRY_COUNT, HOLOGRAM_CAPTURE_INTERVAL, HOLOGRAM_MAX_FRAMES_WITHOUT_FACE, MIN_BRIGHTNESS_THRESHOLD, MAX_BRIGHTNESS_THRESHOLD, FACE_EDGE_MARGIN_PERCENT, MAX_CONSECUTIVE_QUALITY_FAILURES, REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS, SIGNATURE_TEXT_REGEX, MRZ_BLOCK_PATTERN, MIN_CARD_FACE_SIZE_PERCENT } from "./IdentityDocumentCamera.constants.js";
27
27
 
28
28
  // Re-export types for backward compatibility
29
29
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -817,22 +817,38 @@ const IdentityDocumentCamera = ({
817
817
  const hasFaces = primaryFaces.length > 0;
818
818
  const hasSignature = /signature|imza|İmza/i.test(text);
819
819
  const hasPassportMRZ = parsedMRZData?.fields?.documentCode === 'P';
820
- const hasPassportMRZPattern = mrzText && PASSPORT_MRZ_PATTERN.test(mrzText);
821
- if (hasFaces || hasSignature || hasPassportMRZ || hasPassportMRZPattern) {
822
- setStatus('INCORRECT');
823
- return;
824
- }
825
820
 
826
- // Safety: passport somehow reached ID_BACK step
827
- if (detectedDocumentType === 'PASSPORT') {
821
+ // RECOVERY (must run BEFORE the wrong-side guard below): a passport was
822
+ // misrouted to SCAN_ID_BACK. A passport has only its data page, which
823
+ // carries a face + MRZ — exactly what the wrong-side guard treats as
824
+ // "wrong side", so without this it would loop on INCORRECT forever
825
+ // (the reported "stuck asking for ID back" case). If we have positive
826
+ // passport evidence here, the front was already captured, so complete
827
+ // as a passport instead of waiting for a back that does not exist.
828
+ //
829
+ // We deliberately gate ONLY on authoritative signals: a locked PASSPORT
830
+ // classification, or a PARSED MRZ document code of 'P'. We do NOT test
831
+ // the raw OCR `text` against PASSPORT_MRZ_PATTERN here — an ID card's
832
+ // BACK legitimately carries an MRZ whose name line ("PAPPAS<<MARIA…",
833
+ // "…<<PHILIPP…") can false-match the unanchored pattern and would then
834
+ // wrongly complete a real ID card as a passport, skipping the back-side
835
+ // MRZ + barcode validation entirely.
836
+ if (detectedDocumentType === 'PASSPORT' || hasPassportMRZ) {
828
837
  transitionStepWithCallback('COMPLETED', 'SCAN_ID_BACK', {
829
838
  image,
830
839
  documentType: 'PASSPORT',
831
- mrzText: mrzText ?? undefined,
832
- mrzFields: parsedMRZData?.fields
840
+ mrzText: mrzText ?? lastValidMRZText.current ?? undefined,
841
+ mrzFields: parsedMRZData?.fields ?? lastValidMRZFields.current ?? undefined
833
842
  });
834
843
  return;
835
844
  }
845
+
846
+ // Wrong-side detection: a face or signature means we're looking at a
847
+ // front, not an ID-card back (which has neither).
848
+ if (hasFaces || hasSignature) {
849
+ setStatus('INCORRECT');
850
+ return;
851
+ }
836
852
  setElementsOutsideScanArea([]);
837
853
  const flowResult = handleIDBackFlow(mrzText, parsedMRZData?.fields, parsedMRZData?.valid === true, mrzStableAndValid, hasRequiredMRZFields(parsedMRZData?.fields), barcodeToUse?.rawValue, onlyMRZScan);
838
854
  if (!flowResult.shouldProceed) {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  import { Dimensions } from 'react-native';
4
- import { SIGNATURE_REGEX, PASSPORT_MRZ_PATTERN } from "./IdentityDocumentCamera.constants.js";
4
+ import { SIGNATURE_REGEX, PASSPORT_MRZ_PATTERN, MRZ_DENSE_PATTERN } from "./IdentityDocumentCamera.constants.js";
5
5
  import { debugLog, isDebugEnabled } from "../Libs/debug.utils.js";
6
6
 
7
7
  /**
@@ -248,10 +248,17 @@ export function detectDocumentType(faces, ocrText, mrzFields, frameWidth, mrzTex
248
248
  return 'ID_FRONT';
249
249
  }
250
250
 
251
- // If signature present and NO passport MRZ pattern, likely ID_FRONT
252
- // But we can't be 100% sure yet - passport MRZ might appear in next frames
251
+ // If signature present and NO passport MRZ pattern, likely ID_FRONT
252
+ // BUT an ID-card front has no MRZ, so if a real (dense) MRZ is visible here
253
+ // (and it is not a confirmed ID code — that returned above) this is almost
254
+ // certainly a passport data page whose 'P' code just hasn't OCR'd yet.
255
+ // Stay UNKNOWN and let later frames resolve it, rather than locking
256
+ // ID_FRONT and routing a passport to a back side it does not have. We use
257
+ // MRZ_DENSE_PATTERN (needs a `<<` filler run) so a stray single `<` OCR'd
258
+ // from a card-face guilloche cannot wedge a genuine ID front at UNKNOWN.
259
+ const hasUnresolvedMrzBlock = !!mrzText || MRZ_DENSE_PATTERN.test(ocrText);
253
260
  const hasSignature = hasSignatureMatch;
254
- if (hasSignature && !hasPassportMRZPattern) {
261
+ if (hasSignature && !hasPassportMRZPattern && !hasUnresolvedMrzBlock) {
255
262
  return 'ID_FRONT';
256
263
  }
257
264
  }
@@ -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.11';
5
+ export const SDK_VERSION = '1.495.12';
@@ -14,6 +14,7 @@ export declare const REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS = 3;
14
14
  export declare const SIGNATURE_REGEX: RegExp;
15
15
  export declare const SIGNATURE_TEXT_REGEX: RegExp;
16
16
  export declare const MRZ_BLOCK_PATTERN: RegExp;
17
+ export declare const MRZ_DENSE_PATTERN: RegExp;
17
18
  export declare const PASSPORT_MRZ_PATTERN: RegExp;
18
19
  export declare const MIN_SECURITY_FEATURES_REQUIRED = 4;
19
20
  export declare const SECURITY_FEATURE_NAMES: {
@@ -1 +1 @@
1
- {"version":3,"file":"IdentityDocumentCamera.constants.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAC/C,eAAO,MAAM,sCAAsC,IAAI,CAAC;AAExD,eAAO,MAAM,eAAe,QAAkC,CAAC;AAC/D,eAAO,MAAM,oBAAoB,QAAyB,CAAC;AAC3D,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AASrD,eAAO,MAAM,oBAAoB,QAAyC,CAAC;AAI3E,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,sBAAsB;;;;;;;;;;CAUzB,CAAC;AAKX,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAC"}
1
+ {"version":3,"file":"IdentityDocumentCamera.constants.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Components/IdentityDocumentCamera.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AAEnD,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAC/C,eAAO,MAAM,sCAAsC,IAAI,CAAC;AAExD,eAAO,MAAM,eAAe,QAAkC,CAAC;AAC/D,eAAO,MAAM,oBAAoB,QAAyB,CAAC;AAC3D,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AASrD,eAAO,MAAM,iBAAiB,QAA+B,CAAC;AAS9D,eAAO,MAAM,oBAAoB,QAAyC,CAAC;AAI3E,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAChD,eAAO,MAAM,sBAAsB;;;;;;;;;;CAUzB,CAAC;AAKX,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,gCAAgC,MAAM,CAAC;AACpD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,mBAAmB,MAAM,CAAC"}
@@ -1 +1 @@
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"}
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,4CA+mF7B,CAAC;AAiIF,eAAe,sBAAsB,CAAC"}
@@ -37,7 +37,7 @@ export interface PassportFlowResult {
37
37
  export interface IDFrontFlowResult {
38
38
  shouldProceed: boolean;
39
39
  reason?: string;
40
- nextAction?: 'WAIT_FOR_ELEMENTS' | 'REJECT_AS_PASSPORT' | 'PROCEED_TO_HOLOGRAM';
40
+ nextAction?: 'WAIT_FOR_ELEMENTS' | 'WAIT_FOR_MRZ' | 'REJECT_AS_PASSPORT' | 'PROCEED_TO_HOLOGRAM';
41
41
  }
42
42
  export interface IDBackFlowResult {
43
43
  shouldProceed: boolean;
@@ -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,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"}
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;AAiBpD,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,cAAc,GACd,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,CAoHnB;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"}
@@ -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,2 +1,2 @@
1
- export declare const SDK_VERSION = "1.495.11";
1
+ export declare const SDK_VERSION = "1.495.12";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustchex/react-native-sdk",
3
- "version": "1.495.11",
3
+ "version": "1.495.12",
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",
@@ -17,6 +17,15 @@ export const REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS = 3;
17
17
  export const SIGNATURE_REGEX = /s[gi]g?n[au]?t[u]?r|imz[a]s?/i;
18
18
  export const SIGNATURE_TEXT_REGEX = /signature|imza|İmza/i;
19
19
  export const MRZ_BLOCK_PATTERN = /[A-Z0-9<]{8,}.*</i;
20
+ // A DENSE MRZ run — a contiguous block of MRZ characters that contains a chevron
21
+ // FILLER pair (`<<`). Real machine-readable zones are padded with `<<…`; a card
22
+ // FACE (ID front) has no MRZ, and the occasional stray single `<` OCR'd from a
23
+ // guilloche/chevron glyph will NOT contain a `<<` filler run. Used to decide
24
+ // "is a real MRZ visible on this front?" without the false positives of
25
+ // MRZ_BLOCK_PATTERN (whose `.*<` lets a lone detached `<` anywhere in the frame
26
+ // match) — that looseness could otherwise wedge a legitimate ID front that
27
+ // happens to OCR a stray `<`.
28
+ export const MRZ_DENSE_PATTERN = /[A-Z0-9<]{6,}<<[A-Z0-9<]*/i;
20
29
  // Matches the line-1 start of ANY ICAO TD3 passport MRZ, so the camera routes
21
30
  // every passport variant into the passport flow (not the ID-card back-side
22
31
  // flow). Per ICAO 9303 Part 4: `P` + a type char (filler `<` or a subtype
@@ -30,7 +30,10 @@
30
30
 
31
31
  import type { Face, DocumentType } from './IdentityDocumentCamera.types';
32
32
  import type { MRZFields } from '../Types/mrzFields';
33
- import { PASSPORT_MRZ_PATTERN } from './IdentityDocumentCamera.constants';
33
+ import {
34
+ PASSPORT_MRZ_PATTERN,
35
+ MRZ_DENSE_PATTERN,
36
+ } from './IdentityDocumentCamera.constants';
34
37
  import {
35
38
  isIDCardDocumentCode,
36
39
  isPassportDocumentCode,
@@ -58,6 +61,7 @@ export interface IDFrontFlowResult {
58
61
  reason?: string;
59
62
  nextAction?:
60
63
  | 'WAIT_FOR_ELEMENTS'
64
+ | 'WAIT_FOR_MRZ'
61
65
  | 'REJECT_AS_PASSPORT'
62
66
  | 'PROCEED_TO_HOLOGRAM';
63
67
  }
@@ -187,8 +191,17 @@ export function handleIDFrontFlow(
187
191
  };
188
192
  }
189
193
 
190
- if (mrzText && PASSPORT_MRZ_PATTERN.test(mrzText)) {
191
- // Passport MRZ pattern visible even if not fully parsed
194
+ // Passport MRZ pattern visible even if not fully parsed. Check BOTH the
195
+ // parsed `mrzText` (null until the MRZ validates) AND the raw OCR `text` — a
196
+ // passport's MRZ is dense and frequently OCRs a few frames after its face +
197
+ // signature, so on the accepting frame `mrzText` is often still null while the
198
+ // raw `P<XXX...` run is already visible. Testing only `mrzText` (the old bug)
199
+ // let such a passport pass as an ID front and get routed to a back side that
200
+ // does not exist.
201
+ if (
202
+ (mrzText && PASSPORT_MRZ_PATTERN.test(mrzText)) ||
203
+ PASSPORT_MRZ_PATTERN.test(text)
204
+ ) {
192
205
  return {
193
206
  shouldProceed: false,
194
207
  reason: 'Passport MRZ pattern visible - not an ID card',
@@ -208,6 +221,39 @@ export function handleIDFrontFlow(
208
221
  };
209
222
  }
210
223
 
224
+ // ============================================================================
225
+ // STEP 2b: An ID-card FRONT has NO MRZ (the MRZ is on the back). So if a real
226
+ // MRZ is currently visible on this "front", it is almost certainly a passport
227
+ // data page whose 'P' code we just haven't parsed yet — do NOT accept it as an
228
+ // ID front; wait for the MRZ to resolve (→ a 'P' code routes it to the
229
+ // passport flow). This is the guard that stops a passport (face + signature +
230
+ // not-yet-parsed MRZ) being accepted as ID_FRONT and then getting stuck at
231
+ // SCAN_ID_BACK, which rejects the passport's photo page because it has a face.
232
+ //
233
+ // We use MRZ_DENSE_PATTERN (requires a `<<` filler run), NOT the loose
234
+ // MRZ_BLOCK_PATTERN — a card face has no MRZ, but a stray single `<` OCR'd
235
+ // from a guilloche could match the loose pattern and wedge a real ID front.
236
+ // We ALSO honor the signature retry threshold: a real ID front genuinely has
237
+ // no MRZ, so if a "dense MRZ" keeps matching frame after frame past the
238
+ // threshold it is OCR noise, not a passport — fall through and let STEP 3's
239
+ // face-only acceptance proceed rather than waiting forever.
240
+ const mrzVisibleOnFront = !!mrzText || MRZ_DENSE_PATTERN.test(text);
241
+ const stillWaitingForMrz = retryCount <= SIGNATURE_RETRY_THRESHOLD;
242
+ if (
243
+ mrzVisibleOnFront &&
244
+ stillWaitingForMrz &&
245
+ !isIDCardDocumentCode(mrzFields?.documentCode)
246
+ ) {
247
+ // An ID-card MRZ code (I/A/C) is allowed to proceed (handled in STEP 4); any
248
+ // other visible MRZ → keep waiting for it to parse and reveal a 'P' code.
249
+ return {
250
+ shouldProceed: false,
251
+ reason:
252
+ 'MRZ block visible on front - waiting to confirm it is not a passport',
253
+ nextAction: 'WAIT_FOR_MRZ',
254
+ };
255
+ }
256
+
211
257
  // ============================================================================
212
258
  // STEP 3: Check for signature (optional front side confirmation)
213
259
  // ============================================================================
@@ -69,7 +69,6 @@ import {
69
69
  REQUIRED_CONSISTENT_DOCTYPE_DETECTIONS,
70
70
  SIGNATURE_TEXT_REGEX,
71
71
  MRZ_BLOCK_PATTERN,
72
- PASSPORT_MRZ_PATTERN,
73
72
  MIN_CARD_FACE_SIZE_PERCENT,
74
73
  } from './IdentityDocumentCamera.constants';
75
74
  import type {
@@ -1169,7 +1168,9 @@ const IdentityDocumentCamera = ({
1169
1168
  // Surface the reliable-detection signal to the render so the UI can show a
1170
1169
  // green "GO" the moment (and only once) the consensus is stable + valid.
1171
1170
  // Never flips green on an early/transient valid read.
1172
- setMrzReliable((prev) => (prev === mrzStableAndValid ? prev : mrzStableAndValid));
1171
+ setMrzReliable((prev) =>
1172
+ prev === mrzStableAndValid ? prev : mrzStableAndValid
1173
+ );
1173
1174
 
1174
1175
  // Test-mode panel: show the MRZ ONLY once it is stable + valid (the same
1175
1176
  // consensus matched across `stabilityTarget` consecutive frames). Showing
@@ -1179,9 +1180,7 @@ const IdentityDocumentCamera = ({
1179
1180
  // check-digit-valid consensus and never an unstable intermediate.
1180
1181
  if (testMode && mrzStableAndValid && mrzText) {
1181
1182
  setTestModeData((prev) =>
1182
- prev?.mrzText === mrzText
1183
- ? prev
1184
- : { mrzText, timestamp: Date.now() }
1183
+ prev?.mrzText === mrzText ? prev : { mrzText, timestamp: Date.now() }
1185
1184
  );
1186
1185
  }
1187
1186
 
@@ -1194,30 +1193,42 @@ const IdentityDocumentCamera = ({
1194
1193
  const hasFaces = primaryFaces.length > 0;
1195
1194
  const hasSignature = /signature|imza|İmza/i.test(text);
1196
1195
  const hasPassportMRZ = parsedMRZData?.fields?.documentCode === 'P';
1197
- const hasPassportMRZPattern =
1198
- mrzText && PASSPORT_MRZ_PATTERN.test(mrzText);
1199
1196
 
1200
- if (
1201
- hasFaces ||
1202
- hasSignature ||
1203
- hasPassportMRZ ||
1204
- hasPassportMRZPattern
1205
- ) {
1206
- setStatus('INCORRECT');
1207
- return;
1208
- }
1209
-
1210
- // Safety: passport somehow reached ID_BACK step
1211
- if (detectedDocumentType === 'PASSPORT') {
1197
+ // RECOVERY (must run BEFORE the wrong-side guard below): a passport was
1198
+ // misrouted to SCAN_ID_BACK. A passport has only its data page, which
1199
+ // carries a face + MRZ — exactly what the wrong-side guard treats as
1200
+ // "wrong side", so without this it would loop on INCORRECT forever
1201
+ // (the reported "stuck asking for ID back" case). If we have positive
1202
+ // passport evidence here, the front was already captured, so complete
1203
+ // as a passport instead of waiting for a back that does not exist.
1204
+ //
1205
+ // We deliberately gate ONLY on authoritative signals: a locked PASSPORT
1206
+ // classification, or a PARSED MRZ document code of 'P'. We do NOT test
1207
+ // the raw OCR `text` against PASSPORT_MRZ_PATTERN here — an ID card's
1208
+ // BACK legitimately carries an MRZ whose name line ("PAPPAS<<MARIA…",
1209
+ // "…<<PHILIPP…") can false-match the unanchored pattern and would then
1210
+ // wrongly complete a real ID card as a passport, skipping the back-side
1211
+ // MRZ + barcode validation entirely.
1212
+ if (detectedDocumentType === 'PASSPORT' || hasPassportMRZ) {
1212
1213
  transitionStepWithCallback('COMPLETED', 'SCAN_ID_BACK', {
1213
1214
  image,
1214
1215
  documentType: 'PASSPORT',
1215
- mrzText: mrzText ?? undefined,
1216
- mrzFields: parsedMRZData?.fields,
1216
+ mrzText: mrzText ?? lastValidMRZText.current ?? undefined,
1217
+ mrzFields:
1218
+ parsedMRZData?.fields ??
1219
+ lastValidMRZFields.current ??
1220
+ undefined,
1217
1221
  });
1218
1222
  return;
1219
1223
  }
1220
1224
 
1225
+ // Wrong-side detection: a face or signature means we're looking at a
1226
+ // front, not an ID-card back (which has neither).
1227
+ if (hasFaces || hasSignature) {
1228
+ setStatus('INCORRECT');
1229
+ return;
1230
+ }
1231
+
1221
1232
  setElementsOutsideScanArea([]);
1222
1233
 
1223
1234
  const flowResult = handleIDBackFlow(
@@ -2060,7 +2071,8 @@ const IdentityDocumentCamera = ({
2060
2071
  frameH: ds.frameH,
2061
2072
  avgBrightness:
2062
2073
  ds.frames > 0 ? Math.round(ds.brightnessSum / ds.frames) : 0,
2063
- minBrightness: ds.minBrightness === 255 && ds.frames === 0 ? 0 : ds.minBrightness,
2074
+ minBrightness:
2075
+ ds.minBrightness === 255 && ds.frames === 0 ? 0 : ds.minBrightness,
2064
2076
  maxBrightness: ds.maxBrightness,
2065
2077
  hadLowBrightness: ds.hadLowBrightness,
2066
2078
  hadBlurryFrames: ds.hadBlurryFrames,
@@ -2696,8 +2708,7 @@ const IdentityDocumentCamera = ({
2696
2708
  : isBrightnessLow || isFrameBlurry
2697
2709
  ? '#FFC107'
2698
2710
  : 'white',
2699
- borderWidth:
2700
- mrzReliable || status === 'SCANNING' ? 3 : 2,
2711
+ borderWidth: mrzReliable || status === 'SCANNING' ? 3 : 2,
2701
2712
  },
2702
2713
  ]}
2703
2714
  >
@@ -9,6 +9,7 @@ import type { MRZFields } from '../Types/mrzFields';
9
9
  import {
10
10
  SIGNATURE_REGEX,
11
11
  PASSPORT_MRZ_PATTERN,
12
+ MRZ_DENSE_PATTERN,
12
13
  } from './IdentityDocumentCamera.constants';
13
14
  import { debugLog, isDebugEnabled } from '../Libs/debug.utils';
14
15
 
@@ -309,10 +310,18 @@ export function detectDocumentType(
309
310
  return 'ID_FRONT';
310
311
  }
311
312
 
312
- // If signature present and NO passport MRZ pattern, likely ID_FRONT
313
- // But we can't be 100% sure yet - passport MRZ might appear in next frames
313
+ // If signature present and NO passport MRZ pattern, likely ID_FRONT
314
+ // BUT an ID-card front has no MRZ, so if a real (dense) MRZ is visible here
315
+ // (and it is not a confirmed ID code — that returned above) this is almost
316
+ // certainly a passport data page whose 'P' code just hasn't OCR'd yet.
317
+ // Stay UNKNOWN and let later frames resolve it, rather than locking
318
+ // ID_FRONT and routing a passport to a back side it does not have. We use
319
+ // MRZ_DENSE_PATTERN (needs a `<<` filler run) so a stray single `<` OCR'd
320
+ // from a card-face guilloche cannot wedge a genuine ID front at UNKNOWN.
321
+ const hasUnresolvedMrzBlock =
322
+ !!mrzText || MRZ_DENSE_PATTERN.test(ocrText);
314
323
  const hasSignature = hasSignatureMatch;
315
- if (hasSignature && !hasPassportMRZPattern) {
324
+ if (hasSignature && !hasPassportMRZPattern && !hasUnresolvedMrzBlock) {
316
325
  return 'ID_FRONT';
317
326
  }
318
327
  }
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
2
  // Version is synced from package.json during build.
3
- export const SDK_VERSION = '1.495.11';
3
+ export const SDK_VERSION = '1.495.12';