@transfergratis/react-native-sdk 0.1.23 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/android/src/main/AndroidManifest.xml +9 -4
  2. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  3. package/build/components/EnhancedCameraView.js +26 -3
  4. package/build/components/EnhancedCameraView.js.map +1 -1
  5. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  6. package/build/components/EnhancedCameraView.web.js +21 -0
  7. package/build/components/EnhancedCameraView.web.js.map +1 -1
  8. package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
  9. package/build/components/KYCElements/CameraCapture.js +4 -3
  10. package/build/components/KYCElements/CameraCapture.js.map +1 -1
  11. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
  12. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  13. package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
  14. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  15. package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
  16. package/build/components/KYCElements/FileUpload.js +5 -4
  17. package/build/components/KYCElements/FileUpload.js.map +1 -1
  18. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  19. package/build/components/KYCElements/FileUploadTemplate.js +5 -4
  20. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  21. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  22. package/build/components/KYCElements/IDCardCapture.js +193 -237
  23. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  24. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  25. package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
  26. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  27. package/build/components/KYCElements/OrientationVideoCapture.js +3 -2
  28. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  29. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +3 -2
  30. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  31. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +3 -2
  32. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  33. package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  34. package/build/components/KYCElements/SelfieCapture.js +4 -3
  35. package/build/components/KYCElements/SelfieCapture.js.map +1 -1
  36. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  37. package/build/components/KYCElements/SelfieCaptureTemplate.js +182 -39
  38. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  39. package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
  40. package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
  41. package/build/components/KYCElements/WelcomeTemplate.js +243 -0
  42. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
  43. package/build/components/TemplateKYCExample.d.ts +4 -2
  44. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  45. package/build/components/TemplateKYCExample.js +5 -69
  46. package/build/components/TemplateKYCExample.js.map +1 -1
  47. package/build/components/TemplateKYCFlowRefactored.d.ts +2 -1
  48. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  49. package/build/components/TemplateKYCFlowRefactored.js +95 -10
  50. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  51. package/build/components/example/DynamicTemplateExample.d.ts +10 -0
  52. package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
  53. package/build/components/example/DynamicTemplateExample.js +241 -0
  54. package/build/components/example/DynamicTemplateExample.js.map +1 -0
  55. package/build/config/allowedDomains.d.ts +30 -0
  56. package/build/config/allowedDomains.d.ts.map +1 -0
  57. package/build/config/allowedDomains.js +127 -0
  58. package/build/config/allowedDomains.js.map +1 -0
  59. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  60. package/build/hooks/useTemplateKYCFlow.js +31 -11
  61. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  62. package/build/hooks/useTemplateLoader.d.ts +14 -0
  63. package/build/hooks/useTemplateLoader.d.ts.map +1 -0
  64. package/build/hooks/useTemplateLoader.js +85 -0
  65. package/build/hooks/useTemplateLoader.js.map +1 -0
  66. package/build/i18n/en/index.d.ts +9 -0
  67. package/build/i18n/en/index.d.ts.map +1 -1
  68. package/build/i18n/en/index.js +9 -0
  69. package/build/i18n/en/index.js.map +1 -1
  70. package/build/i18n/fr/index.d.ts +9 -0
  71. package/build/i18n/fr/index.d.ts.map +1 -1
  72. package/build/i18n/fr/index.js +9 -0
  73. package/build/i18n/fr/index.js.map +1 -1
  74. package/build/index.d.ts +5 -0
  75. package/build/index.d.ts.map +1 -1
  76. package/build/index.js +8 -0
  77. package/build/index.js.map +1 -1
  78. package/build/modules/api/CardAuthentification.js +1 -0
  79. package/build/modules/api/CardAuthentification.js.map +1 -1
  80. package/build/modules/api/KYCService.d.ts.map +1 -1
  81. package/build/modules/api/KYCService.js +7 -2
  82. package/build/modules/api/KYCService.js.map +1 -1
  83. package/build/modules/api/TemplateService.d.ts +45 -0
  84. package/build/modules/api/TemplateService.d.ts.map +1 -0
  85. package/build/modules/api/TemplateService.js +145 -0
  86. package/build/modules/api/TemplateService.js.map +1 -0
  87. package/build/types/KYC.types.d.ts +144 -4
  88. package/build/types/KYC.types.d.ts.map +1 -1
  89. package/build/types/KYC.types.js +15 -0
  90. package/build/types/KYC.types.js.map +1 -1
  91. package/build/utils/cropByObb.d.ts +1 -0
  92. package/build/utils/cropByObb.d.ts.map +1 -1
  93. package/build/utils/cropByObb.js +70 -0
  94. package/build/utils/cropByObb.js.map +1 -1
  95. package/build/utils/platformAlert.d.ts +20 -0
  96. package/build/utils/platformAlert.d.ts.map +1 -0
  97. package/build/utils/platformAlert.js +67 -0
  98. package/build/utils/platformAlert.js.map +1 -0
  99. package/build/utils/template-transformer.d.ts +10 -0
  100. package/build/utils/template-transformer.d.ts.map +1 -0
  101. package/build/utils/template-transformer.js +353 -0
  102. package/build/utils/template-transformer.js.map +1 -0
  103. package/build/web/WebKYCEntry.d.ts.map +1 -1
  104. package/build/web/WebKYCEntry.js +102 -20
  105. package/build/web/WebKYCEntry.js.map +1 -1
  106. package/package.json +1 -1
  107. package/src/components/EnhancedCameraView.tsx +31 -2
  108. package/src/components/EnhancedCameraView.web.tsx +24 -0
  109. package/src/components/KYCElements/CameraCapture.tsx +4 -3
  110. package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
  111. package/src/components/KYCElements/FileUpload.tsx +5 -4
  112. package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
  113. package/src/components/KYCElements/IDCardCapture.tsx +196 -254
  114. package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
  115. package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
  116. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
  117. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
  118. package/src/components/KYCElements/SelfieCapture.tsx +4 -3
  119. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +195 -41
  120. package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
  121. package/src/components/TemplateKYCExample.tsx +16 -72
  122. package/src/components/TemplateKYCFlowRefactored.tsx +122 -12
  123. package/src/components/example/DynamicTemplateExample.tsx +289 -0
  124. package/src/config/allowedDomains.ts +152 -0
  125. package/src/hooks/useTemplateKYCFlow.tsx +33 -11
  126. package/src/hooks/useTemplateLoader.ts +102 -0
  127. package/src/i18n/en/index.ts +10 -0
  128. package/src/i18n/fr/index.ts +9 -0
  129. package/src/index.ts +11 -0
  130. package/src/modules/api/CardAuthentification.ts +1 -1
  131. package/src/modules/api/KYCService.ts +12 -8
  132. package/src/modules/api/TemplateService.ts +167 -0
  133. package/src/types/KYC.types.ts +188 -3
  134. package/src/utils/cropByObb.ts +83 -3
  135. package/src/utils/platformAlert.ts +85 -0
  136. package/src/utils/template-transformer.ts +433 -0
  137. 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
