@test-web/react-native-sdk 1.0.1 → 2.1.0

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 (125) hide show
  1. package/README.md +543 -26
  2. package/assets/images/Chrome-logo.svg +1 -0
  3. package/assets/images/Firefox-logo.png +0 -0
  4. package/assets/images/IDM-logo.jpg +0 -0
  5. package/assets/images/MRZOverlay.png +0 -0
  6. package/assets/images/Safari-logo.png +0 -0
  7. package/assets/images/aadhar.png +0 -0
  8. package/assets/images/camera-bg.png +0 -0
  9. package/assets/images/card-overlay-back.png +0 -0
  10. package/assets/images/card-overlay.png +0 -0
  11. package/assets/images/card-scan-back-icon.jpg +0 -0
  12. package/assets/images/card-scan-front-icon.png +0 -0
  13. package/assets/images/card-scan-icon-aadhaar-1.png +0 -0
  14. package/assets/images/card-scan-icon-aadhaar-back.png +0 -0
  15. package/assets/images/card-scan-icon-aadhaar-scan-qr.png +0 -0
  16. package/assets/images/card-scan-icon-aadhaar.png +0 -0
  17. package/assets/images/card-scan-icon-can-pr.png +0 -0
  18. package/assets/images/card-scan-icon-default-back.png +0 -0
  19. package/assets/images/card-scan-icon-dl.png +0 -0
  20. package/assets/images/card-scan-icon-greencard-back.jpg +0 -0
  21. package/assets/images/card-scan-icon-greencard.jpg +0 -0
  22. package/assets/images/card-scan-icon-hc.png +0 -0
  23. package/assets/images/card-scan-icon-ni-argentina-back.jpg +0 -0
  24. package/assets/images/card-scan-icon-ni-argentina-old.png +0 -0
  25. package/assets/images/card-scan-icon-ni-argentina.jpg +0 -0
  26. package/assets/images/card-scan-icon-ni-barcode.jpg +0 -0
  27. package/assets/images/card-scan-icon-ni-brazil-back.jpg +0 -0
  28. package/assets/images/card-scan-icon-ni-brazil.jpg +0 -0
  29. package/assets/images/card-scan-icon-ni-dominican-republic-back.png +0 -0
  30. package/assets/images/card-scan-icon-ni-dominican-republic-front.png +0 -0
  31. package/assets/images/card-scan-icon-ni-dominican-republic-mrz.png +0 -0
  32. package/assets/images/card-scan-icon-ni-dominicanaRepublic-back.jpg +0 -0
  33. package/assets/images/card-scan-icon-ni-france-back.png +0 -0
  34. package/assets/images/card-scan-icon-ni-france-front.png +0 -0
  35. package/assets/images/card-scan-icon-ni-france-scan-mrz.png +0 -0
  36. package/assets/images/card-scan-icon-ni-germany-back.jpg +0 -0
  37. package/assets/images/card-scan-icon-ni-germany.jpg +0 -0
  38. package/assets/images/card-scan-icon-ni-paraguay-back.png +0 -0
  39. package/assets/images/card-scan-icon-ni-paraguay-front.png +0 -0
  40. package/assets/images/card-scan-icon-ni-paraguay-scan-mrz.png +0 -0
  41. package/assets/images/card-scan-icon-ni-uae-back.png +0 -0
  42. package/assets/images/card-scan-icon-ni-uae-front.png +0 -0
  43. package/assets/images/card-scan-icon-ni-uae-scan-mrz.png +0 -0
  44. package/assets/images/card-scan-icon-ni-uganda-front.png +0 -0
  45. package/assets/images/card-scan-icon-ni-uganda-scan-mrz.png +0 -0
  46. package/assets/images/card-scan-icon-ni-ukrain-back.png +0 -0
  47. package/assets/images/card-scan-icon-ni-ukrain-front.png +0 -0
  48. package/assets/images/card-scan-icon-ni-ukrain-scan-mrz.png +0 -0
  49. package/assets/images/card-scan-icon-ni.png +0 -0
  50. package/assets/images/card-scan-icon-old.jpg +0 -0
  51. package/assets/images/card-scan-icon-pan.png +0 -0
  52. package/assets/images/card-scan-icon-passport-card-back.jpg +0 -0
  53. package/assets/images/card-scan-icon-passport-card.jpg +0 -0
  54. package/assets/images/card-scan-icon-passport-old.png +0 -0
  55. package/assets/images/card-scan-icon-passport.png +0 -0
  56. package/assets/images/card-scan-icon-pr.png +0 -0
  57. package/assets/images/card-scan-icon.jpg +0 -0
  58. package/assets/images/check.png +0 -0
  59. package/assets/images/chrome-animation-GPS-permissions-setting.gif +0 -0
  60. package/assets/images/chrome-animation-camera-permissions-setting.gif +0 -0
  61. package/assets/images/denied.png +0 -0
  62. package/assets/images/dl.png +0 -0
  63. package/assets/images/driver-license.png +0 -0
  64. package/assets/images/firefox-animation-permissions-setting.gif +0 -0
  65. package/assets/images/flashlight_on.png +0 -0
  66. package/assets/images/gallery.png +0 -0
  67. package/assets/images/greencard.png +0 -0
  68. package/assets/images/header.jpg +0 -0
  69. package/assets/images/health-card.png +0 -0
  70. package/assets/images/ic_camera_front_white_36px.svg +4 -0
  71. package/assets/images/ic_camera_rear_white_36px.svg +4 -0
  72. package/assets/images/ic_fullscreen_exit_white_48px.svg +4 -0
  73. package/assets/images/ic_fullscreen_white_48px.svg +4 -0
  74. package/assets/images/ic_photo_camera_white_48px.svg +5 -0
  75. package/assets/images/id-card.png +0 -0
  76. package/assets/images/idcardimg.png +0 -0
  77. package/assets/images/idmval-barcode.png +0 -0
  78. package/assets/images/information.png +0 -0
  79. package/assets/images/loader.gif +0 -0
  80. package/assets/images/loading.svg +1 -0
  81. package/assets/images/logo.jpg +0 -0
  82. package/assets/images/logo.png +0 -0
  83. package/assets/images/mrz-back.png +0 -0
  84. package/assets/images/mrz-ni.png +0 -0
  85. package/assets/images/mrz.png +0 -0
  86. package/assets/images/mrz1.png +0 -0
  87. package/assets/images/mrz_old.png +0 -0
  88. package/assets/images/mrz_small.png +0 -0
  89. package/assets/images/national-id.png +0 -0
  90. package/assets/images/nationalID.png +0 -0
  91. package/assets/images/no-wifi.png +0 -0
  92. package/assets/images/passport-card.png +0 -0
  93. package/assets/images/passport.png +0 -0
  94. package/assets/images/permit-card.png +0 -0
  95. package/assets/images/photo-overlay.png +0 -0
  96. package/assets/images/placeholder.jpg +0 -0
  97. package/assets/images/qr-code.png +0 -0
  98. package/assets/images/right-checkmark.jpg +0 -0
  99. package/assets/images/selfie.jpg +0 -0
  100. package/assets/images/showing-sec.png +0 -0
  101. package/assets/images/spinner.gif +0 -0
  102. package/assets/images/splash-icon.png +0 -0
  103. package/assets/images/take-selfie.jpg +0 -0
  104. package/assets/images/torch_off.png +0 -0
  105. package/assets/images/warning-icon.jpg +0 -0
  106. package/assets/images/warning-stick.jpg +0 -0
  107. package/assets/images/wrong-checkmark.jpg +0 -0
  108. package/package.json +31 -4
  109. package/src/apis/index.ts +338 -17
  110. package/src/components/common/Loader.tsx +16 -2
  111. package/src/config/apiConfig.ts +6 -0
  112. package/src/index.tsx +123 -7
  113. package/src/screens/BarcodeCapture.tsx +64 -12
  114. package/src/screens/DocumentCaptureBack.tsx +133 -24
  115. package/src/screens/DocumentCaptureFront.tsx +146 -24
  116. package/src/screens/MrzCapture.tsx +77 -12
  117. package/src/screens/SelectDocuments.tsx +37 -56
  118. package/src/screens/SelfieCapture.tsx +114 -18
  119. package/src/screens/ThankYou.tsx +34 -1
  120. package/src/services/getUserData.ts +111 -0
  121. package/src/types/IDMConf.ts +81 -7
  122. package/src/utils/base64.ts +25 -0
  123. package/src/utils/flowManager.ts +138 -0
  124. package/src/utils/imageProcessor.ts +96 -0
  125. package/src/utils/index.ts +18 -0
