@transfergratis/react-native-sdk 0.1.22 → 0.1.24

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 (142) hide show
  1. package/android/src/main/AndroidManifest.xml +9 -4
  2. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  3. package/build/components/EnhancedCameraView.js +26 -3
  4. package/build/components/EnhancedCameraView.js.map +1 -1
  5. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  6. package/build/components/EnhancedCameraView.web.js +21 -0
  7. package/build/components/EnhancedCameraView.web.js.map +1 -1
  8. package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
  9. package/build/components/KYCElements/CameraCapture.js +4 -3
  10. package/build/components/KYCElements/CameraCapture.js.map +1 -1
  11. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
  12. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  13. package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
  14. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  15. package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
  16. package/build/components/KYCElements/FileUpload.js +5 -4
  17. package/build/components/KYCElements/FileUpload.js.map +1 -1
  18. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  19. package/build/components/KYCElements/FileUploadTemplate.js +5 -4
  20. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  21. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  22. package/build/components/KYCElements/IDCardCapture.js +193 -237
  23. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  24. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  25. package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
  26. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  27. package/build/components/KYCElements/OrientationVideoCapture.js +3 -2
  28. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  29. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +3 -2
  30. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  31. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +3 -2
  32. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  33. package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  34. package/build/components/KYCElements/SelfieCapture.js +4 -3
  35. package/build/components/KYCElements/SelfieCapture.js.map +1 -1
  36. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  37. package/build/components/KYCElements/SelfieCaptureTemplate.js +182 -39
  38. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  39. package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
  40. package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
  41. package/build/components/KYCElements/WelcomeTemplate.js +243 -0
  42. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
  43. package/build/components/TemplateKYCExample.d.ts +4 -2
  44. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  45. package/build/components/TemplateKYCExample.js +5 -68
  46. package/build/components/TemplateKYCExample.js.map +1 -1
  47. package/build/components/TemplateKYCFlowRefactored.d.ts +2 -1
  48. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  49. package/build/components/TemplateKYCFlowRefactored.js +95 -9
  50. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  51. package/build/components/example/DynamicTemplateExample.d.ts +10 -0
  52. package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
  53. package/build/components/example/DynamicTemplateExample.js +241 -0
  54. package/build/components/example/DynamicTemplateExample.js.map +1 -0
  55. package/build/config/allowedDomains.d.ts +30 -0
  56. package/build/config/allowedDomains.d.ts.map +1 -0
  57. package/build/config/allowedDomains.js +127 -0
  58. package/build/config/allowedDomains.js.map +1 -0
  59. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  60. package/build/hooks/useTemplateKYCFlow.js +68 -43
  61. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  62. package/build/hooks/useTemplateLoader.d.ts +14 -0
  63. package/build/hooks/useTemplateLoader.d.ts.map +1 -0
  64. package/build/hooks/useTemplateLoader.js +85 -0
  65. package/build/hooks/useTemplateLoader.js.map +1 -0
  66. package/build/i18n/en/index.d.ts +9 -0
  67. package/build/i18n/en/index.d.ts.map +1 -1
  68. package/build/i18n/en/index.js +9 -0
  69. package/build/i18n/en/index.js.map +1 -1
  70. package/build/i18n/fr/index.d.ts +9 -0
  71. package/build/i18n/fr/index.d.ts.map +1 -1
  72. package/build/i18n/fr/index.js +9 -0
  73. package/build/i18n/fr/index.js.map +1 -1
  74. package/build/index.d.ts +5 -0
  75. package/build/index.d.ts.map +1 -1
  76. package/build/index.js +8 -0
  77. package/build/index.js.map +1 -1
  78. package/build/modules/api/CardAuthentification.js +1 -0
  79. package/build/modules/api/CardAuthentification.js.map +1 -1
  80. package/build/modules/api/KYCService.d.ts +4 -1
  81. package/build/modules/api/KYCService.d.ts.map +1 -1
  82. package/build/modules/api/KYCService.js +17 -5
  83. package/build/modules/api/KYCService.js.map +1 -1
  84. package/build/modules/api/TemplateService.d.ts +45 -0
  85. package/build/modules/api/TemplateService.d.ts.map +1 -0
  86. package/build/modules/api/TemplateService.js +145 -0
  87. package/build/modules/api/TemplateService.js.map +1 -0
  88. package/build/modules/api/types.d.ts +1 -0
  89. package/build/modules/api/types.d.ts.map +1 -1
  90. package/build/modules/api/types.js.map +1 -1
  91. package/build/types/KYC.types.d.ts +144 -4
  92. package/build/types/KYC.types.d.ts.map +1 -1
  93. package/build/types/KYC.types.js +15 -0
  94. package/build/types/KYC.types.js.map +1 -1
  95. package/build/utils/cropByObb.d.ts +1 -0
  96. package/build/utils/cropByObb.d.ts.map +1 -1
  97. package/build/utils/cropByObb.js +70 -0
  98. package/build/utils/cropByObb.js.map +1 -1
  99. package/build/utils/platformAlert.d.ts +20 -0
  100. package/build/utils/platformAlert.d.ts.map +1 -0
  101. package/build/utils/platformAlert.js +67 -0
  102. package/build/utils/platformAlert.js.map +1 -0
  103. package/build/utils/template-transformer.d.ts +10 -0
  104. package/build/utils/template-transformer.d.ts.map +1 -0
  105. package/build/utils/template-transformer.js +353 -0
  106. package/build/utils/template-transformer.js.map +1 -0
  107. package/build/web/WebKYCEntry.d.ts.map +1 -1
  108. package/build/web/WebKYCEntry.js +102 -20
  109. package/build/web/WebKYCEntry.js.map +1 -1
  110. package/package.json +1 -1
  111. package/src/components/EnhancedCameraView.tsx +31 -2
  112. package/src/components/EnhancedCameraView.web.tsx +24 -0
  113. package/src/components/KYCElements/CameraCapture.tsx +4 -3
  114. package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
  115. package/src/components/KYCElements/FileUpload.tsx +5 -4
  116. package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
  117. package/src/components/KYCElements/IDCardCapture.tsx +196 -254
  118. package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
  119. package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
  120. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
  121. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
  122. package/src/components/KYCElements/SelfieCapture.tsx +4 -3
  123. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +195 -41
  124. package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
  125. package/src/components/TemplateKYCExample.tsx +16 -71
  126. package/src/components/TemplateKYCFlowRefactored.tsx +121 -11
  127. package/src/components/example/DynamicTemplateExample.tsx +289 -0
  128. package/src/config/allowedDomains.ts +152 -0
  129. package/src/hooks/useTemplateKYCFlow.tsx +71 -46
  130. package/src/hooks/useTemplateLoader.ts +102 -0
  131. package/src/i18n/en/index.ts +10 -0
  132. package/src/i18n/fr/index.ts +9 -0
  133. package/src/index.ts +11 -0
  134. package/src/modules/api/CardAuthentification.ts +1 -1
  135. package/src/modules/api/KYCService.ts +18 -8
  136. package/src/modules/api/TemplateService.ts +167 -0
  137. package/src/modules/api/types.ts +1 -0
  138. package/src/types/KYC.types.ts +188 -3
  139. package/src/utils/cropByObb.ts +83 -3
  140. package/src/utils/platformAlert.ts +85 -0
  141. package/src/utils/template-transformer.ts +433 -0
  142. package/src/web/WebKYCEntry.tsx +122 -24
