@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.
Files changed (216) hide show
  1. package/build/api/axios.d.ts +30 -0
  2. package/build/api/axios.d.ts.map +1 -0
  3. package/build/api/axios.js +92 -0
  4. package/build/api/axios.js.map +1 -0
  5. package/build/components/EnhancedCameraView.d.ts +1 -41
  6. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  7. package/build/components/EnhancedCameraView.js +75 -34
  8. package/build/components/EnhancedCameraView.js.map +1 -1
  9. package/build/components/EnhancedCameraView.web.d.ts +1 -41
  10. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  11. package/build/components/EnhancedCameraView.web.js +28 -4
  12. package/build/components/EnhancedCameraView.web.js.map +1 -1
  13. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +2 -2
  14. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  15. package/build/components/KYCElements/CountrySelectionTemplate.js +77 -114
  16. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  17. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  18. package/build/components/KYCElements/FileUploadTemplate.js +7 -3
  19. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  20. package/build/components/KYCElements/IDCardCapture.d.ts +7 -2
  21. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  22. package/build/components/KYCElements/IDCardCapture.js +253 -104
  23. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  24. package/build/components/KYCElements/InitializationStep.d.ts +5 -0
  25. package/build/components/KYCElements/InitializationStep.d.ts.map +1 -0
  26. package/build/components/KYCElements/InitializationStep.js +41 -0
  27. package/build/components/KYCElements/InitializationStep.js.map +1 -0
  28. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  29. package/build/components/KYCElements/LocationCaptureTemplate.js +15 -13
  30. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  31. package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -2
  32. package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
  33. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  34. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -2
  35. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
  36. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  37. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -2
  38. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
  39. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  40. package/build/components/KYCElements/ReviewSubmitTemplate.d.ts +12 -0
  41. package/build/components/KYCElements/ReviewSubmitTemplate.d.ts.map +1 -0
  42. package/build/components/KYCElements/ReviewSubmitTemplate.js +171 -0
  43. package/build/components/KYCElements/ReviewSubmitTemplate.js.map +1 -0
  44. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts +6 -2
  45. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  46. package/build/components/KYCElements/SelfieCaptureTemplate.js +137 -38
  47. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  48. package/build/components/KYCElements/VerificationProgressTemplate.d.ts +12 -0
  49. package/build/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -0
  50. package/build/components/KYCElements/VerificationProgressTemplate.js +194 -0
  51. package/build/components/KYCElements/VerificationProgressTemplate.js.map +1 -0
  52. package/build/components/OverLay/IdCard.d.ts +1 -1
  53. package/build/components/OverLay/IdCard.d.ts.map +1 -1
  54. package/build/components/OverLay/IdCard.js +10 -6
  55. package/build/components/OverLay/IdCard.js.map +1 -1
  56. package/build/components/OverLay/SelfieOverlay.d.ts +1 -1
  57. package/build/components/OverLay/SelfieOverlay.d.ts.map +1 -1
  58. package/build/components/OverLay/SelfieOverlay.js +5 -4
  59. package/build/components/OverLay/SelfieOverlay.js.map +1 -1
  60. package/build/components/OverLay/type.d.ts +71 -1
  61. package/build/components/OverLay/type.d.ts.map +1 -1
  62. package/build/components/OverLay/type.js.map +1 -1
  63. package/build/components/TemplateKYCExample.d.ts +4 -1
  64. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  65. package/build/components/TemplateKYCExample.js +74 -199
  66. package/build/components/TemplateKYCExample.js.map +1 -1
  67. package/build/components/TemplateKYCFlowRefactored.d.ts +3 -2
  68. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  69. package/build/components/TemplateKYCFlowRefactored.js +64 -40
  70. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  71. package/build/components/example/OrientationVideoExample.d.ts.map +1 -1
  72. package/build/components/example/OrientationVideoExample.js +1 -5
  73. package/build/components/example/OrientationVideoExample.js.map +1 -1
  74. package/build/config/countriesData.d.ts +3 -0
  75. package/build/config/countriesData.d.ts.map +1 -0
  76. package/build/config/countriesData.js +79 -0
  77. package/build/config/countriesData.js.map +1 -0
  78. package/build/config/region_mapping.d.ts +3 -0
  79. package/build/config/region_mapping.d.ts.map +1 -0
  80. package/build/config/region_mapping.js +687 -0
  81. package/build/config/region_mapping.js.map +1 -0
  82. package/build/hooks/useI18n.d.ts +11 -0
  83. package/build/hooks/useI18n.d.ts.map +1 -0
  84. package/build/hooks/useI18n.js +37 -0
  85. package/build/hooks/useI18n.js.map +1 -0
  86. package/build/hooks/useOrientationVideo.d.ts +1 -2
  87. package/build/hooks/useOrientationVideo.d.ts.map +1 -1
  88. package/build/hooks/useOrientationVideo.js +2 -1
  89. package/build/hooks/useOrientationVideo.js.map +1 -1
  90. package/build/hooks/useRealtimeVerifier.d.ts +28 -0
  91. package/build/hooks/useRealtimeVerifier.d.ts.map +1 -0
  92. package/build/hooks/useRealtimeVerifier.js +91 -0
  93. package/build/hooks/useRealtimeVerifier.js.map +1 -0
  94. package/build/hooks/useTemplateKYCFlow.d.ts +6 -3
  95. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  96. package/build/hooks/useTemplateKYCFlow.js +356 -42
  97. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  98. package/build/i18n/en/index.d.ts +168 -0
  99. package/build/i18n/en/index.d.ts.map +1 -0
  100. package/build/i18n/en/index.js +195 -0
  101. package/build/i18n/en/index.js.map +1 -0
  102. package/build/i18n/fr/index.d.ts +168 -0
  103. package/build/i18n/fr/index.d.ts.map +1 -0
  104. package/build/i18n/fr/index.js +194 -0
  105. package/build/i18n/fr/index.js.map +1 -0
  106. package/build/i18n/index.d.ts +10 -0
  107. package/build/i18n/index.d.ts.map +1 -0
  108. package/build/i18n/index.js +56 -0
  109. package/build/i18n/index.js.map +1 -0
  110. package/build/i18n/types.d.ts +153 -0
  111. package/build/i18n/types.d.ts.map +1 -0
  112. package/build/i18n/types.js +3 -0
  113. package/build/i18n/types.js.map +1 -0
  114. package/build/i18n/usage-example.d.ts +4 -0
  115. package/build/i18n/usage-example.d.ts.map +1 -0
  116. package/build/i18n/usage-example.js +189 -0
  117. package/build/i18n/usage-example.js.map +1 -0
  118. package/build/index.d.ts +1 -0
  119. package/build/index.d.ts.map +1 -1
  120. package/build/index.js +2 -0
  121. package/build/index.js.map +1 -1
  122. package/build/modules/api/CardAuthentification.d.ts +22 -0
  123. package/build/modules/api/CardAuthentification.d.ts.map +1 -0
  124. package/build/modules/api/CardAuthentification.js +107 -0
  125. package/build/modules/api/CardAuthentification.js.map +1 -0
  126. package/build/modules/api/KYCService.d.ts +58 -1
  127. package/build/modules/api/KYCService.d.ts.map +1 -1
  128. package/build/modules/api/KYCService.js +304 -27
  129. package/build/modules/api/KYCService.js.map +1 -1
  130. package/build/modules/api/SelfieVerification.d.ts +3 -0
  131. package/build/modules/api/SelfieVerification.d.ts.map +1 -0
  132. package/build/modules/api/SelfieVerification.js +9 -0
  133. package/build/modules/api/SelfieVerification.js.map +1 -0
  134. package/build/modules/api/backendApi.d.ts +2 -0
  135. package/build/modules/api/backendApi.d.ts.map +1 -0
  136. package/build/modules/api/backendApi.js +6 -0
  137. package/build/modules/api/backendApi.js.map +1 -0
  138. package/build/modules/api/types.d.ts +45 -0
  139. package/build/modules/api/types.d.ts.map +1 -0
  140. package/build/modules/api/types.js +2 -0
  141. package/build/modules/api/types.js.map +1 -0
  142. package/build/types/KYC.types.d.ts +56 -7
  143. package/build/types/KYC.types.d.ts.map +1 -1
  144. package/build/types/KYC.types.js +9 -1
  145. package/build/types/KYC.types.js.map +1 -1
  146. package/build/utils/cropByObb.d.ts +11 -0
  147. package/build/utils/cropByObb.d.ts.map +1 -0
  148. package/build/utils/cropByObb.js +78 -0
  149. package/build/utils/cropByObb.js.map +1 -0
  150. package/build/utils/get-document-type-info.d.ts +13 -0
  151. package/build/utils/get-document-type-info.d.ts.map +1 -0
  152. package/build/utils/get-document-type-info.js +59 -0
  153. package/build/utils/get-document-type-info.js.map +1 -0
  154. package/build/utils/pathToBase64.d.ts +3 -0
  155. package/build/utils/pathToBase64.d.ts.map +1 -0
  156. package/build/utils/pathToBase64.js +47 -0
  157. package/build/utils/pathToBase64.js.map +1 -0
  158. package/build/utils/remove-duplicate.d.ts +2 -0
  159. package/build/utils/remove-duplicate.d.ts.map +1 -0
  160. package/build/utils/remove-duplicate.js +4 -0
  161. package/build/utils/remove-duplicate.js.map +1 -0
  162. package/build/web/WebKYCEntry.d.ts +9 -0
  163. package/build/web/WebKYCEntry.d.ts.map +1 -0
  164. package/build/web/WebKYCEntry.js +156 -0
  165. package/build/web/WebKYCEntry.js.map +1 -0
  166. package/build/web/index.d.ts +2 -0
  167. package/build/web/index.d.ts.map +1 -0
  168. package/build/web/index.js +2 -0
  169. package/build/web/index.js.map +1 -0
  170. package/package.json +3 -1
  171. package/src/api/axios.ts +144 -0
  172. package/src/components/EnhancedCameraView.tsx +96 -78
  173. package/src/components/EnhancedCameraView.web.tsx +41 -40
  174. package/src/components/KYCElements/CountrySelectionTemplate.tsx +111 -136
  175. package/src/components/KYCElements/FileUploadTemplate.tsx +14 -8
  176. package/src/components/KYCElements/IDCardCapture.tsx +311 -115
  177. package/src/components/KYCElements/InitializationStep.tsx +53 -0
  178. package/src/components/KYCElements/LocationCaptureTemplate.tsx +17 -15
  179. package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
  180. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
  181. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
  182. package/src/components/KYCElements/ReviewSubmitTemplate.tsx +201 -0
  183. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +174 -57
  184. package/src/components/KYCElements/VerificationProgressTemplate.tsx +246 -0
  185. package/src/components/OverLay/IdCard.tsx +17 -9
  186. package/src/components/OverLay/SelfieOverlay.tsx +6 -5
  187. package/src/components/OverLay/type.ts +64 -2
  188. package/src/components/TemplateKYCExample.tsx +80 -200
  189. package/src/components/TemplateKYCFlowRefactored.tsx +80 -48
  190. package/src/components/example/OrientationVideoExample.tsx +3 -7
  191. package/src/config/countriesData.ts +84 -0
  192. package/src/config/region_mapping.ts +688 -0
  193. package/src/hooks/useI18n.ts +53 -0
  194. package/src/hooks/useOrientationVideo.ts +2 -2
  195. package/src/hooks/useRealtimeVerifier.ts +128 -0
  196. package/src/hooks/useTemplateKYCFlow.tsx +407 -57
  197. package/src/i18n/README.md +288 -0
  198. package/src/i18n/en/index.ts +206 -0
  199. package/src/i18n/fr/index.ts +205 -0
  200. package/src/i18n/index.ts +65 -0
  201. package/src/i18n/types.ts +172 -0
  202. package/src/i18n/usage-example.tsx +202 -0
  203. package/src/index.ts +3 -0
  204. package/src/modules/api/CardAuthentification.ts +114 -0
  205. package/src/modules/api/KYCService.ts +350 -30
  206. package/src/modules/api/SelfieVerification.ts +11 -0
  207. package/src/modules/api/backendApi.ts +8 -0
  208. package/src/modules/api/types.ts +51 -0
  209. package/src/types/KYC.types.ts +82 -14
  210. package/src/utils/cropByObb.ts +99 -0
  211. package/src/utils/get-document-type-info.ts +62 -0
  212. package/src/utils/pathToBase64.ts +47 -0
  213. package/src/utils/remove-duplicate.ts +3 -0
  214. package/src/web/WebKYCEntry.tsx +215 -0
  215. package/src/web/index.ts +1 -0
  216. 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; [key: string]: string }) => 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: Record<number, any>) => void;
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: Record<number, any>) => void,
61
+ onComplete?: (data: VerificationState) => void,
55
62
  onError?: (error: string) => void,
56
- initialLanguage: string = 'en'
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
- return state.currentComponentIndex === template.components.length - 1 &&
93
- state.completedComponents.length === template.components.length;
94
- }, [state.currentComponentIndex, state.completedComponents.length, template.components.length]);
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; [key: string]: string }): 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
- return componentData && componentData.country;
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: newTemplate,
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
- // Marquer comme complété et passer au suivant
171
- setState(prev => ({
172
- ...prev,
173
- currentComponentIndex: prev.currentComponentIndex + 1,
174
- completedComponents: [...prev.completedComponents, currentComp.id],
175
- errors: {
176
- ...prev.errors,
177
- [currentComp.id]: ''
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
- if (!isComplete) {
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
- const allValid = template.components.every(comp => validateComponent(comp.id));
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
- }, [isComplete, template.components, validateComponent, state.componentData, onComplete, onError]),
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
  };