+
@@ -168,6 +168,9 @@ export const useTemplateKYCFlow = (
168
168
  case 'country_selection':
169
169
  // No direct backend action; pack into metadata of next actionable step
170
170
  return null;
171
+ case 'welcome':
172
+ // UI-only step, no backend action needed
173
+ return null;
171
174
  case 'initialization':
172
175
  return 'initialize_session';
173
176
  case 'verification_progress':
@@ -201,7 +204,7 @@ export const useTemplateKYCFlow = (
201
204
  const buildPayloadForComponent = useCallback((action: string | null, component: TemplateComponent, rawData: any, templateId: string, step: number) => {
202
205
  console.log('apiKey in buildPayloadForComponent', apiKey);
203
206
 
204
- const base = { template_id: null, step: component.order, permissionGranted: true } as any;
207
+ const base = { template_id: templateId || null, templateId: templateId || null, step: component.order, permissionGranted: true } as any;
205
208
  if (!action) {
206
209
  return base;
207
210
  }
@@ -230,9 +233,12 @@ export const useTemplateKYCFlow = (
230
233
  const idCardID = Object.keys(state.componentData).find((c: string) => c === "1");
231
234
  if (idCardID) {
232
235
  const _idCardData = state.componentData[idCardID];
233
- return { ...base, documents, country: _idCardData?.country || '', documentType: _idCardData?.documentType as GovernmentDocumentType || 'identity_card' };
236
+ const documentType = _idCardData?.documentType;
237
+ // Map national_id to identity_card for selfie capture
238
+ const mappedDocumentType = documentType === 'national_id' ? 'identity_card' : (documentType as GovernmentDocumentType || 'identity_card');
239
+ return { ...base, documents, country: _idCardData?.country || '', documentType: mappedDocumentType };
234
240
  }
235
- // return { ...base, documents };
241
+ return { ...base, documents };
236
242
  }
237
243
 
238
244
  if (action === 'location_permission') {
@@ -371,6 +377,10 @@ export const useTemplateKYCFlow = (
371
377
 
372
378
  return componentData && componentData.code && componentData.regionMapping;
373
379
 
380
+ case 'welcome':
381
+ // Welcome is valid once user has given consent (componentData is set when they click Get Started)
382
+ return componentData && componentData.consentGiven !== false;
383
+
374
384
  case 'review_submit':
375
385
  return true;
376
386
  default:
@@ -403,10 +413,16 @@ export const useTemplateKYCFlow = (
403
413
  const currentComp = state.template.components[state.currentComponentIndex];
404
414
  if (!currentComp) return;
405
415
 
406
- setState(prev => ({
407
- ...prev,
408
- isProcessing: true,
409
- }));
416
+ // Prevent multiple simultaneous calls
417
+ setState(prev => {
418
+ if (prev.isProcessing) {
419
+ return prev;
420
+ }
421
+ return {
422
+ ...prev,
423
+ isProcessing: true,
424
+ };
425
+ });
410
426
  // Valider le composant actuel
411
427
  if (!validateComponent(currentComp.id)) {
412
428
  setState(prev => ({
@@ -423,7 +439,13 @@ export const useTemplateKYCFlow = (
423
439
 
424
440
  try {
425
441
  const component = state.template.components.find(c => c.id === currentComp.id);
426
- if (!component) return;
442
+ if (!component) {
443
+ setState(prev => ({
444
+ ...prev,
445
+ isProcessing: false,
446
+ }));
447
+ return;
448
+ }
427
449
  if (component.type === 'review_submit') {
428
450
  // Move to verification screen and mark verification in progress
429
451
  setState(prev => ({
@@ -484,7 +506,7 @@ export const useTemplateKYCFlow = (
484
506
  session_id: state.session.session_id,
485
507
  step: step,
486
508
  data: payloadData,
487
- templateId: null,
509
+ templateId: templateId,
488
510
  token: state.session.token,
489
511
  action: action,
490
512
  apiKey: apiKey ?? "-",
@@ -504,7 +526,7 @@ export const useTemplateKYCFlow = (
504
526
  }));
505
527
 
506
528
  } catch (error) {
507
- // console.error('Error validating component:', error);
529
+ logger.error('Error in nextComponent:', error);
508
530
  setState(prev => ({
509
531
  ...prev,
510
532
  isProcessing: false,
@@ -515,7 +537,7 @@ export const useTemplateKYCFlow = (
515
537
  }));
516
538
  }
517
539
 
518
- }, [canGoNext, state.currentComponentIndex, state.template.components, validateComponent, apiKey, state.session.session_id, state.session.token]),
540
+ }, [canGoNext, state.currentComponentIndex, state.template.components, validateComponent, apiKey, state.session.session_id, state.session.token, buildPayloadForComponent, mapComponentTypeToAction, chooseTemplateId, state.currentLanguage]),
519
541
 
520
542
  // Retourner au composant précédent
521
543
  previousComponent: useCallback(() => {
@@ -0,0 +1,102 @@
1
+ import { useState, useCallback, useRef } from 'react';
2
+ import { KYCTemplate } from '../types/KYC.types';
3
+ import templateService from '../modules/api/TemplateService';
4
+ import { transformBackendTemplateToSDK, validateTransformedTemplate } from '../utils/template-transformer';
5
+ import { logger } from '../utils/logger';
6
+ import { authentification } from '../modules/api/KYCService';
7
+
8
+ export interface UseTemplateLoaderReturn {
9
+ template: KYCTemplate | null;
10
+ isLoading: boolean;
11
+ error: string | null;
12
+ loadTemplate: (templateId: string, apiKey?: string) => Promise<void>;
13
+ refresh: () => void;
14
+ clearError: () => void;
15
+ }
16
+
17
+ /**
18
+ * Hook to load and transform templates from the backend API
19
+ */
20
+ export function useTemplateLoader(): UseTemplateLoaderReturn {
21
+ const [template, setTemplate] = useState<KYCTemplate | null>(null);
22
+ const [isLoading, setIsLoading] = useState<boolean>(false);
23
+ const [error, setError] = useState<string | null>(null);
24
+ const currentTemplateIdRef = useRef<string | null>(null);
25
+ const currentApiKeyRef = useRef<string | undefined>(undefined);
26
+
27
+ /**
28
+ * Load a template by ID
29
+ */
30
+ const loadTemplate = useCallback(async (templateId: string, apiKey?: string) => {
31
+ // Reset state
32
+ setError(null);
33
+ setIsLoading(true);
34
+ currentTemplateIdRef.current = templateId;
35
+ currentApiKeyRef.current = apiKey;
36
+
37
+ try {
38
+ logger.log(`Loading template: ${templateId}`);
39
+
40
+ // Get token if no API key provided
41
+ let token: string | undefined;
42
+ if (!apiKey) {
43
+ try {
44
+ token = await authentification();
45
+ } catch (authError) {
46
+ logger.error('Authentication failed:', authError);
47
+ throw new Error('Failed to authenticate. Please provide a valid API key.');
48
+ }
49
+ }
50
+
51
+ // Fetch template from backend
52
+ const backendTemplate = await templateService.fetchTemplate(templateId, apiKey, token);
53
+
54
+ // Transform to SDK format
55
+ const transformedTemplate = transformBackendTemplateToSDK(backendTemplate);
56
+
57
+ // Validate transformed template
58
+ if (!validateTransformedTemplate(transformedTemplate)) {
59
+ throw new Error('Transformed template validation failed');
60
+ }
61
+
62
+ // Set the template
63
+ setTemplate(transformedTemplate);
64
+ logger.log(`Template loaded successfully: ${templateId}`);
65
+ } catch (err: any) {
66
+ const errorMessage = err.message || 'Failed to load template';
67
+ logger.error(`Error loading template ${templateId}:`, errorMessage);
68
+ setError(errorMessage);
69
+ setTemplate(null);
70
+ } finally {
71
+ setIsLoading(false);
72
+ }
73
+ }, []);
74
+
75
+ /**
76
+ * Refresh the current template
77
+ */
78
+ const refresh = useCallback(() => {
79
+ if (currentTemplateIdRef.current) {
80
+ // Clear cache for this template
81
+ templateService.clearCache(currentTemplateIdRef.current);
82
+ // Reload template
83
+ loadTemplate(currentTemplateIdRef.current, currentApiKeyRef.current);
84
+ }
85
+ }, [loadTemplate]);
86
+
87
+ /**
88
+ * Clear error state
89
+ */
90
+ const clearError = useCallback(() => {
91
+ setError(null);
92
+ }, []);
93
+
94
+ return {
95
+ template,
96
+ isLoading,
97
+ error,
98
+ loadTemplate,
99
+ refresh,
100
+ clearError,
101
+ };
102
+ }
@@ -36,6 +36,16 @@ export const en = {
36
36
  ]
37
37
  },
38
38
 
39
+ welcome: {
40
+ requirements: 'What you\'ll need:',
41
+ estimatedTime: 'Estimated time:',
42
+ consent: 'Please accept the following:',
43
+ privacyPolicy: 'I agree to the Privacy Policy',
44
+ termsOfService: 'I agree to the Terms of Service',
45
+ marketingConsent: 'I agree to receive marketing communications',
46
+ readMore: 'Read more'
47
+ },
48
+
39
49
  // Location Capture
40
50
  locationCapture: {
41
51
  title: 'Secure Your Verification',
@@ -35,6 +35,15 @@ export const fr = {
35
35
  'Une connexion internet stable'
36
36
  ]
37
37
  },
38
+ welcome: {
39
+ requirements: 'Ce dont vous aurez besoin :',
40
+ estimatedTime: 'Temps estimé :',
41
+ consent: 'Veuillez accepter les suivants :',
42
+ privacyPolicy: 'J\'accepte la Politique de Confidentialité',
43
+ termsOfService: 'J\'accepte les Conditions d\'Utilisation',
44
+ marketingConsent: 'J\'accepte de recevoir des communications marketing',
45
+ readMore: 'Lire plus'
46
+ },
38
47
 
39
48
  // Location Capture
40
49
  locationCapture: {
package/src/index.ts CHANGED
@@ -7,5 +7,16 @@ export * from './types/KYC.types';
7
7
 
8
8
  export { TemplateKYCExample as LauchTransferGratisKYC } from './components/TemplateKYCExample';
9
9
 
10
+ // Export Template Flow Components
11
+ export { TemplateKYCFlow } from './components/TemplateKYCFlowRefactored';
12
+
13
+ // Export Template Service and Utilities
14
+ export { default as templateService } from './modules/api/TemplateService';
15
+ export { transformBackendTemplateToSDK, validateTransformedTemplate } from './utils/template-transformer';
16
+ export { useTemplateLoader } from './hooks/useTemplateLoader';
17
+
18
+ // Export Template Components
19
+ export { WelcomeTemplate } from './components/KYCElements/WelcomeTemplate';
20
+
10
21
  // Export Web KYC Components
11
22
  export { WebKYCEntry } from './web';