@transfergratis/react-native-sdk 0.1.26 → 0.1.29

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 (39) hide show
  1. package/build/components/EnhancedCameraView.js +1 -1
  2. package/build/components/EnhancedCameraView.js.map +1 -1
  3. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  4. package/build/components/KYCElements/CountrySelectionTemplate.js +13 -42
  5. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  6. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  7. package/build/components/KYCElements/IDCardCapture.js +76 -35
  8. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  9. package/build/components/KYCElements/SelfieCaptureTemplate.js +2 -2
  10. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  11. package/build/hooks/useTemplateKYCFlow.js +1 -1
  12. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  13. package/build/modules/api/CardAuthentification.d.ts +15 -7
  14. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  15. package/build/modules/api/CardAuthentification.js +215 -44
  16. package/build/modules/api/CardAuthentification.js.map +1 -1
  17. package/build/modules/api/KYCService.d.ts +2 -0
  18. package/build/modules/api/KYCService.d.ts.map +1 -1
  19. package/build/modules/api/KYCService.js +16 -19
  20. package/build/modules/api/KYCService.js.map +1 -1
  21. package/build/modules/camera/VisionCameraModule.js +2 -2
  22. package/build/modules/camera/VisionCameraModule.js.map +1 -1
  23. package/build/utils/cropByObb.d.ts +19 -1
  24. package/build/utils/cropByObb.d.ts.map +1 -1
  25. package/build/utils/cropByObb.js +49 -1
  26. package/build/utils/cropByObb.js.map +1 -1
  27. package/build/utils/pathToBase64.js +1 -1
  28. package/build/utils/pathToBase64.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/components/EnhancedCameraView.tsx +1 -1
  31. package/src/components/KYCElements/CountrySelectionTemplate.tsx +24 -52
  32. package/src/components/KYCElements/IDCardCapture.tsx +129 -90
  33. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +2 -2
  34. package/src/hooks/useTemplateKYCFlow.tsx +1 -1
  35. package/src/modules/api/CardAuthentification.ts +290 -58
  36. package/src/modules/api/KYCService.ts +25 -33
  37. package/src/modules/camera/VisionCameraModule.ts +2 -2
  38. package/src/utils/cropByObb.ts +57 -1
  39. package/src/utils/pathToBase64.ts +1 -1
@@ -43,7 +43,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
43
43
  const { t, locale } = useI18n();
44
44
  const [showCamera, setShowCamera] = useState(false);
45
45
  const [capturedImages, setCapturedImages] = useState<Record<string, IIDCardPayload>>(value || {});
46
- const [cropImageUri, setCropImageUri] = useState<string>('');
46
+ // const [cropImageUri, setCropImageUri] = useState<string>('');
47
47
  const [currentSide, setCurrentSide] = useState<string>('front');
48
48
  // Stocker les bbox par côté pour pouvoir restaurer les images croppées
49
49
  const [bboxBySide, setBboxBySide] = useState<Record<string, IBbox>>({});
@@ -97,6 +97,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
97
97
  return countrySelectionData;
98
98
  }, [countrySelectionData]);
99
99
 
100
+ // console.log();
100
101
 
101
102
  // Synchroniser capturedImages avec value quand les données sont chargées (ex: reprise de session)
102
103
  useEffect(() => {
@@ -131,7 +132,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
131
132
  logger.log("cropImageUri", JSON.stringify(truncateFields({ box: silentCaptureResult }), null, 2));
132
133
  if (capturedImages[currentSide]?.dir && silentCaptureResult?.bbox) {
133
134
  cropImageWithBBox(capturedImages[currentSide].dir, silentCaptureResult.bbox)?.then((uri) => {
134
- setCropImageUri(uri);
135
+ // setCropImageUri(uri);
135
136
  });
136
137
  }
137
138
  }, [capturedImages[currentSide]?.dir, silentCaptureResult?.bbox])
@@ -145,9 +146,11 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
145
146
 
