@transfergratis/react-native-sdk 0.1.5 → 0.1.7
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/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +7 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +32 -3
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/VerificationProgressTemplate.js +106 -5
- package/build/components/KYCElements/VerificationProgressTemplate.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 +4 -4
- 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 +2 -2
- package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/hooks/useTemplateKYCFlow.d.ts +5 -3
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +89 -74
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- 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/KYCService.d.ts +2 -1
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +17 -61
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/types.d.ts +25 -0
- package/build/modules/api/types.d.ts.map +1 -1
- package/build/modules/api/types.js.map +1 -1
- package/build/types/KYC.types.d.ts +2 -5
- package/build/types/KYC.types.d.ts.map +1 -1
- package/build/types/KYC.types.js.map +1 -1
- package/build/utils/cropByObb.js +4 -4
- package/build/utils/cropByObb.js.map +1 -1
- 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 +4 -4
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +8 -1
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +37 -7
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +129 -6
- package/src/components/TemplateKYCExample.tsx +6 -5
- package/src/components/TemplateKYCFlowRefactored.tsx +6 -2
- package/src/hooks/useTemplateKYCFlow.tsx +105 -77
- package/src/index.ts +3 -0
- package/src/modules/api/KYCService.ts +21 -74
- package/src/modules/api/types.ts +30 -3
- package/src/types/KYC.types.ts +4 -5
- package/src/utils/cropByObb.ts +5 -5
- package/src/web/WebKYCEntry.tsx +215 -0
- package/src/web/index.ts +1 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
|
3
3
|
import { TemplateComponent } from '../../types/KYC.types';
|
|
4
4
|
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
5
5
|
import { useI18n } from '../../hooks/useI18n';
|
|
6
|
+
import kycService from '../../modules/api/KYCService';
|
|
6
7
|
|
|
7
8
|
interface VerificationProgressTemplateProps {
|
|
8
9
|
component: TemplateComponent;
|
|
@@ -15,18 +16,124 @@ interface VerificationProgressTemplateProps {
|
|
|
15
16
|
export const VerificationProgressTemplate: React.FC<VerificationProgressTemplateProps> = () => {
|
|
16
17
|
const { t } = useI18n();
|
|
17
18
|
const { state, actions } = useTemplateKYCFlowContext();
|
|
19
|
+
const [retryCount, setRetryCount] = useState(0);
|
|
20
|
+
const [isRetrying, setIsRetrying] = useState(false);
|
|
21
|
+
const [timeoutId, setTimeoutId] = useState<number | null>(null);
|
|
18
22
|
|
|
19
23
|
const verification = state.verification;
|
|
20
24
|
|
|
21
25
|
const isSuccess = verification.status === 'success';
|
|
22
26
|
const isFailed = verification.status === 'failed';
|
|
23
27
|
|
|
28
|
+
// Fonction pour récupérer le résultat de vérification avec retry
|
|
29
|
+
const getVerificationResult = async (currentRetryCount = 0, maxRetries = 40) => {
|
|
30
|
+
try {
|
|
31
|
+
console.log(`Starting verification check (attempt ${currentRetryCount + 1}/${maxRetries + 1})`);
|
|
32
|
+
|
|
33
|
+
const result = await kycService.getVerificationResult(state.session.session_id);
|
|
34
|
+
const verificationResult = result[state.session.session_id].data;
|
|
35
|
+
|
|
36
|
+
console.log('getVerificationResult result', JSON.stringify(result, null, 2));
|
|
37
|
+
|
|
38
|
+
// Vérifier le statut de vérification
|
|
39
|
+
if (verificationResult.verification_status === null || verificationResult.verification_status === "processing" || verificationResult.verification_status === "pending") {
|
|
40
|
+
if (currentRetryCount < maxRetries) {
|
|
41
|
+
const nextRetryCount = currentRetryCount + 1;
|
|
42
|
+
setRetryCount(nextRetryCount);
|
|
43
|
+
setIsRetrying(true);
|
|
44
|
+
|
|
45
|
+
console.log(`Verification still processing, retrying in 10s... (${nextRetryCount}/${maxRetries})`);
|
|
46
|
+
|
|
47
|
+
// Nettoyer le timeout précédent s'il existe
|
|
48
|
+
if (timeoutId) {
|
|
49
|
+
clearTimeout(timeoutId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Attendre 10 secondes avant de relancer
|
|
53
|
+
const newTimeoutId = setTimeout(() => {
|
|
54
|
+
getVerificationResult(nextRetryCount, maxRetries);
|
|
55
|
+
}, 10000);
|
|
56
|
+
|
|
57
|
+
setTimeoutId(newTimeoutId);
|
|
58
|
+
return; // Sortir de la fonction pour éviter de traiter le résultat
|
|
59
|
+
} else {
|
|
60
|
+
console.error('Max retries reached, verification still processing');
|
|
61
|
+
setIsRetrying(false);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Traiter le résultat final (success, failed, etc.)
|
|
67
|
+
if (verificationResult.verification_status === "completed" ||
|
|
68
|
+
verificationResult.verification_status === "approved") {
|
|
69
|
+
// Mettre à jour l'état avec le succès
|
|
70
|
+
actions.setVerificationState({
|
|
71
|
+
status: 'success',
|
|
72
|
+
result: result
|
|
73
|
+
});
|
|
74
|
+
} else if (verificationResult.verification_status === "failed" ||
|
|
75
|
+
verificationResult.verification_status === "rejected") {
|
|
76
|
+
// Mettre à jour l'état avec l'échec
|
|
77
|
+
actions.setVerificationState({
|
|
78
|
+
status: 'failed',
|
|
79
|
+
result: {
|
|
80
|
+
referenceId: verificationResult.verification_id,
|
|
81
|
+
issues: ['Verification failed']
|
|
82
|
+
} as any
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setIsRetrying(false);
|
|
87
|
+
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error('getVerificationResult error', error);
|
|
90
|
+
|
|
91
|
+
// En cas d'erreur, on peut aussi retry
|
|
92
|
+
if (currentRetryCount < maxRetries) {
|
|
93
|
+
const nextRetryCount = currentRetryCount + 1;
|
|
94
|
+
setRetryCount(nextRetryCount);
|
|
95
|
+
setIsRetrying(true);
|
|
96
|
+
|
|
97
|
+
console.log(`Error occurred, retrying in 10s... (${nextRetryCount}/${maxRetries})`);
|
|
98
|
+
|
|
99
|
+
// Nettoyer le timeout précédent s'il existe
|
|
100
|
+
if (timeoutId) {
|
|
101
|
+
clearTimeout(timeoutId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const newTimeoutId = setTimeout(() => {
|
|
105
|
+
getVerificationResult(nextRetryCount, maxRetries);
|
|
106
|
+
}, 10000);
|
|
107
|
+
|
|
108
|
+
setTimeoutId(newTimeoutId);
|
|
109
|
+
} else {
|
|
110
|
+
setIsRetrying(false);
|
|
111
|
+
console.error('Max retries reached after errors');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (state.session.session_id) {
|
|
118
|
+
getVerificationResult();
|
|
119
|
+
}
|
|
120
|
+
}, [state.session.session_id]);
|
|
121
|
+
|
|
122
|
+
// Nettoyage des timeouts lors du démontage du composant
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
return () => {
|
|
125
|
+
if (timeoutId) {
|
|
126
|
+
clearTimeout(timeoutId);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}, [timeoutId]);
|
|
130
|
+
|
|
24
131
|
if (isSuccess) {
|
|
25
132
|
return (
|
|
26
133
|
<View style={styles.container}>
|
|
27
134
|
<Text style={styles.title}>{t('kyc.verificationProgress.steps.complete')}</Text>
|
|
28
135
|
<Text style={styles.subtitle}>{t('kyc.verificationProgress.subtitle')}</Text>
|
|
29
|
-
<TouchableOpacity style={[styles.button, styles.primary]} onPress={() => actions.
|
|
136
|
+
<TouchableOpacity style={[styles.button, styles.primary]} onPress={() => actions.submitVerification()}>
|
|
30
137
|
<Text style={styles.buttonText}>{t('common.continue')}</Text>
|
|
31
138
|
</TouchableOpacity>
|
|
32
139
|
{verification.result?.referenceId ? (
|
|
@@ -43,11 +150,11 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
43
150
|
<Text style={styles.subtitle}>{t('errors.validationError')}</Text>
|
|
44
151
|
<View style={styles.issuesBox}>
|
|
45
152
|
<Text style={styles.issuesTitle}>{t('common.error')}</Text>
|
|
46
|
-
{(verification.result?.issues
|
|
153
|
+
{Array.isArray(verification.result?.issues) && verification.result.issues.map((issue: string, idx: number) => (
|
|
47
154
|
<Text key={idx} style={styles.issueItem}>• {issue}</Text>
|
|
48
155
|
))}
|
|
49
156
|
</View>
|
|
50
|
-
<TouchableOpacity style={[styles.button, styles.primary]} onPress={() => actions.goToComponent(
|
|
157
|
+
<TouchableOpacity style={[styles.button, styles.primary]} onPress={() => actions.goToComponent(state.template.components.find(c => c.type !== 'review_submit' && c.type !== 'verification_progress')?.id || 0)}>
|
|
51
158
|
<Text style={styles.buttonText}>{t('common.retry')}</Text>
|
|
52
159
|
</TouchableOpacity>
|
|
53
160
|
<TouchableOpacity style={[styles.button, styles.secondary]}>
|
|
@@ -60,10 +167,21 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
60
167
|
);
|
|
61
168
|
}
|
|
62
169
|
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
63
176
|
return (
|
|
64
177
|
<View style={styles.container}>
|
|
65
178
|
<Text style={styles.title}>{t('kyc.verificationProgress.title')}</Text>
|
|
66
|
-
<Text style={styles.subtitle}>
|
|
179
|
+
<Text style={styles.subtitle}>
|
|
180
|
+
{isRetrying
|
|
181
|
+
? `${t('kyc.verificationProgress.retrying')} (${retryCount}/10)`
|
|
182
|
+
: t('kyc.verificationProgress.estimatedTime')
|
|
183
|
+
}
|
|
184
|
+
</Text>
|
|
67
185
|
|
|
68
186
|
<View style={styles.timeline}>
|
|
69
187
|
<View style={[styles.dot, styles.done]}><Text style={styles.stepCheckmark}>✓</Text></View>
|
|
@@ -85,7 +203,12 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
85
203
|
<Text style={styles.cardTitle}>{t('kyc.selfieCapture.title')}</Text>
|
|
86
204
|
<Text style={styles.cardLine}>{t('kyc.verificationProgress.steps.analyzing')}</Text>
|
|
87
205
|
<Text style={styles.cardTitle}>{t('kyc.verificationProgress.title')}</Text>
|
|
88
|
-
<Text style={styles.cardLine}>
|
|
206
|
+
<Text style={styles.cardLine}>
|
|
207
|
+
{isRetrying
|
|
208
|
+
? `${t('kyc.verificationProgress.status.processing')}...`
|
|
209
|
+
: t('kyc.verificationProgress.status.pending')
|
|
210
|
+
}
|
|
211
|
+
</Text>
|
|
89
212
|
</View>
|
|
90
213
|
</View>
|
|
91
214
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { View, StyleSheet } from 'react-native';
|
|
3
3
|
import { TemplateKYCFlow } from './TemplateKYCFlowRefactored';
|
|
4
|
-
import { KYCTemplate } from '../types/KYC.types';
|
|
4
|
+
import { KYCTemplate, VerificationState } from '../types/KYC.types';
|
|
5
5
|
|
|
6
6
|
// Template JSON basé sur votre exemple
|
|
7
7
|
const advancedVerificationTemplate: KYCTemplate = {
|
|
@@ -56,7 +56,7 @@ const advancedVerificationTemplate: KYCTemplate = {
|
|
|
56
56
|
buttonText: { en: "Confirm", fr: "Confirmer" }
|
|
57
57
|
},
|
|
58
58
|
config: {
|
|
59
|
-
allowed_countries: ["CA", "FR", "CM", "US", "DE", "
|
|
59
|
+
allowed_countries: ["CA", "FR", "CM", "US", "DE", "SN", "NG", "MA", "DZ","CI", "KE","GH"],
|
|
60
60
|
default_country: "CA",
|
|
61
61
|
required: true
|
|
62
62
|
} as const
|
|
@@ -224,8 +224,8 @@ const advancedVerificationTemplate: KYCTemplate = {
|
|
|
224
224
|
]
|
|
225
225
|
};
|
|
226
226
|
|
|
227
|
-
export const TemplateKYCExample: React.FC<{ onComplete: (data:
|
|
228
|
-
const handleComplete = (data:
|
|
227
|
+
export const TemplateKYCExample: React.FC<{ onComplete: (data: VerificationState) => void, onCancel: () => void, onError: (error: string) => void, language: string, API_KEY: string }> = ({ onComplete, onCancel, onError, language, API_KEY }) => {
|
|
228
|
+
const handleComplete = (data: VerificationState) => {
|
|
229
229
|
console.log('KYC Template completed with data:', data);
|
|
230
230
|
onComplete(data);
|
|
231
231
|
// Ici vous pouvez envoyer les données à votre API
|
|
@@ -248,7 +248,8 @@ export const TemplateKYCExample: React.FC<{ onComplete: (data: Record<number, an
|
|
|
248
248
|
onComplete={handleComplete}
|
|
249
249
|
onError={handleError}
|
|
250
250
|
onCancel={handleCancel}
|
|
251
|
-
language=
|
|
251
|
+
language={language} // ou "en" pour l'anglais
|
|
252
|
+
API_KEY={API_KEY}
|
|
252
253
|
/>
|
|
253
254
|
</View>
|
|
254
255
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, SafeAreaView, TouchableOpacity, ActivityIndicator, Dimensions } from 'react-native';
|
|
3
|
-
import { KYCTemplate } from '../types/KYC.types';
|
|
3
|
+
import { KYCTemplate, VerificationState } from '../types/KYC.types';
|
|
4
4
|
import { TemplateKYCFlowProvider, useTemplateKYCFlowContext } from '../hooks/useTemplateKYCFlow';
|
|
5
5
|
import { useI18n } from '../hooks/useI18n';
|
|
6
6
|
import { IDCardCapture } from './KYCElements/IDCardCapture';
|
|
@@ -14,10 +14,11 @@ import { VerificationProgressTemplate } from './KYCElements/VerificationProgress
|
|
|
14
14
|
|
|
15
15
|
interface TemplateKYCFlowProps {
|
|
16
16
|
template: KYCTemplate;
|
|
17
|
-
onComplete?: (data:
|
|
17
|
+
onComplete?: (data: VerificationState) => void;
|
|
18
18
|
onError?: (error: string) => void;
|
|
19
19
|
language?: string;
|
|
20
20
|
onCancel?: () => void;
|
|
21
|
+
API_KEY?: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
|
|
@@ -26,13 +27,16 @@ export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
|
|
|
26
27
|
onError,
|
|
27
28
|
language = 'fr',
|
|
28
29
|
onCancel,
|
|
30
|
+
API_KEY,
|
|
29
31
|
}) => {
|
|
30
32
|
return (
|
|
31
33
|
<TemplateKYCFlowProvider
|
|
32
34
|
template={template}
|
|
33
35
|
onComplete={onComplete}
|
|
34
36
|
onError={onError}
|
|
37
|
+
onCancel={onCancel}
|
|
35
38
|
initialLanguage={language}
|
|
39
|
+
apiKey={API_KEY}
|
|
36
40
|
>
|
|
37
41
|
<TemplateKYCFlowContent onCancel={onCancel} />
|
|
38
42
|
</TemplateKYCFlowProvider>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useCallback, useMemo, createContext, useContext, ReactNode, useEffect } from 'react';
|
|
2
|
-
import { KYCTemplate, TemplateState, TemplateActions, UseTemplateReturn, TemplateComponent } from '../types/KYC.types';
|
|
2
|
+
import { KYCTemplate, TemplateState, TemplateActions, UseTemplateReturn, TemplateComponent, GovernmentDocumentType, VerificationState } from '../types/KYC.types';
|
|
3
3
|
import kycService, { authentification, truncateFields } from '../modules/api/KYCService';
|
|
4
4
|
import useI18n from './useI18n';
|
|
5
5
|
|
|
@@ -22,9 +22,11 @@ const TemplateKYCFlowContext = createContext<TemplateKYCFlowContextType | undefi
|
|
|
22
22
|
interface TemplateKYCFlowProviderProps {
|
|
23
23
|
children: ReactNode;
|
|
24
24
|
template: KYCTemplate;
|
|
25
|
-
onComplete?: (data:
|
|
25
|
+
onComplete?: (data: VerificationState) => void;
|
|
26
26
|
onError?: (error: string) => void;
|
|
27
|
+
onCancel?: () => void;
|
|
27
28
|
initialLanguage?: string;
|
|
29
|
+
apiKey?: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export const TemplateKYCFlowProvider: React.FC<TemplateKYCFlowProviderProps> = ({
|
|
@@ -32,9 +34,11 @@ export const TemplateKYCFlowProvider: React.FC<TemplateKYCFlowProviderProps> = (
|
|
|
32
34
|
template,
|
|
33
35
|
onComplete,
|
|
34
36
|
onError,
|
|
37
|
+
onCancel,
|
|
35
38
|
initialLanguage = 'en',
|
|
39
|
+
apiKey,
|
|
36
40
|
}) => {
|
|
37
|
-
const hookResult = useTemplateKYCFlow(template, onComplete, onError, initialLanguage);
|
|
41
|
+
const hookResult = useTemplateKYCFlow(template, onComplete, onError, onCancel, initialLanguage, apiKey);
|
|
38
42
|
|
|
39
43
|
return (
|
|
40
44
|
<TemplateKYCFlowContext.Provider value={hookResult}>
|
|
@@ -54,9 +58,11 @@ export const useTemplateKYCFlowContext = (): TemplateKYCFlowContextType => {
|
|
|
54
58
|
|
|
55
59
|
export const useTemplateKYCFlow = (
|
|
56
60
|
template: KYCTemplate,
|
|
57
|
-
onComplete?: (data:
|
|
61
|
+
onComplete?: (data: VerificationState) => void,
|
|
58
62
|
onError?: (error: string) => void,
|
|
63
|
+
onCancel?: () => void,
|
|
59
64
|
initialLanguage: string = 'en',
|
|
65
|
+
apiKey?: string,
|
|
60
66
|
): UseTemplateReturn => {
|
|
61
67
|
|
|
62
68
|
const { setLocale } = useI18n();
|
|
@@ -70,6 +76,80 @@ export const useTemplateKYCFlow = (
|
|
|
70
76
|
return type === 'verification_progress';
|
|
71
77
|
}, []);
|
|
72
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
|
+
|
|
132
|
+
// État du flux
|
|
133
|
+
const [state, setState] = useState<TemplateState>({
|
|
134
|
+
template: templateWithReviewAndVerification,
|
|
135
|
+
currentComponentIndex: 0,
|
|
136
|
+
completedComponents: [],
|
|
137
|
+
componentData: {},
|
|
138
|
+
errors: {},
|
|
139
|
+
isProcessing: false,
|
|
140
|
+
currentLanguage: initialLanguage,
|
|
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
|
+
},
|
|
152
|
+
});
|
|
73
153
|
const mapComponentTypeToAction = useCallback((type: TemplateComponent['type']): string | null => {
|
|
74
154
|
switch (type) {
|
|
75
155
|
case 'id_card':
|
|
@@ -134,14 +214,19 @@ export const useTemplateKYCFlow = (
|
|
|
134
214
|
}
|
|
135
215
|
|
|
136
216
|
if (action === 'selfie_capture') {
|
|
137
|
-
const
|
|
217
|
+
const documents: Record<string, any> = {};
|
|
138
218
|
if (rawData && typeof rawData === 'object') {
|
|
139
219
|
|
|
140
220
|
Object.keys(rawData).forEach((key) => {
|
|
141
|
-
|
|
221
|
+
documents[key] = rawData[key];
|
|
142
222
|
});
|
|
143
223
|
}
|
|
144
|
-
|
|
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 };
|
|
145
230
|
}
|
|
146
231
|
|
|
147
232
|
if (action === 'location_permission') {
|
|
@@ -150,79 +235,12 @@ export const useTemplateKYCFlow = (
|
|
|
150
235
|
|
|
151
236
|
// Default: wrap as metadata
|
|
152
237
|
return { ...base, metadata: { ...(rawData || {}) } };
|
|
153
|
-
}, []);
|
|
238
|
+
}, [state.componentData]);
|
|
154
239
|
// Ensure the template contains a final review step
|
|
155
|
-
const ensureReviewSubmitStep = useCallback((tpl: KYCTemplate): KYCTemplate => {
|
|
156
|
-
const hasReview = tpl.components.some(c => c.type === 'review_submit');
|
|
157
|
-
if (hasReview) return tpl;
|
|
158
240
|
|
|
159
|
-
const lastOrder = tpl.components.reduce((acc, c) => Math.max(acc, c.order ?? 0), 0);
|
|
160
|
-
const lastId = tpl.components.reduce((acc, c) => Math.max(acc, c.id), 0);
|
|
161
|
-
|
|
162
|
-
const reviewComponent: TemplateComponent = {
|
|
163
|
-
id: lastId + 1,
|
|
164
|
-
type: 'review_submit',
|
|
165
|
-
order: lastOrder + 1,
|
|
166
|
-
labels: { en: 'Review & Submit', fr: 'Revoir & Soumettre' },
|
|
167
|
-
instructions: { en: 'Confirm and submit', fr: 'Confirmer et soumettre' },
|
|
168
|
-
ui: { buttonText: { en: 'Complete Verification', fr: 'Terminer la vérification' } },
|
|
169
|
-
// @ts-ignore - config unused for review component
|
|
170
|
-
config: {},
|
|
171
|
-
} as TemplateComponent;
|
|
172
241
|
|
|
173
|
-
return {
|
|
174
|
-
...tpl,
|
|
175
|
-
components: [...tpl.components, reviewComponent],
|
|
176
|
-
};
|
|
177
|
-
}, []);
|
|
178
242
|
|
|
179
|
-
const ensureVerificationProgressStep = useCallback((tpl: KYCTemplate): KYCTemplate => {
|
|
180
|
-
const hasVerification = tpl.components.some(c => c.type === 'verification_progress');
|
|
181
|
-
if (hasVerification) return tpl;
|
|
182
243
|
|
|
183
|
-
const lastOrder = tpl.components.reduce((acc, c) => Math.max(acc, c.order ?? 0), 0);
|
|
184
|
-
const lastId = tpl.components.reduce((acc, c) => Math.max(acc, c.id), 0);
|
|
185
|
-
|
|
186
|
-
const verificationComponent: TemplateComponent = {
|
|
187
|
-
id: lastId + 2,
|
|
188
|
-
type: 'verification_progress',
|
|
189
|
-
order: lastOrder + 2,
|
|
190
|
-
labels: { en: 'Verification', fr: 'Vérification' },
|
|
191
|
-
instructions: { en: 'We\'re reviewing your documents', fr: 'Nous analysons vos documents' },
|
|
192
|
-
ui: { buttonText: { en: '', fr: '' } },
|
|
193
|
-
// @ts-ignore - config unused for progress component
|
|
194
|
-
config: {},
|
|
195
|
-
} as TemplateComponent;
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
...tpl,
|
|
199
|
-
components: [...tpl.components, verificationComponent],
|
|
200
|
-
};
|
|
201
|
-
}, []);
|
|
202
|
-
|
|
203
|
-
const templateWithReview = useMemo(() => ensureReviewSubmitStep(template), [template, ensureReviewSubmitStep]);
|
|
204
|
-
const templateWithReviewAndVerification = useMemo(() => ensureVerificationProgressStep(templateWithReview), [templateWithReview, ensureVerificationProgressStep]);
|
|
205
|
-
// État du flux
|
|
206
|
-
const [state, setState] = useState<TemplateState>({
|
|
207
|
-
template: templateWithReviewAndVerification,
|
|
208
|
-
currentComponentIndex: 0,
|
|
209
|
-
completedComponents: [],
|
|
210
|
-
componentData: {},
|
|
211
|
-
errors: {},
|
|
212
|
-
isProcessing: false,
|
|
213
|
-
currentLanguage: initialLanguage,
|
|
214
|
-
showCustomStepper: true,
|
|
215
|
-
session: {
|
|
216
|
-
session_id: '',
|
|
217
|
-
token: '',
|
|
218
|
-
isInitialized: false,
|
|
219
|
-
isProcessing: false,
|
|
220
|
-
error: null,
|
|
221
|
-
},
|
|
222
|
-
verification: {
|
|
223
|
-
status: 'idle',
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
244
|
|
|
227
245
|
// Composant actuel
|
|
228
246
|
const currentComponent = useMemo(() => {
|
|
@@ -534,7 +552,17 @@ export const useTemplateKYCFlow = (
|
|
|
534
552
|
validateComponent: useCallback((componentId: number) => {
|
|
535
553
|
return validateComponent(componentId);
|
|
536
554
|
}, [validateComponent]),
|
|
555
|
+
// complet verification
|
|
556
|
+
submitVerification: useCallback(async () => {
|
|
557
|
+
setState(prev => ({ ...prev, isProcessing: true }));
|
|
558
|
+
try {
|
|
559
|
+
onComplete?.(state.verification);
|
|
537
560
|
|
|
561
|
+
} catch (error) {
|
|
562
|
+
setState(prev => ({ ...prev, isProcessing: false }));
|
|
563
|
+
}
|
|
564
|
+
}, [state.session.session_id, state.verification, onComplete]),
|
|
565
|
+
|
|
538
566
|
// Soumettre le template complet
|
|
539
567
|
submitTemplate: useCallback(async () => {
|
|
540
568
|
// Allow submission when on the review step and all previous steps are valid
|
|
@@ -554,14 +582,14 @@ export const useTemplateKYCFlow = (
|
|
|
554
582
|
if (!allValid) throw new Error('Certaines étapes ne sont pas complètes');
|
|
555
583
|
|
|
556
584
|
// Appeler le callback de completion
|
|
557
|
-
onComplete?.(state.componentData);
|
|
585
|
+
// onComplete?.(state.componentData);
|
|
558
586
|
|
|
559
587
|
setState(prev => ({ ...prev, isProcessing: false }));
|
|
560
588
|
} catch (error) {
|
|
561
589
|
setState(prev => ({ ...prev, isProcessing: false }));
|
|
562
590
|
onError?.(error instanceof Error ? error.message : 'Erreur lors de la soumission');
|
|
563
591
|
}
|
|
564
|
-
}, [state.template.components, state.currentComponentIndex, validateComponent, state.componentData, onComplete, onError]),
|
|
592
|
+
}, [state.template.components, state.verification, state.currentComponentIndex, validateComponent, state.componentData, onComplete, onError]),
|
|
565
593
|
|
|
566
594
|
// Réinitialiser le template
|
|
567
595
|
resetTemplate: useCallback(() => {
|
package/src/index.ts
CHANGED
|
@@ -42,6 +42,9 @@ export { FileUploadTemplate } from './components/KYCElements/FileUploadTemplate'
|
|
|
42
42
|
export { LocationCaptureTemplate } from './components/KYCElements/LocationCaptureTemplate';
|
|
43
43
|
export { CountrySelectionTemplate } from './components/KYCElements/CountrySelectionTemplate';
|
|
44
44
|
|
|
45
|
+
// Export Web KYC Components
|
|
46
|
+
export { WebKYCEntry } from './web';
|
|
47
|
+
|
|
45
48
|
// Export KYC Service
|
|
46
49
|
export { default as KYCService } from './modules/api/KYCService';
|
|
47
50
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import { GovernmentDocumentType, GovernmentDocumentTypeShorted, OrientationVideoResponse } from '../../types/KYC.types';
|
|
3
3
|
import { ExtractMrzTextResponse } from '../../components/OverLay/type';
|
|
4
|
-
import { SessionResponse, VerificationSessionRequest } from './types';
|
|
4
|
+
import { SessionResponse, VerificationResult, VerificationSessionRequest } from './types';
|
|
5
5
|
|
|
6
6
|
export interface KYCRequest {
|
|
7
7
|
userId: string;
|
|
@@ -42,12 +42,12 @@ export class KYCService {
|
|
|
42
42
|
private baseURL: string;
|
|
43
43
|
private apiKey: string;
|
|
44
44
|
// Additional service base URLs (fixed as per current infra)
|
|
45
|
-
private faceServiceURL = '
|
|
46
|
-
private textExtractionServiceURL = '
|
|
47
|
-
private mrzServiceURL = '
|
|
48
|
-
private barcodeServiceURL = '
|
|
45
|
+
private faceServiceURL = 'https://kyc-engine.transfergratis.net:8000';
|
|
46
|
+
private textExtractionServiceURL = 'https://kyc-engine.transfergratis.net:8006';
|
|
47
|
+
private mrzServiceURL = 'https://kyc-engine.transfergratis.net:8002';
|
|
48
|
+
private barcodeServiceURL = 'https://kyc-engine.transfergratis.net:8000';
|
|
49
49
|
private orientationServiceURL = 'http://18.188.180.154:8080';
|
|
50
|
-
private backendServiceURL = '
|
|
50
|
+
private backendServiceURL = 'https://kyc-backend.transfergratis.net/api/v1';
|
|
51
51
|
|
|
52
52
|
constructor(baseURL: string, apiKey: string) {
|
|
53
53
|
this.baseURL = baseURL;
|
|
@@ -387,77 +387,15 @@ export class KYCService {
|
|
|
387
387
|
...({ session_id: session_id }),
|
|
388
388
|
timestamp: new Date().toISOString()
|
|
389
389
|
}
|
|
390
|
-
|
|
390
|
+
const url = `${this.backendServiceURL}/verification/api/kyc/sessions/${session_id}/steps/${step}/`;
|
|
391
391
|
|
|
392
392
|
|
|
393
393
|
const logPayload = truncateFields({ payloadData, session_id, step });
|
|
394
394
|
console.log('verificationSession payload',
|
|
395
|
-
JSON.stringify({ logPayload, token, path:
|
|
396
|
-
null, 2))
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// if (action === "document_upload") {
|
|
400
|
-
// // Upload un document à la fois pour éviter les timeouts
|
|
401
|
-
// const documents = data.documents;
|
|
402
|
-
// const sides = Object.keys(documents);
|
|
403
|
-
// const results = [];
|
|
404
|
-
|
|
405
|
-
// for (const side of sides) {
|
|
406
|
-
// console.log(`📤 Uploading ${side} document...`);
|
|
407
|
-
// const startTime = Date.now();
|
|
408
|
-
|
|
409
|
-
// const formData = new FormData();
|
|
410
|
-
// formData.append('action', 'document_upload');
|
|
411
|
-
// formData.append('session_id', session_id);
|
|
412
|
-
// formData.append('timestamp', new Date().toISOString());
|
|
413
|
-
// formData.append('side', side);
|
|
414
|
-
|
|
415
|
-
// const fileUri = documents[side].dir;
|
|
416
|
-
// const rnFile: any = {
|
|
417
|
-
// uri: fileUri,
|
|
418
|
-
// type: 'image/jpeg',
|
|
419
|
-
// name: `id_card_${side}.jpg`
|
|
420
|
-
// };
|
|
421
|
-
// formData.append('front_image', rnFile);
|
|
422
|
-
|
|
423
|
-
// // Ajouter MRZ si disponible
|
|
424
|
-
// if (documents[side].mrz) {
|
|
425
|
-
// formData.append('mrz', documents[side].mrz);
|
|
426
|
-
// }
|
|
427
|
-
|
|
428
|
-
// try {
|
|
429
|
-
// const res = await axios.post<SessionResponse>(
|
|
430
|
-
// `${this.backendServiceURL}/verification/api/kyc/sessions/${session_id}/steps/${step}/`,
|
|
431
|
-
// formData,
|
|
432
|
-
// {
|
|
433
|
-
// headers: {
|
|
434
|
-
// 'Authorization': `Bearer ${token}`
|
|
435
|
-
// // Pas de Content-Type, laissez axios le gérer
|
|
436
|
-
// },
|
|
437
|
-
// timeout: 30000, // 30 secondes max
|
|
438
|
-
// maxContentLength: 50 * 1024 * 1024, // 50MB max
|
|
439
|
-
// maxBodyLength: 50 * 1024 * 1024
|
|
440
|
-
// }
|
|
441
|
-
// );
|
|
442
|
-
|
|
443
|
-
// const uploadTime = Date.now() - startTime;
|
|
444
|
-
// console.log(`✅ ${side} uploaded in ${uploadTime}ms:`, res.data);
|
|
445
|
-
// results.push({ side, result: res.data, uploadTime });
|
|
446
|
-
|
|
447
|
-
// } catch (error) {
|
|
448
|
-
// console.error(`❌ ${side} upload failed:`, error);
|
|
449
|
-
// throw new Error(`Upload failed for ${side}: ${error}`);
|
|
450
|
-
// }
|
|
451
|
-
// }
|
|
452
|
-
|
|
453
|
-
// return {
|
|
454
|
-
// success: true,
|
|
455
|
-
// message: 'All documents uploaded successfully',
|
|
456
|
-
// results
|
|
457
|
-
// };
|
|
458
|
-
// }
|
|
459
|
-
|
|
460
|
-
const res = await axios.post<SessionResponse>(`${this.backendServiceURL}/verification/api/kyc/sessions/${session_id}/steps/${step}/`,
|
|
395
|
+
JSON.stringify({ logPayload, token, path: url },
|
|
396
|
+
null, 2))
|
|
397
|
+
|
|
398
|
+
const res = await axios.post<SessionResponse>(url,
|
|
461
399
|
payloadData,
|
|
462
400
|
{
|
|
463
401
|
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }
|
|
@@ -471,6 +409,15 @@ export class KYCService {
|
|
|
471
409
|
throw new Error(errorMessage(error));
|
|
472
410
|
}
|
|
473
411
|
}
|
|
412
|
+
// reultat de la verification
|
|
413
|
+
async getVerificationResult(session_id: string): Promise<VerificationResult> {
|
|
414
|
+
const token = await authentification();
|
|
415
|
+
const url = `${this.backendServiceURL}/verification/api/kyc/result/?session_id=${session_id}`;
|
|
416
|
+
const res = await axios.get<VerificationResult>(url,
|
|
417
|
+
{ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } });
|
|
418
|
+
console.log('getVerificationResult res', JSON.stringify(res.data, null, 2));
|
|
419
|
+
return res.data;
|
|
420
|
+
}
|
|
474
421
|
}
|
|
475
422
|
|
|
476
423
|
|
|
@@ -502,7 +449,7 @@ export const authentification = async () => {
|
|
|
502
449
|
params.append('client_id', 'kyc_frontend');
|
|
503
450
|
params.append('client_secret', 'ZCW4mGaJXWR0UuI40lM1pHNQmYLw2xvX');
|
|
504
451
|
params.append('grant_type', 'client_credentials');
|
|
505
|
-
const res = await axios.post(`
|
|
452
|
+
const res = await axios.post(`https://keycloak.transfergratis.net/realms/kyc/protocol/openid-connect/token`,
|
|
506
453
|
params,
|
|
507
454
|
{
|
|
508
455
|
headers: {
|