@@ -1,13 +1,22 @@
1
1
  import React, { memo } from 'react';
2
- import { View, ActivityIndicator, StyleSheet } from 'react-native';
2
+ import { View, ActivityIndicator, StyleSheet, Text } from 'react-native';
3
3
  import { useTheme } from '../../context/ThemeContext';
4
4
 
5
- function Loader() {
5
+ interface LoaderProps {
6
+ message?: string;
7
+ }
8
+
9
+ function Loader({ message }: LoaderProps) {
6
10
  const { theme } = useTheme();
7
11
 
8
12
  return (
9
13
  <View style={[styles.container, { backgroundColor: theme.colors.background }]}>
10
14
  <ActivityIndicator size="large" color={theme.colors.primary} />
15
+ {message && (
16
+ <Text style={[styles.message, { color: theme.colors.text }]}>
17
+ {message}
18
+ </Text>
19
+ )}
11
20
  </View>
12
21
  );
13
22
  }
@@ -18,6 +27,11 @@ const styles = StyleSheet.create({
18
27
  justifyContent: 'center',
19
28
  alignItems: 'center',
20
29
  },
30
+ message: {
31
+ marginTop: 16,
32
+ fontSize: 16,
33
+ textAlign: 'center',
34
+ },
21
35
  });
22
36
 