146
147
  return {
147
148
  aspectRatio: 4 / 3,
148
- quality: 0.8,
149
+ quality: 0.7,
149
150
  flashMode: 'auto' as const,
150
151
  cameraType: 'back' as const,
152
+ autoFocus: 'on',
153
+ whiteBalance: 'auto',
151
154
  allowRetake: true,
152
155
  maxRetakes: 3,
153
156
  overlay: {
@@ -170,8 +173,19 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
170
173
  setShowCamera(true);
171
174
  actions.showCustomStepper(false);
172
175
  setCapturedImages({ ...capturedImages, [currentSide]: { dir: '', file: '', mrz: '' } });
173
- setSilentCaptureResult((prev) => ({ ...prev, path: '', success: false, isAnalyzing: false, error: '' }));
174
- setCropImageUri('');
176
+
177
+ setSilentCaptureResult((prev) => ({
178
+ ...prev,
179
+ path: '',
180
+ success: false,
181
+ isAnalyzing: false,
182
+ error: '',
183
+ templatePath: '',
184
+ bbox: undefined,
185
+ mrz: ''
186
+ }));
187
+
188
+ // setCropImageUri('');
175
189
  onValueChange({ ...value, [currentSide]: { dir: '', file: '', mrz: '' } });
176
190
  };
177
191
 
@@ -324,8 +338,13 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
324
338
  }).catch((e: any) => {
325
339
  console.log("error front verification", e);
326
340
  logger.log("error front verification", truncateFields(e));
327
- const isCardNotFullyInFrame = e?.message?.includes('entirement') || e?.message?.includes('fully in frame');
328
- const errorMessage = isCardNotFullyInFrame ? t('kyc.idCardCapture.cardNotFullyInFrame') : (e?.message || 'Erreur de détection du MRZ');
341
+ const isCardNotFullyInFrame =
342
+ e?.message === 'CARD_NOT_FULLY_IN_FRAME' ||
343
+ e?.message?.includes('entirement') ||
344
+ e?.message?.includes('fully in frame');
345
+ const errorMessage = isCardNotFullyInFrame
346
+ ? t('kyc.idCardCapture.cardNotFullyInFrame')
347
+ : (e?.message || 'Erreur de détection du MRZ');
329
348
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
330
349
  });
331
350
  } catch (error: any) {
@@ -362,8 +381,13 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
362
381
  }
363
382
  }).catch((e: any) => {
364
383
  logger.log("error back verification", truncateFields(e));
365
- const isCardNotFullyInFrame = e?.message?.includes('entirement') || e?.message?.includes('fully in frame');
366
- const errorMessage = isCardNotFullyInFrame ? t('kyc.idCardCapture.cardNotFullyInFrame') : (e?.message || 'Erreur de détection du MRZ');
384
+ const isCardNotFullyInFrame =
385
+ e?.message === 'CARD_NOT_FULLY_IN_FRAME' ||
386
+ e?.message?.includes('entirement') ||
387
+ e?.message?.includes('fully in frame');
388
+ const errorMessage = isCardNotFullyInFrame
389
+ ? t('kyc.idCardCapture.cardNotFullyInFrame')
390
+ : (e?.message || 'Erreur de détection du MRZ');
367
391
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
368
392
  })
369
393
  }
@@ -371,31 +395,32 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
371
395
  }
372
396
  }
373
397
 
374
- // Handle capture
375
398
  const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
