@transfergratis/react-native-sdk 0.1.24 → 0.1.26

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 (182) hide show
  1. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +12 -5
  2. package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
  3. package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
  4. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
  5. package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
  6. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +61 -59
  7. package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-transfergratis-react-native-sdk.jar +0 -0
  8. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +12 -5
  9. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  10. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  11. package/android/build/outputs/aar/transfergratis-react-native-sdk-debug.aar +0 -0
  12. package/android/build/outputs/logs/manifest-merger-debug-report.txt +26 -34
  13. package/android/src/main/AndroidManifest.xml +22 -7
  14. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  15. package/build/components/EnhancedCameraView.web.js +76 -21
  16. package/build/components/EnhancedCameraView.web.js.map +1 -1
  17. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts +12 -0
  18. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
  19. package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
  20. package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
  21. package/build/components/KYCElements/EmailVerificationTemplate.d.ts +12 -0
  22. package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
  23. package/build/components/KYCElements/EmailVerificationTemplate.js +212 -0
  24. package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
  25. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  26. package/build/components/KYCElements/IDCardCapture.js +216 -14
  27. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  28. package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -0
  29. package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
  30. package/build/components/KYCElements/OrientationVideoCapture.js +2 -2
  31. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  32. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
  33. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
  34. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +2 -2
  35. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  36. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
  37. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
  38. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +2 -2
  39. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  40. package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
  41. package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
  42. package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
  43. package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
  44. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
  45. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
  46. package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
  47. package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
  48. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  49. package/build/components/KYCElements/SelfieCaptureTemplate.js +7 -3
  50. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  51. package/build/components/KYCElements/WelcomeTemplate.js +2 -1
  52. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -1
  53. package/build/components/OverLay/type.d.ts +2 -0
  54. package/build/components/OverLay/type.d.ts.map +1 -1
  55. package/build/components/OverLay/type.js.map +1 -1
  56. package/build/components/TemplateKYCExample.d.ts +10 -0
  57. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  58. package/build/components/TemplateKYCExample.js +7 -30
  59. package/build/components/TemplateKYCExample.js.map +1 -1
  60. package/build/components/TemplateKYCFlowRefactored.d.ts +12 -0
  61. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  62. package/build/components/TemplateKYCFlowRefactored.js +25 -3
  63. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  64. package/build/config/KYCConfig.d.ts +14 -0
  65. package/build/config/KYCConfig.d.ts.map +1 -0
  66. package/build/config/KYCConfig.js +26 -0
  67. package/build/config/KYCConfig.js.map +1 -0
  68. package/build/config/allowedDomains.d.ts.map +1 -1
  69. package/build/config/allowedDomains.js +4 -19
  70. package/build/config/allowedDomains.js.map +1 -1
  71. package/build/hooks/useOrientationVideo.d.ts +2 -1
  72. package/build/hooks/useOrientationVideo.d.ts.map +1 -1
  73. package/build/hooks/useOrientationVideo.js +3 -3
  74. package/build/hooks/useOrientationVideo.js.map +1 -1
  75. package/build/hooks/useTemplateKYCFlow.d.ts +18 -1
  76. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  77. package/build/hooks/useTemplateKYCFlow.js +410 -56
  78. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  79. package/build/i18n/en/index.d.ts +42 -0
  80. package/build/i18n/en/index.d.ts.map +1 -1
  81. package/build/i18n/en/index.js +44 -2
  82. package/build/i18n/en/index.js.map +1 -1
  83. package/build/i18n/fr/index.d.ts +28 -0
  84. package/build/i18n/fr/index.d.ts.map +1 -1
  85. package/build/i18n/fr/index.js +30 -2
  86. package/build/i18n/fr/index.js.map +1 -1
  87. package/build/i18n/types.d.ts +2 -0
  88. package/build/i18n/types.d.ts.map +1 -1
  89. package/build/i18n/types.js.map +1 -1
  90. package/build/index.d.ts +1 -0
  91. package/build/index.d.ts.map +1 -1
  92. package/build/index.js +2 -0
  93. package/build/index.js.map +1 -1
  94. package/build/modules/api/CardAuthentification.d.ts +24 -3
  95. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  96. package/build/modules/api/CardAuthentification.js +90 -12
  97. package/build/modules/api/CardAuthentification.js.map +1 -1
  98. package/build/modules/api/KYCService.d.ts +17 -7
  99. package/build/modules/api/KYCService.d.ts.map +1 -1
  100. package/build/modules/api/KYCService.js +125 -37
  101. package/build/modules/api/KYCService.js.map +1 -1
  102. package/build/modules/api/SelfieVerification.d.ts +3 -1
  103. package/build/modules/api/SelfieVerification.d.ts.map +1 -1
  104. package/build/modules/api/SelfieVerification.js +17 -1
  105. package/build/modules/api/SelfieVerification.js.map +1 -1
  106. package/build/modules/api/TemplateService.d.ts +0 -1
  107. package/build/modules/api/TemplateService.d.ts.map +1 -1
  108. package/build/modules/api/TemplateService.js +3 -3
  109. package/build/modules/api/TemplateService.js.map +1 -1
  110. package/build/modules/camera/VisionCameraModule.web.d.ts.map +1 -1
  111. package/build/modules/camera/VisionCameraModule.web.js +27 -8
  112. package/build/modules/camera/VisionCameraModule.web.js.map +1 -1
  113. package/build/types/KYC.types.d.ts +130 -5
  114. package/build/types/KYC.types.d.ts.map +1 -1
  115. package/build/types/KYC.types.js.map +1 -1
  116. package/build/types/env.types.d.ts +13 -0
  117. package/build/types/env.types.d.ts.map +1 -0
  118. package/build/types/env.types.js +2 -0
  119. package/build/types/env.types.js.map +1 -0
  120. package/build/utils/cropByObb.d.ts +7 -0
  121. package/build/utils/cropByObb.d.ts.map +1 -1
  122. package/build/utils/cropByObb.js +20 -1
  123. package/build/utils/cropByObb.js.map +1 -1
  124. package/build/utils/deviceDetection.d.ts +6 -0
  125. package/build/utils/deviceDetection.d.ts.map +1 -0
  126. package/build/utils/deviceDetection.js +12 -0
  127. package/build/utils/deviceDetection.js.map +1 -0
  128. package/build/utils/platformAlert.d.ts.map +1 -1
  129. package/build/utils/platformAlert.js.map +1 -1
  130. package/build/utils/template-transformer.d.ts.map +1 -1
  131. package/build/utils/template-transformer.js +12 -0
  132. package/build/utils/template-transformer.js.map +1 -1
  133. package/build/web/WebKYCEntry.d.ts.map +1 -1
  134. package/build/web/WebKYCEntry.js +88 -38
  135. package/build/web/WebKYCEntry.js.map +1 -1
  136. package/package.json +1 -1
  137. package/plugin/build/index.d.ts +1 -0
  138. package/plugin/build/index.js +3 -1
  139. package/plugin/build/withRemovePermissions.d.ts +3 -0
  140. package/plugin/build/withRemovePermissions.js +67 -0
  141. package/plugin/build/withVisionCamera.js +3 -4
  142. package/plugin/src/index.ts +2 -1
  143. package/plugin/src/withRemovePermissions.js +85 -0
  144. package/plugin/src/withRemovePermissions.ts +83 -0
  145. package/plugin/src/withVisionCamera.js +3 -4
  146. package/plugin/src/withVisionCamera.ts +3 -4
  147. package/plugin/tsconfig.tsbuildinfo +1 -1
  148. package/plugin.js +6 -1
  149. package/src/components/EnhancedCameraView.web.tsx +76 -21
  150. package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
  151. package/src/components/KYCElements/EmailVerificationTemplate.tsx +278 -0
  152. package/src/components/KYCElements/IDCardCapture.tsx +253 -21
  153. package/src/components/KYCElements/OrientationVideoCapture.tsx +4 -1
  154. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +4 -1
  155. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +4 -1
  156. package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
  157. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
  158. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +6 -3
  159. package/src/components/KYCElements/WelcomeTemplate.tsx +2 -1
  160. package/src/components/OverLay/type.ts +2 -0
  161. package/src/components/TemplateKYCExample.tsx +35 -46
  162. package/src/components/TemplateKYCFlowRefactored.tsx +46 -2
  163. package/src/config/KYCConfig.ts +34 -0
  164. package/src/config/allowedDomains.ts +7 -26
  165. package/src/hooks/useOrientationVideo.ts +5 -4
  166. package/src/hooks/useTemplateKYCFlow.tsx +443 -56
  167. package/src/i18n/en/index.ts +46 -3
  168. package/src/i18n/fr/index.ts +31 -2
  169. package/src/i18n/types.ts +2 -0
  170. package/src/index.ts +3 -0
  171. package/src/modules/api/CardAuthentification.ts +98 -12
  172. package/src/modules/api/KYCService.ts +158 -37
  173. package/src/modules/api/SelfieVerification.ts +25 -3
  174. package/src/modules/api/TemplateService.ts +4 -4
  175. package/src/modules/camera/VisionCameraModule.web.ts +30 -12
  176. package/src/types/KYC.types.ts +153 -6
  177. package/src/types/env.types.ts +13 -0
  178. package/src/utils/cropByObb.ts +20 -1
  179. package/src/utils/deviceDetection.ts +11 -0
  180. package/src/utils/platformAlert.ts +1 -0
  181. package/src/utils/template-transformer.ts +20 -8
  182. package/src/web/WebKYCEntry.tsx +123 -61