23
37
  export default memo(Loader);
@@ -0,0 +1,6 @@
1
+ export const SANDBOX_BASE_URL = 'https://sandbox-api.idmerit.com';
2
+ export const PRODUCTION_BASE_URL = 'https://api.idmerit.com';
3
+
4
+ export const getBaseURL = (environment: 'sandbox' | 'production'): string => {
5
+ return environment?.toLowerCase() === 'sandbox' ? SANDBOX_BASE_URL : PRODUCTION_BASE_URL;
6
+ };
package/src/index.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useMemo, useCallback } from 'react';
1
+ import React, { useState, useMemo, useCallback, useEffect } from 'react';
2
2
  import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
3
3
  import { createNativeStackNavigator } from '@react-navigation/native-stack';
4
4
  import BarcodeCapture from './screens/BarcodeCapture';
@@ -21,10 +21,18 @@ import MrzAdvice from './screens/MrzAdvice';
21
21
  import { ThemeProvider } from './context/ThemeContext';
22
22
  import { themes } from './context/themes';
23
23
  import { IDMConf } from './types/IDMConf';
24
- import { IDMProvider } from './context/IDMConfigurationContext';
24
+ import { IDMProvider, useIDM } from './context/IDMConfigurationContext';
25
25
  import { KeyboardProvider, useKeyboard } from './context/KeyboardContext';
26
26
  import Footer from './components/common/Footer';
27
27
  import Header from './components/common/Header';
28
+ import Loader from './components/common/Loader';
29
+ import {
30
+ getUserDataAPI,
31
+ generateAccessToken,
32
+ generateRequest,
33
+ getConfiguration,
34
+ getCountries,
35
+ } from './apis';
28
36
 
29
37
  const Stack = createNativeStackNavigator();
30
38
 
@@ -41,23 +49,26 @@ const HIDE_HEADER_FOOTER_ROUTES = new Set([
41
49
  ]);
42
50
 
43
51
  interface IDMScanProps {
44
- idmConf?: IDMConf;
52
+ idmConf: IDMConf;
45
53
  }
46
54
 