376
- console.log("handleCapture", JSON.stringify(truncateFields({ result, silentCaptureResult }), null, 2));
377
-
378
399
  if (silentCaptureResult.path) {
379
- // Créer une image rognée avec tolérance de 10% pour l'envoi
380
400
  let imagePathForUpload = silentCaptureResult.path;
381
401
  if (silentCaptureResult.bbox) {
382
402
  try {
383
- logger.log("Début du rognage avec tolérance, URI original:", silentCaptureResult.path);
384
403
  imagePathForUpload = await cropImageWithBBoxWithTolerance(silentCaptureResult.path, silentCaptureResult.bbox, 0.05);
385
- logger.log("Image rognée avec tolérance créée pour l'envoi, URI final:", imagePathForUpload);
386
404
  } catch (error) {
387
- logger.log("Erreur lors du rognage avec tolérance, utilisation de l'image originale:", truncateFields(error));
388
- // En cas d'erreur, on utilise l'image originale
389
405
  imagePathForUpload = silentCaptureResult.path;
390
406
  }
391
407
  }
392
408
 
393
409
  const base64 = await pathToBase64(imagePathForUpload);
394
410
 
395
- logger.log("silentCaptureResult captured", JSON.stringify(truncateFields(silentCaptureResult), null, 2));
396
- // Utiliser l'image originale pour l'affichage (dir) mais l'image rognée avec tolérance pour l'envoi (file)
397
- const newImages = { ...capturedImages, [currentSide]: { dir: silentCaptureResult.path, file: base64, mrz: silentCaptureResult.mrz || "", templatePath: silentCaptureResult.templatePath } };
411
+ // ✅ FIX: Use 'imagePathForUpload' (the cropped image) for the local display directory!
412
+ const newImages = {
413
+ ...capturedImages,
414
+ [currentSide]: {
415
+ dir: imagePathForUpload, // <--- CHANGED THIS LINE
416
+ file: base64,
417
+ mrz: silentCaptureResult.mrz || "",
418
+ templatePath: silentCaptureResult.templatePath
419
+ }
420
+ };
421
+
398
422
  setCapturedImages(newImages);
423
+
399
424
  if (silentCaptureResult.country && silentCaptureResult.documentType) {
400
425
  onValueChange({
401
426
  ...newImages,
@@ -415,10 +440,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
415
440
  setShowCamera(false);
416
441
  };
417
442
 
418
-
419
-
420
-
421
-
422
443
  useEffect(() => {
423
444
  actions.showCustomStepper(!showCamera);
424
445
  }, [showCamera]);
@@ -521,7 +542,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
521
542
 
522
543
 
523
544
  if (showCamera) {
524
-
525
545
  return (
526
546
  <View style={styles.cameraContainer}>
527
547
  <EnhancedCameraView
@@ -538,54 +558,63 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
538
558
  silentCaptureResult={silentCaptureResult}
539
559
  captureStabilizationDelayMs={3000}
540
560
  enableFlash={cameraConfig.flashMode === 'auto' || cameraConfig.flashMode === 'on'}
541
- overlayComponent={<IdCardOverlay
542
- xMin={cameraConfig.overlay.bbox.xMin}
543
- yMin={cameraConfig.overlay.bbox.yMin}
544
- xMax={cameraConfig.overlay.bbox.xMax}
545
- yMax={cameraConfig.overlay.bbox.yMax}
546
- instructions={cameraConfig.overlay.guideText}
547
- cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0 as number}
548
- isSuccess={silentCaptureResult.success}
549
- language={state.currentLanguage}
550
- stepperProps={{
551
- back: () => {
552
- if (currentSide === 'back') {
553
- setCurrentSide('front');
554
- setShowCamera(false);
555
- // Si une image front existe, on la restaure pour l'afficher
556
- if (capturedImages['front']?.dir) {
557
- // Restaurer l'état de capture du front pour afficher l'image
558
- const frontImage = capturedImages['front'];
559
- const frontBbox = bboxBySide['front'];
560
- setSilentCaptureResult((prev) => ({
561
- path: frontImage.dir,
562
- success: true,
563
- isAnalyzing: false,
564
- error: '',
565
- mrz: frontImage.mrz || '',
566
- templatePath: frontImage.templatePath || '',
567
- country: silentCaptureResult.country,
568
- documentType: silentCaptureResult.documentType,
569
- bbox: frontBbox // Restaurer le bbox pour recalculer le cropImageUri
570
- }));
571
- // Le useEffect va automatiquement recalculer le cropImageUri si bbox existe
561
+ overlayComponent={
562
+ <IdCardOverlay
563
+ xMin={cameraConfig.overlay.bbox.xMin}
564
+ yMin={cameraConfig.overlay.bbox.yMin}
565
+ xMax={cameraConfig.overlay.bbox.xMax}
566
+ yMax={cameraConfig.overlay.bbox.yMax}
567
+ instructions={cameraConfig.overlay.guideText}
568
+ cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0 as number}
569
+ isSuccess={silentCaptureResult.success}
570
+ language={state.currentLanguage}
571
+ stepperProps={{
572
+ back: () => {
573
+ if (currentSide === 'back') {
574
+ setCurrentSide('front');
575
+ setShowCamera(false);
576
+ // Si une image front existe, on la restaure pour l'afficher
577
+ if (capturedImages['front']?.dir) {
578
+ // Restaurer l'état de capture du front pour afficher l'image
579
+ const frontImage = capturedImages['front'];
580
+ const frontBbox = bboxBySide['front'];
581
+ setSilentCaptureResult((prev) => ({
582
+ path: frontImage.dir,
583
+ success: true,
584
+ isAnalyzing: false,
585
+ error: '',
586
+ mrz: frontImage.mrz || '',
587
+ templatePath: frontImage.templatePath || '',
588
+ country: silentCaptureResult.country,
589
+ documentType: silentCaptureResult.documentType,
590
+ bbox: frontBbox // Restaurer le bbox pour recalculer le cropImageUri
591
+ }));
592
+ } else {
593
+ // Pas d'image front, on réinitialise
594
+ setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '' }));
595
+ }
572
596
  } else {
573
- // Pas d'image front, on réinitialise
574
- setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '' }));
597
+ // Retour au composant précédent (country_selection)
598
+ actions.previousComponent();
575
599
  }
576
- } else {
577
- // Retour au composant précédent (country_selection)
578
- actions.previousComponent();
579
- }
580
- },
581
- selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
582
- step: state.currentComponentIndex + 1,
583
- totalSteps: state.template.components.length,
584
- side: currentSide,
585
- }}
586
- />
600
+ },
601
+ selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
602
+ step: state.currentComponentIndex + 1,
603
+ totalSteps: state.template.components.length,
604
+ side: currentSide,
605
+ }}
606
+ />
587
607
  }