@@ -1,7 +1,8 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
- import { View, Text, TouchableOpacity, StyleSheet, Image, Alert, ScrollView, Dimensions } from 'react-native';
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { View, Text, StyleSheet, Image, ScrollView } from 'react-native';
3
+ import { showAlert } from '../../utils/platformAlert';
3
4
  import { EnhancedCameraView } from '../EnhancedCameraView';
4
- import { TemplateComponent, IDCardConfig, LocalizedText, GovernmentDocumentType, ISilentCaptureResult, IBbox } from '../../types/KYC.types';
5
+ import { TemplateComponent, LocalizedText, GovernmentDocumentType, ISilentCaptureResult, IBbox, GovernmentDocumentTypeShorted, GovernmentDocumentTypeBackend } from '../../types/KYC.types';
5
6
  import IdCardOverlay from '../OverLay/IdCard';
6
7
  import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
7
8
  import { useI18n } from '../../hooks/useI18n';
@@ -11,7 +12,7 @@ import { backVerification, checkTemplateType, frontVerification } from '../../mo
11
12
  import { getDocumentTypeInfo } from '../../utils/get-document-type-info';
12
13
  import pathToBase64 from '../../utils/pathToBase64';
13
14
  import { truncateFields } from '../../modules/api/KYCService';
14
- import { cropByObb, cropImageWithBBox } from '../../utils/cropByObb';
15
+ import { cropByObb, cropImageWithBBox, cropImageWithBBoxWithTolerance } from '../../utils/cropByObb';
15
16
  import { logger } from '../../utils/logger';
16
17
 
17
18
 
@@ -45,24 +46,19 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
45
46
  const [currentSide, setCurrentSide] = useState<string>('front');
