@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.
- package/android/src/main/AndroidManifest.xml +9 -4
- package/build/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.js +26 -3
- package/build/components/EnhancedCameraView.js.map +1 -1
- package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.web.js +21 -0
- package/build/components/EnhancedCameraView.web.js.map +1 -1
- package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
- package/build/components/KYCElements/CameraCapture.js +4 -3
- package/build/components/KYCElements/CameraCapture.js.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
- package/build/components/KYCElements/FileUpload.js +5 -4
- package/build/components/KYCElements/FileUpload.js.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.js +5 -4
- package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +193 -237
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
- package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.js +3 -2
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +3 -2
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js +3 -2
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCapture.js +4 -3
- package/build/components/KYCElements/SelfieCapture.js.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +182 -39
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
- package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/WelcomeTemplate.js +243 -0
- package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
- package/build/components/TemplateKYCExample.d.ts +4 -2
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +5 -68
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +2 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +95 -9
- package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/components/example/DynamicTemplateExample.d.ts +10 -0
- package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
- package/build/components/example/DynamicTemplateExample.js +241 -0
- package/build/components/example/DynamicTemplateExample.js.map +1 -0
- package/build/config/allowedDomains.d.ts +30 -0
- package/build/config/allowedDomains.d.ts.map +1 -0
- package/build/config/allowedDomains.js +127 -0
- package/build/config/allowedDomains.js.map +1 -0
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +68 -43
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/hooks/useTemplateLoader.d.ts +14 -0
- package/build/hooks/useTemplateLoader.d.ts.map +1 -0
- package/build/hooks/useTemplateLoader.js +85 -0
- package/build/hooks/useTemplateLoader.js.map +1 -0
- package/build/i18n/en/index.d.ts +9 -0
- package/build/i18n/en/index.d.ts.map +1 -1
- package/build/i18n/en/index.js +9 -0
- package/build/i18n/en/index.js.map +1 -1
- package/build/i18n/fr/index.d.ts +9 -0
- package/build/i18n/fr/index.d.ts.map +1 -1
- package/build/i18n/fr/index.js +9 -0
- package/build/i18n/fr/index.js.map +1 -1
- package/build/index.d.ts +5 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +8 -0
- package/build/index.js.map +1 -1
- package/build/modules/api/CardAuthentification.js +1 -0
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +4 -1
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +17 -5
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/TemplateService.d.ts +45 -0
- package/build/modules/api/TemplateService.d.ts.map +1 -0
- package/build/modules/api/TemplateService.js +145 -0
- package/build/modules/api/TemplateService.js.map +1 -0
- package/build/modules/api/types.d.ts +1 -0
- package/build/modules/api/types.d.ts.map +1 -1
- package/build/modules/api/types.js.map +1 -1
- package/build/types/KYC.types.d.ts +144 -4
- package/build/types/KYC.types.d.ts.map +1 -1
- package/build/types/KYC.types.js +15 -0
- package/build/types/KYC.types.js.map +1 -1
- package/build/utils/cropByObb.d.ts +1 -0
- package/build/utils/cropByObb.d.ts.map +1 -1
- package/build/utils/cropByObb.js +70 -0
- package/build/utils/cropByObb.js.map +1 -1
- package/build/utils/platformAlert.d.ts +20 -0
- package/build/utils/platformAlert.d.ts.map +1 -0
- package/build/utils/platformAlert.js +67 -0
- package/build/utils/platformAlert.js.map +1 -0
- package/build/utils/template-transformer.d.ts +10 -0
- package/build/utils/template-transformer.d.ts.map +1 -0
- package/build/utils/template-transformer.js +353 -0
- package/build/utils/template-transformer.js.map +1 -0
- package/build/web/WebKYCEntry.d.ts.map +1 -1
- package/build/web/WebKYCEntry.js +102 -20
- package/build/web/WebKYCEntry.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +31 -2
- package/src/components/EnhancedCameraView.web.tsx +24 -0
- package/src/components/KYCElements/CameraCapture.tsx +4 -3
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
- package/src/components/KYCElements/FileUpload.tsx +5 -4
- package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
- package/src/components/KYCElements/IDCardCapture.tsx +196 -254
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
- package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
- package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
- package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
- package/src/components/KYCElements/SelfieCapture.tsx +4 -3
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +195 -41
- package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
- package/src/components/TemplateKYCExample.tsx +16 -71
- package/src/components/TemplateKYCFlowRefactored.tsx +121 -11
- package/src/components/example/DynamicTemplateExample.tsx +289 -0
- package/src/config/allowedDomains.ts +152 -0
- package/src/hooks/useTemplateKYCFlow.tsx +71 -46
- package/src/hooks/useTemplateLoader.ts +102 -0
- package/src/i18n/en/index.ts +10 -0
- package/src/i18n/fr/index.ts +9 -0
- package/src/index.ts +11 -0
- package/src/modules/api/CardAuthentification.ts +1 -1
- package/src/modules/api/KYCService.ts +18 -8
- package/src/modules/api/TemplateService.ts +167 -0
- package/src/modules/api/types.ts +1 -0
- package/src/types/KYC.types.ts +188 -3
- package/src/utils/cropByObb.ts +83 -3
- package/src/utils/platformAlert.ts +85 -0
- package/src/utils/template-transformer.ts +433 -0
- package/src/web/WebKYCEntry.tsx +122 -24
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { BackendKYCTemplate, TemplateAPIResponse } from '../../types/KYC.types';
|
|
3
|
+
import { logger } from '../../utils/logger';
|
|
4
|
+
import { errorMessage } from './KYCService';
|
|
5
|
+
|
|
6
|
+
export class TemplateServiceClass {
|
|
7
|
+
private backendServiceURL = 'https://service.sanctumkey.com/api/v1';
|
|
8
|
+
private cache: Map<string, { template: BackendKYCTemplate; timestamp: number }> = new Map();
|
|
9
|
+
private cacheTimeout = 5 * 60 * 1000; // 5 minutes
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get headers for API requests
|
|
13
|
+
*/
|
|
14
|
+
private getHeaders(apiKey?: string, token?: string) {
|
|
15
|
+
const headers: Record<string, string> = {
|
|
16
|
+
'Content-Type': 'application/json',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (apiKey) {
|
|
20
|
+
headers['Authorization'] = `ApiKey ${apiKey}`;
|
|
21
|
+
} else if (token) {
|
|
22
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return headers;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Fetch a template from the backend API
|
|
30
|
+
* @param templateId - The ID of the template to fetch
|
|
31
|
+
* @param apiKey - Optional API key for authentication
|
|
32
|
+
* @param token - Optional Bearer token for authentication
|
|
33
|
+
* @param useCache - Whether to use cached version if available (default: true)
|
|
34
|
+
* @returns The backend template
|
|
35
|
+
*/
|
|
36
|
+
async fetchTemplate(
|
|
37
|
+
templateId: string,
|
|
38
|
+
apiKey?: string,
|
|
39
|
+
token?: string,
|
|
40
|
+
useCache: boolean = true
|
|
41
|
+
): Promise<BackendKYCTemplate> {
|
|
42
|
+
// Check cache first
|
|
43
|
+
if (useCache) {
|
|
44
|
+
const cached = this.cache.get(templateId);
|
|
45
|
+
if (cached) {
|
|
46
|
+
const age = Date.now() - cached.timestamp;
|
|
47
|
+
if (age < this.cacheTimeout) {
|
|
48
|
+
logger.log(`Template ${templateId} loaded from cache`);
|
|
49
|
+
return cached.template;
|
|
50
|
+
} else {
|
|
51
|
+
// Cache expired, remove it
|
|
52
|
+
this.cache.delete(templateId);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const url = `${this.backendServiceURL}/templates/kyc/template/${templateId}/`;
|
|
59
|
+
logger.log(`Fetching template from: ${url} with apiKey: ${apiKey} and token: ${token}`,{
|
|
60
|
+
headers: this.getHeaders(apiKey, token)
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const response = await axios.get<TemplateAPIResponse>(url, {
|
|
64
|
+
headers: this.getHeaders(apiKey, token),
|
|
65
|
+
timeout: 30000, // 30 seconds timeout
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const template: BackendKYCTemplate = response.data;
|
|
69
|
+
|
|
70
|
+
// Validate template structure
|
|
71
|
+
if (!template.id || !template.name || !Array.isArray(template.components)) {
|
|
72
|
+
throw new Error('Invalid template structure received from API');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Store in cache
|
|
76
|
+
this.cache.set(templateId, {
|
|
77
|
+
template,
|
|
78
|
+
timestamp: Date.now(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
logger.log(`Template ${templateId} fetched successfully`);
|
|
82
|
+
return template;
|
|
83
|
+
} catch (error: any) {
|
|
84
|
+
logger.error(`Error fetching template ${templateId}:`, JSON.stringify(errorMessage(error), null, 2));
|
|
85
|
+
|
|
86
|
+
// Extract backend error message if available
|
|
87
|
+
const backendMessage = errorMessage(error);
|
|
88
|
+
|
|
89
|
+
if (error.response?.status === 404) {
|
|
90
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (error.response?.status === 401 || error.response?.status === 403) {
|
|
94
|
+
throw new Error(`Unauthorized: Invalid API key or token`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (backendMessage) {
|
|
98
|
+
throw new Error(`Failed to fetch template: ${JSON.stringify(backendMessage)}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error(`Failed to fetch template: ${error.message || 'Unknown error'}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Clear the template cache
|
|
107
|
+
* @param templateId - Optional template ID to clear. If not provided, clears all cache.
|
|
108
|
+
*/
|
|
109
|
+
clearCache(templateId?: string): void {
|
|
110
|
+
if (templateId) {
|
|
111
|
+
this.cache.delete(templateId);
|
|
112
|
+
logger.log(`Cache cleared for template: ${templateId}`);
|
|
113
|
+
} else {
|
|
114
|
+
this.cache.clear();
|
|
115
|
+
logger.log('All template cache cleared');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get cached template if available
|
|
121
|
+
* @param templateId - The template ID
|
|
122
|
+
* @returns The cached template or null if not found/expired
|
|
123
|
+
*/
|
|
124
|
+
getCachedTemplate(templateId: string): BackendKYCTemplate | null {
|
|
125
|
+
const cached = this.cache.get(templateId);
|
|
126
|
+
if (cached) {
|
|
127
|
+
const age = Date.now() - cached.timestamp;
|
|
128
|
+
if (age < this.cacheTimeout) {
|
|
129
|
+
return cached.template;
|
|
130
|
+
} else {
|
|
131
|
+
this.cache.delete(templateId);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Fetch list of available templates (optional feature)
|
|
139
|
+
* @param apiKey - Optional API key for authentication
|
|
140
|
+
* @param token - Optional Bearer token for authentication
|
|
141
|
+
* @returns Array of template metadata
|
|
142
|
+
*/
|
|
143
|
+
async fetchTemplateList(apiKey?: string, token?: string): Promise<Array<{ id: string; name: Record<string, string>; version?: string; status?: string }>> {
|
|
144
|
+
try {
|
|
145
|
+
const url = `${this.backendServiceURL}/templates/kyc/templates`;
|
|
146
|
+
logger.log(`Fetching template list from: ${url}`);
|
|
147
|
+
|
|
148
|
+
const response = await axios.get<Array<{ id: string; name: Record<string, string>; version?: string; status?: string }>>(url, {
|
|
149
|
+
headers: this.getHeaders(apiKey, token),
|
|
150
|
+
timeout: 30000,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
logger.log(`Fetched ${response.data.length} templates`);
|
|
154
|
+
return response.data;
|
|
155
|
+
} catch (error: any) {
|
|
156
|
+
logger.error('Error fetching template list:', JSON.stringify(errorMessage(error), null, 2));
|
|
157
|
+
throw new Error(`Failed to fetch template list: ${error.message || 'Unknown error'}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Export singleton instance as default
|
|
163
|
+
const templateService = new TemplateServiceClass();
|
|
164
|
+
export default templateService;
|
|
165
|
+
|
|
166
|
+
// Also export the class for advanced usage
|
|
167
|
+
// export { TemplateServiceClass };
|
package/src/modules/api/types.ts
CHANGED
package/src/types/KYC.types.ts
CHANGED
|
@@ -325,6 +325,12 @@ export type GovernmentDocumentType =
|
|
|
325
325
|
| 'permanent_residence'
|
|
326
326
|
| 'national_id'
|
|
327
327
|
| 'work_permit'
|
|
328
|
+
| 'id'
|
|
329
|
+
| 'pp'
|
|
330
|
+
| 'dl'
|
|
331
|
+
| 'hic'
|
|
332
|
+
| 'pr'
|
|
333
|
+
| 'wp'
|
|
328
334
|
|
|
329
335
|
export const GovernmentDocumentTypeShorted: Record<GovernmentDocumentType, string> = {
|
|
330
336
|
identity_card: 'id',
|
|
@@ -334,6 +340,22 @@ export const GovernmentDocumentTypeShorted: Record<GovernmentDocumentType, strin
|
|
|
334
340
|
permanent_residence: 'pr',
|
|
335
341
|
national_id: 'id',
|
|
336
342
|
work_permit: 'wp',
|
|
343
|
+
id: 'id',
|
|
344
|
+
pp: 'pp',
|
|
345
|
+
dl: 'dl',
|
|
346
|
+
hic: 'hic',
|
|
347
|
+
pr: 'pr',
|
|
348
|
+
wp: 'wp',
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export const GovernmentDocumentTypeBackend: Record<string, GovernmentDocumentType> = {
|
|
352
|
+
national_id: 'identity_card',
|
|
353
|
+
passport: 'passport',
|
|
354
|
+
driversLicense: 'drivers_licence',
|
|
355
|
+
healthInsuranceCard: 'health_insurance_card',
|
|
356
|
+
permanentResidence: 'permanent_residence',
|
|
357
|
+
workPermit: 'work_permit',
|
|
358
|
+
residencePermit: 'permanent_residence',
|
|
337
359
|
}
|
|
338
360
|
|
|
339
361
|
// Configuration spécifique pour chaque type de composant
|
|
@@ -342,7 +364,7 @@ export interface IDCardConfig {
|
|
|
342
364
|
allowed_formats: string[];
|
|
343
365
|
max_size_mb: number;
|
|
344
366
|
document_types: GovernmentDocumentType[]; // Types de documents disponibles
|
|
345
|
-
bbox_configs
|
|
367
|
+
bbox_configs?: Record<GovernmentDocumentType, {
|
|
346
368
|
xMin: number;
|
|
347
369
|
yMin: number;
|
|
348
370
|
xMax: number;
|
|
@@ -383,12 +405,13 @@ export type ComponentConfig =
|
|
|
383
405
|
| SelfieConfig
|
|
384
406
|
| FileUploadConfig
|
|
385
407
|
| LocationConfig
|
|
386
|
-
| CountrySelectionConfig
|
|
408
|
+
| CountrySelectionConfig
|
|
409
|
+
| WelcomeConfig;
|
|
387
410
|
|
|
388
411
|
// Nouveau type de composant basé sur le template JSON
|
|
389
412
|
export interface TemplateComponent {
|
|
390
413
|
id: number;
|
|
391
|
-
type: 'id_card' | 'selfie' | 'file_upload' | 'location' | 'country_selection' | 'initialization' | 'review_submit' | 'verification_progress';
|
|
414
|
+
type: 'id_card' | 'selfie' | 'file_upload' | 'location' | 'country_selection' | 'initialization' | 'review_submit' | 'verification_progress' | 'welcome';
|
|
392
415
|
order: number;
|
|
393
416
|
templateId?: number;
|
|
394
417
|
labels: LocalizedText | Record<string, LocalizedText>;
|
|
@@ -502,4 +525,166 @@ export interface Country {
|
|
|
502
525
|
flag: string;
|
|
503
526
|
regionMapping?: ICountryMapping;
|
|
504
527
|
hasRegions?: boolean;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ===== TYPES FOR BACKEND TEMPLATE FORMAT =====
|
|
531
|
+
|
|
532
|
+
// Backend component configuration types
|
|
533
|
+
export interface WelcomeConfig {
|
|
534
|
+
subtitle?: string;
|
|
535
|
+
buttonText?: string;
|
|
536
|
+
requirements?: string[];
|
|
537
|
+
estimatedTime?: string;
|
|
538
|
+
consentOptions?: {
|
|
539
|
+
showPrivacyPolicy?: boolean;
|
|
540
|
+
showTermsOfService?: boolean;
|
|
541
|
+
showMarketingConsent?: boolean;
|
|
542
|
+
};
|
|
543
|
+
welcomeMessage?: string;
|
|
544
|
+
showEstimatedTime?: boolean;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
export interface LocationCaptureConfig {
|
|
548
|
+
showMap?: boolean;
|
|
549
|
+
accuracy?: string;
|
|
550
|
+
required?: boolean;
|
|
551
|
+
maxDistance?: number;
|
|
552
|
+
captureMethod?: string;
|
|
553
|
+
allowManualEntry?: boolean;
|
|
554
|
+
countryRestrictions?: string[];
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export interface GovernmentIdConfig {
|
|
558
|
+
required?: boolean;
|
|
559
|
+
allowSkip?: boolean;
|
|
560
|
+
cameraOverlay?: {
|
|
561
|
+
bbox?: {
|
|
562
|
+
xMax?: number;
|
|
563
|
+
xMin?: number;
|
|
564
|
+
yMax?: number;
|
|
565
|
+
yMin?: number;
|
|
566
|
+
borderColor?: string;
|
|
567
|
+
borderWidth?: number;
|
|
568
|
+
cornerRadius?: number;
|
|
569
|
+
};
|
|
570
|
+
};
|
|
571
|
+
documentTypes?: string[];
|
|
572
|
+
requiredSides?: string;
|
|
573
|
+
cameraSettings?: {
|
|
574
|
+
quality?: number;
|
|
575
|
+
flashMode?: string;
|
|
576
|
+
maxRetakes?: number;
|
|
577
|
+
allowRetake?: boolean;
|
|
578
|
+
aspectRatio?: number;
|
|
579
|
+
};
|
|
580
|
+
validationRules?: string[];
|
|
581
|
+
regionsByCountry?: Record<string, any>;
|
|
582
|
+
selectedCountries?: string[];
|
|
583
|
+
authenticationMethods?: Record<string, Record<string, {
|
|
584
|
+
back?: string[];
|
|
585
|
+
front?: string[];
|
|
586
|
+
mrzTypes?: string[];
|
|
587
|
+
}>>;
|
|
588
|
+
documentTypesByCountry?: Record<string, string[]>;
|
|
589
|
+
instructionsByDocumentType?: Record<string, LocalizedText>;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export interface SelfieCaptureConfig {
|
|
593
|
+
timeout?: number;
|
|
594
|
+
maxAttempts?: number;
|
|
595
|
+
faceMatching?: boolean;
|
|
596
|
+
captureMethod?: string;
|
|
597
|
+
qualityChecks?: string[];
|
|
598
|
+
livenessChecks?: string[];
|
|
599
|
+
matchThreshold?: string;
|
|
600
|
+
livenessEnabled?: boolean;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export interface ReviewSubmitConfig {
|
|
604
|
+
allowEditing?: boolean;
|
|
605
|
+
displayOptions?: string[];
|
|
606
|
+
requireConsent?: boolean;
|
|
607
|
+
postSubmitActions?: string[];
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
export type BackendComponentConfig =
|
|
611
|
+
| WelcomeConfig
|
|
612
|
+
| LocationCaptureConfig
|
|
613
|
+
| GovernmentIdConfig
|
|
614
|
+
| SelfieCaptureConfig
|
|
615
|
+
| ReviewSubmitConfig
|
|
616
|
+
| Record<string, any>;
|
|
617
|
+
|
|
618
|
+
// Backend component translation structure
|
|
619
|
+
export interface BackendComponentTranslations {
|
|
620
|
+
en?: {
|
|
621
|
+
title?: string;
|
|
622
|
+
description?: string;
|
|
623
|
+
instructions?: string;
|
|
624
|
+
buttonText?: string;
|
|
625
|
+
consentText?: string;
|
|
626
|
+
successMessage?: string;
|
|
627
|
+
};
|
|
628
|
+
fr?: {
|
|
629
|
+
title?: string;
|
|
630
|
+
description?: string;
|
|
631
|
+
instructions?: string;
|
|
632
|
+
buttonText?: string;
|
|
633
|
+
consentText?: string;
|
|
634
|
+
successMessage?: string;
|
|
635
|
+
};
|
|
636
|
+
[key: string]: {
|
|
637
|
+
title?: string;
|
|
638
|
+
description?: string;
|
|
639
|
+
instructions?: string;
|
|
640
|
+
buttonText?: string;
|
|
641
|
+
consentText?: string;
|
|
642
|
+
successMessage?: string;
|
|
643
|
+
} | undefined;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Backend template component
|
|
647
|
+
export interface BackendTemplateComponent {
|
|
648
|
+
id: string;
|
|
649
|
+
type: 'welcome' | 'location-capture' | 'government-id' | 'selfie-capture' | 'review-submit' | 'verification-progress';
|
|
650
|
+
order: number;
|
|
651
|
+
config: BackendComponentConfig;
|
|
652
|
+
required?: boolean;
|
|
653
|
+
translations: BackendComponentTranslations;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Backend template structure
|
|
657
|
+
export interface BackendKYCTemplate {
|
|
658
|
+
id: string;
|
|
659
|
+
name: {
|
|
660
|
+
en: string;
|
|
661
|
+
fr: string;
|
|
662
|
+
[key: string]: string;
|
|
663
|
+
};
|
|
664
|
+
status?: string;
|
|
665
|
+
version?: string;
|
|
666
|
+
components: BackendTemplateComponent[];
|
|
667
|
+
description?: {
|
|
668
|
+
en: string;
|
|
669
|
+
fr: string;
|
|
670
|
+
[key: string]: string;
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// API response for template
|
|
675
|
+
export interface TemplateAPIResponse {
|
|
676
|
+
id: string;
|
|
677
|
+
name: {
|
|
678
|
+
en: string;
|
|
679
|
+
fr: string;
|
|
680
|
+
[key: string]: string;
|
|
681
|
+
};
|
|
682
|
+
status?: string;
|
|
683
|
+
version?: string;
|
|
684
|
+
components: BackendTemplateComponent[];
|
|
685
|
+
description?: {
|
|
686
|
+
en: string;
|
|
687
|
+
fr: string;
|
|
688
|
+
[key: string]: string;
|
|
689
|
+
};
|
|
505
690
|
}
|
package/src/utils/cropByObb.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Image as RNImage, Platform } from 'react-native';
|
|
2
2
|
import * as ImageManipulator from "expo-image-manipulator";
|
|
3
|
+
import { truncateFields } from '../modules/api/KYCService';
|
|
4
|
+
import { logger } from './logger';
|
|
3
5
|
|
|
4
6
|
type Point = [number, number];
|
|
5
7
|
|
|
@@ -81,13 +83,14 @@ export async function cropImageWithBBox(uri: string, bbox: any) {
|
|
|
81
83
|
|
|
82
84
|
// 3️⃣ Convertir le bbox à la taille réelle scale 0.10 = 10%
|
|
83
85
|
const crop = {
|
|
84
|
-
originX: bbox.minX
|
|
85
|
-
originY: bbox.minY
|
|
86
|
-
width: bbox.width
|
|
86
|
+
originX: bbox.minX,
|
|
87
|
+
originY: bbox.minY,
|
|
88
|
+
width: bbox.width,
|
|
87
89
|
height: bbox.height,
|
|
88
90
|
};
|
|
89
91
|
|
|
90
92
|
// 4️⃣ Appliquer le crop
|
|
93
|
+
// @ts-ignore - manipulateAsync is deprecated but still functional, new API (useImageManipulator) is a React hook and not suitable for utility functions
|
|
91
94
|
const result = await ImageManipulator.manipulateAsync(
|
|
92
95
|
uri,
|
|
93
96
|
[{ crop }],
|
|
@@ -97,3 +100,80 @@ export async function cropImageWithBBox(uri: string, bbox: any) {
|
|
|
97
100
|
console.log("Image recadrée:", result.uri);
|
|
98
101
|
return result.uri;
|
|
99
102
|
}
|
|
103
|
+
|
|
104
|
+
// Fonction pour rogner avec une tolérance de 10% autour du bbox
|
|
105
|
+
export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tolerance: number = 0.1): Promise<string> {
|
|
106
|
+
console.log("cropImageWithBBoxWithTolerance", JSON.stringify(truncateFields({ uri, bbox, tolerance }), null, 2));
|
|
107
|
+
|
|
108
|
+
return new Promise<string>((resolve, reject) => {
|
|
109
|
+
let isResolved = false;
|
|
110
|
+
|
|
111
|
+
// Timeout de sécurité (15 secondes)
|
|
112
|
+
const timeout = setTimeout(() => {
|
|
113
|
+
if (!isResolved) {
|
|
114
|
+
isResolved = true;
|
|
115
|
+
console.warn("Timeout lors du rognage avec tolérance, utilisation de l'image originale");
|
|
116
|
+
resolve(uri); // Retourner l'URI original en cas de timeout
|
|
117
|
+
}
|
|
118
|
+
}, 15000);
|
|
119
|
+
|
|
120
|
+
// 1️⃣ Récupère la taille originale de l'image
|
|
121
|
+
RNImage.getSize(uri, (imageWidth, imageHeight) => {
|
|
122
|
+
if (isResolved) return;
|
|
123
|
+
|
|
124
|
+
// 2️⃣ Calculer la tolérance en pixels (10% de chaque côté)
|
|
125
|
+
const toleranceX = bbox.width * tolerance;
|
|
126
|
+
const toleranceY = bbox.height * tolerance;
|
|
127
|
+
|
|
128
|
+
// 3️⃣ Ajuster le bbox avec la tolérance
|
|
129
|
+
// Calculer les nouvelles coordonnées avec tolérance
|
|
130
|
+
const newMinX = Math.max(0, bbox.minX - toleranceX);
|
|
131
|
+
const newMinY = Math.max(0, bbox.minY - toleranceY);
|
|
132
|
+
const newMaxX = Math.min(imageWidth, bbox.minX + bbox.width + toleranceX);
|
|
133
|
+
const newMaxY = Math.min(imageHeight, bbox.minY + bbox.height + toleranceY);
|
|
134
|
+
|
|
135
|
+
// Calculer la largeur et hauteur finales
|
|
136
|
+
const finalWidth = newMaxX - newMinX;
|
|
137
|
+
const finalHeight = newMaxY - newMinY;
|
|
138
|
+
|
|
139
|
+
const crop = {
|
|
140
|
+
originX: newMinX,
|
|
141
|
+
originY: newMinY,
|
|
142
|
+
width: finalWidth,
|
|
143
|
+
height: finalHeight,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
logger.log("cropImageWithBBoxWithTolerance - crop params", JSON.stringify(truncateFields({ uri, crop, tolerance, imageWidth, imageHeight }), null, 2));
|
|
147
|
+
|
|
148
|
+
// 4️⃣ Appliquer le crop avec tolérance
|
|
149
|
+
// @ts-ignore - manipulateAsync is deprecated but still functional, new API (useImageManipulator) is a React hook and not suitable for utility functions
|
|
150
|
+
ImageManipulator.manipulateAsync(
|
|
151
|
+
uri,
|
|
152
|
+
[{ crop }],
|
|
153
|
+
{ compress: 0.95, format: ImageManipulator.SaveFormat.JPEG }
|
|
154
|
+
)
|
|
155
|
+
.then((result) => {
|
|
156
|
+
if (isResolved) return;
|
|
157
|
+
isResolved = true;
|
|
158
|
+
clearTimeout(timeout);
|
|
159
|
+
console.log("Image recadrée avec tolérance:", result.uri);
|
|
160
|
+
resolve(result.uri);
|
|
161
|
+
})
|
|
162
|
+
.catch((error) => {
|
|
163
|
+
if (isResolved) return;
|
|
164
|
+
isResolved = true;
|
|
165
|
+
clearTimeout(timeout);
|
|
166
|
+
console.error("Erreur lors du rognage avec tolérance:", error);
|
|
167
|
+
// En cas d'erreur, retourner l'URI original au lieu de rejeter
|
|
168
|
+
resolve(uri);
|
|
169
|
+
});
|
|
170
|
+
}, (error) => {
|
|
171
|
+
if (isResolved) return;
|
|
172
|
+
isResolved = true;
|
|
173
|
+
clearTimeout(timeout);
|
|
174
|
+
console.error("Erreur lors de la récupération de la taille de l'image:", error);
|
|
175
|
+
// En cas d'erreur, retourner l'URI original au lieu de rejeter
|
|
176
|
+
resolve(uri);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Platform, Alert } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface AlertButton {
|
|
4
|
+
text?: string;
|
|
5
|
+
onPress?: () => void;
|
|
6
|
+
style?: 'default' | 'cancel' | 'destructive';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Platform-aware alert helper that works on both native and web platforms
|
|
11
|
+
* On web, uses window.confirm/window.alert since Alert.alert is not supported
|
|
12
|
+
* On native, uses the standard Alert.alert
|
|
13
|
+
*/
|
|
14
|
+
export const showAlert = (
|
|
15
|
+
title: string,
|
|
16
|
+
message?: string,
|
|
17
|
+
buttons?: AlertButton[]
|
|
18
|
+
): void => {
|
|
19
|
+
if (Platform.OS === 'web') {
|
|
20
|
+
// Web implementation using native browser dialogs
|
|
21
|
+
const fullMessage = message ? `${title}\n\n${message}` : title;
|
|
22
|
+
|
|
23
|
+
if (buttons && buttons.length > 1) {
|
|
24
|
+
// Multiple buttons - use confirm dialog
|
|
25
|
+
const confirmed = window.confirm(fullMessage);
|
|
26
|
+
|
|
27
|
+
if (confirmed) {
|
|
28
|
+
// Find and call the primary/positive button callback
|
|
29
|
+
const confirmButton = buttons.find(
|
|
30
|
+
btn => btn.style !== 'cancel' && btn.onPress
|
|
31
|
+
);
|
|
32
|
+
confirmButton?.onPress?.();
|
|
33
|
+
} else {
|
|
34
|
+
// Find and call the cancel button callback
|
|
35
|
+
const cancelButton = buttons.find(btn => btn.style === 'cancel');
|
|
36
|
+
cancelButton?.onPress?.();
|
|
37
|
+
}
|
|
38
|
+
} else if (buttons && buttons.length === 1) {
|
|
39
|
+
// Single button - use alert dialog
|
|
40
|
+
window.alert(fullMessage);
|
|
41
|
+
buttons[0]?.onPress?.();
|
|
42
|
+
} else {
|
|
43
|
+
// No buttons - just show alert
|
|
44
|
+
window.alert(fullMessage);
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
// Native implementation - use standard Alert.alert
|
|
48
|
+
Alert.alert(title, message, buttons);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Simplified confirm dialog for yes/no scenarios
|
|
54
|
+
*/
|
|
55
|
+
export const showConfirm = (
|
|
56
|
+
title: string,
|
|
57
|
+
message: string,
|
|
58
|
+
onConfirm: () => void,
|
|
59
|
+
onCancel?: () => void
|
|
60
|
+
): void => {
|
|
61
|
+
showAlert(title, message, [
|
|
62
|
+
{
|
|
63
|
+
text: 'Cancel',
|
|
64
|
+
style: 'cancel',
|
|
65
|
+
onPress: onCancel,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
text: 'OK',
|
|
69
|
+
onPress: onConfirm,
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Simple alert with just an OK button
|
|
76
|
+
*/
|
|
77
|
+
export const showSimpleAlert = (title: string, message?: string, onPress?: () => void): void => {
|
|
78
|
+
showAlert(title, message, [
|
|
79
|
+
{
|
|
80
|
+
text: 'OK',
|
|
81
|
+
onPress,
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
};
|
|
85
|
+
|