@transfergratis/react-native-sdk 0.1.4 → 0.1.6

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 (216) hide show
  1. package/build/api/axios.d.ts +30 -0
  2. package/build/api/axios.d.ts.map +1 -0
  3. package/build/api/axios.js +92 -0
  4. package/build/api/axios.js.map +1 -0
  5. package/build/components/EnhancedCameraView.d.ts +1 -41
  6. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  7. package/build/components/EnhancedCameraView.js +75 -34
  8. package/build/components/EnhancedCameraView.js.map +1 -1
  9. package/build/components/EnhancedCameraView.web.d.ts +1 -41
  10. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  11. package/build/components/EnhancedCameraView.web.js +28 -4
  12. package/build/components/EnhancedCameraView.web.js.map +1 -1
  13. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +2 -2
  14. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  15. package/build/components/KYCElements/CountrySelectionTemplate.js +77 -114
  16. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  17. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  18. package/build/components/KYCElements/FileUploadTemplate.js +7 -3
  19. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  20. package/build/components/KYCElements/IDCardCapture.d.ts +7 -2
  21. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  22. package/build/components/KYCElements/IDCardCapture.js +253 -104
  23. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  24. package/build/components/KYCElements/InitializationStep.d.ts +5 -0
  25. package/build/components/KYCElements/InitializationStep.d.ts.map +1 -0
  26. package/build/components/KYCElements/InitializationStep.js +41 -0
  27. package/build/components/KYCElements/InitializationStep.js.map +1 -0
  28. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  29. package/build/components/KYCElements/LocationCaptureTemplate.js +15 -13
  30. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  31. package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -2
  32. package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
  33. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  34. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -2
  35. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
  36. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  37. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -2
  38. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
  39. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  40. package/build/components/KYCElements/ReviewSubmitTemplate.d.ts +12 -0
  41. package/build/components/KYCElements/ReviewSubmitTemplate.d.ts.map +1 -0
  42. package/build/components/KYCElements/ReviewSubmitTemplate.js +171 -0
  43. package/build/components/KYCElements/ReviewSubmitTemplate.js.map +1 -0
  44. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts +6 -2
  45. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  46. package/build/components/KYCElements/SelfieCaptureTemplate.js +137 -38
  47. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  48. package/build/components/KYCElements/VerificationProgressTemplate.d.ts +12 -0
  49. package/build/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -0
  50. package/build/components/KYCElements/VerificationProgressTemplate.js +194 -0
  51. package/build/components/KYCElements/VerificationProgressTemplate.js.map +1 -0
  52. package/build/components/OverLay/IdCard.d.ts +1 -1
  53. package/build/components/OverLay/IdCard.d.ts.map +1 -1
  54. package/build/components/OverLay/IdCard.js +10 -6
  55. package/build/components/OverLay/IdCard.js.map +1 -1
  56. package/build/components/OverLay/SelfieOverlay.d.ts +1 -1
  57. package/build/components/OverLay/SelfieOverlay.d.ts.map +1 -1
  58. package/build/components/OverLay/SelfieOverlay.js +5 -4
  59. package/build/components/OverLay/SelfieOverlay.js.map +1 -1
  60. package/build/components/OverLay/type.d.ts +71 -1
  61. package/build/components/OverLay/type.d.ts.map +1 -1
  62. package/build/components/OverLay/type.js.map +1 -1
  63. package/build/components/TemplateKYCExample.d.ts +4 -1
  64. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  65. package/build/components/TemplateKYCExample.js +74 -199
  66. package/build/components/TemplateKYCExample.js.map +1 -1
  67. package/build/components/TemplateKYCFlowRefactored.d.ts +3 -2
  68. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  69. package/build/components/TemplateKYCFlowRefactored.js +64 -40
  70. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  71. package/build/components/example/OrientationVideoExample.d.ts.map +1 -1
  72. package/build/components/example/OrientationVideoExample.js +1 -5
  73. package/build/components/example/OrientationVideoExample.js.map +1 -1
  74. package/build/config/countriesData.d.ts +3 -0
  75. package/build/config/countriesData.d.ts.map +1 -0
  76. package/build/config/countriesData.js +79 -0
  77. package/build/config/countriesData.js.map +1 -0
  78. package/build/config/region_mapping.d.ts +3 -0
  79. package/build/config/region_mapping.d.ts.map +1 -0
  80. package/build/config/region_mapping.js +687 -0
  81. package/build/config/region_mapping.js.map +1 -0
  82. package/build/hooks/useI18n.d.ts +11 -0
  83. package/build/hooks/useI18n.d.ts.map +1 -0
  84. package/build/hooks/useI18n.js +37 -0
  85. package/build/hooks/useI18n.js.map +1 -0
  86. package/build/hooks/useOrientationVideo.d.ts +1 -2
  87. package/build/hooks/useOrientationVideo.d.ts.map +1 -1
  88. package/build/hooks/useOrientationVideo.js +2 -1
  89. package/build/hooks/useOrientationVideo.js.map +1 -1
  90. package/build/hooks/useRealtimeVerifier.d.ts +28 -0
  91. package/build/hooks/useRealtimeVerifier.d.ts.map +1 -0
  92. package/build/hooks/useRealtimeVerifier.js +91 -0
  93. package/build/hooks/useRealtimeVerifier.js.map +1 -0
  94. package/build/hooks/useTemplateKYCFlow.d.ts +6 -3
  95. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  96. package/build/hooks/useTemplateKYCFlow.js +356 -42
  97. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  98. package/build/i18n/en/index.d.ts +168 -0
  99. package/build/i18n/en/index.d.ts.map +1 -0
  100. package/build/i18n/en/index.js +195 -0
  101. package/build/i18n/en/index.js.map +1 -0
  102. package/build/i18n/fr/index.d.ts +168 -0
  103. package/build/i18n/fr/index.d.ts.map +1 -0
  104. package/build/i18n/fr/index.js +194 -0
  105. package/build/i18n/fr/index.js.map +1 -0
  106. package/build/i18n/index.d.ts +10 -0
  107. package/build/i18n/index.d.ts.map +1 -0
  108. package/build/i18n/index.js +56 -0
  109. package/build/i18n/index.js.map +1 -0
  110. package/build/i18n/types.d.ts +153 -0
  111. package/build/i18n/types.d.ts.map +1 -0
  112. package/build/i18n/types.js +3 -0
  113. package/build/i18n/types.js.map +1 -0
  114. package/build/i18n/usage-example.d.ts +4 -0
  115. package/build/i18n/usage-example.d.ts.map +1 -0
  116. package/build/i18n/usage-example.js +189 -0
  117. package/build/i18n/usage-example.js.map +1 -0
  118. package/build/index.d.ts +1 -0
  119. package/build/index.d.ts.map +1 -1
  120. package/build/index.js +2 -0
  121. package/build/index.js.map +1 -1
  122. package/build/modules/api/CardAuthentification.d.ts +22 -0
  123. package/build/modules/api/CardAuthentification.d.ts.map +1 -0
  124. package/build/modules/api/CardAuthentification.js +107 -0
  125. package/build/modules/api/CardAuthentification.js.map +1 -0
  126. package/build/modules/api/KYCService.d.ts +58 -1
  127. package/build/modules/api/KYCService.d.ts.map +1 -1
  128. package/build/modules/api/KYCService.js +304 -27
  129. package/build/modules/api/KYCService.js.map +1 -1
  130. package/build/modules/api/SelfieVerification.d.ts +3 -0
  131. package/build/modules/api/SelfieVerification.d.ts.map +1 -0
  132. package/build/modules/api/SelfieVerification.js +9 -0
  133. package/build/modules/api/SelfieVerification.js.map +1 -0
  134. package/build/modules/api/backendApi.d.ts +2 -0
  135. package/build/modules/api/backendApi.d.ts.map +1 -0
  136. package/build/modules/api/backendApi.js +6 -0
  137. package/build/modules/api/backendApi.js.map +1 -0
  138. package/build/modules/api/types.d.ts +45 -0
  139. package/build/modules/api/types.d.ts.map +1 -0
  140. package/build/modules/api/types.js +2 -0
  141. package/build/modules/api/types.js.map +1 -0
  142. package/build/types/KYC.types.d.ts +56 -7
  143. package/build/types/KYC.types.d.ts.map +1 -1
  144. package/build/types/KYC.types.js +9 -1
  145. package/build/types/KYC.types.js.map +1 -1
  146. package/build/utils/cropByObb.d.ts +11 -0
  147. package/build/utils/cropByObb.d.ts.map +1 -0
  148. package/build/utils/cropByObb.js +78 -0
  149. package/build/utils/cropByObb.js.map +1 -0
  150. package/build/utils/get-document-type-info.d.ts +13 -0
  151. package/build/utils/get-document-type-info.d.ts.map +1 -0
  152. package/build/utils/get-document-type-info.js +59 -0
  153. package/build/utils/get-document-type-info.js.map +1 -0
  154. package/build/utils/pathToBase64.d.ts +3 -0
  155. package/build/utils/pathToBase64.d.ts.map +1 -0
  156. package/build/utils/pathToBase64.js +47 -0
  157. package/build/utils/pathToBase64.js.map +1 -0
  158. package/build/utils/remove-duplicate.d.ts +2 -0
  159. package/build/utils/remove-duplicate.d.ts.map +1 -0
  160. package/build/utils/remove-duplicate.js +4 -0
  161. package/build/utils/remove-duplicate.js.map +1 -0
  162. package/build/web/WebKYCEntry.d.ts +9 -0
  163. package/build/web/WebKYCEntry.d.ts.map +1 -0
  164. package/build/web/WebKYCEntry.js +156 -0
  165. package/build/web/WebKYCEntry.js.map +1 -0
  166. package/build/web/index.d.ts +2 -0
  167. package/build/web/index.d.ts.map +1 -0
  168. package/build/web/index.js +2 -0
  169. package/build/web/index.js.map +1 -0
  170. package/package.json +3 -1
  171. package/src/api/axios.ts +144 -0
  172. package/src/components/EnhancedCameraView.tsx +96 -78
  173. package/src/components/EnhancedCameraView.web.tsx +41 -40
  174. package/src/components/KYCElements/CountrySelectionTemplate.tsx +111 -136
  175. package/src/components/KYCElements/FileUploadTemplate.tsx +14 -8
  176. package/src/components/KYCElements/IDCardCapture.tsx +311 -115
  177. package/src/components/KYCElements/InitializationStep.tsx +53 -0
  178. package/src/components/KYCElements/LocationCaptureTemplate.tsx +17 -15
  179. package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
  180. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
  181. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
  182. package/src/components/KYCElements/ReviewSubmitTemplate.tsx +201 -0
  183. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +174 -57
  184. package/src/components/KYCElements/VerificationProgressTemplate.tsx +246 -0
  185. package/src/components/OverLay/IdCard.tsx +17 -9
  186. package/src/components/OverLay/SelfieOverlay.tsx +6 -5
  187. package/src/components/OverLay/type.ts +64 -2
  188. package/src/components/TemplateKYCExample.tsx +80 -200
  189. package/src/components/TemplateKYCFlowRefactored.tsx +80 -48
  190. package/src/components/example/OrientationVideoExample.tsx +3 -7
  191. package/src/config/countriesData.ts +84 -0
  192. package/src/config/region_mapping.ts +688 -0
  193. package/src/hooks/useI18n.ts +53 -0
  194. package/src/hooks/useOrientationVideo.ts +2 -2
  195. package/src/hooks/useRealtimeVerifier.ts +128 -0
  196. package/src/hooks/useTemplateKYCFlow.tsx +407 -57
  197. package/src/i18n/README.md +288 -0
  198. package/src/i18n/en/index.ts +206 -0
  199. package/src/i18n/fr/index.ts +205 -0
  200. package/src/i18n/index.ts +65 -0
  201. package/src/i18n/types.ts +172 -0
  202. package/src/i18n/usage-example.tsx +202 -0
  203. package/src/index.ts +3 -0
  204. package/src/modules/api/CardAuthentification.ts +114 -0
  205. package/src/modules/api/KYCService.ts +350 -30
  206. package/src/modules/api/SelfieVerification.ts +11 -0
  207. package/src/modules/api/backendApi.ts +8 -0
  208. package/src/modules/api/types.ts +51 -0
  209. package/src/types/KYC.types.ts +82 -14
  210. package/src/utils/cropByObb.ts +99 -0
  211. package/src/utils/get-document-type-info.ts +62 -0
  212. package/src/utils/pathToBase64.ts +47 -0
  213. package/src/utils/remove-duplicate.ts +3 -0
  214. package/src/web/WebKYCEntry.tsx +215 -0
  215. package/src/web/index.ts +1 -0
  216. package/src/types/nativewind.d.ts +0 -2
