@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,353 @@
|
|
|
1
|
+
import { logger } from './logger';
|
|
2
|
+
/**
|
|
3
|
+
* Hash a string to a number (simple hash function)
|
|
4
|
+
*/
|
|
5
|
+
function hashStringToNumber(str) {
|
|
6
|
+
let hash = 0;
|
|
7
|
+
for (let i = 0; i < str.length; i++) {
|
|
8
|
+
const char = str.charCodeAt(i);
|
|
9
|
+
hash = ((hash << 5) - hash) + char;
|
|
10
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
11
|
+
}
|
|
12
|
+
return Math.abs(hash);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Map backend component types to SDK component types
|
|
16
|
+
*/
|
|
17
|
+
const COMPONENT_TYPE_MAPPING = {
|
|
18
|
+
'welcome': 'welcome',
|
|
19
|
+
'government-id': 'id_card',
|
|
20
|
+
'selfie-capture': 'selfie',
|
|
21
|
+
'location-capture': 'location',
|
|
22
|
+
'review-submit': 'review_submit',
|
|
23
|
+
'verification-progress': 'verification_progress',
|
|
24
|
+
'file-upload': 'file_upload',
|
|
25
|
+
'country-selection': 'country_selection',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Transform backend translations to SDK labels and instructions
|
|
29
|
+
*/
|
|
30
|
+
function transformTranslations(translations, defaultLanguage = 'en') {
|
|
31
|
+
const labels = { en: '', fr: '' };
|
|
32
|
+
const instructions = { en: '', fr: '' };
|
|
33
|
+
// Extract title as label
|
|
34
|
+
if (translations.en?.title)
|
|
35
|
+
labels.en = translations.en.title;
|
|
36
|
+
if (translations.fr?.title)
|
|
37
|
+
labels.fr = translations.fr.title;
|
|
38
|
+
// Extract instructions
|
|
39
|
+
if (translations.en?.instructions)
|
|
40
|
+
instructions.en = translations.en.instructions;
|
|
41
|
+
if (translations.fr?.instructions)
|
|
42
|
+
instructions.fr = translations.fr.instructions;
|
|
43
|
+
// Fallback to description if instructions not available
|
|
44
|
+
if (!instructions.en && translations.en?.description) {
|
|
45
|
+
instructions.en = translations.en.description;
|
|
46
|
+
}
|
|
47
|
+
if (!instructions.fr && translations.fr?.description) {
|
|
48
|
+
instructions.fr = translations.fr.description;
|
|
49
|
+
}
|
|
50
|
+
// Support other languages
|
|
51
|
+
Object.keys(translations).forEach((lang) => {
|
|
52
|
+
if (lang !== 'en' && lang !== 'fr' && translations[lang]) {
|
|
53
|
+
labels[lang] = translations[lang]?.title || '';
|
|
54
|
+
instructions[lang] = translations[lang]?.instructions || translations[lang]?.description || '';
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return { labels, instructions };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Transform UI configuration
|
|
61
|
+
*/
|
|
62
|
+
function transformUI(translations) {
|
|
63
|
+
const buttonText = { en: '', fr: '' };
|
|
64
|
+
if (translations.en?.buttonText)
|
|
65
|
+
buttonText.en = translations.en.buttonText;
|
|
66
|
+
if (translations.fr?.buttonText)
|
|
67
|
+
buttonText.fr = translations.fr.buttonText;
|
|
68
|
+
// Support other languages
|
|
69
|
+
Object.keys(translations).forEach((lang) => {
|
|
70
|
+
if (lang !== 'en' && lang !== 'fr' && translations[lang]?.buttonText) {
|
|
71
|
+
buttonText[lang] = translations[lang].buttonText;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
buttonText,
|
|
76
|
+
themeColor: '#2DBD60', // Default theme color
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Transform welcome config
|
|
81
|
+
*/
|
|
82
|
+
function transformWelcomeConfig(config) {
|
|
83
|
+
return {
|
|
84
|
+
subtitle: config.subtitle,
|
|
85
|
+
buttonText: config.buttonText,
|
|
86
|
+
requirements: config.requirements || [],
|
|
87
|
+
estimatedTime: config.estimatedTime,
|
|
88
|
+
consentOptions: config.consentOptions || {
|
|
89
|
+
showPrivacyPolicy: true,
|
|
90
|
+
showTermsOfService: true,
|
|
91
|
+
showMarketingConsent: false,
|
|
92
|
+
},
|
|
93
|
+
welcomeMessage: config.welcomeMessage,
|
|
94
|
+
showEstimatedTime: config.showEstimatedTime !== false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Transform government ID config to IDCardConfig
|
|
99
|
+
*/
|
|
100
|
+
function transformGovernmentIdConfig(config) {
|
|
101
|
+
const documentTypes = config.documentTypes || ['passport', 'nationalId'];
|
|
102
|
+
const sides = [];
|
|
103
|
+
if (config.requiredSides === 'front-back' || config.requiredSides === 'both') {
|
|
104
|
+
sides.push('front', 'back');
|
|
105
|
+
}
|
|
106
|
+
else if (config.requiredSides === 'front') {
|
|
107
|
+
sides.push('front');
|
|
108
|
+
}
|
|
109
|
+
else if (config.requiredSides === 'back') {
|
|
110
|
+
sides.push('back');
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Default to both sides
|
|
114
|
+
sides.push('front', 'back');
|
|
115
|
+
}
|
|
116
|
+
const idCardConfig = {
|
|
117
|
+
sides,
|
|
118
|
+
allowed_formats: ['jpg', 'jpeg', 'png'],
|
|
119
|
+
max_size_mb: 10,
|
|
120
|
+
document_types: documentTypes.map((dt) => {
|
|
121
|
+
const mapping = {
|
|
122
|
+
'passport': 'passport',
|
|
123
|
+
'nationalId': 'national_id',
|
|
124
|
+
'driversLicense': 'drivers_licence',
|
|
125
|
+
'identityCard': 'identity_card',
|
|
126
|
+
};
|
|
127
|
+
return mapping[dt] || dt;
|
|
128
|
+
}),
|
|
129
|
+
};
|
|
130
|
+
// Add bbox configs if available
|
|
131
|
+
if (config.cameraOverlay?.bbox) {
|
|
132
|
+
const bbox = config.cameraOverlay.bbox;
|
|
133
|
+
idCardConfig.bbox_configs = {};
|
|
134
|
+
documentTypes.forEach((dt) => {
|
|
135
|
+
const docType = dt;
|
|
136
|
+
idCardConfig.bbox_configs[docType] = {
|
|
137
|
+
xMin: bbox.xMin || 20,
|
|
138
|
+
yMin: bbox.yMin || 140,
|
|
139
|
+
xMax: bbox.xMax || 370,
|
|
140
|
+
yMax: bbox.yMax || 340,
|
|
141
|
+
borderColor: bbox.borderColor || '#2DBD60',
|
|
142
|
+
borderWidth: bbox.borderWidth || 3,
|
|
143
|
+
cornerRadius: bbox.cornerRadius || 8,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
// Store additional backend-specific config in a way that can be accessed
|
|
148
|
+
idCardConfig.authenticationMethods = config.authenticationMethods;
|
|
149
|
+
idCardConfig.documentTypesByCountry = config.documentTypesByCountry;
|
|
150
|
+
idCardConfig.selectedCountries = config.selectedCountries;
|
|
151
|
+
idCardConfig.instructionsByDocumentType = config.instructionsByDocumentType;
|
|
152
|
+
idCardConfig.cameraSettings = config.cameraSettings;
|
|
153
|
+
idCardConfig.validationRules = config.validationRules;
|
|
154
|
+
return idCardConfig;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Transform location capture config
|
|
158
|
+
*/
|
|
159
|
+
function transformLocationConfig(config) {
|
|
160
|
+
return {
|
|
161
|
+
accuracy: config.accuracy || 'high',
|
|
162
|
+
required: config.required !== false,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Transform selfie capture config
|
|
167
|
+
*/
|
|
168
|
+
function transformSelfieConfig(config) {
|
|
169
|
+
return {
|
|
170
|
+
liveness_check: config.livenessEnabled !== false,
|
|
171
|
+
max_attempts: config.maxAttempts || 5,
|
|
172
|
+
orientations: config.orientations || ['center'],
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Transform component config based on type
|
|
177
|
+
*/
|
|
178
|
+
function transformComponentConfig(type, backendConfig) {
|
|
179
|
+
switch (type) {
|
|
180
|
+
case 'welcome':
|
|
181
|
+
return transformWelcomeConfig(backendConfig);
|
|
182
|
+
case 'id_card':
|
|
183
|
+
return transformGovernmentIdConfig(backendConfig);
|
|
184
|
+
case 'location':
|
|
185
|
+
return transformLocationConfig(backendConfig);
|
|
186
|
+
case 'selfie':
|
|
187
|
+
return transformSelfieConfig(backendConfig);
|
|
188
|
+
case 'file_upload':
|
|
189
|
+
return {
|
|
190
|
+
allowed_formats: backendConfig.allowed_formats || ['jpg', 'jpeg', 'png', 'pdf'],
|
|
191
|
+
max_size_mb: backendConfig.max_size_mb || 10,
|
|
192
|
+
required: backendConfig.required !== false,
|
|
193
|
+
};
|
|
194
|
+
case 'country_selection':
|
|
195
|
+
return {
|
|
196
|
+
allowed_countries: backendConfig.allowed_countries || [],
|
|
197
|
+
default_country: backendConfig.default_country || '',
|
|
198
|
+
required: backendConfig.required !== false,
|
|
199
|
+
};
|
|
200
|
+
default:
|
|
201
|
+
return backendConfig;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Transform a single backend component to SDK component
|
|
206
|
+
*/
|
|
207
|
+
function transformComponent(backendComponent, componentIndex) {
|
|
208
|
+
const mappedType = COMPONENT_TYPE_MAPPING[backendComponent.type];
|
|
209
|
+
if (!mappedType) {
|
|
210
|
+
logger.warn(`Unknown component type: ${backendComponent.type}, defaulting to initialization`);
|
|
211
|
+
}
|
|
212
|
+
const componentId = hashStringToNumber(backendComponent.id);
|
|
213
|
+
const { labels, instructions } = transformTranslations(backendComponent.translations);
|
|
214
|
+
const ui = transformUI(backendComponent.translations);
|
|
215
|
+
const config = transformComponentConfig(mappedType || 'initialization', backendComponent.config);
|
|
216
|
+
// For components with multiple sides (like id_card), create nested structure
|
|
217
|
+
let finalLabels = labels;
|
|
218
|
+
let finalInstructions = instructions;
|
|
219
|
+
let finalUI = ui;
|
|
220
|
+
if (mappedType === 'id_card' && backendComponent.config.requiredSides === 'front-back') {
|
|
221
|
+
// Create separate labels/instructions for front and back
|
|
222
|
+
finalLabels = {
|
|
223
|
+
front: {
|
|
224
|
+
en: backendComponent.translations.en?.title || labels.en,
|
|
225
|
+
fr: backendComponent.translations.fr?.title || labels.fr,
|
|
226
|
+
},
|
|
227
|
+
back: {
|
|
228
|
+
en: `${labels.en} (Back)`,
|
|
229
|
+
fr: `${labels.fr} (Verso)`,
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
finalInstructions = {
|
|
233
|
+
front: instructions,
|
|
234
|
+
back: instructions,
|
|
235
|
+
};
|
|
236
|
+
finalUI = {
|
|
237
|
+
front: ui,
|
|
238
|
+
back: ui,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
id: componentId,
|
|
243
|
+
type: mappedType || 'initialization',
|
|
244
|
+
order: backendComponent.order,
|
|
245
|
+
labels: finalLabels,
|
|
246
|
+
instructions: finalInstructions,
|
|
247
|
+
ui: finalUI,
|
|
248
|
+
config,
|
|
249
|
+
required: backendComponent.required !== false,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Transform backend template to SDK template format
|
|
254
|
+
*/
|
|
255
|
+
export function transformBackendTemplateToSDK(backendTemplate) {
|
|
256
|
+
try {
|
|
257
|
+
// Validate backend template
|
|
258
|
+
if (!backendTemplate.id || !backendTemplate.name || !Array.isArray(backendTemplate.components)) {
|
|
259
|
+
throw new Error('Invalid backend template structure');
|
|
260
|
+
}
|
|
261
|
+
// Sort components by order
|
|
262
|
+
const sortedComponents = [...backendTemplate.components].sort((a, b) => a.order - b.order);
|
|
263
|
+
// Transform each component
|
|
264
|
+
const transformedComponents = sortedComponents.map((comp, index) => transformComponent(comp, index));
|
|
265
|
+
// Auto-create country_selection component before id_card if government-id has selection data
|
|
266
|
+
// Check if country_selection already exists in backend template
|
|
267
|
+
const hasCountrySelectionInBackend = sortedComponents.some(c => c.type === 'country-selection');
|
|
268
|
+
if (!hasCountrySelectionInBackend) {
|
|
269
|
+
// Find the first government-id component with selection data
|
|
270
|
+
const govIdWithSelection = sortedComponents.find(backendComp => backendComp.type === 'government-id' &&
|
|
271
|
+
backendComp.config && (backendComp.config.selectedCountries ||
|
|
272
|
+
backendComp.config.documentTypesByCountry));
|
|
273
|
+
if (govIdWithSelection) {
|
|
274
|
+
// Find the corresponding transformed id_card component
|
|
275
|
+
const transformedIdCard = transformedComponents.find(c => hashStringToNumber(govIdWithSelection.id) === c.id);
|
|
276
|
+
if (transformedIdCard) {
|
|
277
|
+
// Create a country_selection component before id_card
|
|
278
|
+
const countrySelectionComponent = {
|
|
279
|
+
id: hashStringToNumber(`${govIdWithSelection.id}-country-selection`),
|
|
280
|
+
type: 'country_selection',
|
|
281
|
+
order: transformedIdCard.order - 0.5, // Insert before id_card
|
|
282
|
+
labels: {
|
|
283
|
+
en: govIdWithSelection.translations.en?.title || 'Select Country and Document',
|
|
284
|
+
fr: govIdWithSelection.translations.fr?.title || 'Sélectionnez le pays et le document',
|
|
285
|
+
},
|
|
286
|
+
instructions: {
|
|
287
|
+
en: 'Please select your country and document type',
|
|
288
|
+
fr: 'Veuillez sélectionner votre pays et le type de document',
|
|
289
|
+
},
|
|
290
|
+
ui: {
|
|
291
|
+
themeColor: '#2DBD60',
|
|
292
|
+
buttonText: {
|
|
293
|
+
en: 'Continue',
|
|
294
|
+
fr: 'Continuer',
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
config: {
|
|
298
|
+
allowed_countries: govIdWithSelection.config.selectedCountries || [],
|
|
299
|
+
default_country: '',
|
|
300
|
+
required: true,
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
// Insert country_selection before id_card in transformedComponents
|
|
304
|
+
const idCardIndex = transformedComponents.indexOf(transformedIdCard);
|
|
305
|
+
transformedComponents.splice(idCardIndex, 0, countrySelectionComponent);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Re-assign order values sequentially after insertion
|
|
310
|
+
transformedComponents.forEach((comp, index) => {
|
|
311
|
+
comp.order = index + 1;
|
|
312
|
+
});
|
|
313
|
+
// Create SDK template
|
|
314
|
+
const sdkTemplate = {
|
|
315
|
+
id: hashStringToNumber(backendTemplate.id),
|
|
316
|
+
name: backendTemplate.name,
|
|
317
|
+
description: backendTemplate.description || {
|
|
318
|
+
en: '',
|
|
319
|
+
fr: '',
|
|
320
|
+
},
|
|
321
|
+
version: backendTemplate.version || '1.0.0',
|
|
322
|
+
components: transformedComponents,
|
|
323
|
+
};
|
|
324
|
+
logger.log(`Template transformed: ${backendTemplate.id} -> ${sdkTemplate.id} with ${transformedComponents.length} components`);
|
|
325
|
+
return sdkTemplate;
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
logger.error('Error transforming template:', error);
|
|
329
|
+
throw new Error(`Failed to transform template: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Validate transformed template
|
|
334
|
+
*/
|
|
335
|
+
export function validateTransformedTemplate(template) {
|
|
336
|
+
if (!template.id || !template.name || !Array.isArray(template.components)) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
if (!template.name.en || !template.name.fr) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
// Validate each component
|
|
343
|
+
for (const component of template.components) {
|
|
344
|
+
if (!component.id || !component.type || typeof component.order !== 'number') {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
if (!component.labels || !component.instructions || !component.config) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=template-transformer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-transformer.js","sourceRoot":"","sources":["../../src/utils/template-transformer.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BackendKYCTemplate,\n BackendTemplateComponent,\n KYCTemplate,\n TemplateComponent,\n LocalizedText,\n ComponentUI,\n IDCardConfig,\n LocationConfig,\n SelfieConfig,\n FileUploadConfig,\n CountrySelectionConfig,\n ComponentConfig,\n WelcomeConfig,\n GovernmentDocumentType,\n GovernmentIdConfig,\n} from '../types/KYC.types';\nimport { logger } from './logger';\n\n/**\n * Hash a string to a number (simple hash function)\n */\nfunction hashStringToNumber(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash);\n}\n\n/**\n * Map backend component types to SDK component types\n */\nconst COMPONENT_TYPE_MAPPING: Record<string, TemplateComponent['type']> = {\n 'welcome': 'welcome',\n 'government-id': 'id_card',\n 'selfie-capture': 'selfie',\n 'location-capture': 'location',\n 'review-submit': 'review_submit',\n 'verification-progress': 'verification_progress',\n 'file-upload': 'file_upload',\n 'country-selection': 'country_selection',\n};\n\n/**\n * Transform backend translations to SDK labels and instructions\n */\nfunction transformTranslations(\n translations: BackendTemplateComponent['translations'],\n defaultLanguage: string = 'en'\n): { labels: LocalizedText; instructions: LocalizedText } {\n const labels: LocalizedText = { en: '', fr: '' };\n const instructions: LocalizedText = { en: '', fr: '' };\n\n // Extract title as label\n if (translations.en?.title) labels.en = translations.en.title;\n if (translations.fr?.title) labels.fr = translations.fr.title;\n\n // Extract instructions\n if (translations.en?.instructions) instructions.en = translations.en.instructions;\n if (translations.fr?.instructions) instructions.fr = translations.fr.instructions;\n\n // Fallback to description if instructions not available\n if (!instructions.en && translations.en?.description) {\n instructions.en = translations.en.description;\n }\n if (!instructions.fr && translations.fr?.description) {\n instructions.fr = translations.fr.description;\n }\n\n // Support other languages\n Object.keys(translations).forEach((lang) => {\n if (lang !== 'en' && lang !== 'fr' && translations[lang]) {\n labels[lang] = translations[lang]?.title || '';\n instructions[lang] = translations[lang]?.instructions || translations[lang]?.description || '';\n }\n });\n\n return { labels, instructions };\n}\n\n/**\n * Transform UI configuration\n */\nfunction transformUI(translations: BackendTemplateComponent['translations']): ComponentUI {\n const buttonText: LocalizedText = { en: '', fr: '' };\n\n if (translations.en?.buttonText) buttonText.en = translations.en.buttonText;\n if (translations.fr?.buttonText) buttonText.fr = translations.fr.buttonText;\n\n // Support other languages\n Object.keys(translations).forEach((lang) => {\n if (lang !== 'en' && lang !== 'fr' && translations[lang]?.buttonText) {\n buttonText[lang] = translations[lang]!.buttonText!;\n }\n });\n\n return {\n buttonText,\n themeColor: '#2DBD60', // Default theme color\n };\n}\n\n/**\n * Transform welcome config\n */\nfunction transformWelcomeConfig(config: any): WelcomeConfig {\n return {\n subtitle: config.subtitle,\n buttonText: config.buttonText,\n requirements: config.requirements || [],\n estimatedTime: config.estimatedTime,\n consentOptions: config.consentOptions || {\n showPrivacyPolicy: true,\n showTermsOfService: true,\n showMarketingConsent: false,\n },\n welcomeMessage: config.welcomeMessage,\n showEstimatedTime: config.showEstimatedTime !== false,\n } as WelcomeConfig;\n}\n\n/**\n * Transform government ID config to IDCardConfig\n */\nfunction transformGovernmentIdConfig(config: any): IDCardConfig {\n const documentTypes: string[] = config.documentTypes || ['passport', 'nationalId'];\n const sides: string[] = [];\n\n if (config.requiredSides === 'front-back' || config.requiredSides === 'both') {\n sides.push('front', 'back');\n } else if (config.requiredSides === 'front') {\n sides.push('front');\n } else if (config.requiredSides === 'back') {\n sides.push('back');\n } else {\n // Default to both sides\n sides.push('front', 'back');\n }\n\n const idCardConfig: IDCardConfig = {\n sides,\n allowed_formats: ['jpg', 'jpeg', 'png'],\n max_size_mb: 10,\n document_types: documentTypes.map((dt: string) => {\n const mapping: Record<string, any> = {\n 'passport': 'passport',\n 'nationalId': 'national_id',\n 'driversLicense': 'drivers_licence',\n 'identityCard': 'identity_card',\n };\n return mapping[dt] || dt;\n }) as any[],\n };\n\n // Add bbox configs if available\n if (config.cameraOverlay?.bbox) {\n const bbox = config.cameraOverlay.bbox;\n idCardConfig.bbox_configs = {} as Record<GovernmentDocumentType, {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n borderColor?: string;\n borderWidth?: number;\n cornerRadius?: number;\n }>;\n documentTypes.forEach((dt: string) => {\n const docType = dt as GovernmentDocumentType;\n (idCardConfig.bbox_configs as any)[docType] = {\n xMin: bbox.xMin || 20,\n yMin: bbox.yMin || 140,\n xMax: bbox.xMax || 370,\n yMax: bbox.yMax || 340,\n borderColor: bbox.borderColor || '#2DBD60',\n borderWidth: bbox.borderWidth || 3,\n cornerRadius: bbox.cornerRadius || 8,\n };\n });\n }\n\n // Store additional backend-specific config in a way that can be accessed\n (idCardConfig as any).authenticationMethods = config.authenticationMethods;\n (idCardConfig as any).documentTypesByCountry = config.documentTypesByCountry;\n (idCardConfig as any).selectedCountries = config.selectedCountries;\n (idCardConfig as any).instructionsByDocumentType = config.instructionsByDocumentType;\n (idCardConfig as any).cameraSettings = config.cameraSettings;\n (idCardConfig as any).validationRules = config.validationRules;\n\n return idCardConfig;\n}\n\n/**\n * Transform location capture config\n */\nfunction transformLocationConfig(config: any): LocationConfig {\n return {\n accuracy: config.accuracy || 'high',\n required: config.required !== false,\n } as LocationConfig;\n}\n\n/**\n * Transform selfie capture config\n */\nfunction transformSelfieConfig(config: any): SelfieConfig {\n return {\n liveness_check: config.livenessEnabled !== false,\n max_attempts: config.maxAttempts || 5,\n orientations: config.orientations || ['center'],\n } as SelfieConfig;\n}\n\n/**\n * Transform component config based on type\n */\nfunction transformComponentConfig(\n type: TemplateComponent['type'],\n backendConfig: any\n): ComponentConfig {\n switch (type) {\n case 'welcome':\n return transformWelcomeConfig(backendConfig);\n case 'id_card':\n return transformGovernmentIdConfig(backendConfig);\n case 'location':\n return transformLocationConfig(backendConfig);\n case 'selfie':\n return transformSelfieConfig(backendConfig);\n case 'file_upload':\n return {\n allowed_formats: backendConfig.allowed_formats || ['jpg', 'jpeg', 'png', 'pdf'],\n max_size_mb: backendConfig.max_size_mb || 10,\n required: backendConfig.required !== false,\n } as FileUploadConfig;\n case 'country_selection':\n return {\n allowed_countries: backendConfig.allowed_countries || [],\n default_country: backendConfig.default_country || '',\n required: backendConfig.required !== false,\n } as CountrySelectionConfig;\n default:\n return backendConfig as ComponentConfig;\n }\n}\n\n/**\n * Transform a single backend component to SDK component\n */\nfunction transformComponent(\n backendComponent: BackendTemplateComponent,\n componentIndex: number\n): TemplateComponent {\n const mappedType = COMPONENT_TYPE_MAPPING[backendComponent.type];\n\n if (!mappedType) {\n logger.warn(`Unknown component type: ${backendComponent.type}, defaulting to initialization`);\n }\n\n const componentId = hashStringToNumber(backendComponent.id);\n const { labels, instructions } = transformTranslations(backendComponent.translations);\n const ui = transformUI(backendComponent.translations);\n const config = transformComponentConfig(mappedType || 'initialization', backendComponent.config);\n\n // For components with multiple sides (like id_card), create nested structure\n let finalLabels: LocalizedText | Record<string, LocalizedText> = labels;\n let finalInstructions: LocalizedText | Record<string, LocalizedText> = instructions;\n let finalUI: ComponentUI | Record<string, ComponentUI> = ui;\n\n if (mappedType === 'id_card' && (backendComponent.config as GovernmentIdConfig).requiredSides === 'front-back') {\n // Create separate labels/instructions for front and back\n finalLabels = {\n front: {\n en: backendComponent.translations.en?.title || labels.en,\n fr: backendComponent.translations.fr?.title || labels.fr,\n },\n back: {\n en: `${labels.en} (Back)`,\n fr: `${labels.fr} (Verso)`,\n },\n };\n finalInstructions = {\n front: instructions,\n back: instructions,\n };\n finalUI = {\n front: ui,\n back: ui,\n };\n }\n\n return {\n id: componentId,\n type: mappedType || 'initialization',\n order: backendComponent.order,\n labels: finalLabels,\n instructions: finalInstructions,\n ui: finalUI,\n config,\n required: backendComponent.required !== false,\n } as TemplateComponent;\n}\n\n/**\n * Transform backend template to SDK template format\n */\nexport function transformBackendTemplateToSDK(\n backendTemplate: BackendKYCTemplate\n): KYCTemplate {\n try {\n // Validate backend template\n if (!backendTemplate.id || !backendTemplate.name || !Array.isArray(backendTemplate.components)) {\n throw new Error('Invalid backend template structure');\n }\n\n // Sort components by order\n const sortedComponents = [...backendTemplate.components].sort(\n (a, b) => a.order - b.order\n );\n\n // Transform each component\n const transformedComponents = sortedComponents.map((comp, index) =>\n transformComponent(comp, index)\n );\n\n // Auto-create country_selection component before id_card if government-id has selection data\n // Check if country_selection already exists in backend template\n const hasCountrySelectionInBackend = sortedComponents.some(c => (c as any).type === 'country-selection');\n \n if (!hasCountrySelectionInBackend) {\n // Find the first government-id component with selection data\n const govIdWithSelection = sortedComponents.find(backendComp => \n backendComp.type === 'government-id' && \n backendComp.config && (\n (backendComp.config as any).selectedCountries ||\n (backendComp.config as any).documentTypesByCountry\n )\n );\n \n if (govIdWithSelection) {\n // Find the corresponding transformed id_card component\n const transformedIdCard = transformedComponents.find(c => \n hashStringToNumber(govIdWithSelection.id) === c.id\n );\n \n if (transformedIdCard) {\n // Create a country_selection component before id_card\n const countrySelectionComponent: TemplateComponent = {\n id: hashStringToNumber(`${govIdWithSelection.id}-country-selection`),\n type: 'country_selection',\n order: transformedIdCard.order - 0.5, // Insert before id_card\n labels: {\n en: govIdWithSelection.translations.en?.title || 'Select Country and Document',\n fr: govIdWithSelection.translations.fr?.title || 'Sélectionnez le pays et le document',\n },\n instructions: {\n en: 'Please select your country and document type',\n fr: 'Veuillez sélectionner votre pays et le type de document',\n },\n ui: {\n themeColor: '#2DBD60',\n buttonText: {\n en: 'Continue',\n fr: 'Continuer',\n },\n } as ComponentUI,\n config: {\n allowed_countries: (govIdWithSelection.config as any).selectedCountries || [],\n default_country: '',\n required: true,\n } as CountrySelectionConfig,\n };\n \n // Insert country_selection before id_card in transformedComponents\n const idCardIndex = transformedComponents.indexOf(transformedIdCard);\n transformedComponents.splice(idCardIndex, 0, countrySelectionComponent);\n }\n }\n }\n \n // Re-assign order values sequentially after insertion\n transformedComponents.forEach((comp, index) => {\n comp.order = index + 1;\n });\n\n // Create SDK template\n const sdkTemplate: KYCTemplate = {\n id: hashStringToNumber(backendTemplate.id),\n name: backendTemplate.name as LocalizedText,\n description: backendTemplate.description || {\n en: '',\n fr: '',\n },\n version: backendTemplate.version || '1.0.0',\n components: transformedComponents,\n };\n\n logger.log(`Template transformed: ${backendTemplate.id} -> ${sdkTemplate.id} with ${transformedComponents.length} components`);\n\n return sdkTemplate;\n } catch (error: any) {\n logger.error('Error transforming template:', error);\n throw new Error(`Failed to transform template: ${error.message}`);\n }\n}\n\n/**\n * Validate transformed template\n */\nexport function validateTransformedTemplate(template: KYCTemplate): boolean {\n if (!template.id || !template.name || !Array.isArray(template.components)) {\n return false;\n }\n\n if (!template.name.en || !template.name.fr) {\n return false;\n }\n\n // Validate each component\n for (const component of template.components) {\n if (!component.id || !component.type || typeof component.order !== 'number') {\n return false;\n }\n\n if (!component.labels || !component.instructions || !component.config) {\n return false;\n }\n }\n\n return true;\n}"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebKYCEntry.d.ts","sourceRoot":"","sources":["../../src/web/WebKYCEntry.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"WebKYCEntry.d.ts","sourceRoot":"","sources":["../../src/web/WebKYCEntry.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAQhE,UAAU,gBAAgB;IACxB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAiBD,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAiO3C,CAAC;AA0DF,eAAe,WAAW,CAAC"}
|
package/build/web/WebKYCEntry.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import React, { useEffect, useState, useCallback } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet, SafeAreaView } from 'react-native';
|
|
2
|
+
import { View, Text, StyleSheet, SafeAreaView, ActivityIndicator } from 'react-native';
|
|
3
3
|
// import { TemplateKYCFlow } from '../components/TemplateKYCFlowRefactored';
|
|
4
4
|
// import { KYCTemplate } from '../types/KYC.types';
|
|
5
5
|
import { useI18n } from '../hooks/useI18n';
|
|
6
6
|
import { TemplateKYCExample } from '../components/TemplateKYCExample';
|
|
7
|
+
import { isCallbackUrlAllowed, generateCallbackSignature } from '../config/allowedDomains';
|
|
7
8
|
const WebKYCEntry = ({ onComplete, onError, onCancel, }) => {
|
|
8
9
|
const { setLocale } = useI18n();
|
|
9
10
|
const [urlParams, setUrlParams] = useState({});
|
|
10
11
|
const [isLoading, setIsLoading] = useState(true);
|
|
11
12
|
const [error, setError] = useState(null);
|
|
13
|
+
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
|
12
14
|
// Parse URL parameters
|
|
13
15
|
const parseUrlParams = useCallback(() => {
|
|
14
16
|
if (typeof window === 'undefined')
|
|
@@ -20,80 +22,117 @@ const WebKYCEntry = ({ onComplete, onError, onCancel, }) => {
|
|
|
20
22
|
lang: urlParams.get('lang') || 'en',
|
|
21
23
|
theme: urlParams.get('theme') || 'light',
|
|
22
24
|
kyc_id: urlParams.get('kyc_id') || undefined,
|
|
25
|
+
secret: urlParams.get('secret') || undefined,
|
|
23
26
|
};
|
|
24
27
|
}, []);
|
|
25
|
-
// Safe redirect function with validation
|
|
26
|
-
const redirectToReturnUrl = useCallback((params) => {
|
|
27
|
-
const { return_url } = urlParams;
|
|
28
|
+
// Safe redirect function with enhanced validation
|
|
29
|
+
const redirectToReturnUrl = useCallback(async (params) => {
|
|
30
|
+
const { return_url, secret } = urlParams;
|
|
28
31
|
if (!return_url) {
|
|
29
32
|
console.warn('No return_url provided');
|
|
30
33
|
return;
|
|
31
34
|
}
|
|
32
|
-
// Basic URL validation - ensure it's a valid URL
|
|
33
35
|
try {
|
|
36
|
+
// Enhanced URL validation with domain whitelist
|
|
37
|
+
const validation = isCallbackUrlAllowed(return_url);
|
|
38
|
+
if (!validation.allowed) {
|
|
39
|
+
console.error('Callback URL validation failed:', validation.reason);
|
|
40
|
+
setError(`Security Error: ${validation.reason}`);
|
|
41
|
+
// Log suspicious redirect attempt
|
|
42
|
+
console.warn('Suspicious redirect attempt blocked:', {
|
|
43
|
+
url: return_url,
|
|
44
|
+
reason: validation.reason,
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
34
49
|
const returnUrl = new URL(return_url);
|
|
35
|
-
// Optional: Add domain allowlist validation here
|
|
36
|
-
// const allowedDomains = ['example.com', 'trusted-site.com'];
|
|
37
|
-
// if (!allowedDomains.some(domain => returnUrl.hostname.endsWith(domain))) {
|
|
38
|
-
// console.error('Return URL not in allowlist');
|
|
39
|
-
// return;
|
|
40
|
-
// }
|
|
41
50
|
// Build redirect URL with parameters
|
|
42
51
|
const redirectUrl = new URL(returnUrl);
|
|
43
|
-
|
|
52
|
+
const redirectParams = {
|
|
53
|
+
status: params.status,
|
|
54
|
+
};
|
|
44
55
|
if (params.kyc_id) {
|
|
45
|
-
|
|
56
|
+
redirectParams.kyc_id = params.kyc_id;
|
|
46
57
|
}
|
|
47
58
|
if (params.message) {
|
|
48
|
-
|
|
59
|
+
redirectParams.message = params.message;
|
|
60
|
+
}
|
|
61
|
+
if (params.processing_state) {
|
|
62
|
+
redirectParams.processing_state = params.processing_state;
|
|
63
|
+
}
|
|
64
|
+
if (params.verification_steps) {
|
|
65
|
+
redirectParams.verification_steps = JSON.stringify(params.verification_steps);
|
|
49
66
|
}
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
// Generate signature for integrity verification
|
|
68
|
+
if (secret) {
|
|
69
|
+
const signature = await generateCallbackSignature(redirectParams, secret);
|
|
70
|
+
if (signature) {
|
|
71
|
+
redirectParams.sig = signature;
|
|
72
|
+
}
|
|
52
73
|
}
|
|
53
|
-
//
|
|
74
|
+
// Add all params to URL
|
|
75
|
+
Object.entries(redirectParams).forEach(([key, value]) => {
|
|
76
|
+
redirectUrl.searchParams.set(key, value);
|
|
77
|
+
});
|
|
78
|
+
// Send postMessage to parent if in iframe
|
|
54
79
|
if (window.parent !== window) {
|
|
55
80
|
window.parent.postMessage({
|
|
56
81
|
type: 'kyc_result',
|
|
57
82
|
status: params.status,
|
|
58
83
|
kyc_id: params.kyc_id,
|
|
59
84
|
message: params.message,
|
|
60
|
-
|
|
85
|
+
processing_state: params.processing_state,
|
|
86
|
+
verification_steps: params.verification_steps,
|
|
87
|
+
}, returnUrl.origin); // Use specific origin instead of '*'
|
|
61
88
|
}
|
|
62
89
|
// Redirect to return URL
|
|
63
90
|
window.location.href = redirectUrl.toString();
|
|
64
91
|
}
|
|
65
92
|
catch (error) {
|
|
66
|
-
console.error('
|
|
67
|
-
setError('
|
|
93
|
+
console.error('Error during redirect:', error);
|
|
94
|
+
setError('Failed to redirect to callback URL');
|
|
68
95
|
}
|
|
69
96
|
}, [urlParams]);
|
|
70
97
|
// Handle KYC completion
|
|
71
98
|
const handleComplete = useCallback((data) => {
|
|
72
99
|
console.log('KYC completed:', data);
|
|
100
|
+
// Check if still processing/analyzing
|
|
101
|
+
const isStillProcessing = data.isProcessing || data.session?.isProcessing;
|
|
73
102
|
redirectToReturnUrl({
|
|
74
103
|
status: 'completed',
|
|
75
104
|
kyc_id: data.session_id || urlParams.kyc_id,
|
|
76
105
|
message: 'KYC process completed successfully',
|
|
106
|
+
processing_state: isStillProcessing ? 'analyzing' : 'completed',
|
|
107
|
+
verification_steps: {
|
|
108
|
+
document_analyzed: data.documentAnalysisComplete || false,
|
|
109
|
+
selfie_analyzed: data.selfieAnalysisComplete || false,
|
|
110
|
+
liveness_checked: data.livenessCheckComplete || false,
|
|
111
|
+
},
|
|
77
112
|
});
|
|
78
113
|
onComplete?.(data);
|
|
79
114
|
}, [redirectToReturnUrl, urlParams.kyc_id, onComplete]);
|
|
80
115
|
// Handle KYC error
|
|
81
116
|
const handleError = useCallback((error) => {
|
|
82
117
|
console.error('KYC error:', error);
|
|
118
|
+
setIsAnalyzing(false);
|
|
83
119
|
redirectToReturnUrl({
|
|
84
120
|
status: 'error',
|
|
85
121
|
kyc_id: urlParams.kyc_id,
|
|
86
122
|
message: error,
|
|
123
|
+
processing_state: 'pending',
|
|
87
124
|
});
|
|
88
125
|
onError?.(error);
|
|
89
126
|
}, [redirectToReturnUrl, urlParams.kyc_id, onError]);
|
|
90
127
|
// Handle KYC cancellation
|
|
91
128
|
const handleCancel = useCallback(() => {
|
|
92
129
|
console.log('KYC cancelled');
|
|
130
|
+
setIsAnalyzing(false);
|
|
93
131
|
redirectToReturnUrl({
|
|
94
132
|
status: 'cancelled',
|
|
95
133
|
kyc_id: urlParams.kyc_id,
|
|
96
134
|
message: 'KYC process was cancelled',
|
|
135
|
+
processing_state: 'pending',
|
|
97
136
|
});
|
|
98
137
|
onCancel?.();
|
|
99
138
|
}, [redirectToReturnUrl, urlParams.kyc_id, onCancel]);
|
|
@@ -130,6 +169,13 @@ const WebKYCEntry = ({ onComplete, onError, onCancel, }) => {
|
|
|
130
169
|
</View>);
|
|
131
170
|
}
|
|
132
171
|
return (<SafeAreaView style={styles.container}>
|
|
172
|
+
{isAnalyzing && (<View style={styles.analyzingOverlay}>
|
|
173
|
+
<View style={styles.analyzingContainer}>
|
|
174
|
+
<ActivityIndicator size="large" color="#2DBD60"/>
|
|
175
|
+
<Text style={styles.analyzingText}>Analyzing verification data...</Text>
|
|
176
|
+
<Text style={styles.analyzingSubtext}>This may take a few moments</Text>
|
|
177
|
+
</View>
|
|
178
|
+
</View>)}
|
|
133
179
|
<TemplateKYCExample onComplete={handleComplete} onCancel={handleCancel} onError={handleError} language={urlParams.lang || 'en'} API_KEY={urlParams.token}/>
|
|
134
180
|
</SafeAreaView>);
|
|
135
181
|
};
|
|
@@ -137,6 +183,7 @@ const styles = StyleSheet.create({
|
|
|
137
183
|
container: {
|
|
138
184
|
flex: 1,
|
|
139
185
|
backgroundColor: '#f5f5f5',
|
|
186
|
+
overflow: 'visible', // Allow scrolling on web
|
|
140
187
|
},
|
|
141
188
|
loadingText: {
|
|
142
189
|
fontSize: 18,
|
|
@@ -151,6 +198,41 @@ const styles = StyleSheet.create({
|
|
|
151
198
|
color: '#dc2626',
|
|
152
199
|
paddingHorizontal: 20,
|
|
153
200
|
},
|
|
201
|
+
analyzingOverlay: {
|
|
202
|
+
position: 'absolute',
|
|
203
|
+
top: 0,
|
|
204
|
+
left: 0,
|
|
205
|
+
right: 0,
|
|
206
|
+
bottom: 0,
|
|
207
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
208
|
+
justifyContent: 'center',
|
|
209
|
+
alignItems: 'center',
|
|
210
|
+
zIndex: 9999,
|
|
211
|
+
},
|
|
212
|
+
analyzingContainer: {
|
|
213
|
+
backgroundColor: 'white',
|
|
214
|
+
borderRadius: 12,
|
|
215
|
+
padding: 32,
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
shadowColor: '#000',
|
|
218
|
+
shadowOffset: { width: 0, height: 4 },
|
|
219
|
+
shadowOpacity: 0.3,
|
|
220
|
+
shadowRadius: 8,
|
|
221
|
+
elevation: 8,
|
|
222
|
+
},
|
|
223
|
+
analyzingText: {
|
|
224
|
+
fontSize: 18,
|
|
225
|
+
fontWeight: '600',
|
|
226
|
+
color: '#333',
|
|
227
|
+
marginTop: 16,
|
|
228
|
+
textAlign: 'center',
|
|
229
|
+
},
|
|
230
|
+
analyzingSubtext: {
|
|
231
|
+
fontSize: 14,
|
|
232
|
+
color: '#666',
|
|
233
|
+
marginTop: 8,
|
|
234
|
+
textAlign: 'center',
|
|
235
|
+
},
|
|
154
236
|
});
|
|
155
237
|
export default WebKYCEntry;
|
|
156
238
|
//# sourceMappingURL=WebKYCEntry.js.map
|