@sanctum-key/react-native-sdk 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/README.md +3 -3
  2. package/android/build/.transforms/{f62cb96b2d1f78ca96ab35932dd530dc → c9d62bb333688ab562f51958998d5a48}/transformed/classes/classes_dex/classes.dex +0 -0
  3. package/android/build/generated/source/buildConfig/debug/kyc/{transfergratis → SanctumKey}/com/BuildConfig.java +2 -2
  4. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +1 -1
  5. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +1 -1
  6. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  7. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  8. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -2
  9. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/kyc/SanctumKey/com/BuildConfig.class +0 -0
  10. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +1 -1
  11. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +1 -1
  12. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  13. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -1
  14. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
  15. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
  16. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab +0 -0
  17. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
  18. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream.len +0 -0
  19. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
  20. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
  21. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
  22. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len +0 -0
  23. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
  24. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
  25. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
  26. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len +0 -0
  27. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
  28. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab +0 -0
  29. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
  30. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream.len +0 -0
  31. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
  32. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
  33. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
  34. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
  35. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab +0 -0
  36. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
  37. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab +0 -0
  38. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
  39. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len +0 -0
  40. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
  41. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
  42. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
  43. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream.len +0 -0
  44. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
  45. package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
  46. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  47. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  48. package/android/build/outputs/logs/manifest-merger-debug-report.txt +1 -1
  49. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  50. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$1$5$1.class +0 -0
  51. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$1$5$2.class +0 -0
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. 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
  61. 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
  62. package/android/build/tmp/kotlin-classes/debug/kyc/SanctumKey/com/SanctumKeySdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
  63. 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
  64. 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
  65. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkModule.class → SanctumKey/com/SanctumKeySdkModule.class} +0 -0
  66. package/android/build/tmp/kotlin-classes/debug/kyc/{transfergratis/com/TransfergratisSdkView.class → SanctumKey/com/SanctumKeySdkView.class} +0 -0
  67. package/android/build.gradle +2 -2
  68. package/android/src/main/AndroidManifest.xml +1 -1
  69. package/android/src/main/java/kyc/transfergratis/com/TransfergratisSdkModule.kt +6 -6
  70. package/android/src/main/java/kyc/transfergratis/com/TransfergratisSdkView.kt +2 -2
  71. package/build/package.json +9 -7
  72. package/build/src/App.d.ts +2 -2
  73. package/build/src/App.d.ts.map +1 -1
  74. package/build/src/App.js +2 -2
  75. package/build/src/App.js.map +1 -1
  76. package/build/src/{TransfergratisSdk.types.d.ts → SanctumKeySdk.types.d.ts} +3 -3
  77. package/build/src/SanctumKeySdk.types.d.ts.map +1 -0
  78. package/build/src/SanctumKeySdk.types.js +2 -0
  79. package/build/src/SanctumKeySdk.types.js.map +1 -0
  80. package/build/src/{TransfergratisSdkModule.d.ts → SanctumKeySdkModule.d.ts} +4 -4
  81. package/build/src/SanctumKeySdkModule.d.ts.map +1 -0
  82. package/build/src/{TransfergratisSdkModule.js → SanctumKeySdkModule.js} +2 -2
  83. package/build/src/SanctumKeySdkModule.js.map +1 -0
  84. package/build/src/{TransfergratisSdkModule.web.d.ts → SanctumKeySdkModule.web.d.ts} +4 -4
  85. package/build/src/SanctumKeySdkModule.web.d.ts.map +1 -0
  86. package/build/src/{TransfergratisSdkModule.web.js → SanctumKeySdkModule.web.js} +3 -3
  87. package/build/src/SanctumKeySdkModule.web.js.map +1 -0
  88. package/build/src/SanctumKeySdkView.d.ts +4 -0
  89. package/build/src/SanctumKeySdkView.d.ts.map +1 -0
  90. package/build/src/SanctumKeySdkView.js +7 -0
  91. package/build/src/SanctumKeySdkView.js.map +1 -0
  92. package/build/src/SanctumKeySdkView.web.d.ts +4 -0
  93. package/build/src/SanctumKeySdkView.web.d.ts.map +1 -0
  94. package/build/src/{TransfergratisSdkView.web.js → SanctumKeySdkView.web.js} +2 -2
  95. package/build/src/SanctumKeySdkView.web.js.map +1 -0
  96. package/build/src/api/axios.js +2 -2
  97. package/build/src/api/axios.js.map +1 -1
  98. package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
  99. package/build/src/components/EnhancedCameraView.js +66 -338
  100. package/build/src/components/EnhancedCameraView.js.map +1 -1
  101. package/build/src/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -1
  102. package/build/src/components/KYCElements/EmailVerificationTemplate.js +93 -15
  103. package/build/src/components/KYCElements/EmailVerificationTemplate.js.map +1 -1
  104. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  105. package/build/src/components/KYCElements/IDCardCapture.js +167 -695
  106. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  107. package/build/src/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -1
  108. package/build/src/components/KYCElements/PhoneVerificationTemplate.js +269 -40
  109. package/build/src/components/KYCElements/PhoneVerificationTemplate.js.map +1 -1
  110. package/build/src/components/KYCElements/SelfieCapture.d.ts +1 -1
  111. package/build/src/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  112. package/build/src/components/KYCElements/SelfieCapture.js +130 -192
  113. package/build/src/components/KYCElements/SelfieCapture.js.map +1 -1
  114. package/build/src/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  115. package/build/src/components/KYCElements/SelfieCaptureTemplate.js +131 -433
  116. package/build/src/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  117. package/build/src/components/NativeCameraView.js +1 -1
  118. package/build/src/components/NativeCameraView.js.map +1 -1
  119. package/build/src/components/OverLay/IdCard.d.ts +3 -2
  120. package/build/src/components/OverLay/IdCard.d.ts.map +1 -1
  121. package/build/src/components/OverLay/IdCard.js +149 -141
  122. package/build/src/components/OverLay/IdCard.js.map +1 -1
  123. package/build/src/components/OverLay/SelfieOverlay.d.ts +2 -1
  124. package/build/src/components/OverLay/SelfieOverlay.d.ts.map +1 -1
  125. package/build/src/components/OverLay/SelfieOverlay.js +37 -95
  126. package/build/src/components/OverLay/SelfieOverlay.js.map +1 -1
  127. package/build/src/components/OverLay/type.d.ts +1 -0
  128. package/build/src/components/OverLay/type.d.ts.map +1 -1
  129. package/build/src/components/OverLay/type.js.map +1 -1
  130. package/build/src/components/Svgs/scanningLine.d.ts +2 -1
  131. package/build/src/components/Svgs/scanningLine.d.ts.map +1 -1
  132. package/build/src/components/Svgs/scanningLine.js +55 -51
  133. package/build/src/components/Svgs/scanningLine.js.map +1 -1
  134. package/build/src/config/KYCConfig.js +1 -1
  135. package/build/src/config/KYCConfig.js.map +1 -1
  136. package/build/src/config/allowedDomains.js +6 -6
  137. package/build/src/config/allowedDomains.js.map +1 -1
  138. package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  139. package/build/src/hooks/useTemplateKYCFlow.js +37 -38
  140. package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
  141. package/build/src/index.d.ts +3 -3
  142. package/build/src/index.d.ts.map +1 -1
  143. package/build/src/index.js +3 -3
  144. package/build/src/index.js.map +1 -1
  145. package/build/src/modules/api/CardAuthentification.d.ts +0 -5
  146. package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
  147. package/build/src/modules/api/CardAuthentification.js +114 -116
  148. package/build/src/modules/api/CardAuthentification.js.map +1 -1
  149. package/build/src/modules/api/KYCService.d.ts +11 -1
  150. package/build/src/modules/api/KYCService.d.ts.map +1 -1
  151. package/build/src/modules/api/KYCService.js +101 -38
  152. package/build/src/modules/api/KYCService.js.map +1 -1
  153. package/build/src/modules/camera/NativeCameraModule.js +17 -17
  154. package/build/src/modules/camera/NativeCameraModule.js.map +1 -1
  155. package/expo-module.config.json +2 -2
  156. package/ios/TransfergratisSdk.podspec +2 -2
  157. package/ios/TransfergratisSdkModule.swift +12 -12
  158. package/package.json +9 -7
  159. package/src/App.tsx +2 -2
  160. package/src/{TransfergratisSdk.types.ts → SanctumKeySdk.types.ts} +2 -2
  161. package/src/{TransfergratisSdkModule.ts → SanctumKeySdkModule.ts} +3 -3
  162. package/src/{TransfergratisSdkModule.web.ts → SanctumKeySdkModule.web.ts} +3 -3
  163. package/src/SanctumKeySdkView.tsx +11 -0
  164. package/src/{TransfergratisSdkView.web.tsx → SanctumKeySdkView.web.tsx} +2 -2
  165. package/src/api/axios.ts +2 -2
  166. package/src/components/EnhancedCameraView.tsx +81 -400
  167. package/src/components/KYCElements/EmailVerificationTemplate.tsx +115 -26
  168. package/src/components/KYCElements/IDCardCapture.tsx +228 -868
  169. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +328 -60
  170. package/src/components/KYCElements/SelfieCapture.tsx +184 -213
  171. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +330 -662
  172. package/src/components/NativeCameraView.tsx +1 -1
  173. package/src/components/OverLay/IdCard.tsx +218 -217
  174. package/src/components/OverLay/SelfieOverlay.tsx +56 -134
  175. package/src/components/OverLay/type.ts +1 -0
  176. package/src/components/Svgs/scanningLine.tsx +71 -72
  177. package/src/config/KYCConfig.ts +1 -1
  178. package/src/config/allowedDomains.ts +6 -6
  179. package/src/hooks/useTemplateKYCFlow.tsx +45 -39
  180. package/src/i18n/README.md +1 -1
  181. package/src/index.ts +3 -3
  182. package/src/modules/api/CardAuthentification.ts +202 -200
  183. package/src/modules/api/KYCService.ts +168 -53
  184. package/src/modules/camera/NativeCameraModule.ts +17 -17
  185. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$1$5$1.class +0 -0
  186. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$1$5$2.class +0 -0
  187. package/android/build/tmp/kotlin-classes/debug/kyc/transfergratis/com/TransfergratisSdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
  188. package/build/src/TransfergratisSdk.types.d.ts.map +0 -1
  189. package/build/src/TransfergratisSdk.types.js +0 -2
  190. package/build/src/TransfergratisSdk.types.js.map +0 -1
  191. package/build/src/TransfergratisSdkModule.d.ts.map +0 -1
  192. package/build/src/TransfergratisSdkModule.js.map +0 -1
  193. package/build/src/TransfergratisSdkModule.web.d.ts.map +0 -1
  194. package/build/src/TransfergratisSdkModule.web.js.map +0 -1
  195. package/build/src/TransfergratisSdkView.d.ts +0 -4
  196. package/build/src/TransfergratisSdkView.d.ts.map +0 -1
  197. package/build/src/TransfergratisSdkView.js +0 -7
  198. package/build/src/TransfergratisSdkView.js.map +0 -1
  199. package/build/src/TransfergratisSdkView.web.d.ts +0 -4
  200. package/build/src/TransfergratisSdkView.web.d.ts.map +0 -1
  201. package/build/src/TransfergratisSdkView.web.js.map +0 -1
  202. package/src/TransfergratisSdkView.tsx +0 -11
  203. /package/android/build/.transforms/{532c0e65d82f446633d0a7dab2772198 → ab90740579f5bd05b27b4343ada2d1c9}/results.bin +0 -0
  204. /package/android/build/.transforms/{532c0e65d82f446633d0a7dab2772198 → ab90740579f5bd05b27b4343ada2d1c9}/transformed/classes/classes_dex/classes.dex +0 -0
  205. /package/android/build/.transforms/{f62cb96b2d1f78ca96ab35932dd530dc → c9d62bb333688ab562f51958998d5a48}/results.bin +0 -0
  206. /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: { path?: string, regionMapping: { authMethod: string[], mrzTypes: string[] }, selectedDocumentType: string, code: string, currentSide: string, templatePath?: string, mrzType?: string },
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
- // SANDBOX mode
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
- ...(result.regionMapping?.authMethod?.includes('MRZ') ? {
104
+ ...(hasMrz ? {
51
105
  mrz: {
52
106
  success: true,
53
107
  parsed_data: {
54
- status: 'success',
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
- // Cast the response so TypeScript knows about card_obb
67
- const detected = await kycService.detectFaceOnId(result?.path || '', token, result?.selectedDocumentType || '', env) as ApiVerificationResponse;
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
- // --- 2. STRICT DISTANCE CHECK (ONLY RUNS IF FULLY IN FRAME) ---
97
- if (points && points.length === 4) {
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
- // MRZ Extraction: only run when a concrete mrzType is available
135
- const shouldRunMrzExtraction =
136
- result.regionMapping?.authMethod?.length > 0 &&
137
- result.regionMapping.authMethod.includes('MRZ') &&
138
- !!result?.mrzType;
139
- if (shouldRunMrzExtraction) {
140
- const mrzResponse = await kycService.extractMrzText(
141
- {
142
- fileUri: result.path || '',
143
- docType: result?.selectedDocumentType || '',
144
- docRegion: result?.code || "",
145
- postfix: result?.currentSide,
146
- token: token,
147
- template_path: result?.templatePath || '',
148
- mrz_type: result?.mrzType || ''
149
- },
150
- env
151
- );
152
-
153
- // Safety check: parse if it was returned as a raw string
154
- mrz = typeof mrzResponse === 'string' ? JSON.parse(mrzResponse) : mrzResponse;
155
-
156
- if (mrz && (!mrz.success || mrz.parsed_data?.status === 'FAILURE')) {
157
- logger.log("🛑 Front MRZ Extraction Failed:", mrz.parsed_data?.status_message);
158
- throw new Error(`MRZ illisible: ${mrz.parsed_data?.status_message || 'Veuillez nettoyer l\'objectif et réessayer'}`);
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 (result.regionMapping?.authMethod?.includes('MRZ')) {
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 (result.regionMapping?.authMethod?.includes('2D_barcode')) {
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 || await kycService.checkTemplateType({
229
- fileUri: result.path,
230
- docType: result.selectedDocumentType as any,
231
- docRegion: result.code,
232
- postfix: 'back',
233
- token: token
234
- }, env);
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
- // Safely extract coordinates and frame status
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
- // --- 3. STRICT DISTANCE CHECK (ONLY RUNS IF FULLY IN FRAME) ---
282
- if (points && points.length === 4) {
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 country: ${result.code} (${expectedCountryName}), Detected: ${activeTemplatePath}`);
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
- // --- 4. Try MRZ First (If required or default) ---
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 || '' // Safe to send empty string
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 (!mrz || !mrz.success || mrz.parsed_data?.status === 'FAILURE') {
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 = { mrz };
345
-
361
+ extractionResult.mrz = mrz; // Attaching securely to result object
362
+
346
363
  } catch (mrzError: any) {
347
- if (hasBarcode) {
348
- logger.log(`MRZ échoué (${mrzError?.message}), tentative d'extraction barcode...`);
349
- try {
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
- // --- 5. Try Barcode Only ---
361
- else if (hasBarcode) {
362
- try {
363
- logger.log("Tentative d'extraction barcode seule");
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 = { barcode };
366
- } catch (e: any) {
367
- throw new Error(`Lecture Barcode échouée: ${e?.message}`);
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
- * Check template type
399
- * @param result
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({ fileUri: result.path || '', docType: result?.docType as GovernmentDocumentType, docRegion: result?.docRegion || "", postfix: result?.postfix, token: token }, env);
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
- // Default to FALSE so it must prove it is in the frame
433
- let points = null;
434
- let isCardInFrame = false;
435
- let hasCroppedSides = true;
442
+ let points: number[][] | null = null;
443
+ let isCardInFrame = false;
444
+ let hasCroppedSides = true;
436
445
 
437
- // Extract data safely based on API response format
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; // Passed initial existence check
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. Coordinates hit image boundary.`);
467
+ logger.log(`Template Framing failed.`);
464
468
  throw new Error('CARD_NOT_FULLY_IN_FRAME');
465
469
  }
466
470
 
467
-
468
- const LPIPS_THRESHOLD = 0.75;
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 Score too high: ${templateType.lpips_score}`);
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
  }