@@ -1,15 +1,23 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
2
  import { View, Text, StyleSheet, Alert, ScrollView } from 'react-native';
3
- import { TemplateComponent, SelfieConfig, LocalizedText } from '../../types/KYC.types';
3
+ import { TemplateComponent, SelfieConfig, LocalizedText, ISilentCaptureResult, OrientationType, GovernmentDocumentType } from '../../types/KYC.types';
4
4
  import { EnhancedCameraView } from '../EnhancedCameraView';
5
5
  import { Button } from '../ui/Button';
6
6
  import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
7
+ import { useI18n } from '../../hooks/useI18n';
7
8
  import SelfieOverlay from '../OverLay/SelfieOverlay';
9
+ import selfieVerification from '../../modules/api/SelfieVerification';
10
+ import pathToBase64 from '../../utils/pathToBase64';
11
+ import { truncateFields } from '../../modules/api/KYCService';
8
12
 
13
+ interface IImagePayload {
14
+ dir: string;
15
+ file: string;
16
+ }
9
17
  interface SelfieCaptureTemplateProps {
10
18
  component: TemplateComponent;
11
- value: Record<string, string> | undefined;
12
- onValueChange: (value: Record<string, string>) => void;
19
+ value: Record<string, IImagePayload> | undefined;
20
+ onValueChange: (value: Record<string, IImagePayload | string>) => void;
13
21
  error?: string;
14
22
  language: string;
15
23
  }
