@transfergratis/react-native-sdk 0.1.28 → 0.1.30

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 +113 -49
  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 +360 -104
  16. package/build/modules/api/CardAuthentification.js.map +1 -1
  17. package/build/modules/api/KYCService.d.ts +3 -1
  18. package/build/modules/api/KYCService.d.ts.map +1 -1
  19. package/build/modules/api/KYCService.js +25 -24
  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 +8 -0
  24. package/build/utils/cropByObb.d.ts.map +1 -1
  25. package/build/utils/cropByObb.js +20 -3
  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 +179 -109
  33. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +2 -2
  34. package/src/hooks/useTemplateKYCFlow.tsx +1 -1
  35. package/src/modules/api/CardAuthentification.ts +450 -113
  36. package/src/modules/api/KYCService.ts +52 -39
  37. package/src/modules/camera/VisionCameraModule.ts +2 -2
  38. package/src/utils/cropByObb.ts +22 -3
  39. package/src/utils/pathToBase64.ts +1 -1
@@ -19,9 +19,8 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
19
19
  const { t, locale } = useI18n();
20
20
  const [showCamera, setShowCamera] = useState(false);
21
21
  const [capturedImages, setCapturedImages] = useState(value || {});
22
- const [cropImageUri, setCropImageUri] = useState('');
22
+ // const [cropImageUri, setCropImageUri] = useState<string>('');
23
23
  const [currentSide, setCurrentSide] = useState('front');
24
- // Stocker les bbox par côté pour pouvoir restaurer les images croppées
25
24
  const [bboxBySide, setBboxBySide] = useState({});
26
25
  const [silentCaptureResult, setSilentCaptureResult] = useState({ success: false, isAnalyzing: false });
27
26
  const [showQRModal, setShowQRModal] = useState(false);
@@ -63,6 +62,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
63
62
  const countryData = useMemo(() => {
64
63
  return countrySelectionData;
65
64
  }, [countrySelectionData]);
65
+ // console.log();
66
66
  // Synchroniser capturedImages avec value quand les données sont chargées (ex: reprise de session)
67
67
  useEffect(() => {
68
68
  if (value && Object.keys(value).length > 0) {
@@ -94,7 +94,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
94
94
  logger.log("cropImageUri", JSON.stringify(truncateFields({ box: silentCaptureResult }), null, 2));
95
95
  if (capturedImages[currentSide]?.dir && silentCaptureResult?.bbox) {
96
96
  cropImageWithBBox(capturedImages[currentSide].dir, silentCaptureResult.bbox)?.then((uri) => {
97
- setCropImageUri(uri);
97
+ // setCropImageUri(uri);
98
98
  });
99
99
  }
100
100
  }, [capturedImages[currentSide]?.dir, silentCaptureResult?.bbox]);
@@ -104,9 +104,11 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
104
104
  : getLocalizedText(component.instructions);
105
105
  return {
106
106
  aspectRatio: 4 / 3,
107
- quality: 0.8,
107
+ quality: 0.7,
108
108
  flashMode: 'auto',
109
109
  cameraType: 'back',
110
+ autoFocus: 'on',
111
+ whiteBalance: 'auto',
110
112
  allowRetake: true,
111
113
  maxRetakes: 3,
112
114
  overlay: {
@@ -124,13 +126,30 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
124
126
  }
125
127
  };
126
128
  }, [selectedDocumentType, locale, component.instructions]);