588
608
  />
609
+
610
+ {silentCaptureResult.error ? (
611
+ <View style={styles.floatingErrorBanner}>
612
+ <Text style={styles.floatingErrorText}>
613
+ {silentCaptureResult.error}
614
+ </Text>
615
+ </View>
616
+ ) : null}
617
+
589
618
  </View>
590
619
  );
591
620
  }
@@ -604,26 +633,25 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
604
633
  </Text>
605
634
 
606
635
  <View style={{ alignItems: 'center', justifyContent: 'center', flexDirection: "column", gap: 16 }}>
607
- <View
636
+ <View
608
637
  style={{
609
638
  width: '100%',
610
639
  height: 200,
611
640
  borderRadius: 12,
612
641
  padding: 1,
613
642
  overflow: 'hidden',
614
- // Shadow for iOS
615
643
  shadowColor: '#000',
616
644
  shadowOffset: { width: 0, height: 4 },
617
645
  shadowOpacity: 0.18,
618
646
  shadowRadius: 8,
619
- // Shadow for Android
620
647
  elevation: 8,
621
- backgroundColor: '#fff', // Needed for shadow to show on iOS
648
+ backgroundColor: '#fff',
622
649
  }}
623
650
  >
624
- {cropImageUri ? (
651
+ {/* Simplified logic: Always show the saved, pre-cropped capture! */}
652
+ {capturedImages[currentSide]?.dir ? (
625
653
  <Image
626
- source={{ uri: cropImageUri }}
654
+ source={{ uri: capturedImages[currentSide].dir }}
627
655
  style={{
628
656
  width: '100%',
629
657
  height: 200,
@@ -631,8 +659,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
631
659
  resizeMode: 'cover',
632
660
  }}
633
661
  />
634
- ) : null}
635
- {!cropImageUri && silentCaptureResult.path ? (
662
+ ) : silentCaptureResult.path ? (
636
663
  <Image
637
664
  source={{ uri: silentCaptureResult.path }}
638
665
  style={{
@@ -643,18 +670,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
643
670
  }}
644
671
  />
645
672
  ) : null}
646
- {!cropImageUri && !silentCaptureResult.path && capturedImages[currentSide]?.dir ? (
647
- <Image
648
- source={{ uri: capturedImages[currentSide].dir }}
649
- style={{
650
- width: '100%',
651
- height: 200,
652
- borderRadius: 12,
653
- resizeMode: 'cover',
654
- }}
655
- />
656
- ) : null}
657
-
658
673
  </View>
659
674
  {/* Capture button si aucune image n'a été capturée */}
660
675
  {!capturedImages[currentSide]?.dir && (
@@ -691,7 +706,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
691
706
  setShowCamera(true);
692
707
  setCurrentSide('back');
693
708
  setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '', templatePath: '' }));
694
- setCropImageUri('');
709
+ // setCropImageUri('');
695
710
  }