@@ -21,30 +29,41 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
21
29
  error,
22
30
  language,
23
31
  }) => {
32
+ const { t } = useI18n();
24
33
  const config = component.config as SelfieConfig;
25
- const maxAttempts = config.max_attempts || 3;
26
- const orientations = config.orientations || ['front'];
34
+ const orientations: OrientationType[] = (config.orientations || ['center']) as OrientationType[];
27
35
  const { actions, state, } = useTemplateKYCFlowContext();
28
36
 
37
+ const [silentCaptureResult, setSilentCaptureResult] = useState<ISilentCaptureResult>({ success: false, isAnalyzing: false });
38
+
39
+
29
40
  const [showCamera, setShowCamera] = useState(false);
30
- const [currentOrientation, setCurrentOrientation] = useState<string>(orientations[0]);
31
- const [capturedImages, setCapturedImages] = useState<Record<string, string>>(value || {});
32
- const [attempts, setAttempts] = useState(0);
41
+ const [currentOrientation, setCurrentOrientation] = useState<OrientationType>(orientations[0]);
42
+ const [capturedImages, setCapturedImages] = useState<Record<string, IImagePayload>>(value || {});
33
43
 
34
- const getLocalizedText = (text: LocalizedText): string => {
35
- return text[language] || text.en || '';
44
+ const getLocalizedText = (text: LocalizedText | Record<string, LocalizedText>): string => {
45
+ if (!text) return '';
46
+ // If text is a nested record (e.g., per theme/device), try to pick a default then localize
47
+ const maybeNested = text as Record<string, LocalizedText>;
48
+ if (maybeNested && typeof maybeNested === 'object' && 'en' in maybeNested === false && 'fr' in maybeNested === false) {
49
+ const firstKey = Object.keys(maybeNested)[0];
50
+ const nested = maybeNested[firstKey] as LocalizedText;
51
+ return nested?.[language] || nested?.en || '';
52
+ }
53
+ const loc = text as LocalizedText;
54
+ return loc[language] || loc.en || '';
36
55
  };
37
56
 
38
- const getOrientationInstructions = (orientation: string): string => {
57
+ const getOrientationInstructions = (orientation: OrientationType): string => {
39
58
  switch (orientation) {
40
- case 'front':
41
- return 'Prenez un selfie de face, regardez droit devant vous';
59
+ case 'center':
60
+ return state.currentLanguage === "en" ? "Take a selfie of face, look straight ahead you" : "Prenez un selfie de face, regardez droit devant vous";
42
61
  case 'left':
43
- return 'Tournez votre tête vers la gauche, gardez les épaules droites';
62
+ return state.currentLanguage === "en" ? "Turn your head to the left, keep your shoulders straight" : "Tournez votre tête vers la gauche, gardez les épaules droites";
44
63
  case 'right':
45
- return 'Tournez votre tête vers la droite, gardez les épaules droites';
64
+ return state.currentLanguage === "en" ? "Turn your head to the right, keep your shoulders straight" : "Tournez votre tête vers la droite, gardez les épaules droites";
46
65
  default:
47
- return getLocalizedText(component.instructions);
66
+ return getLocalizedText(component.instructions as LocalizedText);
48
67
  }
49
68
  };
50
69
 
@@ -52,60 +71,136 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
52
71
  actions.showCustomStepper(!showCamera);
53
72
  }, [showCamera]);
54
73
 
55
- const getOrientationLabel = (orientation: string): string => {
74
+ const getOrientationLabel = (orientation: OrientationType): string => {
56
75
  switch (orientation) {
57
- case 'front':
58
- return 'Selfie de face';
76
+ case 'center':
77
+ return state.currentLanguage === "en" ? "Selfie de face" : "Selfie de face";
59
78
  case 'left':
60
- return 'Selfie profil gauche';
79
+ return state.currentLanguage === "en" ? "Selfie profil gauche" : "Selfie profil gauche";
61
80
  case 'right':
62
- return 'Selfie profil droit';
81
+ return state.currentLanguage === "en" ? "Selfie profil droit" : "Selfie profil droit";
63
82
  default:
64
- return getLocalizedText(component.labels);
83
+ return getLocalizedText(component.labels as LocalizedText);
65
84
  }
66
85
  };
86
+ const getOrientationOpposite = (orientation: OrientationType): OrientationType => {
87
+ switch (orientation) {
88
+ case 'center':
89
+
90
+ return 'center';
91
+ case 'left':
92
+ return 'left';
93
+ case 'right':
94
+ return 'right';
95
+ default:
96
+ return orientation;
97
+ }
98
+ }
99
+ // const countryData = useMemo(() => {
100
+ // const geCountryID = Object.keys(state.componentData).find((c: string) => c === "6");
67
101
 
68
- const getInstructions = (orientation: string): { title: string, subtitle: string } => {
102
+ // if (geCountryID) {
103
+ // const countryMapping = state.componentData[geCountryID];
104
+ // return countryMapping?.code;
105
+ // }
106
+ // return null;
107
+ // }, [state.componentData]);
108
+
109
+ const getInstructions = (orientation: OrientationType): { title: string, subtitle: string } => {
110
+ const lang = state.currentLanguage;
69
111
  switch (orientation) {
70
- case 'front':
71
- return { title: 'Center your face', subtitle: 'Align your face to the center of the selfie area and then take a photo' };
112
+ case 'center':
113
+ return {
114
+ title:
115
+
116
+ lang === "en" ? 'Center your face' : 'Mettez votre face au centre',
117
+ subtitle: lang === "en"
118
+ ? 'Align your face to the center of the selfie area and then take a photo'
119
+ : 'Alignez votre visage au centre de la zone selfie puis prenez une photo'
120
+ };
72
121
  case 'left':
73
- return { title: 'Turn your head left', subtitle: 'Slowly turn your head to the left while keeping your face in the selfie area' };
122
+ return {
123
+ title:
124
+ lang === 'en'
125
+ ? 'Turn your head left'
126
+ : 'Tournez la tête vers la gauche',
127
+ subtitle:
128
+ lang === 'en'
129
+ ? 'Slowly turn your head to the left while keeping your face in the selfie area'
130
+ : 'Tournez lentement la tête vers la gauche en gardant votre visage dans la zone selfie'
131
+ };
74
132
  case 'right':
75
- return { title: 'Turn your head right', subtitle: 'Slowly turn your head to the right while keeping your face in the selfie area' };
133
+ return {
134
+ title:
135
+ lang === 'en'
136
+ ? 'Turn your head right'
137
+ : 'Tournez la tête vers la droite',
138
+ subtitle:
139
+ lang === 'en'
140
+ ? 'Slowly turn your head to the right while keeping your face in the selfie area'
141
+ : 'Tournez lentement la tête vers la droite en gardant votre visage dans la zone selfie'
142
+ };
76
143
  default:
77
- return { title: getLocalizedText(component.labels), subtitle: getLocalizedText(component.instructions) };
144
+ return { title: getLocalizedText(component.labels as LocalizedText), subtitle: getLocalizedText(component.instructions as LocalizedText) };
78
145
  }
79
146
  };
80
147
 
81
- const handleCapture = (result: { success: boolean; path?: string; error?: string }) => {
148
+ const handleSilentCapture = (result: { success: boolean; path?: string; error?: string }) => {
149
+ if (silentCaptureResult.isAnalyzing) {
150
+ return;
151
+ }
82
152
  if (result.success && result.path) {
83
- const newImages = { ...capturedImages, [currentOrientation]: result.path };
153
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: true, success: false, error: '' }));
154
+ selfieVerification(result.path).then((response) => {
155
+ if (response.length > 0) {
156
+ const res = response[0];
157
+ if (res?.orientation_direction === getOrientationOpposite(currentOrientation) && res?.capture) {
158
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: true, error: '', path: result.path }));
159
+ } else {
160
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Le selfie n\'est pas correct' }));
161
+ }
162
+ } else {
163
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Le selfie n\'est pas correct' }));
164
+ }
165
+ }).catch((e: any) => {
166
+
167
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de vérification du selfie' }));
168
+ });
169
+ }
170
+ }
171
+
172
+ const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
173
+ console.log("handleCapturessss", silentCaptureResult);
174
+
175
+ if (silentCaptureResult.success && silentCaptureResult.path) {
176
+ const base64 = await pathToBase64(silentCaptureResult.path);
177
+ // Keep backward-compatible structure for UI validation and add *_base64 fields for backend payload
178
+ const newImages: Record<string, any> = {
179
+ ...capturedImages,
180
+ [currentOrientation]: { dir: silentCaptureResult.path, file: base64 } as IImagePayload,
181
+ };
84
182
  setCapturedImages(newImages);
85
183
  onValueChange(newImages);
86
184
  setShowCamera(false);
87
- setAttempts(0);
185
+
88
186
 
89
187
  // Passer à l'orientation suivante si disponible
90
- const currentIndex = orientations.indexOf(currentOrientation as any);
188
+ const currentIndex = orientations.indexOf(currentOrientation as OrientationType);
91
189
  if (currentIndex < orientations.length - 1) {
92
190
  setCurrentOrientation(orientations[currentIndex + 1]);
93
191
  }
94
- } else {
95
- const newAttempts = attempts + 1;
96
- setAttempts(newAttempts);
97
-
98
- if (newAttempts >= maxAttempts) {
99
- Alert.alert(
100
- 'Limite atteinte',
101
- `Vous avez atteint le nombre maximum de tentatives (${maxAttempts}).`
102
- );
103
- setShowCamera(false);
104
- } else {
105
- Alert.alert('Erreur', result.error || 'Impossible de prendre le selfie');
106
- }
107
192
  }
