@transfergratis/react-native-sdk 0.1.24 → 0.1.25

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 (149) 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 +10 -7
  14. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts +12 -0
  15. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
  16. package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
  17. package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
  18. package/build/components/KYCElements/EmailVerificationTemplate.d.ts +12 -0
  19. package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
  20. package/build/components/KYCElements/EmailVerificationTemplate.js +193 -0
  21. package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
  22. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  23. package/build/components/KYCElements/IDCardCapture.js +180 -7
  24. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  25. package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -0
  26. package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
  27. package/build/components/KYCElements/OrientationVideoCapture.js +2 -2
  28. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  29. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
  30. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
  31. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +2 -2
  32. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  33. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
  34. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
  35. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +2 -2
  36. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  37. package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
  38. package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
  39. package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
  40. package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
  41. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
  42. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
  43. package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
  44. package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
  45. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  46. package/build/components/KYCElements/SelfieCaptureTemplate.js +7 -3
  47. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  48. package/build/components/TemplateKYCExample.d.ts +4 -0
  49. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  50. package/build/components/TemplateKYCExample.js +7 -30
  51. package/build/components/TemplateKYCExample.js.map +1 -1
  52. package/build/components/TemplateKYCFlowRefactored.d.ts +4 -0
  53. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  54. package/build/components/TemplateKYCFlowRefactored.js +14 -2
  55. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  56. package/build/config/KYCConfig.d.ts +14 -0
  57. package/build/config/KYCConfig.d.ts.map +1 -0
  58. package/build/config/KYCConfig.js +26 -0
  59. package/build/config/KYCConfig.js.map +1 -0
  60. package/build/config/allowedDomains.d.ts.map +1 -1
  61. package/build/config/allowedDomains.js +4 -19
  62. package/build/config/allowedDomains.js.map +1 -1
  63. package/build/hooks/useOrientationVideo.d.ts +2 -1
  64. package/build/hooks/useOrientationVideo.d.ts.map +1 -1
  65. package/build/hooks/useOrientationVideo.js +3 -3
  66. package/build/hooks/useOrientationVideo.js.map +1 -1
  67. package/build/hooks/useTemplateKYCFlow.d.ts +6 -1
  68. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  69. package/build/hooks/useTemplateKYCFlow.js +286 -23
  70. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  71. package/build/i18n/en/index.d.ts +40 -0
  72. package/build/i18n/en/index.d.ts.map +1 -1
  73. package/build/i18n/en/index.js +41 -1
  74. package/build/i18n/en/index.js.map +1 -1
  75. package/build/i18n/fr/index.d.ts +26 -0
  76. package/build/i18n/fr/index.d.ts.map +1 -1
  77. package/build/i18n/fr/index.js +27 -1
  78. package/build/i18n/fr/index.js.map +1 -1
  79. package/build/index.d.ts +1 -0
  80. package/build/index.d.ts.map +1 -1
  81. package/build/index.js +2 -0
  82. package/build/index.js.map +1 -1
  83. package/build/modules/api/CardAuthentification.d.ts +24 -3
  84. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  85. package/build/modules/api/CardAuthentification.js +68 -10
  86. package/build/modules/api/CardAuthentification.js.map +1 -1
  87. package/build/modules/api/KYCService.d.ts +7 -7
  88. package/build/modules/api/KYCService.d.ts.map +1 -1
  89. package/build/modules/api/KYCService.js +101 -37
  90. package/build/modules/api/KYCService.js.map +1 -1
  91. package/build/modules/api/SelfieVerification.d.ts +3 -1
  92. package/build/modules/api/SelfieVerification.d.ts.map +1 -1
  93. package/build/modules/api/SelfieVerification.js +17 -1
  94. package/build/modules/api/SelfieVerification.js.map +1 -1
  95. package/build/modules/api/TemplateService.d.ts +0 -1
  96. package/build/modules/api/TemplateService.d.ts.map +1 -1
  97. package/build/modules/api/TemplateService.js +3 -3
  98. package/build/modules/api/TemplateService.js.map +1 -1
  99. package/build/types/KYC.types.d.ts +124 -3
  100. package/build/types/KYC.types.d.ts.map +1 -1
  101. package/build/types/KYC.types.js.map +1 -1
  102. package/build/types/env.types.d.ts +13 -0
  103. package/build/types/env.types.d.ts.map +1 -0
  104. package/build/types/env.types.js +2 -0
  105. package/build/types/env.types.js.map +1 -0
  106. package/build/utils/deviceDetection.d.ts +6 -0
  107. package/build/utils/deviceDetection.d.ts.map +1 -0
  108. package/build/utils/deviceDetection.js +12 -0
  109. package/build/utils/deviceDetection.js.map +1 -0
  110. package/build/utils/platformAlert.d.ts.map +1 -1
  111. package/build/utils/platformAlert.js.map +1 -1
  112. package/build/utils/template-transformer.d.ts.map +1 -1
  113. package/build/utils/template-transformer.js +12 -0
  114. package/build/utils/template-transformer.js.map +1 -1
  115. package/build/web/WebKYCEntry.d.ts.map +1 -1
  116. package/build/web/WebKYCEntry.js +82 -38
  117. package/build/web/WebKYCEntry.js.map +1 -1
  118. package/package.json +1 -1
  119. package/plugin/build/withVisionCamera.js +3 -4
  120. package/plugin/src/withVisionCamera.js +3 -4
  121. package/plugin/src/withVisionCamera.ts +3 -4
  122. package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
  123. package/src/components/KYCElements/EmailVerificationTemplate.tsx +264 -0
  124. package/src/components/KYCElements/IDCardCapture.tsx +216 -15
  125. package/src/components/KYCElements/OrientationVideoCapture.tsx +4 -1
  126. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +4 -1
  127. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +4 -1
  128. package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
  129. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
  130. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +6 -3
  131. package/src/components/TemplateKYCExample.tsx +31 -46
  132. package/src/components/TemplateKYCFlowRefactored.tsx +27 -1
  133. package/src/config/KYCConfig.ts +34 -0
  134. package/src/config/allowedDomains.ts +7 -26
  135. package/src/hooks/useOrientationVideo.ts +5 -4
  136. package/src/hooks/useTemplateKYCFlow.tsx +314 -21
  137. package/src/i18n/en/index.ts +43 -2
  138. package/src/i18n/fr/index.ts +28 -1
  139. package/src/index.ts +3 -0
  140. package/src/modules/api/CardAuthentification.ts +75 -10
  141. package/src/modules/api/KYCService.ts +117 -37
  142. package/src/modules/api/SelfieVerification.ts +25 -3
  143. package/src/modules/api/TemplateService.ts +4 -4
  144. package/src/types/KYC.types.ts +146 -3
  145. package/src/types/env.types.ts +13 -0
  146. package/src/utils/deviceDetection.ts +11 -0
  147. package/src/utils/platformAlert.ts +1 -0
  148. package/src/utils/template-transformer.ts +20 -8
  149. package/src/web/WebKYCEntry.tsx +112 -61