46
47
  // Stocker les bbox par côté pour pouvoir restaurer les images croppées
47
48
  const [bboxBySide, setBboxBySide] = useState<Record<string, IBbox>>({});
48
- const [selectedDocumentType, setSelectedDocumentType] = useState<{
49
- type: GovernmentDocumentType;
50
- region: string;
51
- }>({
52
- type: 'identity_card',
53
- region: 'root'
54
- });
55
- const [showDocumentSelection, setShowDocumentSelection] = useState(
56
- {
57
- documentSelection: true,
58
- regionSelection: false
59
- }
60
- );
61
49
  const [silentCaptureResult, setSilentCaptureResult] = useState<ISilentCaptureResult>({ success: false, isAnalyzing: false });
50
+
51
+ // Mapping des types de documents backend vers SDK
52
+ const documentTypeMapping: Record<string, GovernmentDocumentType> = {
53
+ 'nationalId': 'national_id',
54
+ 'passport': 'passport',
55
+ 'driversLicense': 'drivers_licence',
56
+ 'residencePermit': 'permanent_residence',
57
+ 'healthInsuranceCard': 'health_insurance_card',
58
+ };
62
59
  // const [imageNaturalSize, setImageNaturalSize] = useState<{ width: number; height: number } | null>(null);
63
60
 
64
- const { actions, state, } = useTemplateKYCFlowContext();
65
- const config = component.config as IDCardConfig;
61
+ const { actions, state } = useTemplateKYCFlowContext();
66
62
 
67
63
 
68
64
 
@@ -74,31 +70,30 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
74
70
  return "";
75
71
  };
76
72
 