108
193
  };
194
+ const idCardData = useMemo(() => {
195
+ const idCardID = Object.keys(state.componentData).find((c: string) => c === "1");
196
+ if (idCardID) {
197
+ const _idCardData = state.componentData[idCardID];
198
+ return _idCardData as unknown as { country: string; documentType: GovernmentDocumentType };
199
+ }
200
+ return null;
201
+ }, [state.componentData]);
202
+ console.log("idCardData", truncateFields(idCardData), JSON.stringify(truncateFields(state.componentData), null, 2));
203
+
109
204
 
110
205
  const handleError = (event: { message: string }) => {
111
206
  Alert.alert('Erreur', event.message);
@@ -113,13 +208,15 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
113
208
  };
114
209
 
115
210
 
116
- const isOrientationCompleted = (orientation: string): boolean => {
211
+ const isOrientationCompleted = (orientation: OrientationType): boolean => {
117
212
  return !!capturedImages[orientation];
118
213
  };
119
214
 
120
215
  const isAllOrientationsCompleted = (): boolean => {
121
216
  return orientations.every(orientation => isOrientationCompleted(orientation));
122
217
  };
218
+ console.log("Current Orientation", currentOrientation);
219
+
123
220
 
124
221
  if (showCamera) {
125
222
  return (
@@ -136,6 +233,8 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
136
233
  showCaptureButton={true}
137
234
  showSwitchCamera={false}
138
235
  enableFlash={false}
236
+ silentCaptureResult={silentCaptureResult}
237
+ onSilentCapture={handleSilentCapture}
139
238
  overlayComponent={<SelfieOverlay
140
239
  xMin={20}
141
240
  yMin={140}
@@ -143,7 +242,8 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
143
242
  yMax={340}
144
243
  cornerOpacity={1}
145
244
  instructions={""}
146
- orientation={currentOrientation as 'front' | 'left' | 'right'}
245
+ language={state.currentLanguage}
246
+ orientation={currentOrientation as 'center' | 'left' | 'right'}
147
247
  instructionsTile={getInstructions(currentOrientation).title}
148
248
  instructionsSubtitle={getInstructions(currentOrientation).subtitle}
149
249
  stepperProps={{
@@ -159,7 +259,6 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
159
259
  );
160
260
  }
161
261
 
162
-
163
262
  return (
164
263
  <View style={[{
165
264
  maxWidth: 760,
@@ -167,8 +266,8 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
167
266
  height: '100%',
168
267
  }]}>
169
268
  <View style={styles.container}>
170
- <Text style={styles.title}>{getLocalizedText(component.labels)}</Text>
171
- <Text style={styles.description}>{getLocalizedText(component.instructions)}</Text>
269
+ <Text style={styles.title}>{getLocalizedText(component.labels as LocalizedText)}</Text>
270
+ <Text style={styles.description}>{getLocalizedText(component.instructions as LocalizedText)}</Text>
172
271
 
173
272
  <ScrollView style={styles.orientationsContainer} showsVerticalScrollIndicator={false}>
174
273
  <View style={{ flexDirection: 'column', alignItems: 'center', gap: 10 }}>
@@ -207,25 +306,43 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
207
306
  ))}
208
307
  <View style={{ backgroundColor: "#F6CB0D1A", width: "100%", padding: 10, borderRadius: 10 }} >
209
308
  <Text style={{ color: '#997E06', fontSize: 16, fontWeight: 'bold' }}>
210
- Tips for a good selfie:
309
+ {state.currentLanguage === "en"
310
+ ? "Tips for a good selfie:"
311
+ : "Conseils pour une bonne photo selfie :"}
211
312
  </Text>
212
313
  <View style={{ paddingLeft: 10, paddingTop: 4 }}>
213
314
  <Text style={{ color: '#997E06', fontSize: 14, fontWeight: 'bold' }}>
214
- {`• Remove glasses and hat \n• Look directly at the camera \n• Ensure good lighting \n`}
315
+ {state.currentLanguage === "en"
316
+ ? `• Remove glasses and hat \n• Look directly at the camera \n• Ensure good lighting \n`
317
+ : `• Retirez vos lunettes et votre chapeau \n• Regardez directement la caméra \n• Assurez-vous d'une bonne luminosité \n`
318
+ }
215
319
  </Text>
216
320
  </View>
217
321
  </View>
218
322
  <View style={{ height: 10 }} />
219
323
  {isAllOrientationsCompleted() ? <>
220
- <Button title="Continuer" fullWidth style={{ paddingVertical: 20, paddingTop: 12 }} onPress={() => {
221
- onValueChange(capturedImages);
324
+ <Button title={t('common.continue')} fullWidth style={{ paddingVertical: 20, paddingTop: 12 }} onPress={async () => {
325
+ if (idCardData?.country && idCardData?.documentType) {
326
+ const value = {
327
+ ...capturedImages,
328
+ country: idCardData?.country || '',
329
+ documentType: idCardData?.documentType as GovernmentDocumentType,
330
+ }
331
+ console.log("value", JSON.stringify(truncateFields(value), null, 2));
332
+ onValueChange(value);
333
+ } else {
334
+ Alert.alert('Erreur', 'Veuillez sélectionner un pays et un type de document');
335
+ return;
336
+ }
222
337
  actions.nextComponent();
223
338
  }} />
224
339
  </> : (
225
- <Button title="Start Selfie" fullWidth style={{ paddingVertical: 20, paddingTop: 12 }} onPress={() => {
340
+ <Button title={t('kyc.selfieCapture.captureButton')} fullWidth style={{ paddingVertical: 20, paddingTop: 12 }} onPress={() => {
226
341
  if (isAllOrientationsCompleted()) {
227
- onValueChange(capturedImages);
342
+
343
+ actions.nextComponent();
228
344
  } else {
345
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: '' }));
229
346
  setShowCamera(true);
230
347
  }
231
348
  }} />
@@ -0,0 +1,246 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
3
+ import { TemplateComponent } from '../../types/KYC.types';
4
+ import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
5
+ import { useI18n } from '../../hooks/useI18n';
6
+ import kycService from '../../modules/api/KYCService';
7
+
8
+ interface VerificationProgressTemplateProps {
9
+ component: TemplateComponent;
10
+ value: unknown;
11
+ onValueChange: (data: unknown) => void;
12
+ error?: string;
13
+ language?: string;
14
+ }
15
+
16
+ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplateProps> = () => {
17
+ const { t } = useI18n();
18
+ const { state, actions } = useTemplateKYCFlowContext();
19
+ const [retryCount, setRetryCount] = useState(0);
20
+ const [isRetrying, setIsRetrying] = useState(false);
21
+ const [timeoutId, setTimeoutId] = useState<number | null>(null);
22
+
23
+ const verification = state.verification;
24
+
25
+ const isSuccess = verification.status === 'success';
26
+ const isFailed = verification.status === 'failed';
27
+
28
+ // Fonction pour récupérer le résultat de vérification avec retry
29
+ const getVerificationResult = async (currentRetryCount = 0, maxRetries = 40) => {
30
+ try {
31
+ console.log(`Starting verification check (attempt ${currentRetryCount + 1}/${maxRetries + 1})`);
32
+
33
+ const result = await kycService.getVerificationResult(state.session.session_id);
34
+ const verificationResult = result[state.session.session_id].data;
35
+
36
+ console.log('getVerificationResult result', JSON.stringify(result, null, 2));
37
+
38
+ // Vérifier le statut de vérification
39
+ if (verificationResult.verification_status === null || verificationResult.verification_status === "processing" || verificationResult.verification_status === "pending") {
40
+ if (currentRetryCount < maxRetries) {
41
+ const nextRetryCount = currentRetryCount + 1;
42
+ setRetryCount(nextRetryCount);
43
+ setIsRetrying(true);
44
+
45
+ console.log(`Verification still processing, retrying in 10s... (${nextRetryCount}/${maxRetries})`);
46
+
47
+ // Nettoyer le timeout précédent s'il existe
48
+ if (timeoutId) {
49
+ clearTimeout(timeoutId);
50
+ }
51
+
52
+ // Attendre 10 secondes avant de relancer
53
+ const newTimeoutId = setTimeout(() => {
54
+ getVerificationResult(nextRetryCount, maxRetries);
55
+ }, 10000);
56
+
57
+ setTimeoutId(newTimeoutId);
58
+ return; // Sortir de la fonction pour éviter de traiter le résultat
59
+ } else {
60
+ console.error('Max retries reached, verification still processing');
61
+ setIsRetrying(false);
62
+ return;
63
+ }
64
+ }
65
+
66
+ // Traiter le résultat final (success, failed, etc.)
67
+ if (verificationResult.verification_status === "completed" ||
68
+ verificationResult.verification_status === "approved") {
69
+ // Mettre à jour l'état avec le succès
70
+ actions.setVerificationState({
71
+ status: 'success',
72
+ result: result
73
+ });
74
+ } else if (verificationResult.verification_status === "failed" ||
75
+ verificationResult.verification_status === "rejected") {
76
+ // Mettre à jour l'état avec l'échec
77
+ actions.setVerificationState({
78
+ status: 'failed',
79
+ result: {
80
+ referenceId: verificationResult.verification_id,
81
+ issues: ['Verification failed']
82
+ } as any
83
+ });
84
+ }
85
+
86
+ setIsRetrying(false);
87
+
88
+ } catch (error) {
89
+ console.error('getVerificationResult error', error);
90
+
91
+ // En cas d'erreur, on peut aussi retry
92
+ if (currentRetryCount < maxRetries) {
93
+ const nextRetryCount = currentRetryCount + 1;
94
+ setRetryCount(nextRetryCount);
95
+ setIsRetrying(true);
96
+
97
+ console.log(`Error occurred, retrying in 10s... (${nextRetryCount}/${maxRetries})`);
98
+
99
+ // Nettoyer le timeout précédent s'il existe
100
+ if (timeoutId) {
101
+ clearTimeout(timeoutId);
102
+ }
103
+
104
+ const newTimeoutId = setTimeout(() => {
105
+ getVerificationResult(nextRetryCount, maxRetries);
106
+ }, 10000);
107
+
108
+ setTimeoutId(newTimeoutId);
109
+ } else {
110
+ setIsRetrying(false);
111
+ console.error('Max retries reached after errors');
112
+ }
113
+ }
114
+ };
115
+
116
+ useEffect(() => {
117
+ if (state.session.session_id) {
118
+ getVerificationResult();
119
+ }
120
+ }, [state.session.session_id]);
121
+
122
+ // Nettoyage des timeouts lors du démontage du composant
123
+ useEffect(() => {
124
+ return () => {
125
+ if (timeoutId) {
126
+ clearTimeout(timeoutId);
127
+ }
128
+ };
129
+ }, [timeoutId]);
130
+
131
+ if (isSuccess) {
132
+ return (
133
+ <View style={styles.container}>
134
+ <Text style={styles.title}>{t('kyc.verificationProgress.steps.complete')}</Text>
135
+ <Text style={styles.subtitle}>{t('kyc.verificationProgress.subtitle')}</Text>
136
+ <TouchableOpacity style={[styles.button, styles.primary]} onPress={() => actions.submitVerification()}>
137
+ <Text style={styles.buttonText}>{t('common.continue')}</Text>
138
+ </TouchableOpacity>
139
+ {verification.result?.referenceId ? (
140
+ <Text style={styles.refText}>{`Verification ID: ${verification.result.referenceId}`}</Text>
141
+ ) : null}
142
+ </View>
143
+ );
144
+ }
145
+
146
+ if (isFailed) {
147
+ return (
148
+ <View style={styles.container}>
149
+ <Text style={styles.title}>{t('kyc.verificationProgress.status.failed')}</Text>
150
+ <Text style={styles.subtitle}>{t('errors.validationError')}</Text>
151
+ <View style={styles.issuesBox}>
152
+ <Text style={styles.issuesTitle}>{t('common.error')}</Text>
153
+ {Array.isArray(verification.result?.issues) && verification.result.issues.map((issue: string, idx: number) => (
154
+ <Text key={idx} style={styles.issueItem}>• {issue}</Text>
155
+ ))}
156
+ </View>
157
+ <TouchableOpacity style={[styles.button, styles.primary]} onPress={() => actions.goToComponent(state.template.components.find(c => c.type !== 'review_submit' && c.type !== 'verification_progress')?.id || 0)}>
158
+ <Text style={styles.buttonText}>{t('common.retry')}</Text>
159
+ </TouchableOpacity>
160
+ <TouchableOpacity style={[styles.button, styles.secondary]}>
161
+ <Text style={styles.secondaryText}>{t('common.info')}</Text>
162
+ </TouchableOpacity>
163
+ {verification.result?.referenceId ? (
164
+ <Text style={styles.refText}>{`Reference ID: ${verification.result.referenceId}`}</Text>
165
+ ) : null}
166
+ </View>
167
+ );
168
+ }
169
+
170
+
171
+
172
+
173
+
174
+
175
+
176
+ return (
177
+ <View style={styles.container}>
178
+ <Text style={styles.title}>{t('kyc.verificationProgress.title')}</Text>
179
+ <Text style={styles.subtitle}>
180
+ {isRetrying
181
+ ? `${t('kyc.verificationProgress.retrying')} (${retryCount}/10)`
182
+ : t('kyc.verificationProgress.estimatedTime')
183
+ }
184
+ </Text>
185
+
186
+ <View style={styles.timeline}>
187
+ <View style={[styles.dot, styles.done]}><Text style={styles.stepCheckmark}>✓</Text></View>
188
+ <View style={styles.line} />
189
+ <View style={[styles.dot, styles.active]}><Text style={styles.stepCheckmark}>2</Text></View>
190
+ <View style={styles.line} />
191
+ <View style={[styles.dot, styles.pending]}><Text style={styles.stepCheckmark}>3</Text></View>
192
+ </View>
193
+
194
+ <View style={styles.row}>
195
+ <Text style={styles.stepLabel}>{t('kyc.verificationProgress.status.completed')}</Text>
196
+ <Text style={styles.stepLabel}>{t('kyc.verificationProgress.steps.analyzing')}</Text>
197
+ <Text style={styles.stepLabel}>{t('kyc.verificationProgress.steps.verifying')}</Text>
198
+ </View>
199
+
200
+ <View style={styles.card}>
201
+ <Text style={styles.cardTitle}>{t('kyc.idCardCapture.title')}</Text>
202
+ <Text style={styles.cardLine}>{t('kyc.verificationProgress.steps.extracting')}</Text>
203
+ <Text style={styles.cardTitle}>{t('kyc.selfieCapture.title')}</Text>
204
+ <Text style={styles.cardLine}>{t('kyc.verificationProgress.steps.analyzing')}</Text>
205
+ <Text style={styles.cardTitle}>{t('kyc.verificationProgress.title')}</Text>
206
+ <Text style={styles.cardLine}>
207
+ {isRetrying
208
+ ? `${t('kyc.verificationProgress.status.processing')}...`
209
+ : t('kyc.verificationProgress.status.pending')
210
+ }
211
+ </Text>
212
+ </View>
213
+ </View>
214
+ );
215
+ };
216
+
217
+ const styles = StyleSheet.create({
218
+ container: { flex: 1, width: '100%', paddingHorizontal: 16, paddingVertical: 12 },
219
+ title: { fontSize: 24, fontWeight: 'bold', color: '#111827', textAlign: 'center', marginTop: 12 },
220
+ subtitle: { fontSize: 14, color: '#6B7280', textAlign: 'center', marginTop: 8 },
221
+ timeline: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginTop: 24 },
222
+ dot: { width: 38, height: 38, borderRadius: 34, justifyContent: 'center', alignItems: 'center' },
223
+ stepCheckmark: { color: 'white', fontSize: 16, fontWeight: 'bold' },
224
+ line: { flex: 1, height: 4, backgroundColor: '#E5E7EB' },
225
+ done: { backgroundColor: '#10B981' },
226
+ active: { backgroundColor: '#F59E0B' },
227
+ pending: { backgroundColor: '#E5E7EB' },
228
+ row: { flexDirection: 'row', justifyContent: 'space-between', marginTop: 8 },
229
+ stepLabel: { fontSize: 14, color: '#111827', fontWeight: '600' },
230
+ card: { backgroundColor: 'white', padding: 16, borderRadius: 16, marginTop: 24 },
231
+ cardTitle: { fontSize: 16, color: '#6B7280', marginTop: 8 },
232
+ cardLine: { fontSize: 18, color: '#111827', fontWeight: '600', marginTop: 2 },
233
+ // Success/Failure specific
234
+ button: { paddingVertical: 14, borderRadius: 10, alignItems: 'center', marginTop: 20 },
235
+ primary: { backgroundColor: '#2DBD60' },
236
+ buttonText: { color: 'white', fontSize: 16, fontWeight: '600' },
237
+ refText: { marginTop: 12, textAlign: 'center', color: '#6B7280' },
238
+ issuesBox: { backgroundColor: '#FEF2F2', borderRadius: 16, padding: 16, marginTop: 20 },
239
+ issuesTitle: { color: '#991B1B', fontWeight: '700', marginBottom: 8 },
240
+ issueItem: { color: '#991B1B', marginTop: 4 },
241
+ secondary: { backgroundColor: '#F3F4F6' },
242
+ secondaryText: { color: '#111827', fontSize: 16, fontWeight: '600' },
243
+ });
244
+
245
+
246
+
@@ -7,18 +7,18 @@ import StepOverlay from "./StepOverlay";
7
7
 
8
8
 
9
9
 
10
- const IdCardOverlay = ({ xMin: xMinProps, yMin: yMinProps, xMax: xMaxProps, yMax: yMaxProps, cornerOpacity, instructions, cornerColor = "#4CAF50", stepperProps }: IdCardOverlayProps) => {
10
+ const IdCardOverlay = ({ xMin: xMinProps, yMin: yMinProps, xMax: xMaxProps, yMax: yMaxProps, cornerOpacity, instructions, cornerColor = "#4CAF50", stepperProps, isSuccess, language }: IdCardOverlayProps) => {
11
11
  const screenWidth = Dimensions.get('window').width;
12
12
  const screenHeight = Dimensions.get('window').height;
13
13
 
14
14
  const boxWidth = xMaxProps - xMinProps;
15
15
  const boxHeight = yMaxProps - yMinProps;
16
16
 
17
-
18
17
 
19
18
 
20
-
21
-
19
+
20
+
21
+
22
22
  // Calculer les coordonnées pour centrer le cadre
23
23
  const xMin = (screenWidth - boxWidth) / 2;
24
24
  const yMin = (screenHeight - boxHeight) / 3;
@@ -30,7 +30,7 @@ const IdCardOverlay = ({ xMin: xMinProps, yMin: yMinProps, xMax: xMaxProps, yMax
30
30
  {stepperProps ? (
31
31
  <StepOverlay {...stepperProps} />
32
32
  ) : null}
33
-
33
+
34
34
  <Animated.View style={[styles.topIntruction]}>
35
35
  <Text style={styles.topIntructionText}>
36
36
  💡 {instructions}
@@ -134,15 +134,23 @@ const IdCardOverlay = ({ xMin: xMinProps, yMin: yMinProps, xMax: xMaxProps, yMax
134
134
  opacity={cornerOpacity}
135
135
  />
136
136
  </Svg>
137
-
138
- <ScanningLine yMin={yMin} screenWidth={screenWidth} height={screenHeight} yMax={yMax} />
137
+ {isSuccess ? null : (
138
+ <ScanningLine yMin={yMin} screenWidth={screenWidth} height={screenHeight} yMax={yMax} />
139
+ )}
140
+ {/* <ScanningLine yMin={yMin} screenWidth={screenWidth} height={screenHeight} yMax={yMax} /> */}
139
141
 
140
142
  <View style={styles.bottomIntruction}>
141
143
  <Text style={styles.bottomTitle}>
142
- Mesurez 100cm de hauteur.
144
+ {language === "en" ? "Measure 100cm in height.":"Mesurez 100cm de hauteur."}
145
+
143
146
  </Text>
144
147
  <Text style={styles.bottomSubtitle}>
145
- Ne pas déplacer la caméra pendant la capture. • Ne pas déplacer la caméra pendant la capture.
148
+ {
149
+ language === "en"
150
+ ? "Do not move the camera during capture. • Do not move the camera during capture."
151
+ : "Ne pas déplacer la caméra pendant la capture. • Ne pas déplacer la caméra pendant la capture."
152
+ }
153
+
146
154
  </Text>
147
155
  </View>
148
156
  </View>)