@sanctum-key/react-native-sdk 1.0.6 → 1.0.8
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/README.md +3 -3
- package/android/build/.transforms/{f62cb96b2d1f78ca96ab35932dd530dc → c9d62bb333688ab562f51958998d5a48}/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/generated/source/buildConfig/debug/kyc/{transfergratis → SanctumKey}/com/BuildConfig.java +2 -2
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +1 -1
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +1 -1
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -2
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/kyc/SanctumKey/com/BuildConfig.class +0 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +1 -1
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +1 -1
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +1 -1
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$1$5$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$1$5$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class → SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule.class → SanctumKey/com/SanctumKeySdkModule.class} +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkView.class → SanctumKey/com/SanctumKeySdkView.class} +0 -0
- package/android/build.gradle +2 -2
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/kyc/transfergratis/com/TransfergratisSdkModule.kt +6 -6
- package/android/src/main/java/kyc/transfergratis/com/TransfergratisSdkView.kt +2 -2
- package/build/package.json +9 -7
- package/build/src/App.d.ts +2 -2
- package/build/src/App.d.ts.map +1 -1
- package/build/src/App.js +2 -2
- package/build/src/App.js.map +1 -1
- package/build/src/{TransfergratisSdk.types.d.ts → SanctumKeySdk.types.d.ts} +3 -3
- package/build/src/SanctumKeySdk.types.d.ts.map +1 -0
- package/build/src/SanctumKeySdk.types.js +2 -0
- package/build/src/SanctumKeySdk.types.js.map +1 -0
- package/build/src/{TransfergratisSdkModule.d.ts → SanctumKeySdkModule.d.ts} +4 -4
- package/build/src/SanctumKeySdkModule.d.ts.map +1 -0
- package/build/src/{TransfergratisSdkModule.js → SanctumKeySdkModule.js} +2 -2
- package/build/src/SanctumKeySdkModule.js.map +1 -0
- package/build/src/{TransfergratisSdkModule.web.d.ts → SanctumKeySdkModule.web.d.ts} +4 -4
- package/build/src/SanctumKeySdkModule.web.d.ts.map +1 -0
- package/build/src/{TransfergratisSdkModule.web.js → SanctumKeySdkModule.web.js} +3 -3
- package/build/src/SanctumKeySdkModule.web.js.map +1 -0
- package/build/src/SanctumKeySdkView.d.ts +4 -0
- package/build/src/SanctumKeySdkView.d.ts.map +1 -0
- package/build/src/SanctumKeySdkView.js +7 -0
- package/build/src/SanctumKeySdkView.js.map +1 -0
- package/build/src/SanctumKeySdkView.web.d.ts +4 -0
- package/build/src/SanctumKeySdkView.web.d.ts.map +1 -0
- package/build/src/{TransfergratisSdkView.web.js → SanctumKeySdkView.web.js} +2 -2
- package/build/src/SanctumKeySdkView.web.js.map +1 -0
- package/build/src/api/axios.js +2 -2
- package/build/src/api/axios.js.map +1 -1
- package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.js +107 -330
- package/build/src/components/EnhancedCameraView.js.map +1 -1
- package/build/src/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/EmailVerificationTemplate.js +114 -15
- package/build/src/components/KYCElements/EmailVerificationTemplate.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +166 -695
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/PhoneVerificationTemplate.js +282 -40
- package/build/src/components/KYCElements/PhoneVerificationTemplate.js.map +1 -1
- package/build/src/components/KYCElements/SelfieCapture.d.ts +1 -1
- package/build/src/components/KYCElements/SelfieCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/SelfieCapture.js +130 -192
- package/build/src/components/KYCElements/SelfieCapture.js.map +1 -1
- package/build/src/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/SelfieCaptureTemplate.js +131 -433
- package/build/src/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/src/components/NativeCameraView.js +1 -1
- package/build/src/components/NativeCameraView.js.map +1 -1
- package/build/src/components/OverLay/IdCard.d.ts +3 -2
- package/build/src/components/OverLay/IdCard.d.ts.map +1 -1
- package/build/src/components/OverLay/IdCard.js +149 -141
- package/build/src/components/OverLay/IdCard.js.map +1 -1
- package/build/src/components/OverLay/SelfieOverlay.d.ts +2 -1
- package/build/src/components/OverLay/SelfieOverlay.d.ts.map +1 -1
- package/build/src/components/OverLay/SelfieOverlay.js +37 -95
- package/build/src/components/OverLay/SelfieOverlay.js.map +1 -1
- package/build/src/components/OverLay/type.d.ts +1 -0
- package/build/src/components/OverLay/type.d.ts.map +1 -1
- package/build/src/components/OverLay/type.js.map +1 -1
- package/build/src/components/Svgs/scanningLine.d.ts +2 -1
- package/build/src/components/Svgs/scanningLine.d.ts.map +1 -1
- package/build/src/components/Svgs/scanningLine.js +55 -51
- package/build/src/components/Svgs/scanningLine.js.map +1 -1
- package/build/src/config/KYCConfig.js +1 -1
- package/build/src/config/KYCConfig.js.map +1 -1
- package/build/src/config/allowedDomains.js +6 -6
- package/build/src/config/allowedDomains.js.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.js +37 -38
- package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/src/index.d.ts +3 -3
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +3 -3
- package/build/src/index.js.map +1 -1
- package/build/src/modules/api/CardAuthentification.d.ts +0 -5
- package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/src/modules/api/CardAuthentification.js +114 -116
- package/build/src/modules/api/CardAuthentification.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts +12 -1
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +102 -38
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/build/src/modules/camera/NativeCameraModule.js +17 -17
- package/build/src/modules/camera/NativeCameraModule.js.map +1 -1
- package/expo-module.config.json +2 -2
- package/ios/TransfergratisSdk.podspec +2 -2
- package/ios/TransfergratisSdkModule.swift +12 -12
- package/package.json +9 -7
- package/src/App.tsx +2 -2
- package/src/{TransfergratisSdk.types.ts → SanctumKeySdk.types.ts} +2 -2
- package/src/{TransfergratisSdkModule.ts → SanctumKeySdkModule.ts} +3 -3
- package/src/{TransfergratisSdkModule.web.ts → SanctumKeySdkModule.web.ts} +3 -3
- package/src/SanctumKeySdkView.tsx +11 -0
- package/src/{TransfergratisSdkView.web.tsx → SanctumKeySdkView.web.tsx} +2 -2
- package/src/api/axios.ts +2 -2
- package/src/components/EnhancedCameraView.tsx +131 -385
- package/src/components/KYCElements/EmailVerificationTemplate.tsx +141 -26
- package/src/components/KYCElements/IDCardCapture.tsx +228 -868
- package/src/components/KYCElements/PhoneVerificationTemplate.tsx +342 -60
- package/src/components/KYCElements/SelfieCapture.tsx +184 -213
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +330 -662
- package/src/components/NativeCameraView.tsx +1 -1
- package/src/components/OverLay/IdCard.tsx +218 -217
- package/src/components/OverLay/SelfieOverlay.tsx +56 -134
- package/src/components/OverLay/type.ts +1 -0
- package/src/components/Svgs/scanningLine.tsx +71 -72
- package/src/config/KYCConfig.ts +1 -1
- package/src/config/allowedDomains.ts +6 -6
- package/src/hooks/useTemplateKYCFlow.tsx +45 -39
- package/src/i18n/README.md +1 -1
- package/src/index.ts +3 -3
- package/src/modules/api/CardAuthentification.ts +202 -200
- package/src/modules/api/KYCService.ts +169 -53
- package/src/modules/camera/NativeCameraModule.ts +17 -17
- package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$1$5$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$1$5$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
- package/build/src/TransfergratisSdk.types.d.ts.map +0 -1
- package/build/src/TransfergratisSdk.types.js +0 -2
- package/build/src/TransfergratisSdk.types.js.map +0 -1
- package/build/src/TransfergratisSdkModule.d.ts.map +0 -1
- package/build/src/TransfergratisSdkModule.js.map +0 -1
- package/build/src/TransfergratisSdkModule.web.d.ts.map +0 -1
- package/build/src/TransfergratisSdkModule.web.js.map +0 -1
- package/build/src/TransfergratisSdkView.d.ts +0 -4
- package/build/src/TransfergratisSdkView.d.ts.map +0 -1
- package/build/src/TransfergratisSdkView.js +0 -7
- package/build/src/TransfergratisSdkView.js.map +0 -1
- package/build/src/TransfergratisSdkView.web.d.ts +0 -4
- package/build/src/TransfergratisSdkView.web.d.ts.map +0 -1
- package/build/src/TransfergratisSdkView.web.js.map +0 -1
- package/src/TransfergratisSdkView.tsx +0 -11
- /package/android/build/.transforms/{532c0e65d82f446633d0a7dab2772198 → ab90740579f5bd05b27b4343ada2d1c9}/results.bin +0 -0
- /package/android/build/.transforms/{532c0e65d82f446633d0a7dab2772198 → ab90740579f5bd05b27b4343ada2d1c9}/transformed/classes/classes_dex/classes.dex +0 -0
- /package/android/build/.transforms/{f62cb96b2d1f78ca96ab35932dd530dc → c9d62bb333688ab562f51958998d5a48}/results.bin +0 -0
- /package/android/build/{intermediates/javac/debug/compileDebugJavaWithJavac/classes/kyc/transfergratis/com/BuildConfig.class → tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/BuildConfig.class.uniqueId0} +0 -0
|
@@ -5,7 +5,6 @@ import { KycEnvironment } from "../../types/env.types";
|
|
|
5
5
|
import { logger } from "../../utils/logger";
|
|
6
6
|
import { countryData } from "../../config/countriesData";
|
|
7
7
|
|
|
8
|
-
// 1. Add this interface to tell TypeScript what the AI response actually looks like
|
|
9
8
|
interface CardObbData {
|
|
10
9
|
obb: number[][];
|
|
11
10
|
confidence: number;
|
|
@@ -17,41 +16,96 @@ interface ApiVerificationResponse {
|
|
|
17
16
|
result?: boolean;
|
|
18
17
|
success?: boolean;
|
|
19
18
|
detail?: any[];
|
|
20
|
-
card_obb?: CardObbData[] | any;
|
|
19
|
+
card_obb?: CardObbData[] | any;
|
|
21
20
|
template_path?: string;
|
|
22
21
|
Error?: any;
|
|
23
|
-
[key: string]: any;
|
|
22
|
+
[key: string]: any;
|
|
24
23
|
}
|
|
25
24
|
|
|
25
|
+
function getDistance(p1: number[], p2: number[]): number {
|
|
26
|
+
return Math.sqrt(Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getPolygonArea(points: number[][]): number {
|
|
30
|
+
const n = points.length;
|
|
31
|
+
let area = 0;
|
|
32
|
+
for (let i = 0; i < n; i++) {
|
|
33
|
+
const j = (i + 1) % n;
|
|
34
|
+
area += points[i][0] * points[j][1];
|
|
35
|
+
area -= points[j][0] * points[i][1];
|
|
36
|
+
}
|
|
37
|
+
return Math.abs(area) / 2;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function assertCardNotTooFar(points: number[][]): void {
|
|
41
|
+
if (!points || points.length < 3) return;
|
|
42
|
+
|
|
43
|
+
const longestCardEdge = Math.max(
|
|
44
|
+
getDistance(points[0], points[1]),
|
|
45
|
+
getDistance(points[1], points[2]),
|
|
46
|
+
getDistance(points[2], points[3] ?? points[0]),
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const cardArea = getPolygonArea(points);
|
|
50
|
+
|
|
51
|
+
logger.log(
|
|
52
|
+
`Card distance check – longest edge: ${Math.round(longestCardEdge)} px, ` +
|
|
53
|
+
`OBB area: ${Math.round(cardArea)} px²`
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (longestCardEdge < 700) {
|
|
57
|
+
logger.log(`🛑 Rejected: longest edge too short (${Math.round(longestCardEdge)} px < 700 px)`);
|
|
58
|
+
throw new Error('TOO_FAR_AWAY');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (cardArea < 400_000) {
|
|
62
|
+
logger.log(`🛑 Rejected: card area too small (${Math.round(cardArea)} px² < 400 000 px²)`);
|
|
63
|
+
throw new Error('TOO_FAR_AWAY');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── frontVerification ────────────────────────────────────────────────────────
|
|
68
|
+
|
|
26
69
|
export async function frontVerification(
|
|
27
|
-
result: {
|
|
70
|
+
result: {
|
|
71
|
+
path?: string,
|
|
72
|
+
regionMapping: { authMethod: string[], mrzTypes: string[] },
|
|
73
|
+
selectedDocumentType: string,
|
|
74
|
+
code: string,
|
|
75
|
+
currentSide: string,
|
|
76
|
+
templatePath?: string,
|
|
77
|
+
mrzType?: string
|
|
78
|
+
},
|
|
28
79
|
env: KycEnvironment = 'PRODUCTION'
|
|
29
80
|
) {
|
|
30
81
|
try {
|
|
31
82
|
console.log("Front verification START", JSON.stringify({ result }, null, 2));
|
|
32
83
|
logger.log("Front verification", JSON.stringify({ result }, null, 2));
|
|
33
|
-
|
|
34
|
-
//
|
|
84
|
+
|
|
85
|
+
// 🚨 FIX 1: Bulletproof Case-Insensitive Check
|
|
86
|
+
const authMethods = Array.isArray(result.regionMapping?.authMethod)
|
|
87
|
+
? result.regionMapping.authMethod.map(m => String(m).toUpperCase())
|
|
88
|
+
: [];
|
|
89
|
+
const hasMrz = authMethods.some(m => m.includes('MRZ'));
|
|
90
|
+
|
|
35
91
|
if (env === 'SANDBOX') {
|
|
36
92
|
console.log("SANDBOX mode: Skipping AI verification for front document");
|
|
37
|
-
logger.log("SANDBOX mode: Returning mock front verification response");
|
|
38
93
|
const mockBbox: IBbox = { minX: 400, minY: 800, width: 2200, height: 1400 };
|
|
39
|
-
|
|
40
94
|
const mockResponse = {
|
|
41
95
|
result: true,
|
|
42
96
|
detail: [{ confidence: 0.95 }],
|
|
43
97
|
card_obb: [{
|
|
44
|
-
obb: [[400,800], [2600,800], [2600,2200], [400,2200]],
|
|
98
|
+
obb: [[400, 800], [2600, 800], [2600, 2200], [400, 2200]],
|
|
45
99
|
confidence: 0.95,
|
|
46
100
|
card_in_frame: true,
|
|
47
101
|
cropped_sides: []
|
|
48
102
|
}],
|
|
49
103
|
bbox: mockBbox,
|
|
50
|
-
...(
|
|
104
|
+
...(hasMrz ? {
|
|
51
105
|
mrz: {
|
|
52
106
|
success: true,
|
|
53
107
|
parsed_data: {
|
|
54
|
-
status: '
|
|
108
|
+
status: 'SUCCESS',
|
|
55
109
|
document_type: result.selectedDocumentType,
|
|
56
110
|
mrz_type: result.mrzType || 'TD1'
|
|
57
111
|
}
|
|
@@ -60,25 +114,24 @@ export async function frontVerification(
|
|
|
60
114
|
};
|
|
61
115
|
return mockResponse;
|
|
62
116
|
}
|
|
63
|
-
|
|
117
|
+
|
|
64
118
|
const token = await authentification();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
119
|
+
const detected = await kycService.detectFaceOnId(
|
|
120
|
+
result?.path || '', token, result?.selectedDocumentType || '', env
|
|
121
|
+
) as ApiVerificationResponse;
|
|
68
122
|
|
|
69
123
|
if (!detected.result) {
|
|
70
124
|
throw new Error('Aucun visage détecté sur la carte. Veuillez reprendre.');
|
|
71
125
|
}
|
|
72
126
|
|
|
73
|
-
const cardData = detected.card_obb && Array.isArray(detected.card_obb) && detected.card_obb.length > 0
|
|
74
|
-
? detected.card_obb[0]
|
|
127
|
+
const cardData = detected.card_obb && Array.isArray(detected.card_obb) && detected.card_obb.length > 0
|
|
128
|
+
? detected.card_obb[0]
|
|
75
129
|
: null;
|
|
76
130
|
|
|
77
|
-
let points = null;
|
|
131
|
+
let points: number[][] | null = null;
|
|
78
132
|
let isCardInFrame = true;
|
|
79
133
|
let hasCroppedSides = false;
|
|
80
134
|
|
|
81
|
-
// Extract data safely
|
|
82
135
|
if (cardData) {
|
|
83
136
|
points = cardData.obb;
|
|
84
137
|
if (typeof cardData.card_in_frame !== 'undefined') {
|
|
@@ -87,86 +140,68 @@ export async function frontVerification(
|
|
|
87
140
|
}
|
|
88
141
|
}
|
|
89
142
|
|
|
90
|
-
// --- 1. STRICT FRAMING CHECK (MUST HAPPEN FIRST) ---
|
|
91
143
|
if (!isCardInFrame || hasCroppedSides) {
|
|
92
144
|
logger.log(`Front Framing failed. Cropped sides detected.`);
|
|
93
145
|
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
94
146
|
}
|
|
95
147
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const getDistance = (p1: number[], p2: number[]) => Math.sqrt(Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2));
|
|
99
|
-
const longestCardEdge = Math.max(getDistance(points[0], points[1]), getDistance(points[1], points[2]));
|
|
100
|
-
|
|
101
|
-
const maxX = Math.max(points[0][0], points[1][0], points[2][0], points[3][0]);
|
|
102
|
-
const maxY = Math.max(points[0][1], points[1][1], points[2][1], points[3][1]);
|
|
103
|
-
const estimatedImageScale = Math.max(maxX, maxY);
|
|
104
|
-
|
|
105
|
-
const fillPercentage = longestCardEdge / estimatedImageScale;
|
|
106
|
-
logger.log(`Front Card Fill Percentage: ${(fillPercentage * 100).toFixed(1)}%`);
|
|
107
|
-
|
|
108
|
-
if (fillPercentage < 0.50) {
|
|
109
|
-
logger.log("🛑 Front Image rejected: Document is too far away.");
|
|
110
|
-
throw new Error("TOO_FAR_AWAY");
|
|
111
|
-
}
|
|
148
|
+
if (points && points.length >= 3) {
|
|
149
|
+
assertCardNotTooFar(points);
|
|
112
150
|
}
|
|
113
151
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Check Confidence Threshold
|
|
119
152
|
const obbConfidence = getObbConfidence(detected.card_obb);
|
|
120
153
|
if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
|
|
121
154
|
throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
|
|
122
155
|
}
|
|
123
156
|
|
|
124
|
-
// Optional: crop image using card_obb for better MRZ/barcode extraction
|
|
125
157
|
let croppedBase64: string | undefined;
|
|
126
158
|
let bbox: IBbox | undefined;
|
|
127
159
|
let mrz: any | undefined;
|
|
160
|
+
|
|
128
161
|
try {
|
|
129
162
|
const crop = await cropByObb(result?.path || '', detected.card_obb);
|
|
130
163
|
croppedBase64 = crop.base64;
|
|
131
164
|
bbox = crop.bbox;
|
|
132
165
|
} catch { }
|
|
133
166
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
167
|
+
// 🚨 FIX 2: Execute MRZ if required (Removed restrictive guards)
|
|
168
|
+
if (hasMrz) {
|
|
169
|
+
try {
|
|
170
|
+
logger.log("Tentative d'extraction MRZ (Front)");
|
|
171
|
+
const mrzResponse = await kycService.extractMrzText(
|
|
172
|
+
{
|
|
173
|
+
fileUri: result.path || '',
|
|
174
|
+
docType: result?.selectedDocumentType || '',
|
|
175
|
+
docRegion: result?.code || "",
|
|
176
|
+
postfix: result?.currentSide || 'front',
|
|
177
|
+
token: token,
|
|
178
|
+
template_path: result?.templatePath || '',
|
|
179
|
+
mrz_type: result?.mrzType || 'TD1' // Fallback to ensure it always runs
|
|
180
|
+
},
|
|
181
|
+
env
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
mrz = typeof mrzResponse === 'string' ? JSON.parse(mrzResponse) : mrzResponse;
|
|
185
|
+
|
|
186
|
+
if (mrz?.parsed_data?.status === 'FAILURE' || mrz?.success === false) {
|
|
187
|
+
logger.log("🛑 Front MRZ Extraction Failed:", mrz?.parsed_data?.status_message);
|
|
188
|
+
throw new Error(`MRZ illisible: ${mrz?.parsed_data?.status_message || 'Veuillez nettoyer l\'objectif et réessayer'}`);
|
|
189
|
+
}
|
|
190
|
+
} catch (err: any) {
|
|
191
|
+
logger.error("MRZ Extraction Error:", err);
|
|
192
|
+
throw new Error(err?.message || "Erreur lors de l'extraction MRZ. Veuillez reprendre la photo.");
|
|
159
193
|
}
|
|
160
194
|
}
|
|
161
195
|
|
|
162
196
|
return { ...detected, croppedBase64, bbox, ...(mrz ? { mrz } : {}) };
|
|
197
|
+
|
|
163
198
|
} catch (e: any) {
|
|
164
199
|
logger.error('Error front verification:', e?.message);
|
|
165
|
-
// Do not use LogBox for background silent errors
|
|
166
200
|
throw new Error(e?.message || 'Erreur de détection du visage');
|
|
167
201
|
}
|
|
168
202
|
}
|
|
169
203
|
|
|
204
|
+
// ─── backVerification ────────────────────────────────────────────────────────
|
|
170
205
|
|
|
171
206
|
export async function backVerification(
|
|
172
207
|
result: {
|
|
@@ -178,31 +213,37 @@ export async function backVerification(
|
|
|
178
213
|
templatePath?: string,
|
|
179
214
|
mrzType?: string,
|
|
180
215
|
templateResponse?: ApiVerificationResponse
|
|
181
|
-
},
|
|
216
|
+
},
|
|
182
217
|
env: KycEnvironment = 'PRODUCTION'
|
|
183
218
|
) {
|
|
184
219
|
try {
|
|
185
220
|
if (!result.path) throw new Error('No path provided');
|
|
186
221
|
logger.log("result.regionMapping", result.regionMapping, result.currentSide, result.code);
|
|
187
|
-
|
|
222
|
+
|
|
223
|
+
// 🚨 FIX 3: Robust Auth Method Resolution for the Back Side
|
|
224
|
+
const authMethods = Array.isArray(result.regionMapping?.authMethod)
|
|
225
|
+
? result.regionMapping.authMethod.map(m => String(m).toUpperCase())
|
|
226
|
+
: [];
|
|
227
|
+
const hasMrz = authMethods.some(m => m.includes('MRZ'));
|
|
228
|
+
const hasBarcode = authMethods.some(m => m.includes('BARCODE') || m.includes('2D'));
|
|
229
|
+
|
|
188
230
|
if (env === 'SANDBOX') {
|
|
189
231
|
console.log("SANDBOX mode: Skipping AI verification for back document");
|
|
190
|
-
|
|
191
232
|
const mockBbox: IBbox = { minX: 400, minY: 800, width: 2200, height: 1400 };
|
|
192
233
|
const mockCardObb = [{
|
|
193
|
-
obb: [[400,800], [2600,800], [2600,2200], [400,2200]],
|
|
234
|
+
obb: [[400, 800], [2600, 800], [2600, 2200], [400, 2200]],
|
|
194
235
|
confidence: 0.95,
|
|
195
236
|
card_in_frame: true,
|
|
196
237
|
cropped_sides: []
|
|
197
238
|
}];
|
|
198
|
-
|
|
199
|
-
if (
|
|
239
|
+
|
|
240
|
+
if (hasMrz) {
|
|
200
241
|
return {
|
|
201
242
|
success: true,
|
|
202
|
-
mrz: {
|
|
243
|
+
mrz: {
|
|
203
244
|
success: true,
|
|
204
245
|
parsed_data: {
|
|
205
|
-
status: 'SUCCESS',
|
|
246
|
+
status: 'SUCCESS',
|
|
206
247
|
document_type: result.selectedDocumentType,
|
|
207
248
|
mrz_type: result.mrzType || 'TD1'
|
|
208
249
|
}
|
|
@@ -210,7 +251,7 @@ export async function backVerification(
|
|
|
210
251
|
bbox: mockBbox,
|
|
211
252
|
card_obb: mockCardObb
|
|
212
253
|
};
|
|
213
|
-
} else if (
|
|
254
|
+
} else if (hasBarcode) {
|
|
214
255
|
return {
|
|
215
256
|
success: true,
|
|
216
257
|
barcode: { barcode_data: 'SANDBOX_MOCK_BARCODE' },
|
|
@@ -220,48 +261,43 @@ export async function backVerification(
|
|
|
220
261
|
}
|
|
221
262
|
return { success: true, bbox: mockBbox, card_obb: mockCardObb };
|
|
222
263
|
}
|
|
223
|
-
|
|
224
|
-
const token = await authentification();
|
|
225
264
|
|
|
265
|
+
const token = await authentification();
|
|
226
266
|
logger.log("1. Checking template and framing for back document...");
|
|
227
|
-
|
|
228
|
-
const templateResponse: ApiVerificationResponse = result.templateResponse ||
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
267
|
+
|
|
268
|
+
const templateResponse: ApiVerificationResponse = result.templateResponse ||
|
|
269
|
+
await kycService.checkTemplateType({
|
|
270
|
+
fileUri: result.path,
|
|
271
|
+
docType: result.selectedDocumentType as any,
|
|
272
|
+
docRegion: result.code,
|
|
273
|
+
postfix: 'back',
|
|
274
|
+
token: token
|
|
275
|
+
}, env);
|
|
235
276
|
|
|
236
277
|
if (templateResponse.success === false || templateResponse.Error) {
|
|
237
|
-
logger.log("Backend returned an error:", templateResponse.Error);
|
|
238
278
|
throw new Error('Impossible de lire le document. Veuillez vous rapprocher et stabiliser la caméra.');
|
|
239
279
|
}
|
|
240
280
|
|
|
241
|
-
const cardData = templateResponse?.card_obb && Array.isArray(templateResponse.card_obb) && templateResponse.card_obb.length > 0
|
|
242
|
-
? templateResponse.card_obb[0]
|
|
281
|
+
const cardData = templateResponse?.card_obb && Array.isArray(templateResponse.card_obb) && templateResponse.card_obb.length > 0
|
|
282
|
+
? templateResponse.card_obb[0]
|
|
243
283
|
: null;
|
|
244
284
|
|
|
245
|
-
if (!cardData)
|
|
246
|
-
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
247
|
-
}
|
|
285
|
+
if (!cardData) throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
248
286
|
|
|
249
|
-
let points = null;
|
|
250
|
-
let isCardInFrame = false;
|
|
251
|
-
let hasCroppedSides = true;
|
|
287
|
+
let points: number[][] | null = null;
|
|
288
|
+
let isCardInFrame = false;
|
|
289
|
+
let hasCroppedSides = true;
|
|
252
290
|
|
|
253
|
-
|
|
254
|
-
if (cardData.obb) {
|
|
291
|
+
if (cardData.obb) {
|
|
255
292
|
points = cardData.obb;
|
|
256
293
|
if (typeof cardData.card_in_frame !== 'undefined') {
|
|
257
294
|
isCardInFrame = cardData.card_in_frame === true;
|
|
258
295
|
hasCroppedSides = Array.isArray(cardData.cropped_sides) && cardData.cropped_sides.length > 0;
|
|
259
296
|
}
|
|
260
|
-
} else if (Array.isArray(cardData) && Array.isArray(cardData[0])) {
|
|
297
|
+
} else if (Array.isArray(cardData) && Array.isArray(cardData[0])) {
|
|
261
298
|
points = cardData[0];
|
|
262
|
-
isCardInFrame = true;
|
|
299
|
+
isCardInFrame = true;
|
|
263
300
|
hasCroppedSides = false;
|
|
264
|
-
|
|
265
301
|
if (points && points.length === 4) {
|
|
266
302
|
const minX = Math.min(...points.map((p: number[]) => p[0]));
|
|
267
303
|
const minY = Math.min(...points.map((p: number[]) => p[1]));
|
|
@@ -272,185 +308,154 @@ export async function backVerification(
|
|
|
272
308
|
}
|
|
273
309
|
}
|
|
274
310
|
|
|
275
|
-
// --- 2. STRICT FRAMING CHECK (MUST HAPPEN FIRST) ---
|
|
276
311
|
if (!isCardInFrame || hasCroppedSides) {
|
|
277
312
|
logger.log(`Back Framing failed. Coordinates hit image boundary.`);
|
|
278
313
|
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
279
314
|
}
|
|
280
315
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const getDistance = (p1: number[], p2: number[]) => Math.sqrt(Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2));
|
|
284
|
-
const longestCardEdge = Math.max(getDistance(points[0], points[1]), getDistance(points[1], points[2]));
|
|
285
|
-
|
|
286
|
-
const maxX = Math.max(points[0][0], points[1][0], points[2][0], points[3][0]);
|
|
287
|
-
const maxY = Math.max(points[0][1], points[1][1], points[2][1], points[3][1]);
|
|
288
|
-
const estimatedImageScale = Math.max(maxX, maxY);
|
|
289
|
-
|
|
290
|
-
const fillPercentage = longestCardEdge / estimatedImageScale;
|
|
291
|
-
logger.log(`Back Card Fill Percentage: ${(fillPercentage * 100).toFixed(1)}%`);
|
|
292
|
-
|
|
293
|
-
if (fillPercentage < 0.50) {
|
|
294
|
-
logger.log("🛑 Back Image rejected: Document is too far away.");
|
|
295
|
-
throw new Error("TOO_FAR_AWAY");
|
|
296
|
-
}
|
|
316
|
+
if (points && points.length >= 3) {
|
|
317
|
+
assertCardNotTooFar(points);
|
|
297
318
|
}
|
|
298
|
-
|
|
319
|
+
|
|
299
320
|
const obbConf = getObbConfidence(templateResponse?.card_obb);
|
|
300
321
|
if (obbConf !== null && obbConf < OBB_CONFIDENCE_THRESHOLD) {
|
|
301
322
|
throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
|
|
302
323
|
}
|
|
303
324
|
|
|
304
325
|
const activeTemplatePath = templateResponse.template_path || result.templatePath || '';
|
|
305
|
-
|
|
306
326
|
if (activeTemplatePath) {
|
|
307
327
|
const expectedCountryName = countryData[result.code]?.name_en || '';
|
|
308
328
|
const hasCodeMatch = activeTemplatePath.includes(`_${result.code}_`);
|
|
309
329
|
const hasNameMatch = expectedCountryName && activeTemplatePath.toLowerCase().includes(expectedCountryName.toLowerCase());
|
|
310
330
|
|
|
311
331
|
if (!hasCodeMatch && !hasNameMatch) {
|
|
312
|
-
logger.log(`Template mismatch! Expected
|
|
332
|
+
logger.log(`Template mismatch! Expected: ${result.code} (${expectedCountryName}), Detected: ${activeTemplatePath}`);
|
|
313
333
|
throw new Error(`Le document ne correspond pas au pays sélectionné (${result.code}).`);
|
|
314
334
|
}
|
|
315
335
|
}
|
|
316
336
|
|
|
317
337
|
logger.log("Framing and Country Template verified successfully. Proceeding to Data Extraction.");
|
|
318
338
|
|
|
319
|
-
const hasMrz = result.regionMapping?.authMethod?.includes('MRZ') && result?.mrzType;
|
|
320
|
-
const hasBarcode = result.regionMapping?.authMethod?.includes('2D_barcode');
|
|
321
|
-
|
|
322
339
|
let extractionResult: any = {};
|
|
323
340
|
|
|
324
|
-
//
|
|
341
|
+
// 🚨 FIX 4: Guaranteed MRZ Extraction Attachment
|
|
325
342
|
if (hasMrz) {
|
|
326
343
|
try {
|
|
327
|
-
logger.log("Tentative d'extraction MRZ");
|
|
344
|
+
logger.log("Tentative d'extraction MRZ (Back)");
|
|
328
345
|
const mrzResponse = await kycService.extractMrzText({
|
|
329
346
|
fileUri: result.path!,
|
|
330
347
|
docType: result?.selectedDocumentType || '',
|
|
331
348
|
docRegion: result?.code || '',
|
|
332
|
-
postfix: 'back',
|
|
349
|
+
postfix: result?.currentSide || 'back',
|
|
333
350
|
token: token,
|
|
334
|
-
template_path: activeTemplatePath,
|
|
335
|
-
mrz_type: result?.mrzType || ''
|
|
351
|
+
template_path: activeTemplatePath,
|
|
352
|
+
mrz_type: result?.mrzType || 'TD1'
|
|
336
353
|
}, env);
|
|
337
|
-
|
|
354
|
+
|
|
338
355
|
const mrz = typeof mrzResponse === 'string' ? JSON.parse(mrzResponse) : mrzResponse;
|
|
339
|
-
|
|
340
|
-
if (
|
|
356
|
+
|
|
357
|
+
if (mrz?.parsed_data?.status === 'FAILURE' || mrz?.success === false) {
|
|
341
358
|
throw new Error(mrz?.parsed_data?.status_message || 'Lecture MRZ échouée');
|
|
342
359
|
}
|
|
343
360
|
|
|
344
|
-
extractionResult =
|
|
345
|
-
|
|
361
|
+
extractionResult.mrz = mrz; // Attaching securely to result object
|
|
362
|
+
|
|
346
363
|
} catch (mrzError: any) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token }, env);
|
|
351
|
-
extractionResult = { barcode };
|
|
352
|
-
} catch (barcodeError: any) {
|
|
353
|
-
throw new Error(`MRZ et Barcode ont échoué. Veuillez stabiliser la carte.`);
|
|
354
|
-
}
|
|
355
|
-
} else {
|
|
356
|
-
throw new Error(`Lecture MRZ invalide: ${mrzError?.message}`);
|
|
357
|
-
}
|
|
364
|
+
logger.log(`MRZ échoué: ${mrzError?.message}`);
|
|
365
|
+
// If MRZ fails, we MUST throw an error so the user is forced to retake it!
|
|
366
|
+
throw new Error(`Lecture MRZ invalide: ${mrzError?.message || 'Veuillez nettoyer l\'objectif et réessayer'}`);
|
|
358
367
|
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
logger.log("Tentative d'extraction barcode
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (hasBarcode) {
|
|
371
|
+
try {
|
|
372
|
+
logger.log("Tentative d'extraction barcode");
|
|
364
373
|
const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token }, env);
|
|
365
|
-
extractionResult =
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
374
|
+
extractionResult.barcode = barcode;
|
|
375
|
+
} catch (e: any) {
|
|
376
|
+
// Only throw barcode error if MRZ wasn't already successfully extracted
|
|
377
|
+
if (!extractionResult.mrz) {
|
|
378
|
+
throw new Error(`Lecture Barcode échouée: ${e?.message}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
369
381
|
}
|
|
370
382
|
|
|
371
383
|
let bbox: IBbox | undefined;
|
|
372
384
|
let croppedBase64: string | undefined;
|
|
373
|
-
|
|
374
385
|
try {
|
|
375
386
|
const crop = await cropByObb(result?.path || '', templateResponse.card_obb);
|
|
376
387
|
bbox = crop.bbox;
|
|
377
388
|
croppedBase64 = crop.base64;
|
|
378
389
|
} catch { }
|
|
379
390
|
|
|
380
|
-
return {
|
|
391
|
+
return {
|
|
381
392
|
success: true,
|
|
382
|
-
...extractionResult,
|
|
383
|
-
bbox,
|
|
384
|
-
croppedBase64,
|
|
385
|
-
card_obb: templateResponse.card_obb
|
|
393
|
+
...extractionResult,
|
|
394
|
+
bbox,
|
|
395
|
+
croppedBase64,
|
|
396
|
+
card_obb: templateResponse.card_obb
|
|
386
397
|
};
|
|
387
398
|
|
|
388
399
|
} catch (e: any) {
|
|
389
|
-
if (e?.message === 'CARD_NOT_FULLY_IN_FRAME' || e?.message === 'TOO_FAR_AWAY')
|
|
390
|
-
throw e;
|
|
391
|
-
}
|
|
400
|
+
if (e?.message === 'CARD_NOT_FULLY_IN_FRAME' || e?.message === 'TOO_FAR_AWAY') throw e;
|
|
392
401
|
throw new Error(e?.message || 'Erreur de détection des données');
|
|
393
402
|
}
|
|
394
403
|
}
|
|
395
404
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
* @returns
|
|
401
|
-
*/
|
|
402
|
-
export async function checkTemplateType(result: { path?: string, docType: string, docRegion: string, postfix: string }, env: KycEnvironment = 'PRODUCTION') {
|
|
405
|
+
export async function checkTemplateType(
|
|
406
|
+
result: { path?: string, docType: string, docRegion: string, postfix: string },
|
|
407
|
+
env: KycEnvironment = 'PRODUCTION'
|
|
408
|
+
) {
|
|
403
409
|
try {
|
|
404
410
|
if (env === 'SANDBOX') {
|
|
405
411
|
return {
|
|
406
412
|
template_path: `templates/${result.docType}_${result.docRegion}_${result.postfix}.jpg`,
|
|
407
413
|
card_obb: [{
|
|
408
|
-
obb: [[400,800], [2600,800], [2600,2200], [400,2200]],
|
|
414
|
+
obb: [[400, 800], [2600, 800], [2600, 2200], [400, 2200]],
|
|
409
415
|
confidence: 0.95,
|
|
410
416
|
card_in_frame: true,
|
|
411
417
|
cropped_sides: []
|
|
412
418
|
}]
|
|
413
419
|
};
|
|
414
420
|
}
|
|
415
|
-
|
|
421
|
+
|
|
416
422
|
const token = await authentification();
|
|
417
|
-
const templateType = await kycService.checkTemplateType({
|
|
423
|
+
const templateType = await kycService.checkTemplateType({
|
|
424
|
+
fileUri: result.path || '',
|
|
425
|
+
docType: result?.docType as GovernmentDocumentType,
|
|
426
|
+
docRegion: result?.docRegion || "",
|
|
427
|
+
postfix: result?.postfix,
|
|
428
|
+
token: token
|
|
429
|
+
}, env);
|
|
418
430
|
|
|
419
431
|
if (templateType.success === false || templateType.Error) {
|
|
420
432
|
logger.log("Backend returned an error:", templateType.Error);
|
|
421
433
|
throw new Error('Impossible de lire le document. Veuillez vous rapprocher et stabiliser la caméra.');
|
|
422
434
|
}
|
|
423
435
|
|
|
424
|
-
const cardData = templateType.card_obb && Array.isArray(templateType.card_obb) && templateType.card_obb.length > 0
|
|
425
|
-
? templateType.card_obb[0]
|
|
436
|
+
const cardData = templateType.card_obb && Array.isArray(templateType.card_obb) && templateType.card_obb.length > 0
|
|
437
|
+
? templateType.card_obb[0]
|
|
426
438
|
: null;
|
|
427
439
|
|
|
428
|
-
if (!cardData)
|
|
429
|
-
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
430
|
-
}
|
|
440
|
+
if (!cardData) throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
431
441
|
|
|
432
|
-
|
|
433
|
-
let
|
|
434
|
-
let
|
|
435
|
-
let hasCroppedSides = true;
|
|
442
|
+
let points: number[][] | null = null;
|
|
443
|
+
let isCardInFrame = false;
|
|
444
|
+
let hasCroppedSides = true;
|
|
436
445
|
|
|
437
|
-
|
|
438
|
-
if (cardData.obb) {
|
|
446
|
+
if (cardData.obb) {
|
|
439
447
|
points = cardData.obb;
|
|
440
448
|
if (typeof cardData.card_in_frame !== 'undefined') {
|
|
441
449
|
isCardInFrame = cardData.card_in_frame === true;
|
|
442
450
|
hasCroppedSides = Array.isArray(cardData.cropped_sides) && cardData.cropped_sides.length > 0;
|
|
443
451
|
}
|
|
444
|
-
} else if (Array.isArray(cardData) && Array.isArray(cardData[0])) {
|
|
452
|
+
} else if (Array.isArray(cardData) && Array.isArray(cardData[0])) {
|
|
445
453
|
points = cardData[0];
|
|
446
|
-
isCardInFrame = true;
|
|
454
|
+
isCardInFrame = true;
|
|
447
455
|
hasCroppedSides = false;
|
|
448
|
-
|
|
449
|
-
// MATH FALLBACK: Check if the coordinates are touching the absolute edge of the photo.
|
|
450
456
|
if (points && points.length === 4) {
|
|
451
457
|
const minX = Math.min(...points.map((p: number[]) => p[0]));
|
|
452
458
|
const minY = Math.min(...points.map((p: number[]) => p[1]));
|
|
453
|
-
// If any corner is within 15 pixels of the edge, it is physically cut off!
|
|
454
459
|
if (minX <= 15 || minY <= 15) {
|
|
455
460
|
isCardInFrame = false;
|
|
456
461
|
hasCroppedSides = true;
|
|
@@ -458,17 +463,18 @@ export async function checkTemplateType(result: { path?: string, docType: string
|
|
|
458
463
|
}
|
|
459
464
|
}
|
|
460
465
|
|
|
461
|
-
// --- 3. STRICT FRAMING CHECK (MUST HAPPEN FIRST) ---
|
|
462
466
|
if (!isCardInFrame || hasCroppedSides) {
|
|
463
|
-
logger.log(`Template Framing failed
|
|
467
|
+
logger.log(`Template Framing failed.`);
|
|
464
468
|
throw new Error('CARD_NOT_FULLY_IN_FRAME');
|
|
465
469
|
}
|
|
466
470
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
471
|
+
if (points && points.length >= 3) {
|
|
472
|
+
assertCardNotTooFar(points);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const LPIPS_THRESHOLD = 0.75;
|
|
470
476
|
if (templateType.lpips_score !== undefined && templateType.lpips_score > LPIPS_THRESHOLD) {
|
|
471
|
-
logger.log(`🛑 Country Mismatch! LPIPS
|
|
477
|
+
logger.log(`🛑 Country Mismatch! LPIPS: ${templateType.lpips_score}`);
|
|
472
478
|
throw new Error(`Le document présenté ne correspond pas au pays sélectionné (${result.docRegion}).`);
|
|
473
479
|
}
|
|
474
480
|
|
|
@@ -476,11 +482,7 @@ export async function checkTemplateType(result: { path?: string, docType: string
|
|
|
476
482
|
return templateType;
|
|
477
483
|
} catch (e: any) {
|
|
478
484
|
logger.error('Error checking template type:', JSON.stringify(errorMessage(e), null, 2));
|
|
479
|
-
|
|
480
|
-
if (e?.message === 'CARD_NOT_FULLY_IN_FRAME' || e?.message?.includes('ne correspond pas')) {
|
|
481
|
-
throw e;
|
|
482
|
-
}
|
|
483
|
-
|
|
485
|
+
if (e?.message === 'CARD_NOT_FULLY_IN_FRAME' || e?.message === 'TOO_FAR_AWAY' || e?.message?.includes('ne correspond pas')) throw e;
|
|
484
486
|
throw new Error(e?.message || 'Erreur de vérification du template');
|
|
485
487
|
}
|
|
486
488
|
}
|