77
- const countryData = useMemo(() => {
78
- const geCountryID = Object.keys(state.componentData).find((c: string) => c === "6");
79
-
80
- if (geCountryID) {
81
- const countryMapping = state.componentData[geCountryID];
82
- return countryMapping;
73
+ // Récupérer les données depuis le composant country_selection
74
+ const countrySelectionData = useMemo(() => {
75
+ const countrySelectionComponent = state.template.components.find(c => c.type === 'country_selection');
76
+ if (countrySelectionComponent) {
77
+ return state.componentData[countrySelectionComponent.id];
83
78
  }
84
79
  return null;
85
- }, [state.componentData]);
80
+ }, [state.template.components, state.componentData]);
86
81
 
82
+ // Extraire selectedDocumentType depuis countrySelectionData
83
+ const selectedDocumentType = useMemo<{ type: GovernmentDocumentType; region: string } | null>(() => {
84
+ if (!countrySelectionData?.documentType) return null;
87
85
 
88
- const availableDocumentTypes = useMemo(() => {
89
- const newconfig: GovernmentDocumentType[] = [];
86
+ const backendDocType = countrySelectionData.documentType;
87
+ const mappedType = documentTypeMapping[backendDocType] || backendDocType as GovernmentDocumentType;
88
+ const region = countrySelectionData.region || 'root';
90
89
 
91
- if (countryData) {
92
- const countryMapping = Object.keys(countryData?.regionMapping);
93
- if (countryMapping && Array.isArray(countryMapping)) {
94
- countryMapping.forEach((c) => {
95
- newconfig.push(c as GovernmentDocumentType);
96
- });
97
- }
98
- }
99
- logger.log("newconfig", truncateFields(newconfig));
100
- return newconfig;
101
- }, []);
90
+ return { type: mappedType, region };
91
+ }, [countrySelectionData, documentTypeMapping]);
92
+
93
+ // Récupérer countryData pour compatibilité avec le code existant
94
+ const countryData = useMemo(() => {
95
+ return countrySelectionData;
96
+ }, [countrySelectionData]);
102
97
 
103
98
 
104
99
  useEffect(() => {
@@ -112,28 +107,33 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
112
107
 
113
108
 
114
109
 
115
- const cameraConfig = {
116
- aspectRatio: 4 / 3,
117
- quality: 0.8,
118
- flashMode: 'auto' as const,
119
- cameraType: 'back' as const,
120
- allowRetake: true,
121
- maxRetakes: 3,
122
- overlay: {
123
- showGuide: true,
124
- guideText: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr) : getLocalizedText(component.instructions as Record<string, LocalizedText>),
125
- bbox: selectedDocumentType && config.bbox_configs[selectedDocumentType.type] ? config.bbox_configs[selectedDocumentType.type] : {
126
- xMin: 20,
127
- yMin: 140,
128
- xMax: 370,
129
- yMax: 340,
130
- borderColor: '#2DBD60',
131
- borderWidth: 3,
132
- cornerRadius: 8
133
- }
110
+ const cameraConfig = useMemo(() => {
111
+ const instructions = selectedDocumentType
112
+ ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr)
113
+ : getLocalizedText(component.instructions as Record<string, LocalizedText>);
134
114
 
135
- }
136
- };
115
+ return {
116
+ aspectRatio: 4 / 3,
117
+ quality: 0.8,
118
+ flashMode: 'auto' as const,
119
+ cameraType: 'back' as const,
120
+ allowRetake: true,
121
+ maxRetakes: 3,
122
+ overlay: {
123
+ showGuide: true,
124
+ guideText: instructions,
125
+ bbox: {
126
+ xMin: 20,
127
+ yMin: 140,
128
+ xMax: 370,
129
+ yMax: 340,
130
+ borderColor: '#2DBD60',
131
+ borderWidth: 3,
132
+ cornerRadius: 8
133
+ }
134
+ }
135
+ };
136
+ }, [selectedDocumentType, locale, component.instructions]);
137
137
 
138
138
  const retakePicture = (currentSide: string) => {
139
139
  setShowCamera(true);
@@ -144,29 +144,18 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
144
144
  onValueChange({ ...value, [currentSide]: { dir: '', file: '', mrz: '' } });
145
145
  };
146
146
 
147
- const hasRegions = useCallback((documentType: GovernmentDocumentType): { hasRegions: boolean, regionMapping: string[] } => {
148
- const regionMapping = countryData?.regionMapping[documentType as GovernmentDocumentType];
149
- if (regionMapping && Object.keys(regionMapping).length > 1) {
150
- return {
151
- hasRegions: true,
152
- regionMapping: Object.keys(regionMapping)
153
- };
154
- }
155
- return {
156
- hasRegions: false,
157
- regionMapping: []
158
- };
159
- }, [countryData]);
160
147
 
161
148
 
162
149
  const getCurrentSideVerification = (currentSide: string) => {
163
- const regionMapping = countryData?.regionMapping[selectedDocumentType?.type as GovernmentDocumentType];
150
+ if (!selectedDocumentType || !countryData?.regionMapping) {
151
+ return { authMethod: [], mrzTypes: [], regionMapping: null, key: 'root' };
152
+ }
153
+
154
+ const regionMapping = countryData.regionMapping[selectedDocumentType.type as GovernmentDocumentType];
164
155
  const authMethod: string[] = [];
165
156
  const mrzTypes: string[] = [];
166
157
 
167
- const key = selectedDocumentType?.region?.trim()?.length > 0 ? selectedDocumentType?.region?.trim() : 'root';
168
- // console.log("regionMapping", JSON.stringify(regionMapping, null, 2), selectedDocumentType?.region);
169
- // const key = selectedDocumentType?.region as keyof typeof regionMapping;
158
+ const key = selectedDocumentType.region?.trim()?.length > 0 ? selectedDocumentType.region.trim() : 'root';
170
159
 
171
160
  if (regionMapping?.[key] && Array.isArray(regionMapping[key])) {
172
161
  regionMapping[key].forEach((item: any) => {
@@ -179,13 +168,13 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
179
168
  });
180
169
  }
181
170
 
182
- logger.log("regionMapping", JSON.stringify(truncateFields({ regionMapping, selectedDocumentType: selectedDocumentType?.region, key }), null, 2));
171
+ logger.log("regionMapping", JSON.stringify(truncateFields({ regionMapping, selectedDocumentType: selectedDocumentType.region, key }), null, 2));
183
172
 
184
173
  return { authMethod: removeDuplicates(authMethod), mrzTypes: removeDuplicates(mrzTypes), regionMapping: regionMapping, key: key };
185
174
  }
186
175
 
187
176
  const getCorrespondingMrzType = (templatePath: string, mapping: any, selectedDocumentType: string = "root") => {
188
- if (!mapping[selectedDocumentType]) return null;
177
+ if (!mapping || !mapping[selectedDocumentType]) return null;
189
178
 
190
179
  // Extraire le nom du fichier depuis le template_path
191
180
  const fileName = templatePath.split("/").pop()?.replace(".jpg", "").replace(".png", "");
@@ -210,10 +199,20 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
210
199
  let templatePath = silentCaptureResult.templatePath || '';
211
200
  let templateBbox: IBbox | undefined;
212
201
 
202
+ if (!selectedDocumentType) {
203
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Document type not selected' }));
204
+ return;
205
+ }
206
+
213
207
  const regionMappings = getCurrentSideVerification(currentSide)
214
208
  // logger.log("regionMappings", JSON.stringify(truncateFields({regionMappings, templatePath}), null, 2));
215
209
  if (templatePath.length === 0) {
216
210
  try {
211
+ logger.log("checkTemplateType - BEFORE call", {
212
+ selectedDocumentTypeType: selectedDocumentType?.type,
213
+ countrySelectionDataDocumentType: countrySelectionData?.documentType,
214
+ docTypeToSend: selectedDocumentType?.type
215
+ });
217
216
  const templateType = await checkTemplateType({ path: result.path || '', docType: selectedDocumentType?.type as GovernmentDocumentType, docRegion: countryData?.code || "", postfix: currentSide });
218
217
 
219
218
  if (templateType.template_path) {
@@ -232,21 +231,38 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
232
231
  } catch (e: any) {
233
232
  logger.log("error checking template type", truncateFields(e));
234
233
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de vérification du template' }));
234
+ return; // Return early if checkTemplateType fails
235
235
  }
236
236
  }
237
- logger.log("templatePath before front verification", templatePath);
237
+ logger.log("templatePath before verification", templatePath, "currentSide", currentSide);
238
238
  if (currentSide === 'front') {
239
-
240
- frontVerification(
241
- {
239
+ logger.log("frontVerification - BEFORE call", {
240
+ selectedDocumentTypeType: selectedDocumentType?.type,
241
+ countrySelectionDataDocumentType: countrySelectionData?.documentType,
242
+ docTypeToSend: selectedDocumentType?.type
243
+ });
244
+ logger.log("Calling frontVerification", { templatePath, selectedDocumentType: selectedDocumentType?.type, regionMappings });
245
+ console.log("About to call frontVerification", typeof frontVerification);
246
+ try {
247
+ const mrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || '';
248
+ console.log("mrzType calculated", mrzType);
249
+ const verificationParams = {
242
250
  path: result.path,
243
- regionMapping: regionMappings,
244
- selectedDocumentType: selectedDocumentType?.type || '',
251
+ regionMapping: {
252
+ authMethod: regionMappings.authMethod,
253
+ mrzTypes: regionMappings.mrzTypes,
254
+ },
255
+ selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType?.type as keyof typeof GovernmentDocumentTypeShorted] || '',
245
256
  code: countryData?.code || '',
246
257
  currentSide: currentSide,
247
258
  templatePath: templatePath,
248
- mrzType: getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || '',
249
- }).then((mrz) => {
259
+ mrzType: mrzType,
260
+ };
261
+ console.log("frontVerification params", verificationParams);
262
+ console.log("About to call frontVerification function");
263
+ const promise = frontVerification(verificationParams);
264
+ console.log("frontVerification promise created", promise);
265
+ promise.then((mrz) => {
250
266
  logger.log("front verification result", truncateFields(mrz));
251
267
  const bbox = (mrz as any)?.bbox || templateBbox;
252
268
  setSilentCaptureResult((prev) => ({
@@ -256,7 +272,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
256
272
  bbox: bbox, success: true,
257
273
  mrz: JSON.stringify(mrz), isAnalyzing: false,
258
274
  country: countryData?.code,
259
- documentType: selectedDocumentType?.type,
275
+ documentType: selectedDocumentType.type,
260
276
  }),
261
277
  );
262
278
  // Stocker le bbox pour ce côté
@@ -265,19 +281,29 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
265
281
  }
266
282
 
267
283
  }).catch((e: any) => {
284
+ console.log("error front verification", e);
268
285
  logger.log("error front verification", truncateFields(e));
269
286
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
270
- // Alert.alert('Erreur', e?.message || 'Erreur de détection du MRZ');
271
- })
287
+ // showAlert('Erreur', e?.message || 'Erreur de détection du MRZ');
288
+ });
289
+ } catch (error: any) {
290
+ console.log("Error setting up frontVerification call", error);
291
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: error?.message || 'Erreur lors de la configuration de la vérification' }));
292
+ }
272
293
  } else {
294
+ const backRegionMappings = getCurrentSideVerification(currentSide);
295
+ logger.log("Calling backVerification", { templatePath, selectedDocumentType: selectedDocumentType.type, backRegionMappings });
273
296
  backVerification({
274
297
  path: result.path,
275
- regionMapping: getCurrentSideVerification(currentSide),
276
- selectedDocumentType: selectedDocumentType?.type || '',
298
+ regionMapping: {
299
+ authMethod: backRegionMappings.authMethod,
300
+ mrzTypes: backRegionMappings.mrzTypes,
301
+ },
302
+ selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType.type as keyof typeof GovernmentDocumentTypeShorted] || '',
277
303
  code: countryData?.code || '',
278
304
  currentSide: currentSide,
279
305
  templatePath: templatePath,
280
- mrzType: getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || '',
306
+ mrzType: getCorrespondingMrzType(templatePath, backRegionMappings.regionMapping, backRegionMappings.key || '') || '',
281
307
  }).then((mrz) => {
282
308
  logger.log("back verification result", truncateFields(mrz));
283
309
  const bbox = (mrz as any)?.bbox || templateBbox;
@@ -285,7 +311,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
285
311
  ...prev, path: result.path, bbox: bbox,
286
312
  success: true, mrz: JSON.stringify(mrz), isAnalyzing: false,
287
313
  country: countryData?.code,
288
- documentType: selectedDocumentType?.type,
314
+ documentType: selectedDocumentType.type,
289
315
 
290
316
  }));
