@transfergratis/react-native-sdk 0.1.4 → 0.1.6
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/build/api/axios.d.ts +30 -0
- package/build/api/axios.d.ts.map +1 -0
- package/build/api/axios.js +92 -0
- package/build/api/axios.js.map +1 -0
- package/build/components/EnhancedCameraView.d.ts +1 -41
- package/build/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.js +75 -34
- package/build/components/EnhancedCameraView.js.map +1 -1
- package/build/components/EnhancedCameraView.web.d.ts +1 -41
- package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.web.js +28 -4
- package/build/components/EnhancedCameraView.web.js.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts +2 -2
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +77 -114
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.js +7 -3
- package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts +7 -2
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +253 -104
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/InitializationStep.d.ts +5 -0
- package/build/components/KYCElements/InitializationStep.d.ts.map +1 -0
- package/build/components/KYCElements/InitializationStep.js +41 -0
- package/build/components/KYCElements/InitializationStep.js.map +1 -0
- package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.js +15 -13
- package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -2
- package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -2
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -2
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- package/build/components/KYCElements/ReviewSubmitTemplate.d.ts +12 -0
- package/build/components/KYCElements/ReviewSubmitTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/ReviewSubmitTemplate.js +171 -0
- package/build/components/KYCElements/ReviewSubmitTemplate.js.map +1 -0
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts +6 -2
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +137 -38
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/VerificationProgressTemplate.d.ts +12 -0
- package/build/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/VerificationProgressTemplate.js +194 -0
- package/build/components/KYCElements/VerificationProgressTemplate.js.map +1 -0
- package/build/components/OverLay/IdCard.d.ts +1 -1
- package/build/components/OverLay/IdCard.d.ts.map +1 -1
- package/build/components/OverLay/IdCard.js +10 -6
- package/build/components/OverLay/IdCard.js.map +1 -1
- package/build/components/OverLay/SelfieOverlay.d.ts +1 -1
- package/build/components/OverLay/SelfieOverlay.d.ts.map +1 -1
- package/build/components/OverLay/SelfieOverlay.js +5 -4
- package/build/components/OverLay/SelfieOverlay.js.map +1 -1
- package/build/components/OverLay/type.d.ts +71 -1
- package/build/components/OverLay/type.d.ts.map +1 -1
- package/build/components/OverLay/type.js.map +1 -1
- package/build/components/TemplateKYCExample.d.ts +4 -1
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +74 -199
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +3 -2
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +64 -40
- package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/components/example/OrientationVideoExample.d.ts.map +1 -1
- package/build/components/example/OrientationVideoExample.js +1 -5
- package/build/components/example/OrientationVideoExample.js.map +1 -1
- package/build/config/countriesData.d.ts +3 -0
- package/build/config/countriesData.d.ts.map +1 -0
- package/build/config/countriesData.js +79 -0
- package/build/config/countriesData.js.map +1 -0
- package/build/config/region_mapping.d.ts +3 -0
- package/build/config/region_mapping.d.ts.map +1 -0
- package/build/config/region_mapping.js +687 -0
- package/build/config/region_mapping.js.map +1 -0
- package/build/hooks/useI18n.d.ts +11 -0
- package/build/hooks/useI18n.d.ts.map +1 -0
- package/build/hooks/useI18n.js +37 -0
- package/build/hooks/useI18n.js.map +1 -0
- package/build/hooks/useOrientationVideo.d.ts +1 -2
- package/build/hooks/useOrientationVideo.d.ts.map +1 -1
- package/build/hooks/useOrientationVideo.js +2 -1
- package/build/hooks/useOrientationVideo.js.map +1 -1
- package/build/hooks/useRealtimeVerifier.d.ts +28 -0
- package/build/hooks/useRealtimeVerifier.d.ts.map +1 -0
- package/build/hooks/useRealtimeVerifier.js +91 -0
- package/build/hooks/useRealtimeVerifier.js.map +1 -0
- package/build/hooks/useTemplateKYCFlow.d.ts +6 -3
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +356 -42
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/i18n/en/index.d.ts +168 -0
- package/build/i18n/en/index.d.ts.map +1 -0
- package/build/i18n/en/index.js +195 -0
- package/build/i18n/en/index.js.map +1 -0
- package/build/i18n/fr/index.d.ts +168 -0
- package/build/i18n/fr/index.d.ts.map +1 -0
- package/build/i18n/fr/index.js +194 -0
- package/build/i18n/fr/index.js.map +1 -0
- package/build/i18n/index.d.ts +10 -0
- package/build/i18n/index.d.ts.map +1 -0
- package/build/i18n/index.js +56 -0
- package/build/i18n/index.js.map +1 -0
- package/build/i18n/types.d.ts +153 -0
- package/build/i18n/types.d.ts.map +1 -0
- package/build/i18n/types.js +3 -0
- package/build/i18n/types.js.map +1 -0
- package/build/i18n/usage-example.d.ts +4 -0
- package/build/i18n/usage-example.d.ts.map +1 -0
- package/build/i18n/usage-example.js +189 -0
- package/build/i18n/usage-example.js.map +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/modules/api/CardAuthentification.d.ts +22 -0
- package/build/modules/api/CardAuthentification.d.ts.map +1 -0
- package/build/modules/api/CardAuthentification.js +107 -0
- package/build/modules/api/CardAuthentification.js.map +1 -0
- package/build/modules/api/KYCService.d.ts +58 -1
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +304 -27
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/SelfieVerification.d.ts +3 -0
- package/build/modules/api/SelfieVerification.d.ts.map +1 -0
- package/build/modules/api/SelfieVerification.js +9 -0
- package/build/modules/api/SelfieVerification.js.map +1 -0
- package/build/modules/api/backendApi.d.ts +2 -0
- package/build/modules/api/backendApi.d.ts.map +1 -0
- package/build/modules/api/backendApi.js +6 -0
- package/build/modules/api/backendApi.js.map +1 -0
- package/build/modules/api/types.d.ts +45 -0
- package/build/modules/api/types.d.ts.map +1 -0
- package/build/modules/api/types.js +2 -0
- package/build/modules/api/types.js.map +1 -0
- package/build/types/KYC.types.d.ts +56 -7
- package/build/types/KYC.types.d.ts.map +1 -1
- package/build/types/KYC.types.js +9 -1
- package/build/types/KYC.types.js.map +1 -1
- package/build/utils/cropByObb.d.ts +11 -0
- package/build/utils/cropByObb.d.ts.map +1 -0
- package/build/utils/cropByObb.js +78 -0
- package/build/utils/cropByObb.js.map +1 -0
- package/build/utils/get-document-type-info.d.ts +13 -0
- package/build/utils/get-document-type-info.d.ts.map +1 -0
- package/build/utils/get-document-type-info.js +59 -0
- package/build/utils/get-document-type-info.js.map +1 -0
- package/build/utils/pathToBase64.d.ts +3 -0
- package/build/utils/pathToBase64.d.ts.map +1 -0
- package/build/utils/pathToBase64.js +47 -0
- package/build/utils/pathToBase64.js.map +1 -0
- package/build/utils/remove-duplicate.d.ts +2 -0
- package/build/utils/remove-duplicate.d.ts.map +1 -0
- package/build/utils/remove-duplicate.js +4 -0
- package/build/utils/remove-duplicate.js.map +1 -0
- package/build/web/WebKYCEntry.d.ts +9 -0
- package/build/web/WebKYCEntry.d.ts.map +1 -0
- package/build/web/WebKYCEntry.js +156 -0
- package/build/web/WebKYCEntry.js.map +1 -0
- package/build/web/index.d.ts +2 -0
- package/build/web/index.d.ts.map +1 -0
- package/build/web/index.js +2 -0
- package/build/web/index.js.map +1 -0
- package/package.json +3 -1
- package/src/api/axios.ts +144 -0
- package/src/components/EnhancedCameraView.tsx +96 -78
- package/src/components/EnhancedCameraView.web.tsx +41 -40
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +111 -136
- package/src/components/KYCElements/FileUploadTemplate.tsx +14 -8
- package/src/components/KYCElements/IDCardCapture.tsx +311 -115
- package/src/components/KYCElements/InitializationStep.tsx +53 -0
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +17 -15
- 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/ReviewSubmitTemplate.tsx +201 -0
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +174 -57
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +246 -0
- package/src/components/OverLay/IdCard.tsx +17 -9
- package/src/components/OverLay/SelfieOverlay.tsx +6 -5
- package/src/components/OverLay/type.ts +64 -2
- package/src/components/TemplateKYCExample.tsx +80 -200
- package/src/components/TemplateKYCFlowRefactored.tsx +80 -48
- package/src/components/example/OrientationVideoExample.tsx +3 -7
- package/src/config/countriesData.ts +84 -0
- package/src/config/region_mapping.ts +688 -0
- package/src/hooks/useI18n.ts +53 -0
- package/src/hooks/useOrientationVideo.ts +2 -2
- package/src/hooks/useRealtimeVerifier.ts +128 -0
- package/src/hooks/useTemplateKYCFlow.tsx +407 -57
- package/src/i18n/README.md +288 -0
- package/src/i18n/en/index.ts +206 -0
- package/src/i18n/fr/index.ts +205 -0
- package/src/i18n/index.ts +65 -0
- package/src/i18n/types.ts +172 -0
- package/src/i18n/usage-example.tsx +202 -0
- package/src/index.ts +3 -0
- package/src/modules/api/CardAuthentification.ts +114 -0
- package/src/modules/api/KYCService.ts +350 -30
- package/src/modules/api/SelfieVerification.ts +11 -0
- package/src/modules/api/backendApi.ts +8 -0
- package/src/modules/api/types.ts +51 -0
- package/src/types/KYC.types.ts +82 -14
- package/src/utils/cropByObb.ts +99 -0
- package/src/utils/get-document-type-info.ts +62 -0
- package/src/utils/pathToBase64.ts +47 -0
- package/src/utils/remove-duplicate.ts +3 -0
- package/src/web/WebKYCEntry.tsx +215 -0
- package/src/web/index.ts +1 -0
- package/src/types/nativewind.d.ts +0 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import React, { useState, useCallback, useMemo, createContext, useContext, ReactNode } from 'react';
|
|
2
|
-
import { KYCTemplate, TemplateState, TemplateActions, UseTemplateReturn } from '../types/KYC.types';
|
|
1
|
+
import React, { useState, useCallback, useMemo, createContext, useContext, ReactNode, useEffect } from 'react';
|
|
2
|
+
import { KYCTemplate, TemplateState, TemplateActions, UseTemplateReturn, TemplateComponent, GovernmentDocumentType, VerificationState } from '../types/KYC.types';
|
|
3
|
+
import kycService, { authentification, truncateFields } from '../modules/api/KYCService';
|
|
4
|
+
import useI18n from './useI18n';
|
|
3
5
|
|
|
4
6
|
// Context pour le provider
|
|
5
7
|
interface TemplateKYCFlowContextType {
|
|
@@ -10,7 +12,8 @@ interface TemplateKYCFlowContextType {
|
|
|
10
12
|
canGoNext: boolean;
|
|
11
13
|
canGoPrevious: boolean;
|
|
12
14
|
isComplete: boolean;
|
|
13
|
-
getLocalizedText: (text: { en: string; fr: string;
|
|
15
|
+
getLocalizedText: (text: { en: string; fr: string;[key: string]: string }) => string;
|
|
16
|
+
initializeSession: () => Promise<void>;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
const TemplateKYCFlowContext = createContext<TemplateKYCFlowContextType | undefined>(undefined);
|
|
@@ -19,9 +22,11 @@ const TemplateKYCFlowContext = createContext<TemplateKYCFlowContextType | undefi
|
|
|
19
22
|
interface TemplateKYCFlowProviderProps {
|
|
20
23
|
children: ReactNode;
|
|
21
24
|
template: KYCTemplate;
|
|
22
|
-
onComplete?: (data:
|
|
25
|
+
onComplete?: (data: VerificationState) => void;
|
|
23
26
|
onError?: (error: string) => void;
|
|
27
|
+
onCancel?: () => void;
|
|
24
28
|
initialLanguage?: string;
|
|
29
|
+
apiKey?: string;
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
export const TemplateKYCFlowProvider: React.FC<TemplateKYCFlowProviderProps> = ({
|
|
@@ -29,10 +34,12 @@ export const TemplateKYCFlowProvider: React.FC<TemplateKYCFlowProviderProps> = (
|
|
|
29
34
|
template,
|
|
30
35
|
onComplete,
|
|
31
36
|
onError,
|
|
37
|
+
onCancel,
|
|
32
38
|
initialLanguage = 'en',
|
|
39
|
+
apiKey,
|
|
33
40
|
}) => {
|
|
34
|
-
const hookResult = useTemplateKYCFlow(template, onComplete, onError, initialLanguage);
|
|
35
|
-
|
|
41
|
+
const hookResult = useTemplateKYCFlow(template, onComplete, onError, onCancel, initialLanguage, apiKey);
|
|
42
|
+
|
|
36
43
|
return (
|
|
37
44
|
<TemplateKYCFlowContext.Provider value={hookResult}>
|
|
38
45
|
{children}
|
|
@@ -51,13 +58,80 @@ export const useTemplateKYCFlowContext = (): TemplateKYCFlowContextType => {
|
|
|
51
58
|
|
|
52
59
|
export const useTemplateKYCFlow = (
|
|
53
60
|
template: KYCTemplate,
|
|
54
|
-
onComplete?: (data:
|
|
61
|
+
onComplete?: (data: VerificationState) => void,
|
|
55
62
|
onError?: (error: string) => void,
|
|
56
|
-
|
|
63
|
+
onCancel?: () => void,
|
|
64
|
+
initialLanguage: string = 'en',
|
|
65
|
+
apiKey?: string,
|
|
57
66
|
): UseTemplateReturn => {
|
|
67
|
+
|
|
68
|
+
const { setLocale } = useI18n();
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
setLocale(initialLanguage);
|
|
72
|
+
}, [initialLanguage]);
|
|
73
|
+
|
|
74
|
+
// Helpers to align SDK steps with backend actions/templates
|
|
75
|
+
const isUiOnlyStep = useCallback((type: TemplateComponent['type']) => {
|
|
76
|
+
return type === 'verification_progress';
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
const ensureReviewSubmitStep = useCallback((tpl: KYCTemplate): KYCTemplate => {
|
|
82
|
+
const hasReview = tpl.components.some(c => c.type === 'review_submit');
|
|
83
|
+
if (hasReview) return tpl;
|
|
84
|
+
|
|
85
|
+
const lastOrder = tpl.components.reduce((acc, c) => Math.max(acc, c.order ?? 0), 0);
|
|
86
|
+
const lastId = tpl.components.reduce((acc, c) => Math.max(acc, c.id), 0);
|
|
87
|
+
|
|
88
|
+
const reviewComponent: TemplateComponent = {
|
|
89
|
+
id: lastId + 1,
|
|
90
|
+
type: 'review_submit',
|
|
91
|
+
order: lastOrder + 1,
|
|
92
|
+
labels: { en: 'Review & Submit', fr: 'Revoir & Soumettre' },
|
|
93
|
+
instructions: { en: 'Confirm and submit', fr: 'Confirmer et soumettre' },
|
|
94
|
+
ui: { buttonText: { en: 'Complete Verification', fr: 'Terminer la vérification' } },
|
|
95
|
+
// @ts-ignore - config unused for review component
|
|
96
|
+
config: {},
|
|
97
|
+
} as TemplateComponent;
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
...tpl,
|
|
101
|
+
components: [...tpl.components, reviewComponent],
|
|
102
|
+
};
|
|
103
|
+
}, []);
|
|
104
|
+
|
|
105
|
+
const ensureVerificationProgressStep = useCallback((tpl: KYCTemplate): KYCTemplate => {
|
|
106
|
+
const hasVerification = tpl.components.some(c => c.type === 'verification_progress');
|
|
107
|
+
if (hasVerification) return tpl;
|
|
108
|
+
|
|
109
|
+
const lastOrder = tpl.components.reduce((acc, c) => Math.max(acc, c.order ?? 0), 0);
|
|
110
|
+
const lastId = tpl.components.reduce((acc, c) => Math.max(acc, c.id), 0);
|
|
111
|
+
|
|
112
|
+
const verificationComponent: TemplateComponent = {
|
|
113
|
+
id: lastId + 2,
|
|
114
|
+
type: 'verification_progress',
|
|
115
|
+
order: lastOrder + 2,
|
|
116
|
+
labels: { en: 'Verification', fr: 'Vérification' },
|
|
117
|
+
instructions: { en: 'We\'re reviewing your documents', fr: 'Nous analysons vos documents' },
|
|
118
|
+
ui: { buttonText: { en: '', fr: '' } },
|
|
119
|
+
// @ts-ignore - config unused for progress component
|
|
120
|
+
config: {},
|
|
121
|
+
} as TemplateComponent;
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
...tpl,
|
|
125
|
+
components: [...tpl.components, verificationComponent],
|
|
126
|
+
};
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
129
|
+
const templateWithReview = useMemo(() => ensureReviewSubmitStep(template), [template, ensureReviewSubmitStep]);
|
|
130
|
+
const templateWithReviewAndVerification = useMemo(() => ensureVerificationProgressStep(templateWithReview), [templateWithReview, ensureVerificationProgressStep]);
|
|
131
|
+
|
|
58
132
|
// État du flux
|
|
59
133
|
const [state, setState] = useState<TemplateState>({
|
|
60
|
-
template,
|
|
134
|
+
template: templateWithReviewAndVerification,
|
|
61
135
|
currentComponentIndex: 0,
|
|
62
136
|
completedComponents: [],
|
|
63
137
|
componentData: {},
|
|
@@ -65,121 +139,375 @@ export const useTemplateKYCFlow = (
|
|
|
65
139
|
isProcessing: false,
|
|
66
140
|
currentLanguage: initialLanguage,
|
|
67
141
|
showCustomStepper: true,
|
|
142
|
+
session: {
|
|
143
|
+
session_id: '',
|
|
144
|
+
token: '',
|
|
145
|
+
isInitialized: false,
|
|
146
|
+
isProcessing: false,
|
|
147
|
+
error: null,
|
|
148
|
+
},
|
|
149
|
+
verification: {
|
|
150
|
+
status: 'idle',
|
|
151
|
+
},
|
|
68
152
|
});
|
|
153
|
+
const mapComponentTypeToAction = useCallback((type: TemplateComponent['type']): string | null => {
|
|
154
|
+
switch (type) {
|
|
155
|
+
case 'id_card':
|
|
156
|
+
case 'file_upload':
|
|
157
|
+
return 'document_upload';
|
|
158
|
+
case 'selfie':
|
|
159
|
+
return 'selfie_capture';
|
|
160
|
+
case 'location':
|
|
161
|
+
return 'location_permission';
|
|
162
|
+
case 'review_submit':
|
|
163
|
+
return 'final_submit';
|
|
164
|
+
case 'country_selection':
|
|
165
|
+
// No direct backend action; pack into metadata of next actionable step
|
|
166
|
+
return null;
|
|
167
|
+
case 'initialization':
|
|
168
|
+
return 'initialize_session';
|
|
169
|
+
case 'verification_progress':
|
|
170
|
+
return null; // UI-only
|
|
171
|
+
default:
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}, []);
|
|
175
|
+
|
|
176
|
+
const chooseTemplateId = useCallback((tpl: KYCTemplate): string => {
|
|
177
|
+
const types = tpl.components.map(c => c.type);
|
|
178
|
+
const hasLocation = types.includes('location');
|
|
179
|
+
const hasSelfie = types.includes('selfie');
|
|
180
|
+
const hasIdDoc = types.includes('id_card') || types.includes('file_upload');
|
|
181
|
+
// Simple heuristic to map to backend examples
|
|
182
|
+
if (hasLocation && hasSelfie && hasIdDoc) return 'enhanced_id';
|
|
183
|
+
if (hasSelfie && hasIdDoc) return 'standard_passport';
|
|
184
|
+
return 'standard_passport';
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
const computeServerStepIndex = useCallback((tpl: KYCTemplate, upToIndex: number): number => {
|
|
189
|
+
// Count actionable steps before the current component (exclude UI-only and country_selection)
|
|
190
|
+
const actionable = tpl.components
|
|
191
|
+
.slice(0, upToIndex)
|
|
192
|
+
.filter(c => !isUiOnlyStep(c.type) && mapComponentTypeToAction(c.type) !== null);
|
|
193
|
+
return actionable.length; // 0-based
|
|
194
|
+
}, [isUiOnlyStep, mapComponentTypeToAction]);
|
|
195
|
+
|
|
196
|
+
// Build backend-friendly payloads per action
|
|
197
|
+
const buildPayloadForComponent = useCallback((action: string | null, component: TemplateComponent, rawData: any, templateId: string, step: number) => {
|
|
198
|
+
const base = { template_id: null, step: component.order, permissionGranted: true } as any;
|
|
199
|
+
if (!action) {
|
|
200
|
+
return base;
|
|
201
|
+
}
|
|
202
|
+
// Document upload expects an array of documents with base64 and metadata
|
|
203
|
+
if (action === 'document_upload') {
|
|
204
|
+
const documents: Record<string, any> = {};
|
|
205
|
+
if (rawData && typeof rawData === 'object') {
|
|
206
|
+
Object.keys(rawData).forEach((key) => {
|
|
207
|
+
documents[key] = rawData[key];
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
...base,
|
|
212
|
+
documents,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (action === 'selfie_capture') {
|
|
217
|
+
const documents: Record<string, any> = {};
|
|
218
|
+
if (rawData && typeof rawData === 'object') {
|
|
219
|
+
|
|
220
|
+
Object.keys(rawData).forEach((key) => {
|
|
221
|
+
documents[key] = rawData[key];
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
const idCardID = Object.keys(state.componentData).find((c: string) => c === "1");
|
|
225
|
+
if (idCardID) {
|
|
226
|
+
const _idCardData = state.componentData[idCardID];
|
|
227
|
+
return { ...base, documents, country: _idCardData?.country || '', documentType: _idCardData?.documentType as GovernmentDocumentType || 'identity_card' };
|
|
228
|
+
}
|
|
229
|
+
// return { ...base, documents };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (action === 'location_permission') {
|
|
233
|
+
return { ...base, ...({ metadata: rawData || {} }), ...({ permissionGranted: true, }) };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Default: wrap as metadata
|
|
237
|
+
return { ...base, metadata: { ...(rawData || {}) } };
|
|
238
|
+
}, [state.componentData]);
|
|
239
|
+
// Ensure the template contains a final review step
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
69
244
|
|
|
70
245
|
// Composant actuel
|
|
71
246
|
const currentComponent = useMemo(() => {
|
|
72
|
-
return template.components[state.currentComponentIndex] || null;
|
|
73
|
-
}, [template.components, state.currentComponentIndex]);
|
|
247
|
+
return state.template.components[state.currentComponentIndex] || null;
|
|
248
|
+
}, [state.template.components, state.currentComponentIndex]);
|
|
74
249
|
|
|
75
250
|
// Progression du flux
|
|
76
251
|
const progress = useMemo(() => {
|
|
77
|
-
return template.components.length > 0
|
|
78
|
-
? ((state.currentComponentIndex + 1) / template.components.length) * 100
|
|
252
|
+
return state.template.components.length > 0
|
|
253
|
+
? ((state.currentComponentIndex + 1) / state.template.components.length) * 100
|
|
79
254
|
: 0;
|
|
80
|
-
}, [state.currentComponentIndex, template.components.length]);
|
|
255
|
+
}, [state.currentComponentIndex, state.template.components.length]);
|
|
81
256
|
|
|
82
257
|
// Vérifications de navigation
|
|
83
258
|
const canGoNext = useMemo(() => {
|
|
84
|
-
return state.currentComponentIndex < template.components.length - 1;
|
|
85
|
-
}, [state.currentComponentIndex, template.components.length]);
|
|
259
|
+
return state.currentComponentIndex < state.template.components.length - 1;
|
|
260
|
+
}, [state.currentComponentIndex, state.template.components.length]);
|
|
86
261
|
|
|
87
262
|
const canGoPrevious = useMemo(() => {
|
|
88
263
|
return state.currentComponentIndex > 0;
|
|
89
264
|
}, [state.currentComponentIndex]);
|
|
90
265
|
|
|
91
266
|
const isComplete = useMemo(() => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
267
|
+
const atReview = state.template.components[state.currentComponentIndex]?.type === 'review_submit';
|
|
268
|
+
const nonReviewCount = state.template.components.filter(c => c.type !== 'review_submit').length;
|
|
269
|
+
const completedNonReview = state.completedComponents.length >= nonReviewCount;
|
|
270
|
+
return atReview && completedNonReview;
|
|
271
|
+
}, [state.currentComponentIndex, state.completedComponents.length, state.template.components]);
|
|
95
272
|
|
|
96
273
|
// Fonction pour obtenir le texte localisé
|
|
97
|
-
const getLocalizedText = useCallback((text: { en: string; fr: string;
|
|
274
|
+
const getLocalizedText = useCallback((text: { en: string; fr: string;[key: string]: string }): string => {
|
|
98
275
|
return text[state.currentLanguage] || text.en || '';
|
|
99
276
|
}, [state.currentLanguage]);
|
|
100
277
|
|
|
278
|
+
const initializeSession = useCallback(async () => {
|
|
279
|
+
try {
|
|
280
|
+
setState(prev => ({
|
|
281
|
+
...prev,
|
|
282
|
+
session: {
|
|
283
|
+
...prev.session,
|
|
284
|
+
isInitialized: false,
|
|
285
|
+
isProcessing: true,
|
|
286
|
+
error: null,
|
|
287
|
+
},
|
|
288
|
+
}));
|
|
289
|
+
console.log('Initializing session');
|
|
290
|
+
|
|
291
|
+
const token = await authentification();
|
|
292
|
+
|
|
293
|
+
const session = await kycService.newSession(token);
|
|
294
|
+
|
|
295
|
+
// Align backend flow from step 0 with initialize_session
|
|
296
|
+
try {
|
|
297
|
+
const templateId = chooseTemplateId(templateWithReviewAndVerification);
|
|
298
|
+
await kycService.verificationSession({
|
|
299
|
+
session_id: session.session_id,
|
|
300
|
+
step: 0,
|
|
301
|
+
data: { template_id: templateId, metadata: { language: initialLanguage } },
|
|
302
|
+
templateId: templateId,
|
|
303
|
+
token: token,
|
|
304
|
+
action: 'initialize_session'
|
|
305
|
+
});
|
|
306
|
+
} catch (e) {
|
|
307
|
+
console.error('Error initializing session:', JSON.stringify(e, null, 2));
|
|
308
|
+
// Non-fatal: we will surface errors via state below if needed
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
setState(prev => ({
|
|
312
|
+
...prev,
|
|
313
|
+
session: {
|
|
314
|
+
...prev.session,
|
|
315
|
+
session_id: session.session_id,
|
|
316
|
+
token: token,
|
|
317
|
+
isInitialized: true,
|
|
318
|
+
isProcessing: false,
|
|
319
|
+
error: null,
|
|
320
|
+
}
|
|
321
|
+
}));
|
|
322
|
+
} catch (error) {
|
|
323
|
+
|
|
324
|
+
console.error('Error initializing session:', JSON.stringify(error, null, 2));
|
|
325
|
+
setState(prev => ({
|
|
326
|
+
...prev,
|
|
327
|
+
session: {
|
|
328
|
+
...prev.session,
|
|
329
|
+
isInitialized: false,
|
|
330
|
+
isProcessing: false,
|
|
331
|
+
error: "Erreur lors de l'initialisation de la session",
|
|
332
|
+
},
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
}, []);
|
|
101
336
|
// Validation d'un composant
|
|
102
337
|
const validateComponent = useCallback((componentId: number): boolean => {
|
|
103
|
-
const component = template.components.find(c => c.id === componentId);
|
|
338
|
+
const component = state.template.components.find(c => c.id === componentId);
|
|
104
339
|
if (!component) return false;
|
|
105
340
|
|
|
106
341
|
const componentData = state.componentData[componentId];
|
|
107
|
-
|
|
342
|
+
|
|
108
343
|
switch (component.type) {
|
|
109
344
|
case 'id_card':
|
|
110
345
|
// Vérifier si au moins un côté a été capturé
|
|
111
346
|
return componentData && Object.keys(componentData).length > 0;
|
|
112
|
-
|
|
347
|
+
|
|
113
348
|
case 'selfie':
|
|
114
349
|
// Vérifier si toutes les orientations ont été capturées
|
|
115
350
|
const config = component.config as any;
|
|
116
351
|
const orientations = config.orientations || ['front'];
|
|
117
352
|
return componentData && orientations.every((orientation: string) => componentData[orientation]);
|
|
118
|
-
|
|
353
|
+
|
|
119
354
|
case 'file_upload':
|
|
120
355
|
// Vérifier si un fichier a été uploadé
|
|
121
356
|
return componentData && componentData.file;
|
|
122
|
-
|
|
357
|
+
|
|
123
358
|
case 'location':
|
|
124
359
|
// Vérifier si la localisation a été obtenue
|
|
125
360
|
return componentData && componentData.latitude && componentData.longitude;
|
|
126
|
-
|
|
361
|
+
|
|
127
362
|
case 'country_selection':
|
|
128
363
|
// Vérifier si un pays a été sélectionné
|
|
129
|
-
|
|
130
|
-
|
|
364
|
+
console.log("componentData", truncateFields(componentData), componentId);
|
|
365
|
+
|
|
366
|
+
return componentData && componentData.code && componentData.regionMapping;
|
|
367
|
+
|
|
368
|
+
case 'review_submit':
|
|
369
|
+
return true;
|
|
131
370
|
default:
|
|
132
371
|
return false;
|
|
133
372
|
}
|
|
134
|
-
}, [template.components, state.componentData]);
|
|
373
|
+
}, [state.template.components, state.componentData]);
|
|
135
374
|
|
|
136
375
|
// Actions du flux
|
|
137
376
|
const actions: TemplateActions = {
|
|
138
377
|
// Initialiser le template
|
|
139
378
|
initializeTemplate: useCallback((newTemplate: KYCTemplate) => {
|
|
379
|
+
const withReview = ensureReviewSubmitStep(newTemplate);
|
|
380
|
+
const withVerification = ensureVerificationProgressStep(withReview);
|
|
140
381
|
setState(prev => ({
|
|
141
382
|
...prev,
|
|
142
|
-
template:
|
|
383
|
+
template: withVerification,
|
|
143
384
|
currentComponentIndex: 0,
|
|
144
385
|
completedComponents: [],
|
|
145
386
|
componentData: {},
|
|
146
387
|
errors: {},
|
|
147
388
|
isProcessing: false,
|
|
389
|
+
verification: { status: 'idle', result: undefined },
|
|
148
390
|
}));
|
|
149
|
-
}, []),
|
|
391
|
+
}, [ensureReviewSubmitStep, ensureVerificationProgressStep]),
|
|
150
392
|
|
|
151
393
|
// Passer au composant suivant
|
|
152
|
-
nextComponent: useCallback(() => {
|
|
394
|
+
nextComponent: useCallback(async () => {
|
|
153
395
|
if (!canGoNext) return;
|
|
154
396
|
|
|
155
|
-
const currentComp = template.components[state.currentComponentIndex];
|
|
397
|
+
const currentComp = state.template.components[state.currentComponentIndex];
|
|
156
398
|
if (!currentComp) return;
|
|
157
399
|
|
|
400
|
+
setState(prev => ({
|
|
401
|
+
...prev,
|
|
402
|
+
isProcessing: true,
|
|
403
|
+
}));
|
|
158
404
|
// Valider le composant actuel
|
|
159
405
|
if (!validateComponent(currentComp.id)) {
|
|
160
406
|
setState(prev => ({
|
|
161
407
|
...prev,
|
|
408
|
+
isProcessing: false,
|
|
162
409
|
errors: {
|
|
163
410
|
...prev.errors,
|
|
164
|
-
[currentComp.id]: 'Veuillez compléter cette étape avant de continuer'
|
|
411
|
+
[currentComp.id]: state.currentLanguage === "en" ? "please complete this step before move on" : " 'Veuillez compléter cette étape avant de continuer'"
|
|
165
412
|
}
|
|
166
413
|
}));
|
|
167
414
|
return;
|
|
415
|
+
|
|
168
416
|
}
|
|
169
417
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
418
|
+
try {
|
|
419
|
+
const component = state.template.components.find(c => c.id === currentComp.id);
|
|
420
|
+
if (!component) return;
|
|
421
|
+
if (component.type === 'review_submit') {
|
|
422
|
+
// Move to verification screen and mark verification in progress
|
|
423
|
+
setState(prev => ({
|
|
424
|
+
...prev,
|
|
425
|
+
currentComponentIndex: prev.currentComponentIndex + 1,
|
|
426
|
+
completedComponents: [...prev.completedComponents, currentComp.id],
|
|
427
|
+
isProcessing: false,
|
|
428
|
+
verification: { status: 'in_progress' },
|
|
429
|
+
errors: {
|
|
430
|
+
...prev.errors,
|
|
431
|
+
[currentComp.id]: ''
|
|
432
|
+
}
|
|
433
|
+
}));
|
|
434
|
+
return;
|
|
178
435
|
}
|
|
179
|
-
|
|
436
|
+
// Determine backend action and step index
|
|
437
|
+
const action = mapComponentTypeToAction(component.type);
|
|
438
|
+
const templateId = chooseTemplateId(state.template);
|
|
439
|
+
const serverStep = computeServerStepIndex(state.template, state.currentComponentIndex);
|
|
440
|
+
|
|
441
|
+
// Optionally send initialize_session at step 0 before first actionable action
|
|
442
|
+
if (serverStep === 0) {
|
|
443
|
+
try {
|
|
444
|
+
// await kycService.verificationSession({
|
|
445
|
+
// sessionId: state.session.session_id,
|
|
446
|
+
// step: 0,
|
|
447
|
+
// data: { template_id: templateId, metadata: { language: state.currentLanguage } },
|
|
448
|
+
// templateId: templateId,
|
|
449
|
+
// token: state.session.token,
|
|
450
|
+
// action: 'initialize_session'
|
|
451
|
+
// });
|
|
452
|
+
} catch (e) {
|
|
453
|
+
// if init fails, surface error below in the main call handling
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Skip UI-only and data-only steps that have no backend action
|
|
458
|
+
if (!action) {
|
|
459
|
+
setState(prev => ({
|
|
460
|
+
...prev,
|
|
461
|
+
currentComponentIndex: prev.currentComponentIndex + 1,
|
|
462
|
+
completedComponents: [...prev.completedComponents, currentComp.id],
|
|
463
|
+
isProcessing: false,
|
|
464
|
+
errors: {
|
|
465
|
+
...prev.errors,
|
|
466
|
+
[currentComp.id]: ''
|
|
467
|
+
}
|
|
468
|
+
}));
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const step = serverStep === 0 && action !== 'initialize_session' ? 1 : serverStep;
|
|
473
|
+
// Build payload data per action
|
|
474
|
+
const payloadData = buildPayloadForComponent(action, component, state.componentData[currentComp.id], templateId, step);
|
|
475
|
+
|
|
476
|
+
await kycService.verificationSession({
|
|
477
|
+
session_id: state.session.session_id,
|
|
478
|
+
step: step,
|
|
479
|
+
data: payloadData,
|
|
480
|
+
templateId: null,
|
|
481
|
+
token: state.session.token,
|
|
482
|
+
action: action
|
|
483
|
+
});
|
|
484
|
+
console.log("currentComp state", truncateFields(state));
|
|
485
|
+
// Marquer comme complété et passer au suivant
|
|
486
|
+
setState(prev => ({
|
|
487
|
+
...prev,
|
|
488
|
+
currentComponentIndex: prev.currentComponentIndex + 1,
|
|
489
|
+
completedComponents: [...prev.completedComponents, currentComp.id],
|
|
490
|
+
isProcessing: false,
|
|
491
|
+
...(action === "location_permission" ? { permissionGranted: true } : {}),
|
|
492
|
+
errors: {
|
|
493
|
+
...prev.errors,
|
|
494
|
+
[currentComp.id]: ''
|
|
495
|
+
}
|
|
496
|
+
}));
|
|
180
497
|
|
|
498
|
+
} catch (error) {
|
|
499
|
+
// console.error('Error validating component:', error);
|
|
500
|
+
setState(prev => ({
|
|
501
|
+
...prev,
|
|
502
|
+
isProcessing: false,
|
|
503
|
+
errors: {
|
|
504
|
+
...prev.errors,
|
|
505
|
+
[currentComp.id]: 'Erreur lors de la validation du composant'
|
|
506
|
+
}
|
|
507
|
+
}));
|
|
508
|
+
}
|
|
181
509
|
|
|
182
|
-
}, [canGoNext, state.currentComponentIndex, template.components, validateComponent]),
|
|
510
|
+
}, [canGoNext, state.currentComponentIndex, state.template.components, validateComponent]),
|
|
183
511
|
|
|
184
512
|
// Retourner au composant précédent
|
|
185
513
|
previousComponent: useCallback(() => {
|
|
@@ -193,17 +521,19 @@ export const useTemplateKYCFlow = (
|
|
|
193
521
|
|
|
194
522
|
// Aller à un composant spécifique
|
|
195
523
|
goToComponent: useCallback((componentId: number) => {
|
|
196
|
-
const componentIndex = template.components.findIndex(c => c.id === componentId);
|
|
524
|
+
const componentIndex = state.template.components.findIndex(c => c.id === componentId);
|
|
197
525
|
if (componentIndex !== -1) {
|
|
198
526
|
setState(prev => ({
|
|
199
527
|
...prev,
|
|
200
528
|
currentComponentIndex: componentIndex,
|
|
201
529
|
}));
|
|
202
530
|
}
|
|
203
|
-
}, [template.components]),
|
|
531
|
+
}, [state.template.components]),
|
|
204
532
|
|
|
205
533
|
// Mettre à jour les données d'un composant
|
|
206
534
|
updateComponentData: useCallback((componentId: number, data: any) => {
|
|
535
|
+
console.log("updateComponentData", componentId, truncateFields(data));
|
|
536
|
+
|
|
207
537
|
setState(prev => ({
|
|
208
538
|
...prev,
|
|
209
539
|
componentData: {
|
|
@@ -215,17 +545,32 @@ export const useTemplateKYCFlow = (
|
|
|
215
545
|
[componentId]: ''
|
|
216
546
|
}
|
|
217
547
|
}));
|
|
218
|
-
|
|
548
|
+
|
|
219
549
|
}, []),
|
|
220
550
|
|
|
221
551
|
// Valider un composant
|
|
222
552
|
validateComponent: useCallback((componentId: number) => {
|
|
223
553
|
return validateComponent(componentId);
|
|
224
554
|
}, [validateComponent]),
|
|
555
|
+
// complet verification
|
|
556
|
+
submitVerification: useCallback(async () => {
|
|
557
|
+
setState(prev => ({ ...prev, isProcessing: true }));
|
|
558
|
+
try {
|
|
559
|
+
onComplete?.(state.verification);
|
|
225
560
|
|
|
561
|
+
} catch (error) {
|
|
562
|
+
setState(prev => ({ ...prev, isProcessing: false }));
|
|
563
|
+
}
|
|
564
|
+
}, [state.session.session_id, state.verification, onComplete]),
|
|
565
|
+
|
|
226
566
|
// Soumettre le template complet
|
|
227
567
|
submitTemplate: useCallback(async () => {
|
|
228
|
-
|
|
568
|
+
// Allow submission when on the review step and all previous steps are valid
|
|
569
|
+
const atReview = state.template.components[state.currentComponentIndex]?.type === 'review_submit';
|
|
570
|
+
const allValid = state.template.components
|
|
571
|
+
.filter(c => c.type !== 'review_submit')
|
|
572
|
+
.every(comp => validateComponent(comp.id));
|
|
573
|
+
if (!(atReview && allValid)) {
|
|
229
574
|
onError?.('Le flux KYC n\'est pas encore terminé');
|
|
230
575
|
return;
|
|
231
576
|
}
|
|
@@ -233,22 +578,18 @@ export const useTemplateKYCFlow = (
|
|
|
233
578
|
setState(prev => ({ ...prev, isProcessing: true }));
|
|
234
579
|
|
|
235
580
|
try {
|
|
236
|
-
// Vérifier que tous les composants sont validés
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (!allValid) {
|
|
240
|
-
throw new Error('Certaines étapes ne sont pas complètes');
|
|
241
|
-
}
|
|
581
|
+
// Vérifier que tous les composants (hors review) sont validés
|
|
582
|
+
if (!allValid) throw new Error('Certaines étapes ne sont pas complètes');
|
|
242
583
|
|
|
243
584
|
// Appeler le callback de completion
|
|
244
|
-
onComplete?.(state.componentData);
|
|
245
|
-
|
|
585
|
+
// onComplete?.(state.componentData);
|
|
586
|
+
|
|
246
587
|
setState(prev => ({ ...prev, isProcessing: false }));
|
|
247
588
|
} catch (error) {
|
|
248
589
|
setState(prev => ({ ...prev, isProcessing: false }));
|
|
249
590
|
onError?.(error instanceof Error ? error.message : 'Erreur lors de la soumission');
|
|
250
591
|
}
|
|
251
|
-
}, [
|
|
592
|
+
}, [state.template.components, state.verification, state.currentComponentIndex, validateComponent, state.componentData, onComplete, onError]),
|
|
252
593
|
|
|
253
594
|
// Réinitialiser le template
|
|
254
595
|
resetTemplate: useCallback(() => {
|
|
@@ -259,6 +600,7 @@ export const useTemplateKYCFlow = (
|
|
|
259
600
|
componentData: {},
|
|
260
601
|
errors: {},
|
|
261
602
|
isProcessing: false,
|
|
603
|
+
verification: { status: 'idle', result: undefined },
|
|
262
604
|
}));
|
|
263
605
|
}, []),
|
|
264
606
|
|
|
@@ -277,6 +619,13 @@ export const useTemplateKYCFlow = (
|
|
|
277
619
|
showCustomStepper: show,
|
|
278
620
|
}));
|
|
279
621
|
}, []),
|
|
622
|
+
|
|
623
|
+
setVerificationState: useCallback((verificationState) => {
|
|
624
|
+
setState(prev => ({
|
|
625
|
+
...prev,
|
|
626
|
+
verification: verificationState,
|
|
627
|
+
}));
|
|
628
|
+
}, []),
|
|
280
629
|
};
|
|
281
630
|
|
|
282
631
|
return {
|
|
@@ -288,5 +637,6 @@ export const useTemplateKYCFlow = (
|
|
|
288
637
|
canGoPrevious,
|
|
289
638
|
isComplete,
|
|
290
639
|
getLocalizedText,
|
|
640
|
+
initializeSession,
|
|
291
641
|
};
|
|
292
642
|
};
|