@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,289 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, SafeAreaView, TextInput, TouchableOpacity, ScrollView } from 'react-native';
|
|
3
|
+
import { TemplateKYCFlow } from '../TemplateKYCFlowRefactored';
|
|
4
|
+
import { VerificationState } from '../../types/KYC.types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Example component demonstrating how to use dynamic template loading
|
|
8
|
+
*
|
|
9
|
+
* This example shows two ways to use templates:
|
|
10
|
+
* 1. With templateId - loads template dynamically from backend API
|
|
11
|
+
* 2. With direct template object - uses hardcoded template (backward compatibility)
|
|
12
|
+
*/
|
|
13
|
+
export const DynamicTemplateExample: React.FC = () => {
|
|
14
|
+
const [templateId, setTemplateId] = useState<string>('');
|
|
15
|
+
const [apiKey, setApiKey] = useState<string>('');
|
|
16
|
+
const [showFlow, setShowFlow] = useState<boolean>(false);
|
|
17
|
+
const [language, setLanguage] = useState<'en' | 'fr'>('en');
|
|
18
|
+
|
|
19
|
+
const handleComplete = (data: VerificationState) => {
|
|
20
|
+
console.log('KYC Verification completed:', data);
|
|
21
|
+
setShowFlow(false);
|
|
22
|
+
alert('Verification completed successfully!');
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const handleError = (error: string) => {
|
|
26
|
+
console.error('KYC Error:', error);
|
|
27
|
+
alert(`Error: ${error}`);
|
|
28
|
+
setShowFlow(false);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const handleCancel = () => {
|
|
32
|
+
console.log('KYC Flow cancelled');
|
|
33
|
+
setShowFlow(false);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const startFlow = () => {
|
|
37
|
+
if (!templateId.trim()) {
|
|
38
|
+
alert('Please enter a template ID');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setShowFlow(true);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
if (showFlow) {
|
|
45
|
+
return (
|
|
46
|
+
<SafeAreaView style={styles.container}>
|
|
47
|
+
<TemplateKYCFlow
|
|
48
|
+
templateId={templateId}
|
|
49
|
+
API_KEY={apiKey || undefined}
|
|
50
|
+
language={language}
|
|
51
|
+
onComplete={handleComplete}
|
|
52
|
+
onError={handleError}
|
|
53
|
+
onCancel={handleCancel}
|
|
54
|
+
/>
|
|
55
|
+
</SafeAreaView>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<SafeAreaView style={styles.container}>
|
|
61
|
+
<ScrollView style={styles.scrollView} contentContainerStyle={styles.content}>
|
|
62
|
+
<Text style={styles.title}>Dynamic Template KYC Example</Text>
|
|
63
|
+
<Text style={styles.subtitle}>
|
|
64
|
+
Load a KYC template dynamically from the backend API
|
|
65
|
+
</Text>
|
|
66
|
+
|
|
67
|
+
<View style={styles.section}>
|
|
68
|
+
<Text style={styles.label}>Template ID *</Text>
|
|
69
|
+
<TextInput
|
|
70
|
+
style={styles.input}
|
|
71
|
+
placeholder="e.g., free-style"
|
|
72
|
+
value={templateId}
|
|
73
|
+
onChangeText={setTemplateId}
|
|
74
|
+
autoCapitalize="none"
|
|
75
|
+
autoCorrect={false}
|
|
76
|
+
/>
|
|
77
|
+
<Text style={styles.hint}>
|
|
78
|
+
Enter the template ID to load from the backend API
|
|
79
|
+
</Text>
|
|
80
|
+
</View>
|
|
81
|
+
|
|
82
|
+
<View style={styles.section}>
|
|
83
|
+
<Text style={styles.label}>API Key (Optional)</Text>
|
|
84
|
+
<TextInput
|
|
85
|
+
style={styles.input}
|
|
86
|
+
placeholder="Your API key"
|
|
87
|
+
value={apiKey}
|
|
88
|
+
onChangeText={setApiKey}
|
|
89
|
+
autoCapitalize="none"
|
|
90
|
+
autoCorrect={false}
|
|
91
|
+
secureTextEntry
|
|
92
|
+
/>
|
|
93
|
+
<Text style={styles.hint}>
|
|
94
|
+
If not provided, the SDK will use token-based authentication
|
|
95
|
+
</Text>
|
|
96
|
+
</View>
|
|
97
|
+
|
|
98
|
+
<View style={styles.section}>
|
|
99
|
+
<Text style={styles.label}>Language</Text>
|
|
100
|
+
<View style={styles.languageContainer}>
|
|
101
|
+
<TouchableOpacity
|
|
102
|
+
style={[
|
|
103
|
+
styles.languageButton,
|
|
104
|
+
language === 'en' && styles.languageButtonActive,
|
|
105
|
+
]}
|
|
106
|
+
onPress={() => setLanguage('en')}
|
|
107
|
+
>
|
|
108
|
+
<Text
|
|
109
|
+
style={[
|
|
110
|
+
styles.languageButtonText,
|
|
111
|
+
language === 'en' && styles.languageButtonTextActive,
|
|
112
|
+
]}
|
|
113
|
+
>
|
|
114
|
+
English
|
|
115
|
+
</Text>
|
|
116
|
+
</TouchableOpacity>
|
|
117
|
+
<TouchableOpacity
|
|
118
|
+
style={[
|
|
119
|
+
styles.languageButton,
|
|
120
|
+
language === 'fr' && styles.languageButtonActive,
|
|
121
|
+
]}
|
|
122
|
+
onPress={() => setLanguage('fr')}
|
|
123
|
+
>
|
|
124
|
+
<Text
|
|
125
|
+
style={[
|
|
126
|
+
styles.languageButtonText,
|
|
127
|
+
language === 'fr' && styles.languageButtonTextActive,
|
|
128
|
+
]}
|
|
129
|
+
>
|
|
130
|
+
Français
|
|
131
|
+
</Text>
|
|
132
|
+
</TouchableOpacity>
|
|
133
|
+
</View>
|
|
134
|
+
</View>
|
|
135
|
+
|
|
136
|
+
<TouchableOpacity
|
|
137
|
+
style={[styles.button, !templateId.trim() && styles.buttonDisabled]}
|
|
138
|
+
onPress={startFlow}
|
|
139
|
+
disabled={!templateId.trim()}
|
|
140
|
+
>
|
|
141
|
+
<Text style={styles.buttonText}>Start KYC Verification</Text>
|
|
142
|
+
</TouchableOpacity>
|
|
143
|
+
|
|
144
|
+
<View style={styles.infoSection}>
|
|
145
|
+
<Text style={styles.infoTitle}>Usage Example:</Text>
|
|
146
|
+
<Text style={styles.code}>
|
|
147
|
+
{`<TemplateKYCFlow
|
|
148
|
+
templateId="free-style"
|
|
149
|
+
API_KEY="your-api-key"
|
|
150
|
+
language="fr"
|
|
151
|
+
onComplete={handleComplete}
|
|
152
|
+
onError={handleError}
|
|
153
|
+
onCancel={handleCancel}
|
|
154
|
+
/>`}
|
|
155
|
+
</Text>
|
|
156
|
+
</View>
|
|
157
|
+
|
|
158
|
+
<View style={styles.infoSection}>
|
|
159
|
+
<Text style={styles.infoTitle}>Notes:</Text>
|
|
160
|
+
<Text style={styles.infoText}>
|
|
161
|
+
• Template ID is required to load from backend{'\n'}
|
|
162
|
+
• API Key is optional but recommended for production{'\n'}
|
|
163
|
+
• The SDK will automatically transform the backend template format to SDK format{'\n'}
|
|
164
|
+
• Templates are cached for 5 minutes to improve performance{'\n'}
|
|
165
|
+
• You can also use a direct template object for backward compatibility
|
|
166
|
+
</Text>
|
|
167
|
+
</View>
|
|
168
|
+
</ScrollView>
|
|
169
|
+
</SafeAreaView>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const styles = StyleSheet.create({
|
|
174
|
+
container: {
|
|
175
|
+
flex: 1,
|
|
176
|
+
backgroundColor: '#f5f5f5',
|
|
177
|
+
},
|
|
178
|
+
scrollView: {
|
|
179
|
+
flex: 1,
|
|
180
|
+
},
|
|
181
|
+
content: {
|
|
182
|
+
padding: 20,
|
|
183
|
+
paddingBottom: 40,
|
|
184
|
+
},
|
|
185
|
+
title: {
|
|
186
|
+
fontSize: 24,
|
|
187
|
+
fontWeight: 'bold',
|
|
188
|
+
color: '#333',
|
|
189
|
+
marginBottom: 8,
|
|
190
|
+
textAlign: 'center',
|
|
191
|
+
},
|
|
192
|
+
subtitle: {
|
|
193
|
+
fontSize: 16,
|
|
194
|
+
color: '#666',
|
|
195
|
+
marginBottom: 32,
|
|
196
|
+
textAlign: 'center',
|
|
197
|
+
},
|
|
198
|
+
section: {
|
|
199
|
+
marginBottom: 24,
|
|
200
|
+
},
|
|
201
|
+
label: {
|
|
202
|
+
fontSize: 16,
|
|
203
|
+
fontWeight: '600',
|
|
204
|
+
color: '#333',
|
|
205
|
+
marginBottom: 8,
|
|
206
|
+
},
|
|
207
|
+
input: {
|
|
208
|
+
backgroundColor: 'white',
|
|
209
|
+
borderWidth: 1,
|
|
210
|
+
borderColor: '#ddd',
|
|
211
|
+
borderRadius: 8,
|
|
212
|
+
padding: 12,
|
|
213
|
+
fontSize: 16,
|
|
214
|
+
color: '#333',
|
|
215
|
+
},
|
|
216
|
+
hint: {
|
|
217
|
+
fontSize: 12,
|
|
218
|
+
color: '#999',
|
|
219
|
+
marginTop: 4,
|
|
220
|
+
},
|
|
221
|
+
languageContainer: {
|
|
222
|
+
flexDirection: 'row',
|
|
223
|
+
gap: 12,
|
|
224
|
+
},
|
|
225
|
+
languageButton: {
|
|
226
|
+
flex: 1,
|
|
227
|
+
padding: 12,
|
|
228
|
+
backgroundColor: 'white',
|
|
229
|
+
borderWidth: 1,
|
|
230
|
+
borderColor: '#ddd',
|
|
231
|
+
borderRadius: 8,
|
|
232
|
+
alignItems: 'center',
|
|
233
|
+
},
|
|
234
|
+
languageButtonActive: {
|
|
235
|
+
backgroundColor: '#2DBD60',
|
|
236
|
+
borderColor: '#2DBD60',
|
|
237
|
+
},
|
|
238
|
+
languageButtonText: {
|
|
239
|
+
fontSize: 16,
|
|
240
|
+
color: '#666',
|
|
241
|
+
fontWeight: '500',
|
|
242
|
+
},
|
|
243
|
+
languageButtonTextActive: {
|
|
244
|
+
color: 'white',
|
|
245
|
+
fontWeight: '600',
|
|
246
|
+
},
|
|
247
|
+
button: {
|
|
248
|
+
backgroundColor: '#2DBD60',
|
|
249
|
+
padding: 16,
|
|
250
|
+
borderRadius: 8,
|
|
251
|
+
alignItems: 'center',
|
|
252
|
+
marginTop: 8,
|
|
253
|
+
marginBottom: 32,
|
|
254
|
+
},
|
|
255
|
+
buttonDisabled: {
|
|
256
|
+
backgroundColor: '#ccc',
|
|
257
|
+
},
|
|
258
|
+
buttonText: {
|
|
259
|
+
color: 'white',
|
|
260
|
+
fontSize: 16,
|
|
261
|
+
fontWeight: '600',
|
|
262
|
+
},
|
|
263
|
+
infoSection: {
|
|
264
|
+
backgroundColor: 'white',
|
|
265
|
+
padding: 16,
|
|
266
|
+
borderRadius: 8,
|
|
267
|
+
marginBottom: 16,
|
|
268
|
+
},
|
|
269
|
+
infoTitle: {
|
|
270
|
+
fontSize: 16,
|
|
271
|
+
fontWeight: '600',
|
|
272
|
+
color: '#333',
|
|
273
|
+
marginBottom: 8,
|
|
274
|
+
},
|
|
275
|
+
code: {
|
|
276
|
+
fontFamily: 'monospace',
|
|
277
|
+
fontSize: 12,
|
|
278
|
+
color: '#555',
|
|
279
|
+
backgroundColor: '#f5f5f5',
|
|
280
|
+
padding: 12,
|
|
281
|
+
borderRadius: 4,
|
|
282
|
+
overflow: 'hidden',
|
|
283
|
+
},
|
|
284
|
+
infoText: {
|
|
285
|
+
fontSize: 14,
|
|
286
|
+
color: '#666',
|
|
287
|
+
lineHeight: 20,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allowed domains configuration for callback URL validation
|
|
3
|
+
* These domains are permitted to receive KYC completion callbacks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface AllowedDomainConfig {
|
|
7
|
+
domains: string[];
|
|
8
|
+
enforceHttps: boolean;
|
|
9
|
+
allowLocalhost: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Default configuration - can be overridden via environment variables
|
|
13
|
+
const DEFAULT_CONFIG: AllowedDomainConfig = {
|
|
14
|
+
domains: [
|
|
15
|
+
'transfergratis.com',
|
|
16
|
+
'www.transfergratis.com',
|
|
17
|
+
'admin.transfergratis.com',
|
|
18
|
+
'dashboard.transfergratis.com',
|
|
19
|
+
// Add other trusted domains here
|
|
20
|
+
],
|
|
21
|
+
enforceHttps: true,
|
|
22
|
+
allowLocalhost: true, // Allow localhost for development
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get allowed domains from environment or use defaults
|
|
27
|
+
*/
|
|
28
|
+
export const getAllowedDomainsConfig = (): AllowedDomainConfig => {
|
|
29
|
+
// Check for environment variable override
|
|
30
|
+
if (typeof window !== 'undefined' && (window as any).KYC_ALLOWED_DOMAINS) {
|
|
31
|
+
const envDomains = (window as any).KYC_ALLOWED_DOMAINS;
|
|
32
|
+
if (Array.isArray(envDomains)) {
|
|
33
|
+
return {
|
|
34
|
+
...DEFAULT_CONFIG,
|
|
35
|
+
domains: envDomains,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return DEFAULT_CONFIG;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if a domain is in the allowed list
|
|
45
|
+
*/
|
|
46
|
+
export const isDomainAllowed = (domain: string): boolean => {
|
|
47
|
+
const config = getAllowedDomainsConfig();
|
|
48
|
+
|
|
49
|
+
// Allow localhost in development
|
|
50
|
+
if (config.allowLocalhost && (domain === 'localhost' || domain.startsWith('127.0.0.1'))) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check if domain matches any in the allowed list
|
|
55
|
+
return config.domains.some(allowedDomain => {
|
|
56
|
+
// Exact match
|
|
57
|
+
if (domain === allowedDomain) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Subdomain match (e.g., app.transfergratis.com matches transfergratis.com)
|
|
62
|
+
if (domain.endsWith('.' + allowedDomain)) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return false;
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Validate if a URL is allowed for callback
|
|
72
|
+
*/
|
|
73
|
+
export const isCallbackUrlAllowed = (url: string): { allowed: boolean; reason?: string } => {
|
|
74
|
+
const config = getAllowedDomainsConfig();
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const urlObj = new URL(url);
|
|
78
|
+
|
|
79
|
+
// Check protocol
|
|
80
|
+
if (config.enforceHttps && urlObj.protocol !== 'https:') {
|
|
81
|
+
// Allow http for localhost in development
|
|
82
|
+
if (!(config.allowLocalhost && (urlObj.hostname === 'localhost' || urlObj.hostname.startsWith('127.0.0.1')))) {
|
|
83
|
+
return {
|
|
84
|
+
allowed: false,
|
|
85
|
+
reason: 'HTTPS required for callback URLs',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check domain
|
|
91
|
+
if (!isDomainAllowed(urlObj.hostname)) {
|
|
92
|
+
return {
|
|
93
|
+
allowed: false,
|
|
94
|
+
reason: `Domain '${urlObj.hostname}' is not in the allowed list`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { allowed: true };
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
allowed: false,
|
|
102
|
+
reason: 'Invalid URL format',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Generate a signature for the callback URL parameters
|
|
109
|
+
* This can be used to verify the integrity of the callback
|
|
110
|
+
*/
|
|
111
|
+
export const generateCallbackSignature = async (
|
|
112
|
+
params: Record<string, string>,
|
|
113
|
+
secret?: string
|
|
114
|
+
): Promise<string> => {
|
|
115
|
+
// Only generate signature if secret is provided
|
|
116
|
+
if (!secret) {
|
|
117
|
+
return '';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Sort params for consistent signature
|
|
121
|
+
const sortedParams = Object.keys(params)
|
|
122
|
+
.sort()
|
|
123
|
+
.map(key => `${key}=${params[key]}`)
|
|
124
|
+
.join('&');
|
|
125
|
+
|
|
126
|
+
const data = `${sortedParams}:${secret}`;
|
|
127
|
+
|
|
128
|
+
// Use Web Crypto API for signature generation
|
|
129
|
+
if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) {
|
|
130
|
+
try {
|
|
131
|
+
const encoder = new TextEncoder();
|
|
132
|
+
const dataBuffer = encoder.encode(data);
|
|
133
|
+
const hashBuffer = await window.crypto.subtle.digest('SHA-256', dataBuffer);
|
|
134
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
135
|
+
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
136
|
+
return hashHex;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('Error generating signature:', error);
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Fallback: simple hash for environments without Web Crypto API
|
|
144
|
+
let hash = 0;
|
|
145
|
+
for (let i = 0; i < data.length; i++) {
|
|
146
|
+
const char = data.charCodeAt(i);
|
|
147
|
+
hash = ((hash << 5) - hash) + char;
|
|
148
|
+
hash = hash & hash;
|
|
149
|
+
}
|
|
150
|
+
return Math.abs(hash).toString(16);
|
|
151
|
+
};
|
|
152
|
+
|