291
317
  // Stocker le bbox pour ce côté
@@ -295,7 +321,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
295
321
  }).catch((e: any) => {
296
322
  logger.log("error back verification", truncateFields(e));
297
323
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
298
- // Alert.alert('Erreur', e?.message || 'Erreur de détection du MRZ');
324
+ // showAlert('Erreur', e?.message || 'Erreur de détection du MRZ');
299
325
  })
300
326
  }
301
327
 
@@ -304,28 +330,45 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
304
330
 
305
331
  // Handle capture
306
332
  const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
333
+ console.log("handleCapture", JSON.stringify(truncateFields({ result, silentCaptureResult }), null, 2));
334
+
307
335
  if (silentCaptureResult.path) {
308
- const base64 = await pathToBase64(silentCaptureResult.path);
336
+ // Créer une image rognée avec tolérance de 10% pour l'envoi
337
+ let imagePathForUpload = silentCaptureResult.path;
338
+ if (silentCaptureResult.bbox) {
339
+ try {
340
+ logger.log("Début du rognage avec tolérance, URI original:", silentCaptureResult.path);
341
+ imagePathForUpload = await cropImageWithBBoxWithTolerance(silentCaptureResult.path, silentCaptureResult.bbox, 0.05);
342
+ logger.log("Image rognée avec tolérance créée pour l'envoi, URI final:", imagePathForUpload);
343
+ } catch (error) {
344
+ logger.log("Erreur lors du rognage avec tolérance, utilisation de l'image originale:", truncateFields(error));
345
+ // En cas d'erreur, on utilise l'image originale
346
+ imagePathForUpload = silentCaptureResult.path;
347
+ }
348
+ }
349
+
350
+ const base64 = await pathToBase64(imagePathForUpload);
309
351
 
310
352
  logger.log("silentCaptureResult captured", JSON.stringify(truncateFields(silentCaptureResult), null, 2));
353
+ // Utiliser l'image originale pour l'affichage (dir) mais l'image rognée avec tolérance pour l'envoi (file)
311
354
  const newImages = { ...capturedImages, [currentSide]: { dir: silentCaptureResult.path, file: base64, mrz: silentCaptureResult.mrz || "", templatePath: silentCaptureResult.templatePath } };
312
355
  setCapturedImages(newImages);
313
356
  if (silentCaptureResult.country && silentCaptureResult.documentType) {
314
357
  onValueChange({
315
358
  ...newImages,
316
359
  country: silentCaptureResult.country,
317
- documentType: silentCaptureResult.documentType,
360
+ documentType: GovernmentDocumentTypeBackend[silentCaptureResult.documentType as keyof typeof GovernmentDocumentTypeBackend] || '',
318
361
  });
319
362
  }
320
363
  setShowCamera(false);
321
364
  actions.showCustomStepper(true);
322
365
  } else {
323
- Alert.alert('Erreur', result.error || 'Impossible de prendre la photo');
366
+ showAlert('Erreur', result.error || 'Impossible de prendre la photo');
324
367
  }
