@transfergratis/react-native-sdk 0.1.23 → 0.1.25
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/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +12 -5
- package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
- package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +61 -59
- package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-transfergratis-react-native-sdk.jar +0 -0
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +12 -5
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/outputs/aar/transfergratis-react-native-sdk-debug.aar +0 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +26 -34
- package/android/src/main/AndroidManifest.xml +13 -5
- 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/AdditionalDocumentsTemplate.d.ts +12 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
- 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/EmailVerificationTemplate.d.ts +12 -0
- package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/EmailVerificationTemplate.js +193 -0
- package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
- 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 +356 -227
- 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.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.js +5 -4
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +5 -4
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js +5 -4
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
- package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
- package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
- 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 +189 -42
- 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 +8 -2
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +10 -97
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +6 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +108 -11
- 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/KYCConfig.d.ts +14 -0
- package/build/config/KYCConfig.d.ts.map +1 -0
- package/build/config/KYCConfig.js +26 -0
- package/build/config/KYCConfig.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 +112 -0
- package/build/config/allowedDomains.js.map +1 -0
- package/build/hooks/useOrientationVideo.d.ts +2 -1
- package/build/hooks/useOrientationVideo.d.ts.map +1 -1
- package/build/hooks/useOrientationVideo.js +3 -3
- package/build/hooks/useOrientationVideo.js.map +1 -1
- package/build/hooks/useTemplateKYCFlow.d.ts +6 -1
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +317 -34
- 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 +49 -0
- package/build/i18n/en/index.d.ts.map +1 -1
- package/build/i18n/en/index.js +50 -1
- package/build/i18n/en/index.js.map +1 -1
- package/build/i18n/fr/index.d.ts +35 -0
- package/build/i18n/fr/index.d.ts.map +1 -1
- package/build/i18n/fr/index.js +36 -1
- package/build/i18n/fr/index.js.map +1 -1
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -0
- package/build/index.js.map +1 -1
- package/build/modules/api/CardAuthentification.d.ts +24 -3
- package/build/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/modules/api/CardAuthentification.js +69 -10
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +7 -7
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +108 -39
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/SelfieVerification.d.ts +3 -1
- package/build/modules/api/SelfieVerification.d.ts.map +1 -1
- package/build/modules/api/SelfieVerification.js +17 -1
- package/build/modules/api/SelfieVerification.js.map +1 -1
- package/build/modules/api/TemplateService.d.ts +44 -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/types/KYC.types.d.ts +265 -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/types/env.types.d.ts +13 -0
- package/build/types/env.types.d.ts.map +1 -0
- package/build/types/env.types.js +2 -0
- package/build/types/env.types.js.map +1 -0
- 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/deviceDetection.d.ts +6 -0
- package/build/utils/deviceDetection.d.ts.map +1 -0
- package/build/utils/deviceDetection.js +12 -0
- package/build/utils/deviceDetection.js.map +1 -0
- 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 +365 -0
- package/build/utils/template-transformer.js.map +1 -0
- package/build/web/WebKYCEntry.d.ts.map +1 -1
- package/build/web/WebKYCEntry.js +158 -32
- package/build/web/WebKYCEntry.js.map +1 -1
- package/package.json +1 -1
- package/plugin/build/withVisionCamera.js +3 -4
- package/plugin/src/withVisionCamera.js +3 -4
- package/plugin/src/withVisionCamera.ts +3 -4
- package/src/components/EnhancedCameraView.tsx +31 -2
- package/src/components/EnhancedCameraView.web.tsx +24 -0
- package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
- package/src/components/KYCElements/CameraCapture.tsx +4 -3
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
- package/src/components/KYCElements/EmailVerificationTemplate.tsx +264 -0
- package/src/components/KYCElements/FileUpload.tsx +5 -4
- package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
- package/src/components/KYCElements/IDCardCapture.tsx +397 -254
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
- package/src/components/KYCElements/OrientationVideoCapture.tsx +6 -3
- package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +6 -3
- package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +6 -3
- package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
- package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
- package/src/components/KYCElements/SelfieCapture.tsx +4 -3
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +201 -44
- package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
- package/src/components/TemplateKYCExample.tsx +37 -108
- package/src/components/TemplateKYCFlowRefactored.tsx +148 -12
- package/src/components/example/DynamicTemplateExample.tsx +289 -0
- package/src/config/KYCConfig.ts +34 -0
- package/src/config/allowedDomains.ts +133 -0
- package/src/hooks/useOrientationVideo.ts +5 -4
- package/src/hooks/useTemplateKYCFlow.tsx +347 -32
- package/src/hooks/useTemplateLoader.ts +102 -0
- package/src/i18n/en/index.ts +53 -2
- package/src/i18n/fr/index.ts +37 -1
- package/src/index.ts +14 -0
- package/src/modules/api/CardAuthentification.ts +76 -11
- package/src/modules/api/KYCService.ts +129 -45
- package/src/modules/api/SelfieVerification.ts +25 -3
- package/src/modules/api/TemplateService.ts +167 -0
- package/src/types/KYC.types.ts +331 -3
- package/src/types/env.types.ts +13 -0
- package/src/utils/cropByObb.ts +83 -3
- package/src/utils/deviceDetection.ts +11 -0
- package/src/utils/platformAlert.ts +86 -0
- package/src/utils/template-transformer.ts +445 -0
- package/src/web/WebKYCEntry.tsx +199 -50
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { View, Text, StyleSheet, SafeAreaView, TouchableOpacity, ActivityIndicator, Dimensions,
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, SafeAreaView, TouchableOpacity, ActivityIndicator, Dimensions, ScrollView, Platform } from 'react-native';
|
|
3
3
|
import { KYCTemplate, VerificationState } from '../types/KYC.types';
|
|
4
4
|
import { TemplateKYCFlowProvider, useTemplateKYCFlowContext } from '../hooks/useTemplateKYCFlow';
|
|
5
5
|
import { useI18n } from '../hooks/useI18n';
|
|
6
|
+
import { showAlert } from '../utils/platformAlert';
|
|
6
7
|
import { IDCardCapture } from './KYCElements/IDCardCapture';
|
|
7
8
|
import { SelfieCaptureTemplate } from './KYCElements/SelfieCaptureTemplate';
|
|
8
9
|
import { FileUploadTemplate } from './KYCElements/FileUploadTemplate';
|
|
@@ -11,28 +12,67 @@ import { CountrySelectionTemplate } from './KYCElements/CountrySelectionTemplate
|
|
|
11
12
|
import { InitializationStep } from './KYCElements/InitializationStep';
|
|
12
13
|
import { ReviewSubmitTemplate } from './KYCElements/ReviewSubmitTemplate';
|
|
13
14
|
import { VerificationProgressTemplate } from './KYCElements/VerificationProgressTemplate';
|
|
15
|
+
import { useTemplateLoader } from '../hooks/useTemplateLoader';
|
|
16
|
+
import { WelcomeTemplate } from './KYCElements/WelcomeTemplate';
|
|
17
|
+
import { EmailVerificationTemplate } from './KYCElements/EmailVerificationTemplate';
|
|
18
|
+
import { PhoneVerificationTemplate } from './KYCElements/PhoneVerificationTemplate';
|
|
19
|
+
import { PersonalInformationTemplate } from './KYCElements/PersonalInformationTemplate';
|
|
20
|
+
import { AdditionalDocumentsTemplate } from './KYCElements/AdditionalDocumentsTemplate';
|
|
21
|
+
import { KycEnvironment } from '../types/env.types';
|
|
14
22
|
|
|
15
23
|
interface TemplateKYCFlowProps {
|
|
16
|
-
template
|
|
24
|
+
template?: KYCTemplate; // Format SDK direct (existing, now optional)
|
|
25
|
+
templateId?: string; // New: ID to load from API
|
|
17
26
|
onComplete?: (data: VerificationState) => void;
|
|
18
27
|
onError?: (error: string) => void;
|
|
19
28
|
language?: string;
|
|
20
29
|
onCancel?: () => void;
|
|
21
|
-
API_KEY?: string;
|
|
30
|
+
API_KEY?: string; // Required if templateId is used
|
|
31
|
+
env?: KycEnvironment; // Environment mode: PRODUCTION or SANDBOX
|
|
32
|
+
existingSessionId?: string;
|
|
33
|
+
initialStep?: number; // Initial step index to resume verification
|
|
22
34
|
}
|
|
23
35
|
|
|
24
36
|
export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
|
|
25
|
-
template,
|
|
37
|
+
template: providedTemplate,
|
|
38
|
+
templateId,
|
|
26
39
|
onComplete,
|
|
27
40
|
onError,
|
|
28
41
|
language = 'fr',
|
|
29
42
|
onCancel,
|
|
30
43
|
API_KEY,
|
|
44
|
+
env = 'PRODUCTION',
|
|
45
|
+
existingSessionId,
|
|
46
|
+
initialStep,
|
|
31
47
|
}) => {
|
|
32
48
|
const { t } = useI18n();
|
|
33
|
-
|
|
49
|
+
const { template: loadedTemplate, isLoading, error, loadTemplate } = useTemplateLoader();
|
|
50
|
+
|
|
51
|
+
// Validate props
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
if (!providedTemplate && !templateId) {
|
|
54
|
+
const errorMsg = 'Either template or templateId must be provided';
|
|
55
|
+
onError?.(errorMsg);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (templateId && !API_KEY) {
|
|
60
|
+
// Try to load without API key (will use token authentication)
|
|
61
|
+
loadTemplate(templateId);
|
|
62
|
+
} else if (templateId && API_KEY) {
|
|
63
|
+
loadTemplate(templateId, API_KEY);
|
|
64
|
+
}
|
|
65
|
+
}, [templateId, API_KEY, providedTemplate, loadTemplate, onError]);
|
|
66
|
+
|
|
67
|
+
// Handle loading errors
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (error) {
|
|
70
|
+
onError?.(error);
|
|
71
|
+
}
|
|
72
|
+
}, [error, onError]);
|
|
73
|
+
|
|
34
74
|
const OnCancel = () => {
|
|
35
|
-
|
|
75
|
+
showAlert(
|
|
36
76
|
t('kyc.cancelConfirmation.title'),
|
|
37
77
|
t('kyc.cancelConfirmation.message'),
|
|
38
78
|
[
|
|
@@ -46,6 +86,46 @@ export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
|
|
|
46
86
|
]
|
|
47
87
|
);
|
|
48
88
|
};
|
|
89
|
+
|
|
90
|
+
// Determine which template to use
|
|
91
|
+
const template = providedTemplate || loadedTemplate;
|
|
92
|
+
|
|
93
|
+
// Show loading state while fetching template
|
|
94
|
+
if (templateId && isLoading && !template) {
|
|
95
|
+
return (
|
|
96
|
+
<View style={styles.loadingContainer}>
|
|
97
|
+
<ActivityIndicator size="large" color="#2DBD60" />
|
|
98
|
+
<Text style={styles.loadingText}>{t('common.loading') || 'Loading template...'}</Text>
|
|
99
|
+
</View>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Show error state if template failed to load
|
|
104
|
+
if (templateId && error && !template) {
|
|
105
|
+
return (
|
|
106
|
+
<View style={styles.errorContainer}>
|
|
107
|
+
<Text style={styles.errorText}>{error}</Text>
|
|
108
|
+
<TouchableOpacity
|
|
109
|
+
style={styles.retryButton}
|
|
110
|
+
onPress={() => loadTemplate(templateId, API_KEY)}
|
|
111
|
+
>
|
|
112
|
+
<Text style={styles.retryButtonText}>{t('common.retry') || 'Retry'}</Text>
|
|
113
|
+
</TouchableOpacity>
|
|
114
|
+
</View>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Show error if no template available
|
|
119
|
+
if (!template) {
|
|
120
|
+
return (
|
|
121
|
+
<View style={styles.errorContainer}>
|
|
122
|
+
<Text style={styles.errorText}>
|
|
123
|
+
{t('errors.unknownError') || 'Template not available'}
|
|
124
|
+
</Text>
|
|
125
|
+
</View>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
49
129
|
return (
|
|
50
130
|
<TemplateKYCFlowProvider
|
|
51
131
|
template={template}
|
|
@@ -54,6 +134,9 @@ export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
|
|
|
54
134
|
onCancel={OnCancel}
|
|
55
135
|
initialLanguage={language}
|
|
56
136
|
apiKey={API_KEY}
|
|
137
|
+
env={env}
|
|
138
|
+
existingSessionId={existingSessionId}
|
|
139
|
+
initialStep={initialStep}
|
|
57
140
|
>
|
|
58
141
|
<TemplateKYCFlowContent onCancel={OnCancel} />
|
|
59
142
|
</TemplateKYCFlowProvider>
|
|
@@ -97,6 +180,9 @@ const TemplateKYCFlowContent: React.FC<{ onCancel?: () => void }> = ({ onCancel
|
|
|
97
180
|
}
|
|
98
181
|
|
|
99
182
|
switch (currentComponent.type) {
|
|
183
|
+
case 'welcome':
|
|
184
|
+
return <WelcomeTemplate {...commonProps} />;
|
|
185
|
+
|
|
100
186
|
case 'id_card':
|
|
101
187
|
return <IDCardCapture {...commonProps} />;
|
|
102
188
|
|
|
@@ -115,6 +201,18 @@ const TemplateKYCFlowContent: React.FC<{ onCancel?: () => void }> = ({ onCancel
|
|
|
115
201
|
case 'review_submit':
|
|
116
202
|
return <ReviewSubmitTemplate {...commonProps} />;
|
|
117
203
|
|
|
204
|
+
case 'email_verification':
|
|
205
|
+
return <EmailVerificationTemplate {...commonProps} />;
|
|
206
|
+
|
|
207
|
+
case 'phone_verification':
|
|
208
|
+
return <PhoneVerificationTemplate {...commonProps} />;
|
|
209
|
+
|
|
210
|
+
case 'personal_information':
|
|
211
|
+
return <PersonalInformationTemplate {...commonProps} />;
|
|
212
|
+
|
|
213
|
+
case 'additional_documents':
|
|
214
|
+
return <AdditionalDocumentsTemplate {...commonProps} />;
|
|
215
|
+
|
|
118
216
|
case 'verification_progress':
|
|
119
217
|
return <VerificationProgressTemplate {...commonProps} />;
|
|
120
218
|
|
|
@@ -166,9 +264,14 @@ const TemplateKYCFlowContent: React.FC<{ onCancel?: () => void }> = ({ onCancel
|
|
|
166
264
|
) : null}
|
|
167
265
|
|
|
168
266
|
{/* Contenu principal */}
|
|
169
|
-
<
|
|
267
|
+
<ScrollView
|
|
268
|
+
style={styles.scrollView}
|
|
269
|
+
contentContainerStyle={styles.content}
|
|
270
|
+
showsVerticalScrollIndicator={Platform.OS === 'web'}
|
|
271
|
+
bounces={false}
|
|
272
|
+
>
|
|
170
273
|
{renderCurrentComponent()}
|
|
171
|
-
</
|
|
274
|
+
</ScrollView>
|
|
172
275
|
|
|
173
276
|
{state.isProcessing && (
|
|
174
277
|
<View style={styles.processingContainer}>
|
|
@@ -253,11 +356,13 @@ const styles = StyleSheet.create({
|
|
|
253
356
|
color: '#666',
|
|
254
357
|
lineHeight: 20,
|
|
255
358
|
},
|
|
256
|
-
|
|
359
|
+
scrollView: {
|
|
257
360
|
flex: 1,
|
|
258
|
-
|
|
361
|
+
},
|
|
362
|
+
content: {
|
|
363
|
+
flexGrow: 1,
|
|
259
364
|
alignItems: 'center',
|
|
260
|
-
|
|
365
|
+
paddingBottom: 20, // Add padding for better mobile scrolling
|
|
261
366
|
},
|
|
262
367
|
navigation: {
|
|
263
368
|
flexDirection: 'row',
|
|
@@ -316,4 +421,35 @@ const styles = StyleSheet.create({
|
|
|
316
421
|
fontWeight: 'bold',
|
|
317
422
|
marginBottom: 10,
|
|
318
423
|
},
|
|
424
|
+
loadingContainer: {
|
|
425
|
+
flex: 1,
|
|
426
|
+
justifyContent: 'center',
|
|
427
|
+
alignItems: 'center',
|
|
428
|
+
backgroundColor: '#f5f5f5',
|
|
429
|
+
},
|
|
430
|
+
loadingText: {
|
|
431
|
+
marginTop: 16,
|
|
432
|
+
fontSize: 16,
|
|
433
|
+
color: '#666',
|
|
434
|
+
textAlign: 'center',
|
|
435
|
+
},
|
|
436
|
+
errorContainer: {
|
|
437
|
+
flex: 1,
|
|
438
|
+
justifyContent: 'center',
|
|
439
|
+
alignItems: 'center',
|
|
440
|
+
backgroundColor: '#f5f5f5',
|
|
441
|
+
padding: 20,
|
|
442
|
+
},
|
|
443
|
+
retryButton: {
|
|
444
|
+
marginTop: 20,
|
|
445
|
+
paddingHorizontal: 24,
|
|
446
|
+
paddingVertical: 12,
|
|
447
|
+
backgroundColor: '#2DBD60',
|
|
448
|
+
borderRadius: 8,
|
|
449
|
+
},
|
|
450
|
+
retryButtonText: {
|
|
451
|
+
color: '#fff',
|
|
452
|
+
fontSize: 16,
|
|
453
|
+
fontWeight: '600',
|
|
454
|
+
},
|
|
319
455
|
});
|
|
@@ -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,34 @@
|
|
|
1
|
+
import { BackendEnvironment } from '../types/env.types';
|
|
2
|
+
|
|
3
|
+
class KYCConfig {
|
|
4
|
+
private static instance: KYCConfig;
|
|
5
|
+
private backendEnvironment: BackendEnvironment = 'PRODUCTION';
|
|
6
|
+
|
|
7
|
+
private backendUrls: Record<BackendEnvironment, string> = {
|
|
8
|
+
PRODUCTION: 'https://service.sanctumkey.com/api/v1',
|
|
9
|
+
TEST: 'https://test-service.sanctumkey.com/api/v1', // Placeholder URL
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
private constructor() { }
|
|
13
|
+
|
|
14
|
+
public static getInstance(): KYCConfig {
|
|
15
|
+
if (!KYCConfig.instance) {
|
|
16
|
+
KYCConfig.instance = new KYCConfig();
|
|
17
|
+
}
|
|
18
|
+
return KYCConfig.instance;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public setBackendEnvironment(env: BackendEnvironment) {
|
|
22
|
+
this.backendEnvironment = env;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public getBackendUrl(): string {
|
|
26
|
+
return this.backendUrls[this.backendEnvironment];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public getBackendEnvironment(): BackendEnvironment {
|
|
30
|
+
return this.backendEnvironment;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default KYCConfig.getInstance();
|
|
@@ -0,0 +1,133 @@
|
|
|
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
|
+
'preweb.transfergratis.net',
|
|
20
|
+
// Add other trusted domains here
|
|
21
|
+
],
|
|
22
|
+
enforceHttps: true,
|
|
23
|
+
allowLocalhost: true, // Allow localhost for development
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get allowed domains from environment or use defaults
|
|
28
|
+
*/
|
|
29
|
+
export const getAllowedDomainsConfig = (): AllowedDomainConfig => {
|
|
30
|
+
// Check for environment variable override
|
|
31
|
+
if (typeof window !== 'undefined' && (window as any).KYC_ALLOWED_DOMAINS) {
|
|
32
|
+
const envDomains = (window as any).KYC_ALLOWED_DOMAINS;
|
|
33
|
+
if (Array.isArray(envDomains)) {
|
|
34
|
+
return {
|
|
35
|
+
...DEFAULT_CONFIG,
|
|
36
|
+
domains: envDomains,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return DEFAULT_CONFIG;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if a domain is in the allowed list
|
|
46
|
+
*/
|
|
47
|
+
export const isDomainAllowed = (domain: string): boolean => {
|
|
48
|
+
const config = getAllowedDomainsConfig();
|
|
49
|
+
|
|
50
|
+
// Allow localhost in development
|
|
51
|
+
if (config.allowLocalhost && (domain === 'localhost' || domain.startsWith('127.0.0.1'))) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check if domain matches any in the allowed list
|
|
56
|
+
return config.domains.some(allowedDomain => {
|
|
57
|
+
// Exact match
|
|
58
|
+
if (domain === allowedDomain) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Subdomain match (e.g., app.transfergratis.com matches transfergratis.com)
|
|
63
|
+
if (domain.endsWith('.' + allowedDomain)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Validate if a URL is allowed for callback
|
|
73
|
+
*/
|
|
74
|
+
export const isCallbackUrlAllowed = (url: string): { allowed: boolean; reason?: string } => {
|
|
75
|
+
try {
|
|
76
|
+
// Check if valid URL
|
|
77
|
+
new URL(url);
|
|
78
|
+
// Allow all URLs for redirection as requested
|
|
79
|
+
return { allowed: true };
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
allowed: false,
|
|
83
|
+
reason: 'Invalid URL format',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generate a signature for the callback URL parameters
|
|
90
|
+
* This can be used to verify the integrity of the callback
|
|
91
|
+
*/
|
|
92
|
+
export const generateCallbackSignature = async (
|
|
93
|
+
params: Record<string, string>,
|
|
94
|
+
secret?: string
|
|
95
|
+
): Promise<string> => {
|
|
96
|
+
// Only generate signature if secret is provided
|
|
97
|
+
if (!secret) {
|
|
98
|
+
return '';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Sort params for consistent signature
|
|
102
|
+
const sortedParams = Object.keys(params)
|
|
103
|
+
.sort()
|
|
104
|
+
.map(key => `${key}=${params[key]}`)
|
|
105
|
+
.join('&');
|
|
106
|
+
|
|
107
|
+
const data = `${sortedParams}:${secret}`;
|
|
108
|
+
|
|
109
|
+
// Use Web Crypto API for signature generation
|
|
110
|
+
if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) {
|
|
111
|
+
try {
|
|
112
|
+
const encoder = new TextEncoder();
|
|
113
|
+
const dataBuffer = encoder.encode(data);
|
|
114
|
+
const hashBuffer = await window.crypto.subtle.digest('SHA-256', dataBuffer);
|
|
115
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
116
|
+
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
117
|
+
return hashHex;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('Error generating signature:', error);
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Fallback: simple hash for environments without Web Crypto API
|
|
125
|
+
let hash = 0;
|
|
126
|
+
for (let i = 0; i < data.length; i++) {
|
|
127
|
+
const char = data.charCodeAt(i);
|
|
128
|
+
hash = ((hash << 5) - hash) + char;
|
|
129
|
+
hash = hash & hash;
|
|
130
|
+
}
|
|
131
|
+
return Math.abs(hash).toString(16);
|
|
132
|
+
};
|
|
133
|
+
|