@transfergratis/react-native-sdk 0.1.25 → 0.1.26

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 (88) hide show
  1. package/android/src/main/AndroidManifest.xml +12 -0
  2. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  3. package/build/components/EnhancedCameraView.web.js +76 -21
  4. package/build/components/EnhancedCameraView.web.js.map +1 -1
  5. package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -1
  6. package/build/components/KYCElements/EmailVerificationTemplate.js +48 -29
  7. package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -1
  8. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  9. package/build/components/KYCElements/IDCardCapture.js +40 -11
  10. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  11. package/build/components/KYCElements/WelcomeTemplate.js +2 -1
  12. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -1
  13. package/build/components/OverLay/type.d.ts +2 -0
  14. package/build/components/OverLay/type.d.ts.map +1 -1
  15. package/build/components/OverLay/type.js.map +1 -1
  16. package/build/components/TemplateKYCExample.d.ts +8 -2
  17. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  18. package/build/components/TemplateKYCExample.js +2 -2
  19. package/build/components/TemplateKYCExample.js.map +1 -1
  20. package/build/components/TemplateKYCFlowRefactored.d.ts +10 -2
  21. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  22. package/build/components/TemplateKYCFlowRefactored.js +13 -3
  23. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  24. package/build/config/KYCConfig.js +1 -1
  25. package/build/config/KYCConfig.js.map +1 -1
  26. package/build/hooks/useTemplateKYCFlow.d.ts +14 -2
  27. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  28. package/build/hooks/useTemplateKYCFlow.js +175 -84
  29. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  30. package/build/i18n/en/index.d.ts +2 -0
  31. package/build/i18n/en/index.d.ts.map +1 -1
  32. package/build/i18n/en/index.js +3 -1
  33. package/build/i18n/en/index.js.map +1 -1
  34. package/build/i18n/fr/index.d.ts +2 -0
  35. package/build/i18n/fr/index.d.ts.map +1 -1
  36. package/build/i18n/fr/index.js +3 -1
  37. package/build/i18n/fr/index.js.map +1 -1
  38. package/build/i18n/types.d.ts +2 -0
  39. package/build/i18n/types.d.ts.map +1 -1
  40. package/build/i18n/types.js.map +1 -1
  41. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  42. package/build/modules/api/CardAuthentification.js +22 -2
  43. package/build/modules/api/CardAuthentification.js.map +1 -1
  44. package/build/modules/api/KYCService.d.ts +10 -0
  45. package/build/modules/api/KYCService.d.ts.map +1 -1
  46. package/build/modules/api/KYCService.js +24 -0
  47. package/build/modules/api/KYCService.js.map +1 -1
  48. package/build/modules/camera/VisionCameraModule.web.d.ts.map +1 -1
  49. package/build/modules/camera/VisionCameraModule.web.js +27 -8
  50. package/build/modules/camera/VisionCameraModule.web.js.map +1 -1
  51. package/build/types/KYC.types.d.ts +6 -2
  52. package/build/types/KYC.types.d.ts.map +1 -1
  53. package/build/types/KYC.types.js.map +1 -1
  54. package/build/utils/cropByObb.d.ts +7 -0
  55. package/build/utils/cropByObb.d.ts.map +1 -1
  56. package/build/utils/cropByObb.js +20 -1
  57. package/build/utils/cropByObb.js.map +1 -1
  58. package/build/web/WebKYCEntry.d.ts.map +1 -1
  59. package/build/web/WebKYCEntry.js +11 -5
  60. package/build/web/WebKYCEntry.js.map +1 -1
  61. package/package.json +1 -1
  62. package/plugin/build/index.d.ts +1 -0
  63. package/plugin/build/index.js +3 -1
  64. package/plugin/build/withRemovePermissions.d.ts +3 -0
  65. package/plugin/build/withRemovePermissions.js +67 -0
  66. package/plugin/src/index.ts +2 -1
  67. package/plugin/src/withRemovePermissions.js +85 -0
  68. package/plugin/src/withRemovePermissions.ts +83 -0
  69. package/plugin/tsconfig.tsbuildinfo +1 -1
  70. package/plugin.js +6 -1
  71. package/src/components/EnhancedCameraView.web.tsx +76 -21
  72. package/src/components/KYCElements/EmailVerificationTemplate.tsx +47 -33
  73. package/src/components/KYCElements/IDCardCapture.tsx +41 -10
  74. package/src/components/KYCElements/WelcomeTemplate.tsx +2 -1
  75. package/src/components/OverLay/type.ts +2 -0
  76. package/src/components/TemplateKYCExample.tsx +9 -5
  77. package/src/components/TemplateKYCFlowRefactored.tsx +24 -6
  78. package/src/config/KYCConfig.ts +1 -1
  79. package/src/hooks/useTemplateKYCFlow.tsx +189 -95
  80. package/src/i18n/en/index.ts +3 -1
  81. package/src/i18n/fr/index.ts +3 -1
  82. package/src/i18n/types.ts +2 -0
  83. package/src/modules/api/CardAuthentification.ts +23 -2
  84. package/src/modules/api/KYCService.ts +41 -0
  85. package/src/modules/camera/VisionCameraModule.web.ts +30 -12
  86. package/src/types/KYC.types.ts +7 -3
  87. package/src/utils/cropByObb.ts +20 -1
  88. package/src/web/WebKYCEntry.tsx +17 -6