325
368
  };
326
369
 
327
370
  const handleError = (event: { message: string }) => {
328
- Alert.alert('Erreur', event.message);
371
+ showAlert('Erreur', event.message);
329
372
  setShowCamera(false);
330
373
  };
331
374
 
@@ -342,93 +385,18 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
342
385
 
343
386
 
344
387
 
345
- const handleDocumentTypeSelection = (docType: GovernmentDocumentType) => {
346
- setSelectedDocumentType({ type: docType, region: hasRegions(docType) ? '' : 'root' });
347
- setSilentCaptureResult((prev) => ({ ...prev, templatePath: '', bbox: undefined, success: false, isAnalyzing: false, error: '', path: '', mrz: '', country: '', documentType: '' }));
348
- setCapturedImages((prev) => ({ front: { dir: '', file: '', mrz: '', templatePath: '' }, back: { dir: '', file: '', mrz: '', templatePath: '' } }));
349
- };
350
- const handleRegionSelection = (region: string) => {
351
- setSelectedDocumentType((prev) => ({ ...prev, region: region }));
352
- };
353
388
 
354
- // Interface de sélection du type de document
355
- if (showDocumentSelection.documentSelection || showDocumentSelection.regionSelection) {
389
+ // Vérifier si les données sont disponibles, sinon afficher un message d'erreur
390
+ if (!countrySelectionData || !selectedDocumentType) {
356
391
  return (
357
392
  <View style={styles.root}>
358
393
  <View style={styles.container}>
359
-
360
394
  <Text style={styles.title}>{getLocalizedText(component.labels)}</Text>
361
- <Text style={styles.description}>{getLocalizedText(component.instructions)}</Text>
362
-
363
- <ScrollView style={styles.documentTypesContainer} showsVerticalScrollIndicator={false}>
364
- {showDocumentSelection.documentSelection ? availableDocumentTypes.map((docType) => {
365
- const docInfo = getDocumentTypeInfo(docType);
366
- return (
367
- <TouchableOpacity
368
- key={docType}
369
- style={[styles.documentTypeButton, { backgroundColor: selectedDocumentType?.type === docType ? '#2DBD6030' : '#F8F9FA', borderColor: selectedDocumentType?.type === docType ? '#2DBD60' : '#E9ECEF', borderWidth: 2 }]}
370
- onPress={() => handleDocumentTypeSelection(docType)}
371
- >
372
- <View style={[styles.documentTypeIconContainer, { backgroundColor: 'gray', }]}>
373
- <Text style={styles.documentTypeIcon}>{docInfo.icon}</Text>
374
- </View>
375
- <Text style={styles.documentTypeName}>
376
- {docInfo.name[language as keyof typeof docInfo.name] || docInfo.name.en}
377
- </Text>
378
- <Text style={styles.documentTypeArrow}>→</Text>
379
- </TouchableOpacity>
380
- );
381
- }) : null}
382
- {showDocumentSelection.regionSelection ?
383
- hasRegions(selectedDocumentType?.type || 'identity_card').regionMapping.map((region) => {
384
- return (
385
- <TouchableOpacity key={region} onPress={() => handleRegionSelection(region)}
386
- style={[styles.documentTypeButton, { backgroundColor: selectedDocumentType?.region === region ? '#2DBD6030' : '#F8F9FA', borderColor: selectedDocumentType?.region === region ? '#2DBD60' : '#E9ECEF', borderWidth: 2 }]}
387
-
388
- >
389
- <Text>{region}</Text>
390
- </TouchableOpacity>
391
- );
392
- }) : null}
393
- </ScrollView>
394
- <Button title={t('common.next')} onPress={() => {
395
- if (!selectedDocumentType) {
396
- Alert.alert(t('common.error'), t('validation.required'));
397
- return;
398
- }
399
-
400
- if (showDocumentSelection.regionSelection) {
401
- logger.log("showDocumentSelection", JSON.stringify(truncateFields(showDocumentSelection), null, 2));
402
- setShowDocumentSelection({
403
- documentSelection: false,
404
- regionSelection: false
405
- });
406
- setShowCamera(true);
407
- actions.showCustomStepper(false);
408
- return;
409
- } else {
410
- if (hasRegions(selectedDocumentType?.type)?.hasRegions) {
411
- setShowDocumentSelection({
412
- documentSelection: false,
413
- regionSelection: true
414
- });
415
- } else {
416
- setShowDocumentSelection({
417
- documentSelection: false,
418
- regionSelection: false
419
- });
420
- setShowCamera(true);
421
- actions.showCustomStepper(false);
422
- }
423
- }
424
-
425
- }}
426
- variant={selectedDocumentType ? 'primary' : "neutral"}
427
- size="large"
428
- fullWidth
429
- />
430
-
431
-
395
+ <Text style={styles.description}>
396
+ {state.currentLanguage === "en"
397
+ ? "Please complete the country and document selection first."
398
+ : "Veuillez d'abord compléter la sélection du pays et du document."}
399
+ </Text>
432
400
  {error && (
433
401
  <Text style={styles.errorText}>{error}</Text>
434
402
  )}
@@ -470,10 +438,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
470
438
  if (currentSide === 'back') {
471
439
  setCurrentSide('front');
472
440
  setShowCamera(false);
473
- setShowDocumentSelection({
474
- documentSelection: false,
475
- regionSelection: false
476
- });
477
441
  // Si une image front existe, on la restaure pour l'afficher
478
442
  if (capturedImages['front']?.dir) {
479
443
  // Restaurer l'état de capture du front pour afficher l'image
@@ -496,14 +460,11 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
496
460
  setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '' }));
497
461
  }
498
462
  } else {
499
- setShowDocumentSelection({
500
- documentSelection: true,
501
- regionSelection: false
502
- });
503
- actions.showCustomStepper(true);
463
+ // Retour au composant précédent (country_selection)
464
+ actions.previousComponent();
504
465
  }