47
- export default function IDMScan({ idmConf = {} }: IDMScanProps) {
55
+ export default function IDMScan({ idmConf }: IDMScanProps) {
48
56
  return (
49
57
  <IDMProvider initialConf={idmConf}>
50
58
  <KeyboardProvider>
51
- <IDMScanContent idmConf={idmConf} />
59
+ <IDMScanContent />
52
60
  </KeyboardProvider>
53
61
  </IDMProvider>
54
62
  );
55
63
  }
56
64
 
57
- function IDMScanContent({ idmConf }: { idmConf: IDMConf }) {
65
+ function IDMScanContent() {
58
66
  const { isKeyboardVisible } = useKeyboard();
67
+ const { idmConf, setIDMConf } = useIDM();
59
68
  const navigationRef = useNavigationContainerRef();
60
69
  const [currentRoute, setCurrentRoute] = useState<string | undefined>();
70
+ const [loading, setLoading] = useState(true);
71
+ const [error, setError] = useState<string | null>(null);
61
72
 
62
73
  // Memoize theme calculation
63
74
  const theme = useMemo(
@@ -80,11 +91,116 @@ function IDMScanContent({ idmConf }: { idmConf: IDMConf }) {
80
91
  setCurrentRoute(route?.name);
81
92
  }, [navigationRef]);
82
93
 
94
+ // Dynamic initialization flow
95
+ useEffect(() => {
96
+ const initializeSDK = async () => {
97
+ try {
98
+ setLoading(true);
99
+ setError(null);
100
+
101
+ // Step 1: Get user data if not provided
102
+ if (!idmConf.userDetails) {
103
+ console.log('Fetching user data...');
104
+ const userData = await getUserDataAPI();
105
+ const updatedConf = { ...idmConf, userDetails: userData };
106
+ setIDMConf(updatedConf);
107
+
108
+ // Step 2: Generate access token if not provided
109
+ if (!idmConf.accessToken) {
110
+ console.log('Generating access token...');
111
+ const token = await generateAccessToken(updatedConf);
112
+ const confWithToken = { ...updatedConf, accessToken: token };
113
+ setIDMConf(confWithToken);
114
+
115
+ // Step 3: Generate verification request
116
+ console.log('Generating verification request...');
117
+ const requestResult = await generateRequest(confWithToken, idmConf.requestData);
118
+ const confWithVerifyCode = {
119
+ ...confWithToken,
120
+ verificationCode: requestResult.verificationCode,
121
+ };
122
+ setIDMConf(confWithVerifyCode);
123
+
124
+ // Step 4: Get configuration
125
+ console.log('Fetching configuration...');
126
+ const fetchedConfig = await getConfiguration(
127
+ confWithVerifyCode,
128
+ requestResult.verificationCode
129
+ );
130
+ setIDMConf({
131
+ ...confWithVerifyCode,
132
+ configuration: fetchedConfig,
133
+ });
134
+ }
135
+ }
136
+ } catch (err: any) {
137
+ console.error('SDK initialization error:', err);
138
+ setError(err.message || 'Failed to initialize SDK');
139
+ } finally {
140
+ setLoading(false);
141
+ }
142
+ };
143
+
144
+ initializeSDK();
145
+ }, []); // Run once on mount
146
+
147
+ // Fetch countries when access token and verification code are available
148
+ useEffect(() => {
149
+ const fetchCountriesData = async () => {
150
+ if (idmConf.accessToken && idmConf.verificationCode && !idmConf.countryDetails) {
151
+ try {
152
+ console.log('Fetching countries...');
153
+ const response = await getCountries(idmConf);
154
+ const countriesArray = Array.isArray(response) ? response : response?.data || [];
155
+
156
+ const mapped = countriesArray.map((singleCountry: any) => ({
157
+ label: singleCountry.country,
158
+ value: singleCountry.index,
159
+ metadata: singleCountry.metadata || [],
160
+ }));
161
+
162
+ setIDMConf({
163
+ ...idmConf,
164
+ countryDetails: mapped,
165
+ });
166
+ } catch (err: any) {
167
+ console.error('Error fetching countries:', err);
168
+ }
169
+ }
170
+ };
171
+
172
+ fetchCountriesData();
173
+ }, [idmConf.accessToken, idmConf.verificationCode]);
174
+
175
+ // Determine initial route based on permissions
176
+ const initialRouteName = useMemo(() => {
177
+ if (idmConf.userDetails?.permissionGranted) {
178
+ return 'VerifyIdentity';
179
+ }
180
+ return 'LocationPermission';
181
+ }, [idmConf.userDetails?.permissionGranted]);
182
+
183
+ if (loading) {
184
+ return (
185
+ <ThemeProvider theme={theme}>
186
+ <Loader />
187
+ </ThemeProvider>
188
+ );
189
+ }
190
+
191
+ if (error) {
192
+ return (
193
+ <ThemeProvider theme={theme}>
194
+ <Loader message={`Error: ${error}`} />
195
+ </ThemeProvider>
196
+ );
197
+ }
198
+
83
199
  return (
84
200
  <ThemeProvider theme={theme}>
85
201
  {shouldShowHeaderFooter && <Header />}
86
202
  <NavigationContainer ref={navigationRef} onStateChange={handleStateChange}>
87
- <Stack.Navigator initialRouteName="VerifyIdentity" screenOptions={SCREEN_OPTIONS}>
203
+ <Stack.Navigator initialRouteName={initialRouteName} screenOptions={SCREEN_OPTIONS}>
88
204
  <Stack.Screen name="VerifyIdentity" component={VerifyIdentity} />
89
205
  <Stack.Screen name="SelfieAdvice" component={SelfieAdvice} />
90
206
  <Stack.Screen name="SelfieCapture" component={SelfieCapture} />
@@ -1,30 +1,70 @@
1
- import React, { useState } from 'react';
2
- import { View, Text, StyleSheet } from 'react-native';
1
+ import React, { useState, useEffect } from 'react';
2
+ import { View, Text, StyleSheet, Alert } from 'react-native';
3
3
  import { useNavigation } from '@react-navigation/native';
4
4
  import { useTheme } from '../context/ThemeContext';
5
5
  import { useOrientation } from '../hooks/useOrientation';
6
+ import { useIDM } from '../context/IDMConfigurationContext';
6
7
  import getStyles from '../styles/ScannerStyles';
7
8
  import Loader from '../components/common/Loader';
9
+ import { captureBarcode } from '../apis';
8
10
 
9
11
  export default function BarcodeCapture() {
10
12
  const { theme } = useTheme();
13
+ const { idmConf, setIDMConf } = useIDM();
11
14
  const orientation = useOrientation();
12
15
  const styles = getStyles(theme, orientation);
13
16
  const navigation = useNavigation();
14
17
  const [loading, setLoading] = useState(false);
18
+ const [scannedData, setScannedData] = useState<string | null>(null);
15
19
 
16
- // Static implementation - simulate barcode scanning
17
- React.useEffect(() => {
18
- const timer = setTimeout(() => {
20
+ // Handle barcode scan
21
+ const handleBarcodeScan = async (barcodeText: string) => {
22
+ if (loading || !barcodeText) return;
23
+
24
+ try {
19
25
  setLoading(true);
20
- setTimeout(() => {
21
- setLoading(false);
26
+
27
+ const barcodeData = {
28
+ token: String(idmConf.verificationCode || ''),
29
+ text: barcodeText,
30
+ };
31
+
32
+ // Call API to submit barcode
33
+ const result = await captureBarcode(idmConf, barcodeData);
34
+
35
+ if (result.status) {
36
+ // Update configuration with response
37
+ setIDMConf({
38
+ ...idmConf,
39
+ requestConfiguration: { ...idmConf.requestConfiguration, ...result },
40
+ });
41
+
42
+ // Navigate to ThankYou
22
43
  navigation.navigate('ThankYou' as never);
23
- }, 2000);
44
+ } else {
45
+ Alert.alert('Error', result.errorMessage || 'Failed to process barcode');
46
+ setLoading(false);
47
+ }
48
+ } catch (error: any) {
49
+ console.error('Error processing barcode:', error);
50
+ Alert.alert('Error', 'Failed to process barcode');
51
+ setLoading(false);
52
+ }
53
+ };
54
+
55
+ // Simulate barcode scanning (replace with actual barcode scanner library)
56
+ useEffect(() => {
57
+ // TODO: Integrate actual barcode scanning library (e.g., react-native-vision-camera with barcode plugin)
58
+ // For now, simulate scanning after 3 seconds
59
+ const timer = setTimeout(() => {
60
+ const simulatedBarcode = 'SIMULATED_BARCODE_DATA_' + Date.now();
61
+ setScannedData(simulatedBarcode);
62
+ handleBarcodeScan(simulatedBarcode);
24
63
  }, 3000);
25
64
 
26
65
  return () => clearTimeout(timer);
27
- }, [navigation]);
66
+ // eslint-disable-next-line react-hooks/exhaustive-deps
67
+ }, []);
28
68
 
29
69
  return (
30
70
  <View style={styles.container}>
@@ -32,7 +72,7 @@ export default function BarcodeCapture() {
32
72
  Fill the box with the barcode{'\n'}until it turns green
33
73
  </Text>
34
74
 
35
- {/* Camera placeholder */}
75
+ {/* Camera placeholder - Replace with actual camera view */}
36
76
  <View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
37
77
  <Text
38
78
  style={{
@@ -42,8 +82,20 @@ export default function BarcodeCapture() {
42
82
  fontSize: 16,
43
83
  }}
44
84
  >
45
- Barcode Scanner (Static)
85
+ {loading ? 'Processing...' : 'Scanning for barcode...'}
46
86
  </Text>
87
+ {scannedData && (
88
+ <Text
89
+ style={{
90
+ color: '#0f0',
91
+ textAlign: 'center',
92
+ marginTop: 20,
93
+ fontSize: 12,
94
+ }}
95
+ >
96
+ Detected: {scannedData.substring(0, 30)}...
97
+ </Text>
98
+ )}
47
99
  </View>
48
100
 
49
101
  {/* Barcode overlay box */}
@@ -55,7 +107,7 @@ export default function BarcodeCapture() {
55
107
  width: 350,
56
108
  height: 100,
57
109
  borderWidth: 2,
58
- borderColor: '#00ff00',
110
+ borderColor: scannedData ? '#00ff00' : '#ffffff',
59
111
  backgroundColor: 'transparent',
60
112
  }}
61
113
  />
@@ -1,54 +1,163 @@
1
- import React, { useState } from 'react';
2
- import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet, Image, Alert } from 'react-native';
3
3
  import { useNavigation } from '@react-navigation/native';
4
4
  import getStyles from '../styles/DocumentCaptureStyles';
5
5
  import { useTheme } from '../context/ThemeContext';
6
6
  import { useOrientation } from '../hooks/useOrientation';
7
+ import { useIDM } from '../context/IDMConfigurationContext';
7
8
  import Loader from '../components/common/Loader';
9
+ import { captureDocumentBack } from '../apis';
10
+ import { processImageToBase64, validateImagePath } from '../utils/imageProcessor';
11
+ import { createFlowManager } from '../utils/flowManager';
8
12
 
9
13
  export default function DocumentCaptureBack() {
10
14
  const { theme } = useTheme();
15
+ const { idmConf, setIDMConf } = useIDM();
11
16
  const orientation = useOrientation();
12
17
  const styles = getStyles(theme, orientation);
13
18
  const navigation = useNavigation();
14
19
  const [loading, setLoading] = useState(false);
20
+ const [hasPermission, setHasPermission] = useState(false);
21
+ const [device, setDevice] = useState<any>(null);
22
+ const cameraRef = useRef<any>(null);
23
+
24
+ // Initialize camera
25
+ useEffect(() => {
26
+ const initializeCamera = async () => {
27
+ try {
28
+ const { Camera, useCameraDevice, useCameraPermission } = require('react-native-vision-camera');
29
+
30
+ const { hasPermission: hasPerm, requestPermission } = useCameraPermission();
31
+
32
+ if (!hasPerm) {
33
+ const granted = await requestPermission();
34
+ if (!granted) {
35
+ navigation.navigate('CameraPermission' as never);
36
+ return;
37
+ }
38
+ }
39
+
40
+ setHasPermission(true);
41
+
42
+ const backDevice = useCameraDevice('back');
43
+ if (!backDevice) {
44
+ navigation.navigate('NoCameraFound' as never);
45
+ return;
46
+ }
47
+
48
+ setDevice(backDevice);
49
+ } catch (error) {
50
+ console.warn('Camera initialization failed:', error);
51
+ setHasPermission(true);
52
+ }
53
+ };
54
+
55
+ initializeCamera();
56
+ }, [navigation]);
57
+
58
+ // Determine next navigation step based on metadata
59
+ const handleNextStep = () => {
60
+ const metaData = idmConf?.selectedCountryDetails?.selectedMetaData;
61
+
62
+ if (!metaData) {
63
+ navigation.navigate('ThankYou' as never);
64
+ return;
65
+ }
66
+
67
+ const flowManager = createFlowManager(metaData);
68
+ const nextScreen = flowManager.getNextScreenAfterBack();
69
+ navigation.navigate(nextScreen as never);
70
+ };
15
71
 
16
72
  const takePhoto = async () => {
73
+ if (!cameraRef.current) {
74
+ Alert.alert('Camera Not Available', 'Camera is not available in this environment.');
75
+ return;
76
+ }
77
+
17
78
  try {
18
79
  setLoading(true);
19
- // Static implementation - simulate photo capture
20
- setTimeout(() => {
80
+
81
+ const photo = await cameraRef.current.takePhoto();
82
+
83
+ if (!validateImagePath(photo?.path)) {
84
+ Alert.alert('Error', 'Failed to capture image');
21
85
  setLoading(false);
22
- // Navigate to ThankYou after capturing back
23
- navigation.navigate('ThankYou' as never);
24
- }, 1000);
25
- } catch (error) {
86
+ return;
87
+ }
88
+
89
+ // Process image to base64
90
+ const base64Image = await processImageToBase64(photo.path);
91
+
92
+ // Prepare photo data for API
93
+ const photoData = {
94
+ token: String(idmConf.verificationCode || ''),
95
+ file: base64Image,
96
+ };
97
+
98
+ // Call API to capture document back
99
+ const result = await captureDocumentBack(idmConf, photoData);
100
+
101
+ if (result.status) {
102
+ // Update configuration with response
103
+ setIDMConf({
104
+ ...idmConf,
105
+ requestConfiguration: { ...idmConf.requestConfiguration, ...result },
106
+ });
107
+
108
+ // Navigate to next step based on metadata
109
+ handleNextStep();
110
+ } else {
111
+ navigation.navigate('RetakeSelfie' as never, {
112
+ errorMessage: result.errorMessage || 'Failed to process document',
113
+ });
114
+ }
115
+ } catch (error: any) {
26
116
  console.error('Error taking photo:', error);
117
+ Alert.alert('Error', 'Failed to capture or process the image.');
118
+ } finally {
27
119
  setLoading(false);
28
120
  }
29
121
  };
30
122
 
123
+ const renderCamera = () => {
124
+ try {
125
+ const { Camera } = require('react-native-vision-camera');
126
+
127
+ if (hasPermission && device) {
128
+ return (
129
+ <Camera
130
+ ref={cameraRef}
131
+ style={StyleSheet.absoluteFill}
132
+ device={device}
133
+ isActive={true}
134
+ photo={true}
135
+ />
136
+ );
137
+ }
138
+ } catch (error) {
139
+ console.warn('Camera render failed:', error);
140
+ }
141
+
142
+ return (
143
+ <View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
144
+ <Text style={{ color: '#fff', textAlign: 'center', marginTop: 100 }}>
145
+ Camera Preview
146
+ </Text>
147
+ </View>
148
+ );
149
+ };
150
+
31
151
  return (
32
152
  <View style={styles.container}>
33
153
  <Text style={styles.topLabel}>Document Back Side</Text>
34
154
 
35
- {/* Camera placeholder */}
36
- <View style={StyleSheet.absoluteFill}>
37
- <View
38
- style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}
39
- ></View>
40
- </View>
155
+ {renderCamera()}
41
156
 
42
- {/* Card overlay placeholder */}
43
- <View
44
- style={[
45
- styles.cardoverlay,
46
- {
47
- borderWidth: 2,
48
- borderColor: '#fff',
49
- backgroundColor: 'transparent',
50
- },
51
- ]}
157
+ <Image
158
+ source={require('../../assets/images/card-overlay-back.png')}
159
+ style={styles.cardoverlay}
160
+ resizeMode="cover"
52
161
  />
53
162
 
54
163
  <Text style={styles.bottomLabel}>