@@ -15,6 +15,19 @@ export const en = {
15
15
  success: 'Success',
16
16
  warning: 'Warning',
17
17
  info: 'Information',
18
+ invalidCode: 'Invalid code',
19
+ resendCode: 'Resend code',
20
+ verify: 'Verify',
21
+ sendCode: 'Send code',
22
+ resend: 'Resend',
23
+ email: 'Email',
24
+ otp: 'OTP',
25
+ verificationCode: 'Verification code',
26
+ changeEmail: 'Change Email',
27
+ processing: "Processing",
28
+ codeResent: 'Code Resent',
29
+ codeResentMessage: 'Code resent to %{email}',
30
+ phone: 'Phone number with country code',
18
31
  },
19
32
 
20
33
  // KYC Flow
@@ -22,7 +35,22 @@ export const en = {
22
35
  title: 'Identity Verification',
23
36
  subtitle: 'Complete your identity verification to continue',
24
37
  step: 'Step %{current} of %{total}',
25
-
38
+ enterCodeSent: "Enter code sent",
39
+
40
+ additionalDocs: {
41
+ title: 'Additional Documents',
42
+ add: "Press to select file",
43
+ subtitle: 'Upload additional documents to complete your identity verification',
44
+ uploadButton: 'Upload',
45
+ uploadButtonText: 'Upload',
46
+ uploadSubtext: 'Upload a clear photo of your document',
47
+ supportedFormats: 'Supported formats: JPG, PNG, PDF',
48
+ maxSize: 'Max file size: 10MB',
49
+ processing: 'Processing file...',
50
+ success: 'File uploaded successfully',
51
+ error: 'Failed to upload file. Please try again.'
52
+ },
53
+
26
54
  // Initialization
27
55
  initialization: {
28
56
  title: 'Welcome to Identity Verification',
@@ -43,7 +71,8 @@ export const en = {
43
71
  privacyPolicy: 'I agree to the Privacy Policy',
44
72
  termsOfService: 'I agree to the Terms of Service',
45
73
  marketingConsent: 'I agree to receive marketing communications',
46
- readMore: 'Read more'
74
+ readMore: 'Read more',
75
+ getStarted: 'Get Started'
47
76
  },
48
77
 
49
78
  // Location Capture
@@ -98,6 +127,10 @@ export const en = {
98
127
  success: 'ID captured successfully',
99
128
  error: 'Failed to capture ID. Please try again.',
100
129
  captureTitle: '%{side} side of your government document',
130
+ cardNotFullyInFrame: 'The ID is not fully in frame. Position the entire card within the frame.',
131
+ continueOnPhone: 'Continue on Phone',
132
+ continueOnMobile: 'Continue on Mobile',
133
+ scanQrCode: 'Scan this QR code with your phone to continue the verification process.',
101
134
  },
102
135
 
103
136
  // Selfie Capture
@@ -214,7 +247,17 @@ export const en = {
214
247
  cameraError: 'Camera error. Please try again.',
215
248
  fileError: 'File error. Please try again.',
216
249
  timeoutError: 'Request timeout. Please try again.',
217
- unknownError: 'An unknown error occurred. Please try again.'
250
+ unknownError: 'An unknown error occurred. Please try again.',
251
+ wrongCode: "Wrong code",
252
+ invalidCode: "Invalid code",
253
+ invalidEmail: "Invalid email",
254
+ invalidPhone: "Invalid phone",
255
+ invalidDate: "Invalid date",
256
+ minLength: "Min length",
257
+ maxLength: "Max length",
258
+ invalidFormat: "Invalid format",
259
+ fileTooLarge: "File too large",
260
+ fileTypeNotSupported: "File type not supported",
218
261
  },
219
262
 
220
263
  // Validation messages
@@ -15,6 +15,20 @@ export const fr = {
15
15
  success: 'Succès',
16
16
  warning: 'Avertissement',
17
17
  info: 'Information',
18
+ invalidCode: 'Code invalide',
19
+ resendCode: 'Renvoyer le code',
20
+ verify: 'Vérifier',
21
+ sendCode: 'Envoyer le code',
22
+ resend: 'Renvoyer',
23
+ email: 'Email',
24
+ otp: 'OTP',
25
+ verificationCode: 'Code de vérification',
26
+ changeEmail: 'Changer d\'email',
27
+ processing: "Traitement",
28
+ codeResent: 'Code Renvoyé',
29
+ codeResentMessage: 'Code renvoyé à %{email}',
30
+ phone: 'Numéro de téléphone avec code pays',
31
+
18
32
  },
19
33
 
20
34
  // KYC Flow
@@ -42,7 +56,8 @@ export const fr = {
42
56
  privacyPolicy: 'J\'accepte la Politique de Confidentialité',
43
57
  termsOfService: 'J\'accepte les Conditions d\'Utilisation',
44
58
  marketingConsent: 'J\'accepte de recevoir des communications marketing',
45
- readMore: 'Lire plus'
59
+ readMore: 'Lire plus',
60
+ getStarted: 'Commencer'
46
61
  },
47
62
 
48
63
  // Location Capture
@@ -97,6 +112,10 @@ export const fr = {
97
112
  success: 'Document d\'identité capturé avec succès',
98
113
  error: 'Échec de la capture du document. Veuillez réessayer.',
99
114
  captureTitle: '%{side} de votre document d\'identité',
115
+ cardNotFullyInFrame: 'La carte n\'est pas entièrement visible. Positionnez toute la carte dans le cadre.',
116
+ continueOnPhone: 'Continuer sur mobile',
117
+ continueOnMobile: 'Continuer sur mobile',
118
+ scanQrCode: 'Scannez ce code QR avec votre téléphone pour continuer la vérification.',
100
119
  },
101
120
 
102
121
  // Selfie Capture
@@ -213,7 +232,17 @@ export const fr = {
213
232
  cameraError: 'Erreur caméra. Veuillez réessayer.',
214
233
  fileError: 'Erreur fichier. Veuillez réessayer.',
215
234
  timeoutError: 'Délai d\'attente dépassé. Veuillez réessayer.',
216
- unknownError: 'Une erreur inconnue s\'est produite. Veuillez réessayer.'
235
+ unknownError: 'Une erreur inconnue s\'est produite. Veuillez réessayer.',
236
+ wrongCode: "Wrong code",
237
+ invalidCode: "Invalid code",
238
+ invalidEmail: "Invalid email",
239
+ invalidPhone: "Invalid phone",
240
+ invalidDate: "Invalid date",
241
+ minLength: "Min length",
242
+ maxLength: "Max length",
243
+ invalidFormat: "Invalid format",
244
+ fileTooLarge: "File too large",
245
+ fileTypeNotSupported: "File type not supported",
217
246
  },
218
247
 
219
248
  validation: {
package/src/i18n/types.ts CHANGED
@@ -53,6 +53,8 @@ export interface KYCTranslations {
53
53
  processing: string;
54
54
  success: string;
55
55
  error: string;
56
+ captureTitle?: string;
57
+ cardNotFullyInFrame?: string;
56
58
  };
57
59
 
58
60
  selfieCapture: {
package/src/index.ts CHANGED
@@ -4,6 +4,9 @@ export * from './TransfergratisSdk.types';
4
4
  // Export KYC types
5
5
  export * from './types/KYC.types';
6
6
 
7
+ // Export Environment types
8
+ export * from './types/env.types';
9
+
7
10
 
8
11
  export { TemplateKYCExample as LauchTransferGratisKYC } from './components/TemplateKYCExample';
9
12
 
@@ -1,20 +1,51 @@
1
1
  import kycService, { authentification, errorMessage, truncateFields } from "./KYCService";
2
- import { cropByObb } from "../../utils/cropByObb";
2
+ import { cropByObb, getObbConfidence, OBB_CONFIDENCE_THRESHOLD } from "../../utils/cropByObb";
3
3
  import { GovernmentDocumentType, IBbox } from "../../types/KYC.types";
4
+ import { KycEnvironment } from "../../types/env.types";
4
5
  import { logger } from "../../utils/logger";
5
6
 
6
- export async function frontVerification(result: { path?: string, regionMapping: { authMethod: string[], mrzTypes: string[] }, selectedDocumentType: string, code: string, currentSide: string, templatePath?: string, mrzType?: string }) {
7
+ export async function frontVerification(result: { path?: string, regionMapping: { authMethod: string[], mrzTypes: string[] }, selectedDocumentType: string, code: string, currentSide: string, templatePath?: string, mrzType?: string }, env: KycEnvironment = 'PRODUCTION') {
7
8
  try {
8
9
  console.log("Front verification START", JSON.stringify({ result }, null, 2));
9
10
  logger.log("Front verification", JSON.stringify({ result }, null, 2));
11
+
12
+ // SANDBOX mode: skip AI verification and return mock response
13
+ if (env === 'SANDBOX') {
14
+ console.log("SANDBOX mode: Skipping AI verification for front document");
15
+ logger.log("SANDBOX mode: Returning mock front verification response");
16
+ const mockBbox: IBbox = { minX: 50, minY: 50, width: 200, height: 200 };
17
+ const mockResponse = {
18
+ result: true,
19
+ detail: [{ confidence: 0.95 }],
20
+ card_obb: { x: 50, y: 50, width: 200, height: 200 },
21
+ bbox: mockBbox,
22
+ ...(result.regionMapping.authMethod.includes('MRZ') ? {
23
+ mrz: {
24
+ success: true,
25
+ parsed_data: {
26
+ status: 'success',
27
+ document_type: result.selectedDocumentType,
28
+ mrz_type: result.mrzType || 'TD1'
29
+ }
30
+ }
31
+ } : {})
32
+ };
33
+ return mockResponse;
34
+ }
35
+
10
36
  const token = await authentification();
11
- const detected = await kycService.detectFaceOnId(result?.path || '', token, result?.selectedDocumentType || '')
37
+ const detected = await kycService.detectFaceOnId(result?.path || '', token, result?.selectedDocumentType || '', env)
12
38
 
13
39
  if (!detected.result) {
14
40
  throw new Error('Aucun visage détecté sur la carte. Veuillez reprendre.');
15
41
  }
16
42
 
17
- // Optional: crop image using card_obb for better MRZ/barcode extraction
43
+ const obbConfidence = getObbConfidence((detected as any).card_obb);
44
+ if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
45
+ throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
46
+ }
47
+
48
+ // Optional: crop image using card_obb for better MRZ/barcode extraction (only when confidence >= threshold)
18
49
  let croppedBase64: string | undefined;
19
50
  let bbox: IBbox | undefined;
20
51
  let mrz: any | undefined;
@@ -36,7 +67,8 @@ export async function frontVerification(result: { path?: string, regionMapping:
36
67
  token: token,
37
68
  template_path: result?.templatePath || '',
38
69
  mrz_type: result?.mrzType || ''
39
- })
70
+ },
71
+ env)
40
72
 
41
73
  }
42
74
 
@@ -47,10 +79,38 @@ export async function frontVerification(result: { path?: string, regionMapping:
47
79
  }
48
80
  }
49
81
 
50
- export async function backVerification(result: { path?: string, regionMapping: { authMethod: string[], mrzTypes: string[] }, selectedDocumentType: string, code: string, currentSide: string, templatePath?: string, mrzType?: string }) {
82
+ export async function backVerification(result: { path?: string, regionMapping: { authMethod: string[], mrzTypes: string[] }, selectedDocumentType: string, code: string, currentSide: string, templatePath?: string, mrzType?: string }, env: KycEnvironment = 'PRODUCTION') {
51
83
  try {
52
84
  if (!result.path) throw new Error('No path provided');
53
85
  logger.log("result.regionMapping", result.regionMapping, result.currentSide, result.code);
86
+
87
+ // SANDBOX mode: skip AI verification and return mock response
88
+ if (env === 'SANDBOX') {
89
+ console.log("SANDBOX mode: Skipping AI verification for back document");
90
+ logger.log("SANDBOX mode: Returning mock back verification response");
91
+ const mockBbox: IBbox = { minX: 50, minY: 50, width: 200, height: 200 };
92
+
93
+ if (result.regionMapping.authMethod.includes('MRZ')) {
94
+ return {
95
+ success: true,
96
+ parsed_data: {
97
+ status: 'success',
98
+ document_type: result.selectedDocumentType,
99
+ mrz_type: result.mrzType || 'TD1'
100
+ },
101
+ bbox: mockBbox,
102
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
103
+ };
104
+ } else if (result.regionMapping.authMethod.includes('2D_barcode')) {
105
+ return {
106
+ barcode_data: 'SANDBOX_MOCK_BARCODE',
107
+ bbox: mockBbox,
108
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
109
+ };
110
+ }
111
+ return { bbox: mockBbox };
112
+ }
113
+
54
114
  const token = await authentification();
55
115
 
56
116
 
@@ -68,7 +128,11 @@ export async function backVerification(result: { path?: string, regionMapping: {
68
128
  token: token,
69
129
  template_path: result?.templatePath || '',
70
130
  mrz_type: result?.mrzType || ''
71
- });
131
+ }, env);
132
+ const mrzObbConf = getObbConfidence((mrz as any).card_obb);
133
+ if (mrzObbConf !== null && mrzObbConf < OBB_CONFIDENCE_THRESHOLD) {
134
+ throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
135
+ }
72
136
  let bbox: IBbox | undefined;
73
137
  let croppedBase64: string | undefined;
74
138
 
@@ -82,7 +146,11 @@ export async function backVerification(result: { path?: string, regionMapping: {
82
146
  } catch (mrzError: any) {
83
147
  logger.log("MRZ échoué, tentative d'extraction barcode");
84
148
  try {
85
- const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token });
149
+ const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token }, env);
150
+ const barcodeObbConf = getObbConfidence((barcode as any).card_obb);
151
+ if (barcodeObbConf !== null && barcodeObbConf < OBB_CONFIDENCE_THRESHOLD) {
152
+ throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
153
+ }
86
154
  return barcode;
87
155
  } catch (barcodeError: any) {
88
156
  throw new Error(`MRZ et barcode ont échoué. MRZ: ${mrzError?.message}, Barcode: ${barcodeError?.message}`);
@@ -108,7 +176,11 @@ export async function backVerification(result: { path?: string, regionMapping: {
108
176
  token: token,
109
177
  template_path: result?.templatePath || '',
110
178
  mrz_type: result?.mrzType || ''
111
- });
179
+ }, env);
180
+ const mrzObbConf = getObbConfidence((mrz as any).card_obb);
181
+ if (mrzObbConf !== null && mrzObbConf < OBB_CONFIDENCE_THRESHOLD) {
182
+ throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
183
+ }
112
184
  let bbox: IBbox | undefined;
113
185
  try {
114
186
  const crop = await cropByObb(result?.path || '', (mrz as any).card_obb);
@@ -123,7 +195,11 @@ export async function backVerification(result: { path?: string, regionMapping: {
123
195
  if (result.regionMapping.authMethod.length > 0 && result.regionMapping.authMethod.includes('2D_barcode')) {
124
196
  try {
125
197
  logger.log("Tentative d'extraction barcode");
126
- const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token });
198
+ const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token }, env);
199
+ const barcodeObbConf = getObbConfidence((barcode as any).card_obb);
200
+ if (barcodeObbConf !== null && barcodeObbConf < OBB_CONFIDENCE_THRESHOLD) {
201
+ throw new Error('Carte non entièrement visible. Positionnez toute la carte dans le cadre.');
202
+ }
127
203
  let bbox: IBbox | undefined;
128
204
  try {
129
205
  const crop = await cropByObb(result?.path || '', (barcode as any).card_obb);
@@ -145,10 +221,20 @@ export async function backVerification(result: { path?: string, regionMapping: {
145
221
  * @param result
146
222
  * @returns
147
223
  */
148
- export async function checkTemplateType(result: { path?: string, docType: string, docRegion: string, postfix: string }) {
224
+ export async function checkTemplateType(result: { path?: string, docType: string, docRegion: string, postfix: string }, env: KycEnvironment = 'PRODUCTION') {
149
225
  try {
226
+ // SANDBOX mode: skip AI verification and return mock response
227
+ if (env === 'SANDBOX') {
228
+ console.log("SANDBOX mode: Skipping AI template type check");
229
+ logger.log("SANDBOX mode: Returning mock template type response");
230
+ return {
231
+ template_path: `templates/${result.docType}_${result.docRegion}_${result.postfix}.jpg`,
232
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
233
+ };
234
+ }
235
+
150
236
  const token = await authentification();
151
- const templateType = await kycService.checkTemplateType({ fileUri: result.path || '', docType: result?.docType as GovernmentDocumentType, docRegion: result?.docRegion || "", postfix: result?.postfix, token: token });
237
+ const templateType = await kycService.checkTemplateType({ fileUri: result.path || '', docType: result?.docType as GovernmentDocumentType, docRegion: result?.docRegion || "", postfix: result?.postfix, token: token }, env);
152
238
 
153
239
  logger.log("templateType result", JSON.stringify(truncateFields(templateType), null, 2));
154
240
  return templateType;
@@ -2,7 +2,21 @@ import axios from 'axios';
2
2
  import { GovernmentDocumentType, GovernmentDocumentTypeShorted, OrientationVideoResponse } from '../../types/KYC.types';
3
3
  import { CheckTemplateTypeResponse, ExtractMrzTextResponse } from '../../components/OverLay/type';
4
4
  import { SessionResponse, VerificationResult, VerificationSessionRequest } from './types';
5
+ import { KycEnvironment } from '../../types/env.types';
5
6
  import { logger } from '../../utils/logger';
7
+ import { Platform } from 'react-native';
8
+ import KYCConfig from '../../config/KYCConfig';
9
+
10
+ const appendFileToFormData = async (formData: FormData, key: string, uri: string, name: string = 'file.jpg', type: string = 'image/jpeg') => {
11
+ if (Platform.OS === 'web') {
12
+ const response = await fetch(uri);
13
+ const blob = await response.blob();
14
+ formData.append(key, blob, name);
15
+ } else {
16
+ formData.append(key, { uri, type, name } as any);
17
+ }
18
+ };
19
+
6
20
 
7
21
  export interface KYCRequest {
8
22
  userId: string;
@@ -48,7 +62,6 @@ export class KYCService {
48
62
  private mrzServiceURL = 'https://kyc-engine.transfergratis.net:8002';
49
63
  private barcodeServiceURL = 'https://kyc-engine.transfergratis.net:8000';
50
64
  private orientationServiceURL = 'http://18.188.180.154:8080';
51
- private backendServiceURL = 'https://service.sanctumkey.com/api/v1';
52
65
 
53
66
  constructor(baseURL: string, apiKey: string) {
54
67
  this.baseURL = baseURL;
@@ -120,12 +133,8 @@ export class KYCService {
120
133
  async processSelfieOrientationPicture(videoFile: string, token: string): Promise<SelfieVideoResponse[]> {
121
134
  try {
122
135
  const formData = new FormData();
123
- const rnFile: any = {
124
- uri: videoFile,
125
- type: 'image/jpeg',
126
- name: 'selfie_picture.jpg',
127
- };
128
- formData.append('file', rnFile);
136
+ await appendFileToFormData(formData, 'file', videoFile, 'selfie_picture.jpg', 'image/jpeg');
137
+
129
138
  const response = await axios.post<SelfieVideoResponse[]>(
130
139
  `${this.faceServiceURL}/detect_face_orientation/`,
131
140
  formData,
@@ -141,17 +150,27 @@ export class KYCService {
141
150
  }
142
151
  }
143
152
 
144
- async processOrientationVideo(videoFile: string): Promise<OrientationVideoResponse> {
153
+ async processOrientationVideo(videoFile: string, env: KycEnvironment = 'PRODUCTION'): Promise<OrientationVideoResponse> {
145
154
  try {
155
+ // SANDBOX mode: skip AI verification and return mock response
156
+ if (env === 'SANDBOX') {
157
+ console.log("SANDBOX mode: Skipping AI orientation video processing");
158
+ logger.log("SANDBOX mode: Returning mock orientation video response");
159
+ return {
160
+ success: true,
161
+ data: {
162
+ center: { captured: true, frame: 10 },
163
+ left: { captured: true, frame: 30 },
164
+ right: { captured: true, frame: 50 }
165
+ },
166
+ message: 'SANDBOX: Orientation video processed successfully (mocked)'
167
+ };
168
+ }
169
+
146
170
  // Create FormData for multipart/form-data request
147
171
  const formData = new FormData();
148
- // In React Native, prefer the { uri, type, name } pattern rather than Blob
149
- const rnFile: any = {
150
- uri: videoFile,
151
- type: 'video/mp4',
152
- name: 'orientation_video.mp4',
153
- };
154
- formData.append('file', rnFile);
172
+ await appendFileToFormData(formData, 'file', videoFile, 'orientation_video.mp4', 'video/mp4');
173
+
155
174
 
156
175
  const response = await axios.post(
157
176
  `${this.orientationServiceURL}/process_orientation_video_stream/`,
@@ -196,10 +215,21 @@ export class KYCService {
196
215
  }
197
216
 
198
217
  // STEP 1 - ID CARD VALIDATION
199
- async detectFaceOnId(idCardImageUri: string, token: string, docType: string): Promise<{ result: boolean, detail: any[] }> {
218
+ async detectFaceOnId(idCardImageUri: string, token: string, docType: string, env: KycEnvironment = 'PRODUCTION'): Promise<{ result: boolean, detail: any[] }> {
219
+ // SANDBOX mode: skip AI verification and return mock response
220
+ if (env === 'SANDBOX') {
221
+ console.log("SANDBOX mode: Skipping AI face detection on ID");
222
+ logger.log("SANDBOX mode: Returning mock face detection response");
223
+ return {
224
+ result: true,
225
+ detail: [{ confidence: 0.95, bbox: [50, 50, 200, 200] }],
226
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
227
+ } as any;
228
+ }
229
+
200
230
  const formData = new FormData();
201
- const rnFile: any = { uri: idCardImageUri, type: 'image/jpeg', name: 'id_card_photo.jpg' };
202
- formData.append('file', rnFile);
231
+ await appendFileToFormData(formData, 'file', idCardImageUri, 'id_card_photo.jpg', 'image/jpeg');
232
+
203
233
  logger.log('detectFaceOnId formData', JSON.stringify(formData, null, 2));
204
234
 
205
235
  const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
@@ -221,11 +251,22 @@ export class KYCService {
221
251
  }
222
252
 
223
253
  //check templatetemplate_type
224
- async checkTemplateType(params: { fileUri: string; docType: string; docRegion: string; token: string; postfix: string }): Promise<any> {
254
+ async checkTemplateType(params: { fileUri: string; docType: string; docRegion: string; token: string; postfix: string }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
255
+ // SANDBOX mode: skip AI verification and return mock response
256
+ if (env === 'SANDBOX') {
257
+ console.log("SANDBOX mode: Skipping AI template type check");
258
+ logger.log("SANDBOX mode: Returning mock template type response");
259
+ const { docType, docRegion, postfix } = params;
260
+ return {
261
+ template_path: `templates/${docType}_${docRegion}_${postfix}.jpg`,
262
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
263
+ };
264
+ }
265
+
225
266
  const { fileUri, docType, docRegion, token, postfix } = params;
226
267
  const formData = new FormData();
227
- const rnFile: any = { uri: fileUri, type: 'image/jpeg', name: 'id_card_front.jpg' };
228
- formData.append('file', rnFile);
268
+ await appendFileToFormData(formData, 'file', fileUri, 'id_card_front.jpg', 'image/jpeg');
269
+
229
270
  const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
230
271
  const url = `${this.mrzServiceURL}/get_template_version/?doc_type=${encodeURIComponent(docTypeShorted)}&doc_region=${encodeURIComponent(docRegion)}&postfix=${postfix}`;
231
272
 
@@ -247,8 +288,8 @@ export class KYCService {
247
288
  async extractDocumentInformation(params: { fileUri: string; docType: string; docRegion: string; token: string }): Promise<any> {
248
289
  const { fileUri, docType, docRegion, token } = params;
249
290
  const formData = new FormData();
250
- const rnFile: any = { uri: fileUri, type: 'image/jpeg', name: 'id_card_front.jpg' };
251
- formData.append('file', rnFile);
291
+ await appendFileToFormData(formData, 'file', fileUri, 'id_card_front.jpg', 'image/jpeg');
292
+
252
293
 
253
294
  const url = `${this.textExtractionServiceURL}/extract_doc_information/?doc_type=${encodeURIComponent(docType)}&doc_region=${encodeURIComponent(docRegion)}`;
254
295
  const attempt = async () => {
@@ -271,11 +312,21 @@ export class KYCService {
271
312
  }
272
313
  }
273
314
  // STEP 2 - barcode extraction
274
- async extractBarcode(params: { fileUri: string; token: string }): Promise<any> {
315
+ async extractBarcode(params: { fileUri: string; token: string }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
316
+ // SANDBOX mode: skip AI verification and return mock response
317
+ if (env === 'SANDBOX') {
318
+ console.log("SANDBOX mode: Skipping AI barcode extraction");
319
+ logger.log("SANDBOX mode: Returning mock barcode response");
320
+ return {
321
+ barcode_data: 'SANDBOX_MOCK_BARCODE_DATA',
322
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
323
+ };
324
+ }
325
+
275
326
  const { fileUri, token } = params;
276
327
  const formData = new FormData();
277
- const rnFile: any = { uri: fileUri, type: 'image/jpeg', name: 'id_card_back.jpg' };
278
- formData.append('file', rnFile);
328
+ await appendFileToFormData(formData, 'file', fileUri, 'id_card_back.jpg', 'image/jpeg');
329
+
279
330
 
280
331
  const url = `${this.barcodeServiceURL}/decode_barcode/`;
281
332
  const attempt = async () => {
@@ -300,11 +351,29 @@ export class KYCService {
300
351
  }
301
352
  }
302
353
  // STEP 3 - MRZ TEXT EXTRACTION
303
- async extractMrzText(params: { fileUri: string; docType: string; docRegion: string; postfix?: string; token: string; template_path: string; mrz_type: string }): Promise<any> {
354
+ async extractMrzText(params: { fileUri: string; docType: string; docRegion: string; postfix?: string; token: string; template_path: string; mrz_type: string }, env: KycEnvironment = 'PRODUCTION'): Promise<any> {
355
+ // SANDBOX mode: skip AI verification and return mock response
356
+ if (env === 'SANDBOX') {
357
+ console.log("SANDBOX mode: Skipping AI MRZ extraction");
358
+ logger.log("SANDBOX mode: Returning mock MRZ response");
359
+ const { docType, docRegion, postfix = 'back', mrz_type } = params;
360
+ return {
361
+ success: true,
362
+ parsed_data: {
363
+ status: 'success',
364
+ document_type: docType,
365
+ mrz_type: mrz_type || 'TD1',
366
+ doc_region: docRegion,
367
+ postfix: postfix
368
+ },
369
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
370
+ };
371
+ }
372
+
304
373
  const { fileUri, docType, docRegion, postfix = 'back', token, template_path, mrz_type } = params;
305
374
  const formData = new FormData();
306
- const rnFile: any = { uri: fileUri, type: 'image/jpeg', name: 'id_card_back.jpg' };
307
- formData.append('file', rnFile);
375
+ await appendFileToFormData(formData, 'file', fileUri, 'id_card_back.jpg', 'image/jpeg');
376
+
308
377
  const docTypeShorted = GovernmentDocumentTypeShorted[docType as GovernmentDocumentType];
309
378
  logger.log("docTypeShorted", docTypeShorted, docRegion, postfix);
310
379
 
@@ -335,13 +404,24 @@ export class KYCService {
335
404
  }
336
405
 
337
406
  // STEP 2 - SELFIE VALIDATION
338
- async recognizeFace(params: { idPhotoUri: string; selfiePhotoUri: string }): Promise<{ is_match: boolean; similarity: number; id_bbox?: number[]; selfie_bbox?: number[] }> {
407
+ async recognizeFace(params: { idPhotoUri: string; selfiePhotoUri: string }, env: KycEnvironment = 'PRODUCTION'): Promise<{ is_match: boolean; similarity: number; id_bbox?: number[]; selfie_bbox?: number[] }> {
408
+ // SANDBOX mode: skip AI verification and return mock response
409
+ if (env === 'SANDBOX') {
410
+ console.log("SANDBOX mode: Skipping AI face recognition");
411
+ logger.log("SANDBOX mode: Returning mock face recognition response");
412
+ return {
413
+ is_match: true,
414
+ similarity: 0.95,
415
+ id_bbox: [50, 50, 200, 200],
416
+ selfie_bbox: [50, 50, 200, 200]
417
+ };
418
+ }
419
+
339
420
  const { idPhotoUri, selfiePhotoUri } = params;
340
421
  const formData = new FormData();
341
- const idFile: any = { uri: idPhotoUri, type: 'image/jpeg', name: 'id_card_photo.jpg' };
342
- const selfieFile: any = { uri: selfiePhotoUri, type: 'image/jpeg', name: 'selfie_final.jpg' };
343
- formData.append('id_photo', idFile);
344
- formData.append('selfie_photo', selfieFile);
422
+ await appendFileToFormData(formData, 'id_photo', idPhotoUri, 'id_card_photo.jpg', 'image/jpeg');
423
+ await appendFileToFormData(formData, 'selfie_photo', selfiePhotoUri, 'selfie_final.jpg', 'image/jpeg');
424
+
345
425
 
346
426
  const res = await axios.post(`${this.faceServiceURL}/recognize_face/`, formData, { timeout: 45000 });
347
427
  if (res.data?.detail) throw new Error(res.data.detail);
@@ -375,7 +455,7 @@ export class KYCService {
375
455
  "status": "PENDING",
376
456
  "metadata": {},
377
457
  }
378
- const res = await axios.post<SessionResponse>(`${this.backendServiceURL}/verification/sessions/`,
458
+ const res = await axios.post<SessionResponse>(`${KYCConfig.getBackendUrl()}/verification/sessions/`,
379
459
  data, {
380
460
  headers: {
381
461
  'Content-Type': 'application/json',
@@ -419,7 +499,7 @@ export class KYCService {
419
499
  ...({ session_id: session_id }),
420
500
  timestamp: new Date().toISOString()
421
501
  }
422
- const url = `${this.backendServiceURL}/verification/api/kyc/sessions/${session_id}/steps/${step}/`;
502
+ const url = `${KYCConfig.getBackendUrl()}/verification/api/kyc/sessions/${session_id}/steps/${step}/`;
423
503
 
424
504
 
425
505
  const logPayload = truncateFields({ payloadData, session_id, step });
@@ -444,11 +524,52 @@ export class KYCService {
444
524
  throw new Error(errorMessage(error));
445
525
  }
446
526
  }
527
+
528
+ /** Send email verification code. POST /api/v1/accounts/email/send/ */
529
+ async sendEmailVerificationCode(
530
+ email: string,
531
+ auth?: { apiKey?: string; token?: string }
532
+ ): Promise<unknown> {
533
+ const url = `${KYCConfig.getBackendUrl()}/accounts/email/send/`;
534
+ const token = auth?.apiKey ? undefined : (auth?.token ?? await authentification());
535
+ const res = await axios.post(
536
+ url,
537
+ { email },
538
+ {
539
+ headers: {
540
+ 'Content-Type': 'application/json',
541
+ ...(auth?.apiKey ? { 'Authorization': `ApiKey ${auth.apiKey}` } : { 'Authorization': `Bearer ${token}` }),
542
+ },
543
+ }
544
+ );
545
+ return res.data;
546
+ }
547
+
548
+ /** Verify email with OTP. POST /api/v1/accounts/email/verify/ */
549
+ async verifyEmailCode(
550
+ otp: string,
551
+ auth?: { apiKey?: string; token?: string }
552
+ ): Promise<unknown> {
553
+ const url = `${KYCConfig.getBackendUrl()}/accounts/email/verify/`;
554
+ const token = auth?.apiKey ? undefined : (auth?.token ?? await authentification());
555
+ const res = await axios.post(
556
+ url,
557
+ { otp },
558
+ {
559
+ headers: {
560
+ 'Content-Type': 'application/json',
561
+ ...(auth?.apiKey ? { 'Authorization': `ApiKey ${auth.apiKey}` } : { 'Authorization': `Bearer ${token}` }),
562
+ },
563
+ }
564
+ );
565
+ return res.data;
566
+ }
567
+
447
568
  // reultat de la verification
448
569
  async getVerificationResult(session_id: string): Promise<VerificationResult> {
449
570
  try {
450
571
  const token = await authentification();
451
- const url = `${this.backendServiceURL}/verification/api/kyc/result/?session_id=${session_id}`;
572
+ const url = `${KYCConfig.getBackendUrl()}/verification/api/kyc/result/?session_id=${session_id}`;
452
573
  const res = await axios.get<VerificationResult>(url,
453
574
  { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } });
454
575
  logger.log('getVerificationResult res', JSON.stringify(truncateFields(res.data), null, 2));