505
466
  },
506
- selectedDocumentType: locale === 'en' ? getDocumentTypeInfo(selectedDocumentType?.type || "identity_card").name.en : getDocumentTypeInfo(selectedDocumentType?.type || "identity_card").name.fr || '',
467
+ selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
507
468
  step: state.currentComponentIndex + 1,
508
469
  totalSteps: state.template.components.length,
509
470
  side: currentSide,
@@ -568,30 +529,50 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
568
529
  />) : null}
569
530
 
570
531
  </View>
571
- {/* retake button */}
572
- <Button title={t('kyc.idCardCapture.retakeButton')} onPress={() => {
573
- retakePicture(currentSide);
574
- }}
575
- variant="outline"
576
- size="medium"
577
- fullWidth
578
-
579
- />
580
- <Button title={t('common.next')} onPress={() => {
581
- if (currentSide === 'back' || selectedDocumentType?.type === 'passport') {
582
- actions.nextComponent();
583
- return;
584
- } else {
585
- setShowCamera(true);
586
- setCurrentSide('back');
587
- setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '', templatePath: '' }));
588
- setCropImageUri('');
589
- }
590
- }}
591
- variant="primary"
592
- size="large"
593
- fullWidth
594
- />
532
+ {/* Capture button si aucune image n'a été capturée */}
533
+ {!capturedImages[currentSide]?.dir && (
534
+ <Button
535
+ title={state.currentLanguage === "en" ? "Take Photo" : "Prendre une photo"}
536
+ onPress={() => {
537
+ setShowCamera(true);
538
+ actions.showCustomStepper(false);
539
+ }}
540
+ variant="primary"
541
+ size="large"
542
+ fullWidth
543
+ />
544
+ )}
545
+ {/* retake button si une image a été capturée */}
546
+ {capturedImages[currentSide]?.dir && (
547
+ <>
548
+ <Button title={t('kyc.idCardCapture.retakeButton')} onPress={() => {
549
+ retakePicture(currentSide);
550
+ }}
551
+ variant="outline"
552
+ size="medium"
553
+ fullWidth
554
+ />
555
+ <Button title={t('common.next')} onPress={() => {
556
+ if (!selectedDocumentType) {
557
+ showAlert('Error', 'Document type not selected');
558
+ return;
559
+ }
560
+ if (currentSide === 'back' || selectedDocumentType.type === 'passport') {
561
+ actions.nextComponent();
562
+ return;
563
+ } else {
564
+ setShowCamera(true);
565
+ setCurrentSide('back');
566
+ setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '', templatePath: '' }));
567
+ setCropImageUri('');
568
+ }
569
+ }}
570
+ variant="primary"
571
+ size="large"
572
+ fullWidth
573
+ />
574
+ </>
575
+ )}
595
576
  </View>