127
- const retakePicture = (currentSide) => {
129
+ const retakePicture = (sideToRetake) => {
130
+ // 1. Turn the camera back on and hide the stepper
128
131
  setShowCamera(true);
129
132
  actions.showCustomStepper(false);
130
- setCapturedImages({ ...capturedImages, [currentSide]: { dir: '', file: '', mrz: '' } });
131
- setSilentCaptureResult((prev) => ({ ...prev, path: '', success: false, isAnalyzing: false, error: '' }));
132
- setCropImageUri('');
133
- onValueChange({ ...value, [currentSide]: { dir: '', file: '', mrz: '' } });
133
+ // 2. WIPE LOCAL STATE: Use functional update to prevent stale closures
134
+ setCapturedImages((prev) => {
135
+ const newState = { ...prev };
136
+ delete newState[sideToRetake];
137
+ return newState;
138
+ });
139
+ setSilentCaptureResult({
140
+ path: '',
141
+ success: false,
142
+ isAnalyzing: false,
143
+ error: '',
144
+ templatePath: '',
145
+ mrz: '',
146
+ bbox: undefined,
147
+ });
148
+ if (value) {
149
+ const newValue = { ...value };
150
+ delete newValue[sideToRetake];
151
+ onValueChange(newValue);
152
+ }
134
153
  };
135
154
  const getCurrentSideVerification = (currentSide) => {
136
155
  if (!selectedDocumentType || !countryData?.regionMapping) {
@@ -247,15 +266,17 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
247
266
  console.log("About to call frontVerification function");
248
267
  const promise = frontVerification(verificationParams, env);
249
268
  console.log("frontVerification promise created", promise);
250
- promise.then((mrz) => {
251
- logger.log("front verification result", truncateFields(mrz));
252
- const bbox = mrz?.bbox || templateBbox;
269
+ promise.then((res) => {
270
+ logger.log("front verification result", truncateFields(res));
271
+ const bbox = res?.bbox || templateBbox;
253
272
  setSilentCaptureResult((prev) => ({
254
273
  ...prev,
255
274
  path: result.path,
256
275
  templatePath: templatePath,
257
- bbox: bbox, success: true,
258
- mrz: JSON.stringify(mrz), isAnalyzing: false,
276
+ bbox: bbox,
277
+ success: true,
278
+ mrz: res?.mrz ? JSON.stringify(res.mrz) : "",
279
+ isAnalyzing: false,
259
280
  country: countryData?.code,
260
281
  documentType: selectedDocumentType.type,
261
282
  }));
@@ -263,6 +284,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
263
284
  if (bbox && typeof bbox === 'object') {
264
285
  setBboxBySide((prev) => ({ ...prev, [currentSide]: bbox }));
265
286
  }
287
+ }).catch((e) => {
266
288
  }).catch((e) => {
267
289
  console.log("error front verification", e);
268
290
  logger.log("error front verification", truncateFields(e));
@@ -294,12 +316,16 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
294
316
  currentSide: currentSide,
295
317
  templatePath: templatePath,
296
318
  mrzType: getCorrespondingMrzType(templatePath, backRegionMappings.regionMapping, backRegionMappings.key || '') || '',
297
- }, env).then((mrz) => {
298
- logger.log("back verification result", truncateFields(mrz));
299
- const bbox = mrz?.bbox || templateBbox;
319
+ }, env).then((res) => {
320
+ logger.log("back verification result", truncateFields(res));
321
+ const bbox = res?.bbox || templateBbox;
300
322
  setSilentCaptureResult((prev) => ({
301
- ...prev, path: result.path, bbox: bbox,
302
- success: true, mrz: JSON.stringify(mrz), isAnalyzing: false,
323
+ ...prev,
324
+ path: result.path,
325
+ bbox: bbox,
326
+ success: true,
327
+ mrz: res?.mrz ? JSON.stringify(res.mrz) : "",
328
+ isAnalyzing: false,
303
329
  country: countryData?.code,
304
330
  documentType: selectedDocumentType.type,
305
331
  }));
@@ -320,28 +346,28 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
320
346
  }
321
347
  }
322
348
  };
323
- // Handle capture
324
349
  const handleCapture = async (result) => {
325
- console.log("handleCapture", JSON.stringify(truncateFields({ result, silentCaptureResult }), null, 2));
326
350
  if (silentCaptureResult.path) {
327
- // Créer une image rognée avec tolérance de 10% pour l'envoi
328
351
  let imagePathForUpload = silentCaptureResult.path;
329
352
  if (silentCaptureResult.bbox) {
330
353
  try {
331
- logger.log("Début du rognage avec tolérance, URI original:", silentCaptureResult.path);
332
354
  imagePathForUpload = await cropImageWithBBoxWithTolerance(silentCaptureResult.path, silentCaptureResult.bbox, 0.05);
333
- logger.log("Image rognée avec tolérance créée pour l'envoi, URI final:", imagePathForUpload);
334
355
  }
335
356
  catch (error) {
336
- logger.log("Erreur lors du rognage avec tolérance, utilisation de l'image originale:", truncateFields(error));
337
- // En cas d'erreur, on utilise l'image originale
338
357
  imagePathForUpload = silentCaptureResult.path;
339
358
  }
340
359
  }
341
360
  const base64 = await pathToBase64(imagePathForUpload);
342
- logger.log("silentCaptureResult captured", JSON.stringify(truncateFields(silentCaptureResult), null, 2));
343
- // Utiliser l'image originale pour l'affichage (dir) mais l'image rognée avec tolérance pour l'envoi (file)
344
- const newImages = { ...capturedImages, [currentSide]: { dir: silentCaptureResult.path, file: base64, mrz: silentCaptureResult.mrz || "", templatePath: silentCaptureResult.templatePath } };
361
+ // ✅ FIX: Use 'imagePathForUpload' (the cropped image) for the local display directory!
362
+ const newImages = {
363
+ ...capturedImages,
364
+ [currentSide]: {
365
+ dir: imagePathForUpload, // <--- CHANGED THIS LINE
366
+ file: base64,
367
+ mrz: silentCaptureResult.mrz || "",
368
+ templatePath: silentCaptureResult.templatePath
369
+ }
370
+ };
345
371
  setCapturedImages(newImages);
346
372
  if (silentCaptureResult.country && silentCaptureResult.documentType) {
347
373
  onValueChange({
@@ -468,7 +494,6 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
468
494
  documentType: silentCaptureResult.documentType,
469
495
  bbox: frontBbox // Restaurer le bbox pour recalculer le cropImageUri
470
496
  }));
471
- // Le useEffect va automatiquement recalculer le cropImageUri si bbox existe
472
497
  }
473
498
  else {
474
499
  // Pas d'image front, on réinitialise
@@ -485,6 +510,13 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
485
510
  totalSteps: state.template.components.length,
486
511
  side: currentSide,
487
512
  }}/>}/>
513
+
514
+ {silentCaptureResult.error ? (<View style={styles.floatingErrorBanner}>
515
+ <Text style={styles.floatingErrorText}>
516
+ {silentCaptureResult.error}
517
+ </Text>
518
+ </View>) : null}
519
+
488
520
  </View>);
489
521
  }
490
522
  return (<View style={styles.root}>
@@ -499,40 +531,48 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
499
531
  </Text>
500
532
 
501
533
  <View style={{ alignItems: 'center', justifyContent: 'center', flexDirection: "column", gap: 16 }}>
502
- <View style={{
534
+ {silentCaptureResult?.error === 'TOO_FAR_AWAY' && (<View style={{
535
+ backgroundColor: '#FF9500',
536
+ padding: 12,
537
+ borderRadius: 8,
538
+ width: '100%',
539
+ shadowColor: '#000',
540
+ shadowOffset: { width: 0, height: 2 },
541
+ shadowOpacity: 0.2,
542
+ shadowRadius: 4,
543
+ elevation: 4
544
+ }}>
545
+ <Text style={{ color: 'white', fontWeight: 'bold', textAlign: 'center', fontSize: 16 }}>
546
+ {state.currentLanguage === "en"
547
+ ? "Move the document closer to the camera."
548
+ : "Veuillez rapprocher le document de la caméra."}
549
+ </Text>
550
+ </View>)}
551
+
552
+ <View style={{
503
553
  width: '100%',
504
554
  height: 200,
505
555
  borderRadius: 12,
506
556
  padding: 1,
507
557
  overflow: 'hidden',
508
- // Shadow for iOS
509
558
  shadowColor: '#000',
510
559
  shadowOffset: { width: 0, height: 4 },
511
560
  shadowOpacity: 0.18,
512
561
  shadowRadius: 8,
513
- // Shadow for Android
514
562
  elevation: 8,
515
- backgroundColor: '#fff', // Needed for shadow to show on iOS
563
+ backgroundColor: '#000',
516
564
  }}>
517
- {cropImageUri ? (<Image source={{ uri: cropImageUri }} style={{
565
+ {capturedImages[currentSide]?.dir ? (<Image source={{ uri: capturedImages[currentSide].dir }} style={{
518
566
  width: '100%',
519
- height: 200,
567
+ height: '100%',
520
568
  borderRadius: 12,
521
- resizeMode: 'cover',
522
- }}/>) : null}
523
- {!cropImageUri && silentCaptureResult.path ? (<Image source={{ uri: silentCaptureResult.path }} style={{
569
+ resizeMode: 'contain',
570
+ }}/>) : silentCaptureResult.path ? (<Image source={{ uri: silentCaptureResult.path }} style={{
524
571
  width: '100%',
525
- height: 200,
572
+ height: '100%',
526
573
  borderRadius: 12,
527
- resizeMode: 'cover',
574
+ resizeMode: 'contain',
528
575
  }}/>) : null}
529
- {!cropImageUri && !silentCaptureResult.path && capturedImages[currentSide]?.dir ? (<Image source={{ uri: capturedImages[currentSide].dir }} style={{
530
- width: '100%',
531
- height: 200,
532
- borderRadius: 12,
533
- resizeMode: 'cover',
534
- }}/>) : null}
535
-
536
576
  </View>
537
577
  {/* Capture button si aucune image n'a été capturée */}
538
578
  {!capturedImages[currentSide]?.dir && (<Button title={state.currentLanguage === "en" ? "Take Photo" : "Prendre une photo"} onPress={() => {
@@ -557,7 +597,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
557
597
  setShowCamera(true);
558
598
  setCurrentSide('back');
559
599
  setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '', templatePath: '' }));
560
- setCropImageUri('');
600
+ // setCropImageUri('');
561
601
  }
562
602
  }} variant="primary" size="large" fullWidth/>
563
603
  </>)}
@@ -809,5 +849,29 @@ const styles = StyleSheet.create({
809
849
  color: '#666',
810
850
  fontWeight: '600',
811
851
  },
852
+ floatingErrorBanner: {
853
+ position: 'absolute',
854
+ top: 60, // Pushes it down slightly from the very top of the screen
855
+ left: '10%',
856
+ right: '10%',
857
+ backgroundColor: 'rgba(220, 38, 38, 0.95)', // Bright red
858
+ paddingVertical: 12,
859
+ paddingHorizontal: 16,
860
+ borderRadius: 8,
861
+ alignItems: 'center',
862
+ justifyContent: 'center',
863
+ shadowColor: '#000',
864
+ shadowOffset: { width: 0, height: 4 },
865
+ shadowOpacity: 0.3,
866
+ shadowRadius: 5,
867
+ elevation: 8,
868
+ zIndex: 100,
869
+ },
870
+ floatingErrorText: {
871
+ color: 'white',
872
+ fontSize: 14,
873
+ fontWeight: '700',
874
+ textAlign: 'center',
875
+ },
812
876
  });
813
877
  //# sourceMappingURL=IDCardCapture.js.map