@@ -1,5 +1,6 @@
1
1
  import React, { useState, useCallback, useMemo, createContext, useContext, ReactNode, useEffect } from 'react';
2
2
  import { KYCTemplate, TemplateState, TemplateActions, UseTemplateReturn, TemplateComponent, GovernmentDocumentType, VerificationState } from '../types/KYC.types';
3
+ import { KycEnvironment } from '../types/env.types';
3
4
  import kycService, { authentification, truncateFields } from '../modules/api/KYCService';
4
5
  import useI18n from './useI18n';
5
6
  import { logger } from '../utils/logger';
@@ -15,6 +16,7 @@ interface TemplateKYCFlowContextType {
15
16
  isComplete: boolean;
16
17
  getLocalizedText: (text: { en: string; fr: string;[key: string]: string }) => string;
17
18
  initializeSession: () => Promise<void>;
19
+ env: KycEnvironment;
18
20
  }
19
21
 
20
22
  const TemplateKYCFlowContext = createContext<TemplateKYCFlowContextType | undefined>(undefined);
@@ -28,6 +30,9 @@ interface TemplateKYCFlowProviderProps {
28
30
  onCancel?: () => void;
29
31
  initialLanguage?: string;
30
32
  apiKey?: string;
33
+ env?: KycEnvironment;
34
+ existingSessionId?: string;
35
+ initialStep?: number;
31
36
  }