596
577
  </View>
597
578
  </ScrollView>
@@ -757,43 +738,4 @@ const styles = StyleSheet.create({
757
738
  marginTop: 8,
758
739
  textAlign: 'center',
759
740
  },
760
- documentTypesContainer: {
761
- // flex: 1,
762
- // marginBottom: 16,
763
- // paddingHorizontal: 16,
764
- maxHeight: Dimensions.get('window').height - 400,
765
- },
766
- documentTypeButton: {
767
- flexDirection: 'row',
768
- alignItems: 'center',
769
- backgroundColor: '#F8F9FA',
770
- padding: 16,
771
- marginBottom: 12,
772
- borderRadius: 12,
773
- borderWidth: 1,
774
- borderColor: '#E9ECEF',
775
- },
776
- documentTypeIconContainer: {
777
-
778
- width: 40, height: 40,
779
- borderRadius: 10,
780
- alignItems: 'center',
781
- justifyContent: 'center',
782
- marginRight: 16,
783
- },
784
- documentTypeIcon: {
785
- fontSize: 24,
786
-
787
- },
788
- documentTypeName: {
789
- flex: 1,
790
- fontSize: 16,
791
- fontWeight: '600',
792
- color: '#333',
793
- },
794
- documentTypeArrow: {
795
- fontSize: 18,
796
- color: '#666',
797
- fontWeight: 'bold',
798
- },
799
741
  });