696
711
  }}
697
712
  variant="primary"
@@ -970,4 +985,28 @@ const styles = StyleSheet.create({
970
985
  color: '#666',
971
986
  fontWeight: '600',
972
987
  },
988
+ floatingErrorBanner: {
989
+ position: 'absolute',
990
+ top: 60, // Pushes it down slightly from the very top of the screen
991
+ left: '10%',
992
+ right: '10%',
993
+ backgroundColor: 'rgba(220, 38, 38, 0.95)', // Bright red
994
+ paddingVertical: 12,
995
+ paddingHorizontal: 16,
996
+ borderRadius: 8,
997
+ alignItems: 'center',
998
+ justifyContent: 'center',
999
+ shadowColor: '#000',
1000
+ shadowOffset: { width: 0, height: 4 },
1001
+ shadowOpacity: 0.3,
1002
+ shadowRadius: 5,
1003
+ elevation: 8,
1004
+ zIndex: 100,
1005
+ },
1006
+ floatingErrorText: {
1007
+ color: 'white',
1008
+ fontSize: 14,
1009
+ fontWeight: '700',
1010
+ textAlign: 'center',
1011
+ },
973
1012
  });
@@ -76,9 +76,9 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
76
76
  const getOrientationLabel = (orientation: OrientationType): string => {
77
77
  switch (orientation) {
78
78
  case 'center':
79
- return state.currentLanguage === "en" ? "Front Profil Selfie" : "Selfie de face";
79
+ return state.currentLanguage === "en" ? "Front Profile Selfie" : "Selfie de face";
80
80
  case 'left':
81
- return state.currentLanguage === "en" ? "Left Profil Selfie" : "Selfie profil gauche";
81
+ return state.currentLanguage === "en" ? "Left Profile Selfie" : "Selfie profil gauche";
82
82
  case 'right':
83
83
  return state.currentLanguage === "en" ? "Right Profile Selfie" : "Selfie profil droit";
84
84
  default:
@@ -822,7 +822,7 @@ export const useTemplateKYCFlow = (
822
822
  isProcessing: false,
823
823
  errors: {
824
824
  ...prev.errors,
825
- [currentComp.id]: state.currentLanguage === "en" ? "please complete this step before move on" : " 'Veuillez compléter cette étape avant de continuer'"
825
+ [currentComp.id]: state.currentLanguage === "en" ? "please complete this step before moving on" : " 'Veuillez compléter cette étape avant de continuer'"
826
826
  }
827
827
  }));
828
828
  return;