32
37
 
33
38
  export const TemplateKYCFlowProvider: React.FC<TemplateKYCFlowProviderProps> = ({
@@ -38,8 +43,10 @@ export const TemplateKYCFlowProvider: React.FC<TemplateKYCFlowProviderProps> = (
38
43
  onCancel,
39
44
  initialLanguage = 'en',
40
45
  apiKey,
46
+ env = 'PRODUCTION',
47
+ existingSessionId,
41
48
  }) => {
42
- const hookResult = useTemplateKYCFlow(template, onComplete, onError, onCancel, initialLanguage, apiKey);
49
+ const hookResult = useTemplateKYCFlow(template, onComplete, onError, onCancel, initialLanguage, apiKey, env, existingSessionId);
43
50
 
44
51
  return (
45
52
  <TemplateKYCFlowContext.Provider value={hookResult}>
@@ -64,6 +71,9 @@ export const useTemplateKYCFlow = (
64
71
  onCancel?: () => void,
65
72
  initialLanguage: string = 'en',
66
73
  apiKey?: string,
74
+ env: KycEnvironment = 'PRODUCTION',
75
+ existingSessionId?: string,
76
+ initialStep?: number,
67
77
  ): UseTemplateReturn => {
68
78
 
69
79
  const { setLocale } = useI18n();
@@ -131,29 +141,286 @@ export const useTemplateKYCFlow = (
131
141
  const templateWithReviewAndVerification = useMemo(() => ensureVerificationProgressStep(templateWithReview), [templateWithReview, ensureVerificationProgressStep, apiKey]);
132
142
 
133
143
  // État initial du flux
134
- const buildInitialState = (): TemplateState => ({
135
- template: templateWithReviewAndVerification,
136
- currentComponentIndex: 0,
137
- completedComponents: [],
138
- componentData: {},
139
- errors: {},
140
- isProcessing: false,
141
- currentLanguage: initialLanguage,
142
- showCustomStepper: true,
143
- session: {
144
- session_id: '',
145
- token: '',
146
- isInitialized: false,
144
+ const buildInitialState = (): TemplateState => {
145
+ // Valider initialStep pour s'assurer qu'il est dans les limites du template
146
+ let validInitialStep = 0;
147
+ let completedComponents: number[] = [];
148
+
149
+ logger.log('buildInitialState called', { initialStep, existingSessionId });
150
+
151
+ if (initialStep !== undefined && initialStep >= 0) {
152
+ const maxIndex = templateWithReviewAndVerification.components.length - 1;
153
+ const requestedStep = Math.min(initialStep, maxIndex);
154
+ const requestedComponent = templateWithReviewAndVerification.components[requestedStep];
155
+
156
+ logger.log('Processing initialStep', {
157
+ initialStep,
158
+ requestedStep,
159
+ maxIndex,
160
+ componentType: requestedComponent?.type,
161
+ componentId: requestedComponent?.id
162
+ });
163
+
164
+ // Si on reprend à l'étape id_card, on peut rester à id_card si on a une session existante
165
+ // car les données de country_selection peuvent être chargées depuis la session
166
+ if (requestedComponent?.type === 'id_card') {
167
+ // Si on a une session existante, on peut rester à id_card et charger les données
168
+ if (existingSessionId) {
169
+ logger.log('id_card with existing session - staying at id_card');
170
+ validInitialStep = requestedStep;
171
+ // Marquer les composants précédents comme complétés
172
+ if (validInitialStep > 0) {
173
+ completedComponents = templateWithReviewAndVerification.components
174
+ .slice(0, validInitialStep)
175
+ .map(component => component.id);
176
+ }
177
+ } else {
178
+ // Si pas de session, revenir à country_selection pour refaire le choix
179
+ const countrySelectionIndex = templateWithReviewAndVerification.components.findIndex(
180
+ c => c.type === 'country_selection'
181
+ );
182
+
183
+ logger.log('id_card without session - going back to country_selection', { countrySelectionIndex });
184
+
185
+ if (countrySelectionIndex >= 0) {
186
+ validInitialStep = countrySelectionIndex;
187
+ // Marquer les composants avant country_selection comme complétés
188
+ if (countrySelectionIndex > 0) {
189
+ completedComponents = templateWithReviewAndVerification.components
190
+ .slice(0, countrySelectionIndex)
191
+ .map(component => component.id);
192
+ }
193
+ } else {
194
+ // Si pas de country_selection, commencer au début
195
+ validInitialStep = 0;
196
+ }
197
+ }
198
+ } else if (requestedComponent?.type === 'review_submit') {
199
+ // Si on reprend au review_submit, on ne marque pas les composants précédents
200
+ // pour permettre à l'utilisateur de revenir en arrière et vérifier/modifier les données
201
+ validInitialStep = requestedStep;
202
+ // Ne pas marquer les composants précédents comme complétés
203
+ completedComponents = [];
204
+ } else {
205
+ // Pour les autres composants (selfie, etc.), commencer directement à l'étape demandée
206
+ validInitialStep = requestedStep;
207
+
208
+ // Marquer tous les composants précédents comme complétés
209
+ // Cela permet à l'utilisateur de continuer sans refaire les étapes précédentes
210
+ if (validInitialStep > 0) {
211
+ completedComponents = templateWithReviewAndVerification.components
212
+ .slice(0, validInitialStep)
213
+ .map(component => component.id);
214
+ }
215
+ }
216
+
217
+ logger.log('Final initial state', {
218
+ validInitialStep,
219
+ completedComponentsCount: completedComponents.length,
220
+ componentAtStep: templateWithReviewAndVerification.components[validInitialStep]?.type
221
+ });
222
+ }
223
+
224
+ return {
225
+ template: templateWithReviewAndVerification,
226
+ currentComponentIndex: validInitialStep,
227
+ completedComponents: completedComponents,
228
+ componentData: {},
229
+ errors: {},
147
230
  isProcessing: false,
148
- error: null,
149
- },
150
- verification: {
151
- status: 'idle',
152
- },
153
- });
231
+ currentLanguage: initialLanguage,
232
+ showCustomStepper: true,
233
+ session: {
234
+ session_id: existingSessionId || '',
235
+ token: '',
236
+ isInitialized: false,
237
+ isProcessing: false,
238
+ error: null,
239
+ },
240
+ verification: {
241
+ status: 'idle',
242
+ },
243
+ };
244
+ };
154
245
 
155
246
  // État du flux
156
247
  const [state, setState] = useState<TemplateState>(() => buildInitialState());
248
+
249
+ // Fonction utilitaire pour convertir base64 en data URI pour l'affichage
250
+ const base64ToDataUri = useCallback((base64: string, mimeType: string = 'image/jpeg'): string => {
251
+ // Si c'est déjà une data URI, retourner tel quel
252
+ if (base64.startsWith('data:')) {
253
+ return base64;
254
+ }
255
+ // Sinon, créer une data URI
256
+ return `data:${mimeType};base64,${base64}`;
257
+ }, []);
258
+
259
+ // Charger les données de session si on reprend une session existante
260
+ useEffect(() => {
261
+ const loadSessionData = async () => {
262
+ // Ne charger que si on a une session existante
263
+ if (!existingSessionId) {
264
+ logger.log('No existingSessionId, skipping data load');
265
+ return;
266
+ }
267
+
268
+ // Si initialStep n'est pas défini ou est 0, on ne charge pas (début de session)
269
+ if (initialStep === undefined || initialStep === 0) {
270
+ logger.log('initialStep is 0 or undefined, skipping data load');
271
+ return;
272
+ }
273
+
274
+ // Attendre que la session soit initialisée
275
+ if (!state.session.isInitialized || !state.session.session_id) {
276
+ logger.log('Session not initialized yet, waiting...', {
277
+ isInitialized: state.session.isInitialized,
278
+ sessionId: state.session.session_id
279
+ });
280
+ return;
281
+ }
282
+
283
+ try {
284
+ logger.log('Loading session data for resume:', { sessionId: existingSessionId, step: initialStep });
285
+ const result = await kycService.getVerificationResult(state.session.session_id);
286
+ const sessionData = result[state.session.session_id]?.data;
287
+
288
+ if (sessionData) {
289
+ // Restaurer les données des composants depuis la session
290
+ // Utiliser 'as any' car VerificationResult peut avoir des propriétés dynamiques
291
+ const data: any = sessionData;
292
+ const restoredComponentData: Record<number, any> = {};
293
+
294
+ // Parcourir les composants jusqu'à l'étape initiale (incluse) pour restaurer leurs données
295
+ // Utiliser initialStep + 1 pour inclure le composant à l'étape initialStep
296
+ templateWithReviewAndVerification.components
297
+ .slice(0, initialStep + 1)
298
+ .forEach((component) => {
299
+ // Essayer de restaurer les données selon le type de composant
300
+ if (component.type === 'id_card' || component.type === 'file_upload') {
301
+ // Les documents peuvent être dans différentes structures
302
+ let documents: any = null;
303
+
304
+ // Chercher dans différentes structures possibles
305
+ if (data.documents) {
306
+ documents = data.documents;
307
+ } else if (data.user_data?.documents) {
308
+ documents = data.user_data.documents;
309
+ } else if (data.document_images) {
310
+ documents = data.document_images;
311
+ }
312
+
313
+ if (documents) {
314
+ // Convertir les images base64 en format utilisable
315
+ const restoredDocuments: Record<string, any> = {};
316
+
317
+ Object.keys(documents).forEach((key) => {
318
+ const doc = documents[key];
319
+ if (typeof doc === 'object' && doc !== null) {
320
+ // Si on a un fichier base64, créer une structure avec dir et file
321
+ if (doc.file || doc.base64) {
322
+ const base64Data = doc.file || doc.base64;
323
+ restoredDocuments[key] = {
324
+ dir: base64ToDataUri(base64Data), // Utiliser data URI pour l'affichage
325
+ file: base64Data, // Garder le base64 pour l'envoi
326
+ mrz: doc.mrz || '',
327
+ templatePath: doc.templatePath || '',
328
+ };
329
+ } else {
330
+ // Sinon, garder la structure originale
331
+ restoredDocuments[key] = doc;
332
+ }
333
+ } else if (typeof doc === 'string') {
334
+ // Si c'est directement une string base64
335
+ restoredDocuments[key] = {
336
+ dir: base64ToDataUri(doc),
337
+ file: doc,
338
+ mrz: '',
339
+ templatePath: '',
340
+ };
341
+ }
342
+ });
343
+
344
+ if (Object.keys(restoredDocuments).length > 0) {
345
+ restoredComponentData[component.id] = restoredDocuments;
346
+ }
347
+ }
348
+ } else if (component.type === 'selfie') {
349
+ // Les selfies peuvent être dans sessionData.selfie_info
350
+ if (data.selfie_info) {
351
+ const selfieData: Record<string, any> = {};
352
+ const selfieInfo: any = data.selfie_info;
353
+
354
+ // Si selfie_info contient une image
355
+ if (selfieInfo.image) {
356
+ const base64Image = selfieInfo.image;
357
+ selfieData['front'] = {
358
+ dir: base64ToDataUri(base64Image),
359
+ file: base64Image,
360
+ };
361
+ }
362
+
363
+ // Si on a plusieurs orientations
364
+ if (selfieInfo.orientations) {
365
+ Object.keys(selfieInfo.orientations).forEach((orientation) => {
366
+ const img = selfieInfo.orientations[orientation];
367
+ if (img) {
368
+ selfieData[orientation] = {
369
+ dir: base64ToDataUri(img),
370
+ file: img,
371
+ };
372
+ }
373
+ });
374
+ }
375
+
376
+ if (Object.keys(selfieData).length > 0) {
377
+ restoredComponentData[component.id] = selfieData;
378
+ }
379
+ }
380
+ } else if (component.type === 'country_selection') {
381
+ // Les données de sélection de pays peuvent être dans metadata
382
+ if (data.metadata || data.user_data) {
383
+ restoredComponentData[component.id] = {
384
+ ...(data.metadata || {}),
385
+ ...(data.user_data || {}),
386
+ };
387
+ }
388
+ } else if (component.type === 'location') {
389
+ // Les données de localisation peuvent être dans metadata
390
+ if (data.metadata?.location || data.user_data?.location) {
391
+ restoredComponentData[component.id] = {
392
+ ...(data.metadata?.location || {}),
393
+ ...(data.user_data?.location || {}),
394
+ };
395
+ }
396
+ }
397
+ });
398
+
399
+ // Mettre à jour l'état avec les données restaurées
400
+ if (Object.keys(restoredComponentData).length > 0) {
401
+ logger.log('Session data restored - components:', Object.keys(restoredComponentData));
402
+ logger.log('Session data restored - sample data:', truncateFields(restoredComponentData));
403
+ setState(prev => ({
404
+ ...prev,
405
+ componentData: {
406
+ ...prev.componentData,
407
+ ...restoredComponentData,
408
+ },
409
+ }));
410
+ logger.log('Component data updated in state');
411
+ } else {
412
+ logger.log('No component data to restore from session');
413
+ }
414
+ }
415
+ } catch (error) {
416
+ logger.error('Error loading session data:', truncateFields(error));
417
+ // Ne pas bloquer le flux si le chargement échoue
418
+ }
419
+ };
420
+
421
+ loadSessionData();
422
+ }, [existingSessionId, initialStep, state.session.isInitialized, state.session.session_id, templateWithReviewAndVerification.components, base64ToDataUri]);
423
+
157
424
  const mapComponentTypeToAction = useCallback((type: TemplateComponent['type']): string | null => {
158
425
  switch (type) {
159
426
  case 'id_card':
@@ -301,7 +568,18 @@ export const useTemplateKYCFlow = (
301
568
 
302
569
  const token = apiKey ? undefined : await authentification();
303
570
  console.log('token in initializeSession', { token, apiKey },);
304
- const session = await kycService.newSession({token,apiKey});
571
+
572
+ // Check if we already have a session ID from URL params (passed via state or prop)
573
+ let session;
574
+ const existingSessionId = state.session.session_id; // This might be set from initial props if we add logic for it
575
+
576
+ if (existingSessionId && existingSessionId.length > 0) {
577
+ logger.log('Resuming existing session:', existingSessionId);
578
+ // Verify existence/validity if needed, for now trust the ID and just fetch/use it
579
+ session = { session_id: existingSessionId };
580
+ } else {
581
+ session = await kycService.newSession({ token, apiKey });
582
+ }
305
583
 
306
584
  // Align backend flow from step 0 with initialize_session
307
585
  try {
@@ -381,6 +659,20 @@ export const useTemplateKYCFlow = (
381
659
  // Welcome is valid once user has given consent (componentData is set when they click Get Started)
382
660
  return componentData && componentData.consentGiven !== false;
383
661
 
662
+ case 'email_verification':
663
+ return componentData && componentData.verified === true;
664
+
665
+ case 'phone_verification':
666
+ return componentData && componentData.verified === true;
667
+
668
+ case 'personal_information':
669
+ return componentData && Object.keys(componentData).length > 0;
670
+
671
+ case 'additional_documents':
672
+ // Optional by default in template config, but if required we should check based on config
673
+ // For now, return true or check length if present
674
+ return true;
675
+
384
676
  case 'review_submit':
385
677
  return true;
386
678
  default:
@@ -660,5 +952,6 @@ export const useTemplateKYCFlow = (
660
952
  isComplete,
661
953
  getLocalizedText,
662
954
  initializeSession,
955
+ env,
663
956
  };
664
957
  };
@@ -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',
@@ -98,6 +126,9 @@ export const en = {
98
126
  success: 'ID captured successfully',
99
127
  error: 'Failed to capture ID. Please try again.',
100
128
  captureTitle: '%{side} side of your government document',
129
+ continueOnPhone: 'Continue on Phone',
130
+ continueOnMobile: 'Continue on Mobile',
131
+ scanQrCode: 'Scan this QR code with your phone to continue the verification process.',
101
132
  },
102
133
 
103
134
  // Selfie Capture
@@ -214,7 +245,17 @@ export const en = {
214
245
  cameraError: 'Camera error. Please try again.',
215
246
  fileError: 'File error. Please try again.',
216
247
  timeoutError: 'Request timeout. Please try again.',
217
- unknownError: 'An unknown error occurred. Please try again.'
248
+ unknownError: 'An unknown error occurred. Please try again.',
249
+ wrongCode: "Wrong code",
250
+ invalidCode: "Invalid code",
251
+ invalidEmail: "Invalid email",
252
+ invalidPhone: "Invalid phone",
253
+ invalidDate: "Invalid date",
254
+ minLength: "Min length",
255
+ maxLength: "Max length",
256
+ invalidFormat: "Invalid format",
257
+ fileTooLarge: "File too large",
258
+ fileTypeNotSupported: "File type not supported",
218
259
  },
219
260
 
220
261
  // 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
@@ -97,6 +111,9 @@ export const fr = {
97
111
  success: 'Document d\'identité capturé avec succès',
98
112
  error: 'Échec de la capture du document. Veuillez réessayer.',
99
113
  captureTitle: '%{side} de votre document d\'identité',
114
+ continueOnPhone: 'Continuer sur mobile',
115
+ continueOnMobile: 'Continuer sur mobile',
116
+ scanQrCode: 'Scannez ce code QR avec votre téléphone pour continuer la vérification.',
100
117
  },
101
118
 
102
119
  // Selfie Capture
@@ -213,7 +230,17 @@ export const fr = {
213
230
  cameraError: 'Erreur caméra. Veuillez réessayer.',
214
231
  fileError: 'Erreur fichier. Veuillez réessayer.',
215
232
  timeoutError: 'Délai d\'attente dépassé. Veuillez réessayer.',
216
- unknownError: 'Une erreur inconnue s\'est produite. Veuillez réessayer.'
233
+ unknownError: 'Une erreur inconnue s\'est produite. Veuillez réessayer.',
234
+ wrongCode: "Wrong code",
235
+ invalidCode: "Invalid code",
236
+ invalidEmail: "Invalid email",
237
+ invalidPhone: "Invalid phone",
238
+ invalidDate: "Invalid date",
239
+ minLength: "Min length",
240
+ maxLength: "Max length",
241
+ invalidFormat: "Invalid format",
242
+ fileTooLarge: "File too large",
243
+ fileTypeNotSupported: "File type not supported",
217
244
  },
218
245
 
219
246
  validation: {
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,14 +1,40 @@
1
1
  import kycService, { authentification, errorMessage, truncateFields } from "./KYCService";
2
2
  import { cropByObb } 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.');
@@ -36,7 +62,8 @@ export async function frontVerification(result: { path?: string, regionMapping:
36
62
  token: token,
37
63
  template_path: result?.templatePath || '',
38
64
  mrz_type: result?.mrzType || ''
39
- })
65
+ },
66
+ env)
40
67
 
41
68
  }
42
69
 
@@ -47,10 +74,38 @@ export async function frontVerification(result: { path?: string, regionMapping:
47
74
  }
48
75
  }
49
76
 
50
- export async function backVerification(result: { path?: string, regionMapping: { authMethod: string[], mrzTypes: string[] }, selectedDocumentType: string, code: string, currentSide: string, templatePath?: string, mrzType?: string }) {
77
+ 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
78
  try {
52
79
  if (!result.path) throw new Error('No path provided');
53
80
  logger.log("result.regionMapping", result.regionMapping, result.currentSide, result.code);
81
+
82
+ // SANDBOX mode: skip AI verification and return mock response
83
+ if (env === 'SANDBOX') {
84
+ console.log("SANDBOX mode: Skipping AI verification for back document");
85
+ logger.log("SANDBOX mode: Returning mock back verification response");
86
+ const mockBbox: IBbox = { minX: 50, minY: 50, width: 200, height: 200 };
87
+
88
+ if (result.regionMapping.authMethod.includes('MRZ')) {
89
+ return {
90
+ success: true,
91
+ parsed_data: {
92
+ status: 'success',
93
+ document_type: result.selectedDocumentType,
94
+ mrz_type: result.mrzType || 'TD1'
95
+ },
96
+ bbox: mockBbox,
97
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
98
+ };
99
+ } else if (result.regionMapping.authMethod.includes('2D_barcode')) {
100
+ return {
101
+ barcode_data: 'SANDBOX_MOCK_BARCODE',
102
+ bbox: mockBbox,
103
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
104
+ };
105
+ }
106
+ return { bbox: mockBbox };
107
+ }
108
+
54
109
  const token = await authentification();
55
110
 
56
111
 
@@ -68,7 +123,7 @@ export async function backVerification(result: { path?: string, regionMapping: {
68
123
  token: token,
69
124
  template_path: result?.templatePath || '',
70
125
  mrz_type: result?.mrzType || ''
71
- });
126
+ }, env);
72
127
  let bbox: IBbox | undefined;
73
128
  let croppedBase64: string | undefined;
74
129
 
@@ -82,7 +137,7 @@ export async function backVerification(result: { path?: string, regionMapping: {
82
137
  } catch (mrzError: any) {
83
138
  logger.log("MRZ échoué, tentative d'extraction barcode");
84
139
  try {
85
- const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token });
140
+ const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token }, env);
86
141
  return barcode;
87
142
  } catch (barcodeError: any) {
88
143
  throw new Error(`MRZ et barcode ont échoué. MRZ: ${mrzError?.message}, Barcode: ${barcodeError?.message}`);
@@ -108,7 +163,7 @@ export async function backVerification(result: { path?: string, regionMapping: {
108
163
  token: token,
109
164
  template_path: result?.templatePath || '',
110
165
  mrz_type: result?.mrzType || ''
111
- });
166
+ }, env);
112
167
  let bbox: IBbox | undefined;
113
168
  try {
114
169
  const crop = await cropByObb(result?.path || '', (mrz as any).card_obb);
@@ -123,7 +178,7 @@ export async function backVerification(result: { path?: string, regionMapping: {
123
178
  if (result.regionMapping.authMethod.length > 0 && result.regionMapping.authMethod.includes('2D_barcode')) {
124
179
  try {
125
180
  logger.log("Tentative d'extraction barcode");
126
- const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token });
181
+ const barcode = await kycService.extractBarcode({ fileUri: result.path!, token: token }, env);
127
182
  let bbox: IBbox | undefined;
128
183
  try {
129
184
  const crop = await cropByObb(result?.path || '', (barcode as any).card_obb);
@@ -145,10 +200,20 @@ export async function backVerification(result: { path?: string, regionMapping: {
145
200
  * @param result
146
201
  * @returns
147
202
  */
148
- export async function checkTemplateType(result: { path?: string, docType: string, docRegion: string, postfix: string }) {
203
+ export async function checkTemplateType(result: { path?: string, docType: string, docRegion: string, postfix: string }, env: KycEnvironment = 'PRODUCTION') {
149
204
  try {
205
+ // SANDBOX mode: skip AI verification and return mock response
206
+ if (env === 'SANDBOX') {
207
+ console.log("SANDBOX mode: Skipping AI template type check");
208
+ logger.log("SANDBOX mode: Returning mock template type response");
209
+ return {
210
+ template_path: `templates/${result.docType}_${result.docRegion}_${result.postfix}.jpg`,
211
+ card_obb: { x: 50, y: 50, width: 200, height: 200 }
212
+ };
213
+ }
214
+
150
215
  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 });
216
+ const templateType = await kycService.checkTemplateType({ fileUri: result.path || '', docType: result?.docType as GovernmentDocumentType, docRegion: result?.docRegion || "", postfix: result?.postfix, token: token }, env);
152
217
 
153
218
  logger.log("templateType result", JSON.stringify(truncateFields(templateType), null, 2));
154
219
  return templateType;