@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.
- package/lib/module/Shared/Components/IdentityDocumentCamera.constants.js +9 -0
- package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +38 -3
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +26 -10
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +11 -4
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts +1 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/Shared/Components/IdentityDocumentCamera.constants.ts +9 -0
- package/src/Shared/Components/IdentityDocumentCamera.flows.ts +49 -3
- package/src/Shared/Components/IdentityDocumentCamera.tsx +35 -24
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +12 -3
- package/src/version.ts +1 -1
|
@@ -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
|
-
|
|
146
|
-
|
|
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,
|
|
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
|
-
//
|
|
827
|
-
|
|
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
|
-
//
|
|
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
|
}
|
package/lib/module/version.js
CHANGED
|
@@ -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":"
|
|
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;
|
|
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":"AACA,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,IAAI,EACL,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;
|
|
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.
|
|
1
|
+
export declare const SDK_VERSION = "1.495.12";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -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 {
|
|
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
|
|
191
|
-
|
|
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) =>
|
|
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
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
//
|
|
1211
|
-
|
|
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:
|
|
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:
|
|
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
|
-
//
|
|
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