@trustchex/react-native-sdk 1.500.1 → 1.501.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/trustchex/reactnativesdk/GeolocationModule.kt +159 -0
- package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKPackage.kt +12 -0
- package/ios/Permission/CoarseLocationProvider.swift +111 -0
- package/ios/Permission/PermissionModule.m +4 -0
- package/ios/Permission/PermissionModule.swift +21 -1
- package/lib/module/Screens/Dynamic/AddressCaptureScreen.js +230 -0
- package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +12 -0
- package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +33 -10
- package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +8 -10
- package/lib/module/Screens/Static/ResultScreen.js +74 -3
- package/lib/module/Shared/Components/EIDScanner.js +59 -3
- package/lib/module/Shared/Components/NavigationManager.js +6 -2
- package/lib/module/Shared/EIDReader/eidReader.js +52 -3
- package/lib/module/Shared/EIDReader/java/inputStream.js +6 -0
- package/lib/module/Shared/EIDReader/lds/icao/dg11File.js +60 -20
- package/lib/module/Shared/EIDReader/passiveAuth/der.js +93 -0
- package/lib/module/Shared/EIDReader/passiveAuth/integrity.js +71 -0
- package/lib/module/Shared/EIDReader/passiveAuth/sodParser.js +123 -0
- package/lib/module/Shared/Libs/chipVizMatch.js +93 -0
- package/lib/module/Shared/Libs/country-allow.js +16 -0
- package/lib/module/Shared/Libs/documentDataNormalizer.js +21 -11
- package/lib/module/Shared/Libs/geolocation.utils.js +77 -0
- package/lib/module/Shared/Libs/mrz.utils.js +24 -1
- package/lib/module/Shared/Libs/promise.utils.js +6 -0
- package/lib/module/Shared/Services/DataUploadService.js +18 -1
- package/lib/module/Translation/Resources/en.js +16 -0
- package/lib/module/Translation/Resources/tr.js +16 -0
- package/lib/module/Trustchex.js +4 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Dynamic/AddressCaptureScreen.d.ts +9 -0
- package/lib/typescript/src/Screens/Dynamic/AddressCaptureScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts +7 -1
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts +14 -1
- package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/java/inputStream.d.ts +3 -0
- package/lib/typescript/src/Shared/EIDReader/java/inputStream.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/lds/icao/dg11File.d.ts +20 -7
- package/lib/typescript/src/Shared/EIDReader/lds/icao/dg11File.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/passiveAuth/der.d.ts +31 -0
- package/lib/typescript/src/Shared/EIDReader/passiveAuth/der.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/passiveAuth/integrity.d.ts +31 -0
- package/lib/typescript/src/Shared/EIDReader/passiveAuth/integrity.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/passiveAuth/sodParser.d.ts +12 -0
- package/lib/typescript/src/Shared/EIDReader/passiveAuth/sodParser.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/chipVizMatch.d.ts +68 -0
- package/lib/typescript/src/Shared/Libs/chipVizMatch.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/country-allow.d.ts +10 -0
- package/lib/typescript/src/Shared/Libs/country-allow.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/documentDataNormalizer.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/geolocation.utils.d.ts +19 -0
- package/lib/typescript/src/Shared/Libs/geolocation.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/documentData.d.ts +7 -0
- package/lib/typescript/src/Shared/Types/documentData.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/documentReadResult.d.ts +20 -0
- package/lib/typescript/src/Shared/Types/documentReadResult.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +95 -2
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +16 -0
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +16 -0
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/Trustchex.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/Screens/Dynamic/AddressCaptureScreen.tsx +196 -0
- package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +12 -0
- package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +46 -14
- package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +9 -11
- package/src/Screens/Static/ResultScreen.tsx +101 -6
- package/src/Shared/Components/EIDScanner.tsx +69 -2
- package/src/Shared/Components/NavigationManager.tsx +6 -0
- package/src/Shared/EIDReader/eidReader.ts +75 -2
- package/src/Shared/EIDReader/java/inputStream.ts +6 -0
- package/src/Shared/EIDReader/lds/icao/dg11File.ts +68 -22
- package/src/Shared/EIDReader/passiveAuth/der.ts +110 -0
- package/src/Shared/EIDReader/passiveAuth/integrity.ts +97 -0
- package/src/Shared/EIDReader/passiveAuth/sodParser.ts +142 -0
- package/src/Shared/Libs/chipVizMatch.ts +149 -0
- package/src/Shared/Libs/country-allow.ts +17 -0
- package/src/Shared/Libs/documentDataNormalizer.ts +24 -11
- package/src/Shared/Libs/geolocation.utils.ts +87 -0
- package/src/Shared/Libs/mrz.utils.ts +27 -1
- package/src/Shared/Libs/promise.utils.ts +11 -0
- package/src/Shared/Services/DataUploadService.ts +23 -0
- package/src/Shared/Types/documentData.ts +8 -0
- package/src/Shared/Types/documentReadResult.ts +20 -0
- package/src/Shared/Types/identificationInfo.ts +97 -2
- package/src/Translation/Resources/en.ts +21 -0
- package/src/Translation/Resources/tr.ts +21 -0
- package/src/Trustchex.tsx +5 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { sha1 } from '@noble/hashes/sha1';
|
|
4
|
+
import { sha256, sha224 } from '@noble/hashes/sha256';
|
|
5
|
+
import { sha512, sha384 } from '@noble/hashes/sha512';
|
|
6
|
+
import { toHex } from "./der.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* On-device Passive Authentication integrity check (ICAO 9303-11 §5.1, step 3).
|
|
10
|
+
* Recompute the hash of each raw data-group the chip returned and compare it to
|
|
11
|
+
* the hash the issuer signed into EF.SOD. A mismatch means the data was altered
|
|
12
|
+
* after signing → TAMPERED. This runs offline, with no trust store; SOD
|
|
13
|
+
* signature + CSCA chaining is verified server-side.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
function digest(algorithm, data) {
|
|
17
|
+
switch (algorithm) {
|
|
18
|
+
case 'SHA-1':
|
|
19
|
+
return sha1(data);
|
|
20
|
+
case 'SHA-224':
|
|
21
|
+
return sha224(data);
|
|
22
|
+
case 'SHA-256':
|
|
23
|
+
return sha256(data);
|
|
24
|
+
case 'SHA-384':
|
|
25
|
+
return sha384(data);
|
|
26
|
+
case 'SHA-512':
|
|
27
|
+
return sha512(data);
|
|
28
|
+
default:
|
|
29
|
+
throw new Error(`Unsupported digest algorithm: ${algorithm}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Compare the raw bytes of each read data group against the SOD's hash list.
|
|
35
|
+
*
|
|
36
|
+
* @param sod parsed LDSSecurityObject (digest alg + per-DG hashes)
|
|
37
|
+
* @param rawDataGroups map of DG number → exact wire bytes read from the chip
|
|
38
|
+
*/
|
|
39
|
+
export function checkIntegrity(sod, rawDataGroups) {
|
|
40
|
+
const results = [];
|
|
41
|
+
for (const [dgStr, bytes] of Object.entries(rawDataGroups)) {
|
|
42
|
+
const dg = Number(dgStr);
|
|
43
|
+
const expectedHash = sod.dataGroupHashes[dg];
|
|
44
|
+
if (!expectedHash) continue; // DG not listed in SOD — nothing to compare
|
|
45
|
+
const actualHash = toHex(digest(sod.digestAlgorithm, bytes));
|
|
46
|
+
results.push({
|
|
47
|
+
dataGroup: dg,
|
|
48
|
+
expectedHash: expectedHash.toLowerCase(),
|
|
49
|
+
actualHash,
|
|
50
|
+
matched: actualHash === expectedHash.toLowerCase()
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// DG hashes present in the SOD that we did not read → cannot verify those.
|
|
55
|
+
const readDgs = new Set(Object.keys(rawDataGroups).map(Number));
|
|
56
|
+
const unverified = Object.keys(sod.dataGroupHashes).map(Number).filter(dg => !readDgs.has(dg)).sort((a, b) => a - b);
|
|
57
|
+
let status;
|
|
58
|
+
if (results.some(r => !r.matched)) {
|
|
59
|
+
status = 'TAMPERED';
|
|
60
|
+
} else if (results.length === 0) {
|
|
61
|
+
status = 'INCOMPLETE';
|
|
62
|
+
} else {
|
|
63
|
+
status = 'MATCH';
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
status,
|
|
67
|
+
digestAlgorithm: sod.digestAlgorithm,
|
|
68
|
+
results,
|
|
69
|
+
unverified
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { readNode, readChildren, decodeOid, decodeInteger, toHex } from "./der.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parse EF.SOD (ICAO 9303-11 §5.1) to extract the LDSSecurityObject: the digest
|
|
7
|
+
* algorithm and the per-data-group hash list the chip's issuer signed. This is
|
|
8
|
+
* the input to the on-device integrity check (recompute each read DG's hash and
|
|
9
|
+
* compare). Signature verification + CSCA chaining is done server-side.
|
|
10
|
+
*
|
|
11
|
+
* Dependency-free DER walking so it is fully unit-testable from hex fixtures.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Map of OID → digest algorithm name used for DG hashing. */
|
|
15
|
+
const DIGEST_OIDS = {
|
|
16
|
+
'1.3.14.3.2.26': 'SHA-1',
|
|
17
|
+
'2.16.840.1.101.3.4.2.1': 'SHA-256',
|
|
18
|
+
'2.16.840.1.101.3.4.2.2': 'SHA-384',
|
|
19
|
+
'2.16.840.1.101.3.4.2.3': 'SHA-512',
|
|
20
|
+
'2.16.840.1.101.3.4.2.4': 'SHA-224'
|
|
21
|
+
};
|
|
22
|
+
const OID_SIGNED_DATA = '1.2.840.113549.1.7.2';
|
|
23
|
+
const TAG_SEQUENCE = 0x10;
|
|
24
|
+
const TAG_OID = 0x06;
|
|
25
|
+
const TAG_OCTET_STRING = 0x04;
|
|
26
|
+
const TAG_INTEGER = 0x02;
|
|
27
|
+
const TAG_SET = 0x11;
|
|
28
|
+
|
|
29
|
+
/** Find the first child whose masked tag number equals `tagNumber`. */
|
|
30
|
+
function findChild(children, tagNumber) {
|
|
31
|
+
return children.find(c => c.tagNumber === tagNumber);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse the EF.SOD bytes (with or without the outer 0x77 application wrapper).
|
|
36
|
+
* Throws on a malformed structure.
|
|
37
|
+
*/
|
|
38
|
+
export function parseSod(sodBytes) {
|
|
39
|
+
let root = readNode(sodBytes, 0);
|
|
40
|
+
|
|
41
|
+
// Unwrap the [APPLICATION 23] (0x77) wrapper if present.
|
|
42
|
+
if (root.tag === 0x77) {
|
|
43
|
+
root = readNode(root.content, 0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// root = ContentInfo SEQUENCE { OID id-signedData, [0] SignedData }
|
|
47
|
+
if (root.tagNumber !== TAG_SEQUENCE) {
|
|
48
|
+
throw new Error('SOD: expected ContentInfo SEQUENCE');
|
|
49
|
+
}
|
|
50
|
+
const contentInfo = readChildren(root.content);
|
|
51
|
+
const ciOid = findChild(contentInfo, TAG_OID);
|
|
52
|
+
if (!ciOid || decodeOid(ciOid.content) !== OID_SIGNED_DATA) {
|
|
53
|
+
throw new Error('SOD: ContentInfo is not signedData');
|
|
54
|
+
}
|
|
55
|
+
// [0] explicit wrapper around SignedData.
|
|
56
|
+
const signedDataWrapper = contentInfo.find(c => c.constructed && c.tag === 0xa0);
|
|
57
|
+
if (!signedDataWrapper) throw new Error('SOD: missing SignedData wrapper');
|
|
58
|
+
const signedData = readNode(signedDataWrapper.content, 0);
|
|
59
|
+
if (signedData.tagNumber !== TAG_SEQUENCE) {
|
|
60
|
+
throw new Error('SOD: SignedData not a SEQUENCE');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// SignedData = { version, digestAlgorithms SET, encapContentInfo SEQUENCE, ... }
|
|
64
|
+
const sdChildren = readChildren(signedData.content);
|
|
65
|
+
// encapContentInfo is the first SEQUENCE after the digestAlgorithms SET.
|
|
66
|
+
const setIdx = sdChildren.findIndex(c => c.tagNumber === TAG_SET);
|
|
67
|
+
const encap = sdChildren.slice(setIdx + 1).find(c => c.tagNumber === TAG_SEQUENCE);
|
|
68
|
+
if (!encap) throw new Error('SOD: missing encapContentInfo');
|
|
69
|
+
|
|
70
|
+
// encapContentInfo = { eContentType OID, [0] eContent OCTET STRING }
|
|
71
|
+
const encapChildren = readChildren(encap.content);
|
|
72
|
+
const eContentWrapper = encapChildren.find(c => c.constructed && c.tag === 0xa0);
|
|
73
|
+
if (!eContentWrapper) throw new Error('SOD: missing eContent');
|
|
74
|
+
const eContentOctet = readNode(eContentWrapper.content, 0);
|
|
75
|
+
if (eContentOctet.tagNumber !== TAG_OCTET_STRING) {
|
|
76
|
+
throw new Error('SOD: eContent not an OCTET STRING');
|
|
77
|
+
}
|
|
78
|
+
return parseLdsSecurityObject(eContentOctet.content);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parse the LDSSecurityObject:
|
|
83
|
+
* SEQUENCE { version INTEGER, hashAlgorithm AlgorithmIdentifier,
|
|
84
|
+
* SEQUENCE OF DataGroupHash { number INTEGER, hash OCTET STRING } }
|
|
85
|
+
*/
|
|
86
|
+
function parseLdsSecurityObject(bytes) {
|
|
87
|
+
const lds = readNode(bytes, 0);
|
|
88
|
+
if (lds.tagNumber !== TAG_SEQUENCE) {
|
|
89
|
+
throw new Error('SOD: LDSSecurityObject not a SEQUENCE');
|
|
90
|
+
}
|
|
91
|
+
const children = readChildren(lds.content);
|
|
92
|
+
|
|
93
|
+
// hashAlgorithm AlgorithmIdentifier = SEQUENCE { OID, params? }
|
|
94
|
+
const algId = children.find(c => c.tagNumber === TAG_SEQUENCE);
|
|
95
|
+
if (!algId) throw new Error('SOD: missing hashAlgorithm');
|
|
96
|
+
const algOidNode = findChild(readChildren(algId.content), TAG_OID);
|
|
97
|
+
if (!algOidNode) throw new Error('SOD: missing hashAlgorithm OID');
|
|
98
|
+
const digestAlgorithm = DIGEST_OIDS[decodeOid(algOidNode.content)];
|
|
99
|
+
if (!digestAlgorithm) {
|
|
100
|
+
throw new Error(`SOD: unsupported digest OID ${decodeOid(algOidNode.content)}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// dataGroupHashValues — the SEQUENCE OF after the hashAlgorithm.
|
|
104
|
+
const algIdx = children.indexOf(algId);
|
|
105
|
+
const hashSeq = children.slice(algIdx + 1).find(c => c.tagNumber === TAG_SEQUENCE);
|
|
106
|
+
if (!hashSeq) throw new Error('SOD: missing dataGroupHashValues');
|
|
107
|
+
const dataGroupHashes = {};
|
|
108
|
+
for (const dgh of readChildren(hashSeq.content)) {
|
|
109
|
+
if (dgh.tagNumber !== TAG_SEQUENCE) continue;
|
|
110
|
+
const parts = readChildren(dgh.content);
|
|
111
|
+
const numNode = parts.find(p => p.tagNumber === TAG_INTEGER);
|
|
112
|
+
const hashNode = parts.find(p => p.tagNumber === TAG_OCTET_STRING);
|
|
113
|
+
if (!numNode || !hashNode) continue;
|
|
114
|
+
dataGroupHashes[decodeInteger(numNode.content)] = toHex(hashNode.content);
|
|
115
|
+
}
|
|
116
|
+
if (Object.keys(dataGroupHashes).length === 0) {
|
|
117
|
+
throw new Error('SOD: no data-group hashes found');
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
digestAlgorithm,
|
|
121
|
+
dataGroupHashes
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Verdict of comparing the NFC chip-read (DG1) identity data against the
|
|
5
|
+
* camera-OCR-read (printed / VIZ) identity data.
|
|
6
|
+
*
|
|
7
|
+
* Required by MASAK Tebliğ No. 32, Article 4/C(1)(b): for remote identity
|
|
8
|
+
* verification of non-Turkish nationals via NFC passport, the chip data must be
|
|
9
|
+
* verified — via NFC — to match the data printed on the passport. If it cannot
|
|
10
|
+
* be verified (`MISMATCH` or `UNVERIFIABLE`), no business relationship may be
|
|
11
|
+
* established through the remote flow.
|
|
12
|
+
*
|
|
13
|
+
* - `MATCH` — every identity-critical field is equal.
|
|
14
|
+
* - `MISMATCH` — at least one identity-critical field differs.
|
|
15
|
+
* - `UNVERIFIABLE` — one of the two sources is missing, so no comparison is possible.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** Why a comparison came back `UNVERIFIABLE`. */
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Identity-critical fields. These are the ICAO check-digit-protected MRZ fields
|
|
22
|
+
* plus the core identity claim; any difference here is a hard mismatch.
|
|
23
|
+
*/
|
|
24
|
+
const IDENTITY_CRITICAL_FIELDS = ['documentNumber', 'dateOfBirth', 'dateOfExpiry', 'nationality', 'sex'];
|
|
25
|
+
|
|
26
|
+
/** Uppercase, strip `<` filler and surrounding whitespace. */
|
|
27
|
+
function normalizeField(value) {
|
|
28
|
+
if (!value) return '';
|
|
29
|
+
return value.replace(/</g, '').trim().toUpperCase();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Collapse internal whitespace runs and strip `<` filler, for name comparison. */
|
|
33
|
+
function normalizeName(value) {
|
|
34
|
+
if (!value) return '';
|
|
35
|
+
return value.replace(/</g, ' ').replace(/\s+/g, ' ').trim().toUpperCase();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Compare the chip-read (DG1) document data against the OCR-read (printed/VIZ)
|
|
40
|
+
* document data and produce a match verdict.
|
|
41
|
+
*
|
|
42
|
+
* Both inputs are the normalized {@link DocumentData} shape, so they are
|
|
43
|
+
* directly comparable regardless of which read path produced them. Pass raw MRZ
|
|
44
|
+
* names via {@link ChipVizCompareOptions} — the `firstName`/`lastName` on
|
|
45
|
+
* `DocumentData` may already be the Unicode display form, which legitimately
|
|
46
|
+
* diverges between paths and would cause false mismatches.
|
|
47
|
+
*
|
|
48
|
+
* @param chip Chip-read data (from `normalizeFromMRZInfo`), or `undefined` if NFC did not complete.
|
|
49
|
+
* @param viz OCR-read data (from `normalizeFromMRZFields`), or `undefined` if no OCR step ran.
|
|
50
|
+
*/
|
|
51
|
+
export function compareChipToViz(chip, viz, options = {}) {
|
|
52
|
+
if (!chip) {
|
|
53
|
+
return {
|
|
54
|
+
verdict: 'UNVERIFIABLE',
|
|
55
|
+
mismatchedFields: [],
|
|
56
|
+
reason: 'missing-chip-source'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (!viz) {
|
|
60
|
+
return {
|
|
61
|
+
verdict: 'UNVERIFIABLE',
|
|
62
|
+
mismatchedFields: [],
|
|
63
|
+
reason: 'missing-ocr-source'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const mismatchedFields = [];
|
|
67
|
+
for (const field of IDENTITY_CRITICAL_FIELDS) {
|
|
68
|
+
if (normalizeField(chip[field]) !== normalizeField(viz[field])) {
|
|
69
|
+
mismatchedFields.push(field);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Names: compare the raw MRZ ASCII when provided, otherwise fall back to the
|
|
74
|
+
// (possibly display-form) DocumentData names. Recorded always; only flips the
|
|
75
|
+
// verdict when strictNames is enabled.
|
|
76
|
+
const chipFirst = options.chipName?.first ?? chip.firstName;
|
|
77
|
+
const chipLast = options.chipName?.last ?? chip.lastName;
|
|
78
|
+
const vizFirst = options.vizName?.first ?? viz.firstName;
|
|
79
|
+
const vizLast = options.vizName?.last ?? viz.lastName;
|
|
80
|
+
const firstDiffers = normalizeName(chipFirst) !== normalizeName(vizFirst);
|
|
81
|
+
const lastDiffers = normalizeName(chipLast) !== normalizeName(vizLast);
|
|
82
|
+
const nameMismatchedFields = [];
|
|
83
|
+
if (firstDiffers) nameMismatchedFields.push('firstName');
|
|
84
|
+
if (lastDiffers) nameMismatchedFields.push('lastName');
|
|
85
|
+
mismatchedFields.push(...nameMismatchedFields);
|
|
86
|
+
|
|
87
|
+
// Identity-critical fields always drive the verdict; names only when strict.
|
|
88
|
+
const hardMismatch = mismatchedFields.some(f => IDENTITY_CRITICAL_FIELDS.includes(f)) || options.strictNames === true && nameMismatchedFields.length > 0;
|
|
89
|
+
return {
|
|
90
|
+
verdict: hardMismatch ? 'MISMATCH' : 'MATCH',
|
|
91
|
+
mismatchedFields
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Country allow-list check for a workflow step's `allowedCountries`.
|
|
5
|
+
*
|
|
6
|
+
* Semantics (mirrors the server engine): an empty or unset list means ALL
|
|
7
|
+
* countries are allowed; a non-empty list allows ONLY those ICAO-3 codes.
|
|
8
|
+
* Matching is case-insensitive. `allowedCountries` carries any ICAO-3 code
|
|
9
|
+
* (server-driven), so the client treats the values as opaque strings.
|
|
10
|
+
*/
|
|
11
|
+
export function isCountryAllowed(issuingState, allowedCountries) {
|
|
12
|
+
if (!allowedCountries || allowedCountries.length === 0) return true;
|
|
13
|
+
const code = issuingState?.toUpperCase();
|
|
14
|
+
if (!code) return false;
|
|
15
|
+
return allowedCountries.map(c => c.toUpperCase()).includes(code);
|
|
16
|
+
}
|
|
@@ -1,26 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Expand a 2-digit MRZ year to a 4-digit calendar year
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Expand a 2-digit MRZ year to a 4-digit calendar year, using a century rule
|
|
5
|
+
* appropriate to the kind of date:
|
|
6
|
+
*
|
|
7
|
+
* - `'birth'`: a birth date is never in the future. Any YY greater than the
|
|
8
|
+
* current 2-digit year must be the previous century (e.g. in 2026, "85" →
|
|
9
|
+
* 1985, "20" → 2020). This is the ICAO-style sliding window pegged to "now".
|
|
10
|
+
* - `'expiry'`: a travel/ID document's expiry is recent-past-to-future and no
|
|
11
|
+
* document valid today expired in the 1900s, so expiry years are always 20YY
|
|
12
|
+
* (e.g. "36" → 2036, not 1936 — the previous fixed-threshold bug).
|
|
7
13
|
*/
|
|
8
|
-
function expandYear(yy) {
|
|
14
|
+
function expandYear(yy, kind) {
|
|
15
|
+
if (kind === 'expiry') return `20${yy}`;
|
|
9
16
|
const n = parseInt(yy, 10);
|
|
10
|
-
|
|
17
|
+
// Current 2-digit year as the sliding pivot (e.g. 26 in 2026). Years after it
|
|
18
|
+
// can't be a birth this century, so they belong to the 1900s.
|
|
19
|
+
const pivot = new Date().getFullYear() % 100;
|
|
20
|
+
return n > pivot ? `19${yy}` : `20${yy}`;
|
|
11
21
|
}
|
|
12
22
|
|
|
13
23
|
/**
|
|
14
24
|
* Convert a YYMMDD MRZ date string to ISO 8601 YYYY-MM-DD.
|
|
15
25
|
* Returns null for empty, null, or malformed input.
|
|
16
26
|
*/
|
|
17
|
-
function mrzDateToISO(raw) {
|
|
27
|
+
function mrzDateToISO(raw, kind) {
|
|
18
28
|
if (!raw || raw.length !== 6) return null;
|
|
19
29
|
const yy = raw.slice(0, 2);
|
|
20
30
|
const mm = raw.slice(2, 4);
|
|
21
31
|
const dd = raw.slice(4, 6);
|
|
22
32
|
if (!/^\d{6}$/.test(raw)) return null;
|
|
23
|
-
return `${expandYear(yy)}-${mm}-${dd}`;
|
|
33
|
+
return `${expandYear(yy, kind)}-${mm}-${dd}`;
|
|
24
34
|
}
|
|
25
35
|
|
|
26
36
|
/**
|
|
@@ -80,8 +90,8 @@ export function normalizeFromMRZInfo(mrz) {
|
|
|
80
90
|
lastName: mrz.getPrimaryIdentifier().trim(),
|
|
81
91
|
firstName: mrz.getSecondaryIdentifier().trim(),
|
|
82
92
|
sex: normalizeSex(mrz.getGender().getStrCode()),
|
|
83
|
-
dateOfBirth: mrzDateToISO(mrz.getDateOfBirth()),
|
|
84
|
-
dateOfExpiry: mrzDateToISO(mrz.getDateOfExpiry())
|
|
93
|
+
dateOfBirth: mrzDateToISO(mrz.getDateOfBirth(), 'birth'),
|
|
94
|
+
dateOfExpiry: mrzDateToISO(mrz.getDateOfExpiry(), 'expiry')
|
|
85
95
|
};
|
|
86
96
|
}
|
|
87
97
|
|
|
@@ -98,7 +108,7 @@ export function normalizeFromMRZFields(fields) {
|
|
|
98
108
|
lastName: clean(fields.lastName) ?? '',
|
|
99
109
|
firstName: clean(fields.firstName) ?? '',
|
|
100
110
|
sex: normalizeSex(fields.sex),
|
|
101
|
-
dateOfBirth: mrzDateToISO(fields.birthDate),
|
|
102
|
-
dateOfExpiry: mrzDateToISO(fields.expirationDate)
|
|
111
|
+
dateOfBirth: mrzDateToISO(fields.birthDate, 'birth'),
|
|
112
|
+
dateOfExpiry: mrzDateToISO(fields.expirationDate, 'expiry')
|
|
103
113
|
};
|
|
104
114
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { Platform, NativeModules, PermissionsAndroid } from 'react-native';
|
|
4
|
+
import { debugError } from "./debug.utils.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Coarse geolocation capture for the MASAK Tebliğ No. 32 Article 4/C(1)(e)
|
|
8
|
+
* technical-data requirement. Invoked when a workflow step enables
|
|
9
|
+
* collectGeolocation.
|
|
10
|
+
*
|
|
11
|
+
* Implemented entirely with the platform's native location framework via the
|
|
12
|
+
* SDK's own native modules (iOS Core Location through PermissionModule, Android
|
|
13
|
+
* LocationManager through GeolocationModule) — no third-party module. The host
|
|
14
|
+
* app must declare the OS permission strings (iOS
|
|
15
|
+
* `NSLocationWhenInUseUsageDescription`, Android `ACCESS_COARSE_LOCATION`).
|
|
16
|
+
*
|
|
17
|
+
* Everything is fail-soft: if the native module is missing, services are off,
|
|
18
|
+
* permission is denied, or the fix times out, this returns null and the flow
|
|
19
|
+
* proceeds — the backend treats a missing geolocation as "not provided".
|
|
20
|
+
*
|
|
21
|
+
* @returns "lat,lng" rounded to ~city-block precision (~110 m), or null.
|
|
22
|
+
*/
|
|
23
|
+
export async function captureCoarseGeolocation(timeoutMs = 8000) {
|
|
24
|
+
try {
|
|
25
|
+
if (Platform.OS === 'android') {
|
|
26
|
+
const granted = await requestAndroidLocationPermission();
|
|
27
|
+
if (!granted) return null;
|
|
28
|
+
const mod = NativeModules.GeolocationModule;
|
|
29
|
+
if (!mod?.getCoarseLocation) return null;
|
|
30
|
+
return normalize(await withJsBackstop(mod.getCoarseLocation(timeoutMs), timeoutMs));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// iOS: Core Location (via the existing PermissionModule) requests
|
|
34
|
+
// when-in-use authorization itself given the Info.plist usage string.
|
|
35
|
+
const mod = NativeModules.PermissionModule;
|
|
36
|
+
if (!mod?.getCoarseLocation) return null;
|
|
37
|
+
return normalize(await withJsBackstop(mod.getCoarseLocation(timeoutMs), timeoutMs));
|
|
38
|
+
} catch (e) {
|
|
39
|
+
debugError('geolocation.utils', 'native capture failed', e);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function normalize(value) {
|
|
44
|
+
if (typeof value !== 'string' || value.length === 0) return null;
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* JS-side backstop: the native side enforces its own timeout, but if a native
|
|
50
|
+
* regression left the promise unsettled, this resolves null so the flow can't
|
|
51
|
+
* hang. The margin keeps the native timeout authoritative on the happy path.
|
|
52
|
+
*/
|
|
53
|
+
async function withJsBackstop(native, timeoutMs) {
|
|
54
|
+
let timer;
|
|
55
|
+
const backstop = new Promise(resolve => {
|
|
56
|
+
timer = setTimeout(() => resolve(null), timeoutMs + 2000);
|
|
57
|
+
});
|
|
58
|
+
try {
|
|
59
|
+
return await Promise.race([native, backstop]);
|
|
60
|
+
} finally {
|
|
61
|
+
if (timer) clearTimeout(timer);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Request coarse location permission on Android. The host app must declare
|
|
67
|
+
* ACCESS_COARSE_LOCATION in its manifest. iOS handles its own prompt through
|
|
68
|
+
* Core Location, so this is Android-only.
|
|
69
|
+
*/
|
|
70
|
+
async function requestAndroidLocationPermission() {
|
|
71
|
+
try {
|
|
72
|
+
const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION);
|
|
73
|
+
return result === PermissionsAndroid.RESULTS.GRANTED;
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -383,7 +383,30 @@ const reconstructMRZCandidates = rawText => {
|
|
|
383
383
|
head = fixed + head.slice(9);
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
|
-
|
|
386
|
+
|
|
387
|
+
// Recover the REAL optional/personal-number field (line-2 positions
|
|
388
|
+
// 29–42, i.e. 0-indexed 28–41) instead of blanking it. Many passports
|
|
389
|
+
// (and most residence permits) carry a personal number here; blanking it
|
|
390
|
+
// silently drops that data. Stitch any split fragment lines onto this
|
|
391
|
+
// line so the optional window is present, then accept the OCR'd optional
|
|
392
|
+
// ONLY if its own check digit (position 43, 0-indexed 42) validates —
|
|
393
|
+
// that proves the 14 chars were read correctly. If it doesn't validate
|
|
394
|
+
// (garbled/split-filler), fall back to the canonical blank, preserving
|
|
395
|
+
// the previous robustness for the empty-optional case.
|
|
396
|
+
let stitched = ocrLines[i];
|
|
397
|
+
let sj = i + 1;
|
|
398
|
+
while (stitched.length < 44 && sj < ocrLines.length) {
|
|
399
|
+
stitched += ocrLines[sj];
|
|
400
|
+
sj++;
|
|
401
|
+
}
|
|
402
|
+
let optional = '<'.repeat(14);
|
|
403
|
+
if (stitched.length >= 43) {
|
|
404
|
+
const candidateOptional = stitched.slice(28, 42);
|
|
405
|
+
const optionalCheck = stitched[42];
|
|
406
|
+
if (/^[A-Z0-9<]{14}$/.test(candidateOptional) && cd731(candidateOptional) === optionalCheck) {
|
|
407
|
+
optional = candidateOptional;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
387
410
|
const l2NoComposite = head + optional + cd731(optional);
|
|
388
411
|
const composite = l2NoComposite.slice(0, 10) + l2NoComposite.slice(13, 20) + l2NoComposite.slice(21, 43);
|
|
389
412
|
return l2NoComposite + cd731(composite);
|
|
@@ -16,6 +16,12 @@ const runWithRetry = async (fn, maxRetries = 3, delay = 1000) => {
|
|
|
16
16
|
}
|
|
17
17
|
return result;
|
|
18
18
|
} catch (error) {
|
|
19
|
+
// Non-retryable errors (e.g. a server-instructed session termination,
|
|
20
|
+
// MASAK 4/C(1)(e)) must propagate immediately, with their flags intact —
|
|
21
|
+
// retrying can't help and would just delay surfacing the outcome.
|
|
22
|
+
if (error && typeof error === 'object' && (error.sessionTerminated || error.nonRetryable)) {
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
19
25
|
lastError = error;
|
|
20
26
|
retries++;
|
|
21
27
|
console.error(`[Retry] ✗ Attempt ${retries}/${maxRetries} failed:`, error instanceof Error ? error.message : error);
|
|
@@ -47,12 +47,29 @@ export class DataUploadService {
|
|
|
47
47
|
gender: this.getGenderEnumType(mrzFields.sex),
|
|
48
48
|
number: mrzFields.documentNumber,
|
|
49
49
|
country: mrzFields.issuingState,
|
|
50
|
+
// Raw ICAO nationality (for the MASAK 4/C(3) risky-country block and the
|
|
51
|
+
// server chip↔VIZ nationality comparison).
|
|
52
|
+
nationality: mrzFields.nationality,
|
|
50
53
|
barcodeValue: scannedDocument.barcodeValue,
|
|
51
54
|
personalNumber: mrzFields.personalNumber || mrzFields.optional1,
|
|
52
55
|
birthDate: mrzUtils.convertMRZDateToISODate(mrzFields.birthDate),
|
|
53
56
|
expiryDate: mrzUtils.convertMRZDateToISODate(mrzFields.expirationDate),
|
|
54
57
|
dataSource: scannedDocument.dataSource,
|
|
55
|
-
mrzText: scannedDocument.mrzText
|
|
58
|
+
mrzText: scannedDocument.mrzText,
|
|
59
|
+
// Chip↔VIZ match (MASAK 4/C(1)(b)). Send the client verdict (advisory)
|
|
60
|
+
// plus the printed/VIZ MRZ comparison fields so the server can
|
|
61
|
+
// independently re-derive the authoritative verdict. Recorded for every
|
|
62
|
+
// EID scan as a security signal; absent on non-EID submissions.
|
|
63
|
+
chipVizMatch: scannedDocument.chipVizMatch,
|
|
64
|
+
vizMrz: scannedDocument.vizMrzFields ? {
|
|
65
|
+
number: scannedDocument.vizMrzFields.documentNumber,
|
|
66
|
+
birthDate: mrzUtils.convertMRZDateToISODate(scannedDocument.vizMrzFields.birthDate),
|
|
67
|
+
expiryDate: mrzUtils.convertMRZDateToISODate(scannedDocument.vizMrzFields.expirationDate),
|
|
68
|
+
nationality: scannedDocument.vizMrzFields.nationality,
|
|
69
|
+
gender: this.getGenderEnumType(scannedDocument.vizMrzFields.sex),
|
|
70
|
+
name: scannedDocument.vizMrzFields.firstName,
|
|
71
|
+
surname: scannedDocument.vizMrzFields.lastName
|
|
72
|
+
} : undefined
|
|
56
73
|
};
|
|
57
74
|
debugLog('DataUploadService', 'Submitting document data:', identificationDocument.type);
|
|
58
75
|
const {
|
|
@@ -12,6 +12,8 @@ export default {
|
|
|
12
12
|
'verificationSessionCheckScreen.or': 'or',
|
|
13
13
|
'general.warning': 'Warning',
|
|
14
14
|
'general.error': 'Error',
|
|
15
|
+
'general.continue': 'Continue',
|
|
16
|
+
'general.cancel': 'Cancel',
|
|
15
17
|
'general.yes': 'Yes',
|
|
16
18
|
'general.no': 'No',
|
|
17
19
|
'general.letsGo': 'Start',
|
|
@@ -26,6 +28,7 @@ export default {
|
|
|
26
28
|
'termsOfUseAndDataPrivacyScreen.acceptAndContinue': 'Accept and Continue',
|
|
27
29
|
'resultScreen.submitting': 'Submitting your information...',
|
|
28
30
|
'resultScreen.submissionFailed': 'An error occurred. Please try again later.',
|
|
31
|
+
'resultScreen.sessionTerminated': 'This verification could not be completed and has been ended. Please contact support if you believe this is an error.',
|
|
29
32
|
'resultScreen.submissionFailedTitle': 'Submission failed',
|
|
30
33
|
'resultScreen.submissionFailedRetryHint': 'We could not submit your verification. Your information is saved — you can try again without re-scanning.',
|
|
31
34
|
'resultScreen.retrySubmission': 'Retry',
|
|
@@ -92,10 +95,23 @@ export default {
|
|
|
92
95
|
'eidScannerScreen.nfcAuthFailed': 'Could not establish a secure connection to the chip. Make sure the document number, date of birth and expiry date are correct, then try again.',
|
|
93
96
|
'eidScannerScreen.nfcTagLost': 'Connection to the document was lost. Keep the document still against the device and try again.',
|
|
94
97
|
'eidScannerScreen.nfcTimeout': 'Reading the chip timed out. Hold the document steady against the device and try again.',
|
|
98
|
+
'addressCaptureScreen.title': 'Confirm your address',
|
|
99
|
+
'addressCaptureScreen.subtitle': 'Enter your current residential address and provide a supporting document.',
|
|
100
|
+
'addressCaptureScreen.prefilledFromChip': 'We pre-filled your address from your document chip. Please review and correct it if needed.',
|
|
101
|
+
'addressCaptureScreen.addressLabel': 'Address',
|
|
102
|
+
'addressCaptureScreen.addressPlaceholder': 'Street, number, city, country',
|
|
103
|
+
'addressCaptureScreen.proofLabel': 'Proof of address',
|
|
104
|
+
'addressCaptureScreen.proofHint': 'A residence certificate, or a utility bill issued within the last 3 months, or a document from a public authority.',
|
|
105
|
+
'addressCaptureScreen.captureProof': 'Capture proof document',
|
|
106
|
+
'addressCaptureScreen.retakeProof': 'Retake proof document',
|
|
107
|
+
'addressCaptureScreen.proofCameraHint': 'Position the document within the frame and capture.',
|
|
108
|
+
'addressCaptureScreen.capture': 'Capture',
|
|
95
109
|
'eidScannerScreen.faceImageNotFound': 'Photo data not found on document.',
|
|
96
110
|
'eidScannerScreen.documentCode': 'Document Type',
|
|
97
111
|
'eidScannerScreen.nationality': 'Nationality',
|
|
98
112
|
'eidScannerScreen.personalNumber': 'Personal Number',
|
|
113
|
+
'eidScannerScreen.address': 'Address',
|
|
114
|
+
'eidScannerScreen.location': 'Location',
|
|
99
115
|
'eidScannerScreen.documentNumber': 'Document Number',
|
|
100
116
|
'eidScannerScreen.name': 'Name',
|
|
101
117
|
'eidScannerScreen.surname': 'Surname',
|
|
@@ -12,6 +12,8 @@ export default {
|
|
|
12
12
|
'verificationSessionCheckScreen.or': 'veya',
|
|
13
13
|
'general.warning': 'Uyarı',
|
|
14
14
|
'general.error': 'Hata',
|
|
15
|
+
'general.continue': 'Devam Et',
|
|
16
|
+
'general.cancel': 'İptal',
|
|
15
17
|
'general.yes': 'Evet',
|
|
16
18
|
'general.no': 'Hayır',
|
|
17
19
|
'general.letsGo': 'Başla',
|
|
@@ -26,6 +28,7 @@ export default {
|
|
|
26
28
|
'termsOfUseAndDataPrivacyScreen.acceptAndContinue': 'Kabul Et ve Devam Et',
|
|
27
29
|
'resultScreen.submitting': 'Bilgileriniz gönderiliyor...',
|
|
28
30
|
'resultScreen.submissionFailed': 'Bir hata oluştu. Lütfen daha sonra tekrar deneyin.',
|
|
31
|
+
'resultScreen.sessionTerminated': 'Bu doğrulama tamamlanamadı ve sonlandırıldı. Bir hata olduğunu düşünüyorsanız lütfen destek ekibiyle iletişime geçin.',
|
|
29
32
|
'resultScreen.submissionFailedTitle': 'Gönderim başarısız',
|
|
30
33
|
'resultScreen.submissionFailedRetryHint': 'Doğrulamanız gönderilemedi. Bilgileriniz kaydedildi — yeniden taramadan tekrar deneyebilirsiniz.',
|
|
31
34
|
'resultScreen.retrySubmission': 'Tekrar Dene',
|
|
@@ -92,10 +95,23 @@ export default {
|
|
|
92
95
|
'eidScannerScreen.nfcAuthFailed': 'Çip ile güvenli bağlantı kurulamadı. Belge numarası, doğum tarihi ve son geçerlilik tarihinin doğru olduğundan emin olup tekrar deneyin.',
|
|
93
96
|
'eidScannerScreen.nfcTagLost': 'Belge ile bağlantı kesildi. Belgeyi cihaza sabit tutun ve tekrar deneyin.',
|
|
94
97
|
'eidScannerScreen.nfcTimeout': 'Çip okuması zaman aşımına uğradı. Belgeyi cihaza sabit tutup tekrar deneyin.',
|
|
98
|
+
'addressCaptureScreen.title': 'Adresinizi doğrulayın',
|
|
99
|
+
'addressCaptureScreen.subtitle': 'Güncel yerleşim adresinizi girin ve destekleyici bir belge sağlayın.',
|
|
100
|
+
'addressCaptureScreen.prefilledFromChip': 'Adresiniz belge yongasından önceden dolduruldu. Lütfen kontrol edin ve gerekirse düzeltin.',
|
|
101
|
+
'addressCaptureScreen.addressLabel': 'Adres',
|
|
102
|
+
'addressCaptureScreen.addressPlaceholder': 'Cadde, numara, şehir, ülke',
|
|
103
|
+
'addressCaptureScreen.proofLabel': 'Adres kanıtı',
|
|
104
|
+
'addressCaptureScreen.proofHint': 'Yerleşim yeri belgesi veya son 3 ay içinde düzenlenmiş bir fatura ya da bir kamu kurumu tarafından verilen belge.',
|
|
105
|
+
'addressCaptureScreen.captureProof': 'Kanıt belgesini çek',
|
|
106
|
+
'addressCaptureScreen.retakeProof': 'Kanıt belgesini yeniden çek',
|
|
107
|
+
'addressCaptureScreen.proofCameraHint': 'Belgeyi çerçeveye yerleştirin ve fotoğraf çekin.',
|
|
108
|
+
'addressCaptureScreen.capture': 'Çek',
|
|
95
109
|
'eidScannerScreen.faceImageNotFound': 'Belgede fotoğraf verisi bulunamadı.',
|
|
96
110
|
'eidScannerScreen.documentCode': 'Belge Türü',
|
|
97
111
|
'eidScannerScreen.nationality': 'Uyruk',
|
|
98
112
|
'eidScannerScreen.personalNumber': 'Vatandaşlık No',
|
|
113
|
+
'eidScannerScreen.address': 'Adres',
|
|
114
|
+
'eidScannerScreen.location': 'Konum',
|
|
99
115
|
'eidScannerScreen.documentNumber': 'Belge No',
|
|
100
116
|
'eidScannerScreen.name': 'Ad',
|
|
101
117
|
'eidScannerScreen.surname': 'Soyad',
|
package/lib/module/Trustchex.js
CHANGED
|
@@ -18,6 +18,7 @@ import VerificationSessionCheckScreen from "./Screens/Static/VerificationSession
|
|
|
18
18
|
import QrCodeScanningScreen from "./Screens/Static/QrCodeScanningScreen.js";
|
|
19
19
|
import VideoCallScreen from "./Screens/Dynamic/VideoCallScreen.js";
|
|
20
20
|
import VerbalConsentScreen from "./Screens/Dynamic/VerbalConsentScreen.js";
|
|
21
|
+
import AddressCaptureScreen from "./Screens/Dynamic/AddressCaptureScreen.js";
|
|
21
22
|
import OTPVerificationScreen from "./Screens/Static/OTPVerificationScreen.js";
|
|
22
23
|
import MRZTestScreen from "./Screens/Debug/MRZTestScreen.js";
|
|
23
24
|
import BarcodeTestScreen from "./Screens/Debug/BarcodeTestScreen.js";
|
|
@@ -237,6 +238,9 @@ const Trustchex = ({
|
|
|
237
238
|
}), /*#__PURE__*/_jsx(Stack.Screen, {
|
|
238
239
|
name: "VerbalConsentScreen",
|
|
239
240
|
component: VerbalConsentScreen
|
|
241
|
+
}), /*#__PURE__*/_jsx(Stack.Screen, {
|
|
242
|
+
name: "AddressCaptureScreen",
|
|
243
|
+
component: AddressCaptureScreen
|
|
240
244
|
}), /*#__PURE__*/_jsx(Stack.Screen, {
|
|
241
245
|
name: "MRZTestScreen",
|
|
242
246
|
component: MRZTestScreen
|
package/lib/module/version.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADDRESS_CAPTURE step (MASAK Tebliğ No. 32 Article 4/C(1)(d)). The user confirms
|
|
3
|
+
* their address — pre-filled from the chip's DG11 address when available — and
|
|
4
|
+
* captures a proof document (residence cert / utility bill ≤3 months / public-
|
|
5
|
+
* authority doc). The address is verified later by an operator (manual review).
|
|
6
|
+
*/
|
|
7
|
+
declare const AddressCaptureScreen: () => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export default AddressCaptureScreen;
|
|
9
|
+
//# sourceMappingURL=AddressCaptureScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddressCaptureScreen.d.ts","sourceRoot":"","sources":["../../../../../src/Screens/Dynamic/AddressCaptureScreen.tsx"],"names":[],"mappings":"AA8BA;;;;;GAKG;AACH,QAAA,MAAM,oBAAoB,+CA6HzB,CAAC;AAkCF,eAAe,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContractAcceptanceScreen.d.ts","sourceRoot":"","sources":["../../../../../src/Screens/Dynamic/ContractAcceptanceScreen.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ContractAcceptanceScreen.d.ts","sourceRoot":"","sources":["../../../../../src/Screens/Dynamic/ContractAcceptanceScreen.tsx"],"names":[],"mappings":"AA8BA,QAAA,MAAM,wBAAwB,+CAoL7B,CAAC;AA8BF,eAAe,wBAAwB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IdentityDocumentEIDScanningScreen.d.ts","sourceRoot":"","sources":["../../../../../src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"IdentityDocumentEIDScanningScreen.d.ts","sourceRoot":"","sources":["../../../../../src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx"],"names":[],"mappings":"AAsBA,QAAA,MAAM,iCAAiC,+CAuPtC,CAAC;AAoBF,eAAe,iCAAiC,CAAC"}
|