@@ -4,6 +4,7 @@ import { TemplateComponent, LocalizedText } from '../../types/KYC.types';
4
4
  import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
5
5
  import { useI18n } from '../../hooks/useI18n';
6
6
  import { Button } from '../ui/Button';
7
+ import kycService, { errorMessage } from '../../modules/api/KYCService';
7
8
 
8
9
  interface EmailVerificationTemplateProps {
9
10
  component: TemplateComponent;
@@ -15,14 +16,21 @@ interface EmailVerificationTemplateProps {
15
16
 
16
17
  type VerificationStep = 'email' | 'otp';
17
18
 
19
+ /** RFC-style email validation: local@domain.tld */
20
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
21
+
22
+ const isValidEmail = (value: string): boolean => EMAIL_REGEX.test((value || '').trim());
23
+
18
24
  export const EmailVerificationTemplate: React.FC<EmailVerificationTemplateProps> = ({
19
25
  component,
20
26
  value,
21
27
  onValueChange,
22
28
  error: propError,
23
29
  }) => {
24
- const { actions, getLocalizedText } = useTemplateKYCFlowContext();
30
+ const { actions, getLocalizedText, state, apiKey } = useTemplateKYCFlowContext();
25
31
  const { t } = useI18n();
32
+
33
+ const auth = apiKey ? { apiKey } : (state.session.token ? { token: state.session.token } : undefined);
26
34
  // const config = component.config as EmailVerificationConfig; // Keep for future use
27
35
 
28
36
  // State
@@ -40,8 +48,9 @@ export const EmailVerificationTemplate: React.FC<EmailVerificationTemplateProps>
40
48
  const sendButtonText = t('common.sendCode') || 'Send Verification Code';
41
49
  const buttonText = step === 'email' ? sendButtonText : verifyButtonText;
42
50
 
43
- const handleSendCode = () => {
44
- if (!email || !email.includes('@')) {
51
+ const handleSendCode = async () => {
52
+ const trimmed = email.trim();
53
+ if (!trimmed || !isValidEmail(trimmed)) {
45
54
  setLocalError(t('errors.invalidEmail') || 'Please enter a valid email address');
46
55
  return;
47
56
  }
@@ -49,16 +58,18 @@ export const EmailVerificationTemplate: React.FC<EmailVerificationTemplateProps>
49
58
  setLocalError(null);
50
59
  setIsSimulating(true);
51
60
 
52
- // Simulate API call to send code
53
- setTimeout(() => {
54
- setIsSimulating(false);
61
+ try {
62
+ await kycService.sendEmailVerificationCode(trimmed, auth);
55
63
  setStep('otp');
56
- // For demo purposes, we could show an alert or toast here with the code
57
- // But we'll just expect them to know 123456 or type anything for now if not strict
58
- }, 1500);
64
+ } catch (err: any) {
65
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send verification code');
66
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
67
+ } finally {
68
+ setIsSimulating(false);
69
+ }
59
70
  };
60
71
 
61
- const handleVerifyCode = () => {
72
+ const handleVerifyCode = async () => {
62
73
  if (!otp || otp.length < 4) {
63
74
  setLocalError(t('errors.invalidCode') || 'Please enter the 6-digit code');
64
75
  return;
@@ -67,24 +78,17 @@ export const EmailVerificationTemplate: React.FC<EmailVerificationTemplateProps>
67
78
  setLocalError(null);
68
79
  setIsSimulating(true);
69
80
 
70
- // Simulate verification API
71
- setTimeout(() => {
81
+ try {
82
+ await kycService.verifyEmailCode(otp.trim(), auth);
83
+ const data = { email, otp, verified: true };
84
+ onValueChange(data);
85
+ actions.nextComponent(data);
86
+ } catch (err: any) {
87
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.wrongCode') || 'Invalid verification code');
88
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
89
+ } finally {
72
90
  setIsSimulating(false);
73
-
74
- // Mock validation logic
75
- // Let's accept '123456' as the correct code or any code for testing if strictly requested?
76
- // User said "verify with error message" implying we should support failure.
77
- // Let's say if code is "000000" it fails, otherwise success, OR hardcode a success one.
78
- // User said "verify with error message and next component if is a good one"
79
- // Let's make "123456" the good one for clarity.
80
-
81
- if (otp === '123456') {
82
- onValueChange({ email, otp, verified: true });
83
- actions.nextComponent();
84
- } else {
85
- setLocalError(t('errors.wrongCode') || 'Invalid verification code. Try 123456');
86
- }
87
- }, 1500);
91
+ }
88
92
  };
89
93
 
90
94
  const onChangeEmail = (text: string) => {
@@ -160,12 +164,22 @@ export const EmailVerificationTemplate: React.FC<EmailVerificationTemplateProps>
160
164
 
161
165
  {step === 'otp' && (
162
166
  <TouchableOpacity
163
- onPress={() => {
164
- // Resend logic
165
- Alert.alert(
166
- t('common.codeResent') || 'Code Resent',
167
- t('common.codeResentMessage', { email }) || 'Code resent to ' + email
168
- );
167
+ onPress={async () => {
168
+ if (isSimulating) return;
169
+ setLocalError(null);
170
+ setIsSimulating(true);
171
+ try {
172
+ await kycService.sendEmailVerificationCode(email.trim(), auth);
173
+ Alert.alert(
174
+ t('common.codeResent') || 'Code Resent',
175
+ t('common.codeResentMessage', { email }) || 'Code resent to ' + email
176
+ );
177
+ } catch (err: any) {
178
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send code');
179
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
180
+ } finally {
181
+ setIsSimulating(false);
182
+ }
169
183
  }}
170
184
  style={styles.resendButton}
171
185
  disabled={isSimulating}
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
- import { View, Text, StyleSheet, Image, ScrollView, Platform, Modal, TouchableOpacity } from 'react-native';
2
+ import { View, Text, StyleSheet, Image, ScrollView, Platform, Modal, TouchableOpacity, ActivityIndicator } from 'react-native';
3
3
  import { showAlert } from '../../utils/platformAlert';
4
4
  import { EnhancedCameraView } from '../EnhancedCameraView';
5
5
  import { TemplateComponent, LocalizedText, GovernmentDocumentType, ISilentCaptureResult, IBbox, GovernmentDocumentTypeShorted, GovernmentDocumentTypeBackend } from '../../types/KYC.types';
@@ -12,7 +12,7 @@ import { backVerification, checkTemplateType, frontVerification } from '../../mo
12
12
  import { getDocumentTypeInfo } from '../../utils/get-document-type-info';
13
13
  import pathToBase64 from '../../utils/pathToBase64';
14
14
  import kycService, { truncateFields } from '../../modules/api/KYCService';
15
- import { cropByObb, cropImageWithBBox, cropImageWithBBoxWithTolerance } from '../../utils/cropByObb';
15
+ import { cropByObb, cropImageWithBBox, cropImageWithBBoxWithTolerance, getObbConfidence, OBB_CONFIDENCE_THRESHOLD } from '../../utils/cropByObb';
16
16
  import { isMobileWeb } from '../../utils/deviceDetection';
17
17
  import { logger } from '../../utils/logger';
18
18
 
@@ -252,6 +252,16 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
252
252
  setSilentCaptureResult((prev) => ({ ...prev, templatePath: templatePath }));
253
253
  }
254
254
  if (templateType.card_obb) {
255
+ const obbConfidence = getObbConfidence((templateType as any).card_obb);
256
+ if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
257
+ setSilentCaptureResult((prev) => ({
258
+ ...prev,
259
+ isAnalyzing: false,
260
+ success: false,
261
+ error: t('kyc.idCardCapture.cardNotFullyInFrame'),
262
+ }));
263
+ return;
264
+ }
255
265
  let bbox: IBbox | undefined;
256
266
  try {
257
267
  const crop = await cropByObb(result?.path || '', (templateType as any).card_obb);
@@ -314,8 +324,9 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
314
324
  }).catch((e: any) => {
315
325
  console.log("error front verification", e);
316
326
  logger.log("error front verification", truncateFields(e));
317
- setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
318
- // showAlert('Erreur', e?.message || 'Erreur de détection du MRZ');
327
+ const isCardNotFullyInFrame = e?.message?.includes('entirement') || e?.message?.includes('fully in frame');
328
+ const errorMessage = isCardNotFullyInFrame ? t('kyc.idCardCapture.cardNotFullyInFrame') : (e?.message || 'Erreur de détection du MRZ');
329
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
319
330
  });
320
331
  } catch (error: any) {
321
332
  console.log("Error setting up frontVerification call", error);
@@ -351,8 +362,9 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
351
362
  }
352
363
  }).catch((e: any) => {
353
364
  logger.log("error back verification", truncateFields(e));
354
- setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
355
- // showAlert('Erreur', e?.message || 'Erreur de détection du MRZ');
365
+ const isCardNotFullyInFrame = e?.message?.includes('entirement') || e?.message?.includes('fully in frame');
366
+ const errorMessage = isCardNotFullyInFrame ? t('kyc.idCardCapture.cardNotFullyInFrame') : (e?.message || 'Erreur de détection du MRZ');
367
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
356
368
  })
357
369
  }
358
370
 
@@ -452,9 +464,11 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
452
464
  if (!currentUrl.searchParams.has('kyc_id') && state.session.session_id) {
453
465
  currentUrl.searchParams.set('kyc_id', state.session.session_id);
454
466
  }
455
- // Ajouter l'étape actuelle pour permettre à l'utilisateur de continuer au bon endroit
456
- if (!currentUrl.searchParams.has('step')) {
457
- currentUrl.searchParams.set('step', String(state.currentComponentIndex));
467
+ currentUrl.searchParams.set('component_index', String(state.currentComponentIndex));
468
+ if (countrySelectionData?.code) {
469
+ currentUrl.searchParams.set('country', countrySelectionData.code);
470
+ if (countrySelectionData.documentType) currentUrl.searchParams.set('document_type', countrySelectionData.documentType);
471
+ if (countrySelectionData.region) currentUrl.searchParams.set('region', countrySelectionData.region);
458
472
  }
459
473
  return `https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=${encodeURIComponent(currentUrl.toString())}`;
460
474
  } catch (error) {
@@ -469,6 +483,22 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
469
483
 
470
484
 
471
485
 
486
+ // En reprise sur un autre appareil: afficher un chargement tant que les données de session ne sont pas restaurées
487
+ const isResumingSession = Boolean(state.session.session_id && state.currentComponentIndex > 0);
488
+ const sessionDataRestored = state.session.sessionDataRestored !== false;
489
+ if (isResumingSession && !sessionDataRestored && (!countrySelectionData || !selectedDocumentType)) {
490
+ return (
491
+ <View style={styles.root}>
492
+ <View style={[styles.container, { justifyContent: 'center', alignItems: 'center' }]}>
493
+ <ActivityIndicator size="large" color="#2DBD60" />
494
+ <Text style={[styles.description, { marginTop: 16 }]}>
495
+ {state.currentLanguage === 'en' ? 'Loading your session...' : 'Chargement de votre session...'}
496
+ </Text>
497
+ </View>
498
+ </View>
499
+ );
500
+ }
501
+
472
502
  // Vérifier si les données sont disponibles, sinon afficher un message d'erreur
473
503
  if (!countrySelectionData || !selectedDocumentType) {
474
504
  return (
@@ -506,6 +536,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
506
536
  showSwitchCamera={true}
507
537
  onSilentCapture={handleSilentCapture}
508
538
  silentCaptureResult={silentCaptureResult}
539
+ captureStabilizationDelayMs={3000}
509
540
  enableFlash={cameraConfig.flashMode === 'auto' || cameraConfig.flashMode === 'on'}
510
541
  overlayComponent={<IdCardOverlay
511
542
  xMin={cameraConfig.overlay.bbox.xMin}
@@ -752,7 +783,7 @@ const styles = StyleSheet.create({
752
783
  height: '100%',
753
784
  },
754
785
  previewContainer: {
755
- width: '100%',
786
+ width: '95%',
756
787
  backgroundColor: 'white',
757
788
  margin: 10,
758
789
  borderRadius: 10,
@@ -150,7 +150,7 @@ export const WelcomeTemplate: React.FC<WelcomeTemplateProps> = ({
150
150
 
151
151
  {/* Get Started Button */}
152
152
  <Button
153
- title={buttonText}
153
+ title={buttonText?.length === 0 ? t('kyc.welcome.getStarted') || 'Get Started' : buttonText}
154
154
  fullWidth
155
155
  onPress={handleGetStarted}
156
156
  style={{ paddingVertical: 20, marginTop: 36 }}
@@ -175,6 +175,7 @@ const styles = StyleSheet.create({
175
175
  shadowRadius: 1.84,
176
176
  elevation: 3,
177
177
  maxWidth: 760,
178
+ width: '94%',
178
179
  },
179
180
  title: {
180
181
  fontSize: 24,
@@ -81,6 +81,8 @@ export interface EnhancedCameraViewProps {
81
81
  onVideoRecordingStart?: () => void;
82
82
  onVideoRecordingStop?: (result: { success: boolean; path?: string; error?: string }) => void;
83
83
  videoDuration?: number;
84
+ /** Delay in ms before first auto-capture (lets camera focus and user position document). Recommended 2500–3000 for ID/document capture. */
85
+ captureStabilizationDelayMs?: number;
84
86
  }
85
87
 
86
88
  export interface CheckTemplateTypeResponse {
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { View, SafeAreaView } from 'react-native';
3
3
  import { TemplateKYCFlow } from './TemplateKYCFlowRefactored';
4
4
  import { KYCTemplate, VerificationState } from '../types/KYC.types';
5
- import { KycEnvironment } from '../types/env.types';
5
+ import { KycEnvironment, BackendEnvironment } from '../types/env.types';
6
6
 
7
7
 
8
8
 
@@ -144,10 +144,12 @@ export const TemplateKYCExample: React.FC<{
144
144
  API_KEY?: string;
145
145
  templateId?: string;
146
146
  template?: KYCTemplate;
147
- env?: KycEnvironment;
147
+ env?: KycEnvironment; // Flow execution: PRODUCTION (full AI) or SANDBOX (skip AI)
148
+ serverEnv?: BackendEnvironment; // Backend to call: PRODUCTION or TEST (API URL)
148
149
  existingSessionId?: string;
149
- initialStep?: number;
150
- }> = ({ onComplete, onCancel, onError, language, API_KEY, templateId, template, env = 'PRODUCTION', existingSessionId, initialStep }) => {
150
+ initialComponentIndex?: number;
151
+ initialCountryResume?: { code: string; documentType: string; region?: string };
152
+ }> = ({ onComplete, onCancel, onError, language, API_KEY, templateId, template, env = 'PRODUCTION', serverEnv, existingSessionId, initialComponentIndex, initialCountryResume }) => {
151
153
  const handleComplete = (data: VerificationState) => {
152
154
  console.log('KYC Template completed with data:', data);
153
155
  onComplete(data);
@@ -180,8 +182,10 @@ export const TemplateKYCExample: React.FC<{
180
182
  language={language} // ou "en" pour l'anglais
181
183
  API_KEY={API_KEY}
182
184
  env={env}
185
+ serverEnv={serverEnv}
183
186
  existingSessionId={existingSessionId}
184
- initialStep={initialStep}
187
+ initialComponentIndex={initialComponentIndex}
188
+ initialCountryResume={initialCountryResume}
185
189
  />
186
190
  </View>
187
191
  </SafeAreaView>
@@ -18,7 +18,8 @@ import { EmailVerificationTemplate } from './KYCElements/EmailVerificationTempla
18
18
  import { PhoneVerificationTemplate } from './KYCElements/PhoneVerificationTemplate';
19
19
  import { PersonalInformationTemplate } from './KYCElements/PersonalInformationTemplate';
20
20
  import { AdditionalDocumentsTemplate } from './KYCElements/AdditionalDocumentsTemplate';
21
- import { KycEnvironment } from '../types/env.types';
21
+ import { KycEnvironment, BackendEnvironment } from '../types/env.types';
22
+ import KYCConfig from '../config/KYCConfig';
22
23
 
23
24
  interface TemplateKYCFlowProps {
24
25
  template?: KYCTemplate; // Format SDK direct (existing, now optional)
@@ -28,9 +29,13 @@ interface TemplateKYCFlowProps {
28
29
  language?: string;
29
30
  onCancel?: () => void;
30
31
  API_KEY?: string; // Required if templateId is used
31
- env?: KycEnvironment; // Environment mode: PRODUCTION or SANDBOX
32
+ env?: KycEnvironment; // Flow execution: PRODUCTION (full AI) or SANDBOX (skip AI)
33
+ serverEnv?: BackendEnvironment; // Backend to call: PRODUCTION or TEST (API URL)
32
34
  existingSessionId?: string;
33
- initialStep?: number; // Initial step index to resume verification
35
+ /** Index in template.components (0-based) to resume at — e.g. from URL component_index or template table */
36
+ initialComponentIndex?: number;
37
+ /** Pays / type de document depuis l'URL de reprise (reprise multi-appareil) */
38
+ initialCountryResume?: { code: string; documentType: string; region?: string };
34
39
  }
35
40
 
36
41
  export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
@@ -42,12 +47,21 @@ export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
42
47
  onCancel,
43
48
  API_KEY,
44
49
  env = 'PRODUCTION',
50
+ serverEnv,
45
51
  existingSessionId,
46
- initialStep,
52
+ initialComponentIndex,
53
+ initialCountryResume,
47
54
  }) => {
48
55
  const { t } = useI18n();
49
56
  const { template: loadedTemplate, isLoading, error, loadTemplate } = useTemplateLoader();
50
57
 
58
+ // Which backend URL to call (independent from flow env)
59
+ useEffect(() => {
60
+ if (serverEnv !== undefined) {
61
+ KYCConfig.setBackendEnvironment(serverEnv);
62
+ }
63
+ }, [serverEnv]);
64
+
51
65
  // Validate props
52
66
  useEffect(() => {
53
67
  if (!providedTemplate && !templateId) {
@@ -136,7 +150,8 @@ export const TemplateKYCFlow: React.FC<TemplateKYCFlowProps> = ({
136
150
  apiKey={API_KEY}
137
151
  env={env}
138
152
  existingSessionId={existingSessionId}
139
- initialStep={initialStep}
153
+ initialComponentIndex={initialComponentIndex}
154
+ initialCountryResume={initialCountryResume}
140
155
  >
141
156
  <TemplateKYCFlowContent onCancel={OnCancel} />
142
157
  </TemplateKYCFlowProvider>
@@ -241,7 +256,10 @@ const TemplateKYCFlowContent: React.FC<{ onCancel?: () => void }> = ({ onCancel
241
256
  {(state.showCustomStepper && state.session.isInitialized) ? (
242
257
  <View style={styles.header}>
243
258
  <Text style={styles.progressText}>
244
- {t('kyc.step', { current: state.currentComponentIndex + 1, total: state.template.components.length })}
259
+ {t('kyc.step', {
260
+ current: (state.template.components.findIndex(c => c.id === currentComponent.id) + 1) || (state.currentComponentIndex + 1),
261
+ total: state.template.components.length,
262
+ })}
245
263
  </Text>
246
264
  <View style={styles.progressContainer}>
247
265
  <View style={styles.progressBar}>
@@ -6,7 +6,7 @@ class KYCConfig {
6
6
 
7
7
  private backendUrls: Record<BackendEnvironment, string> = {
8
8
  PRODUCTION: 'https://service.sanctumkey.com/api/v1',
9
- TEST: 'https://test-service.sanctumkey.com/api/v1', // Placeholder URL
9
+ TEST: 'https://kyc-backend.transfergratis.net/api/v1', // Placeholder URL
10
10
  };
11
11
 
12
12
  private constructor() { }