@sanctum-key/react-native-sdk 1.0.24 → 1.0.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.
- package/build/package.json +5 -2
- package/build/src/components/EnhancedCameraView.d.ts +7 -1
- package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.js +88 -30
- package/build/src/components/EnhancedCameraView.js.map +1 -1
- package/build/src/components/EnhancedCameraView.web.d.ts +5 -1
- package/build/src/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/src/components/EnhancedCameraView.web.js +80 -295
- package/build/src/components/EnhancedCameraView.web.js.map +1 -1
- package/build/src/components/KYCElements/CameraCapture.js +1 -1
- package/build/src/components/KYCElements/CameraCapture.js.map +1 -1
- package/build/src/components/KYCElements/CountrySelection.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelection.js +38 -218
- package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
- package/build/src/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelectionTemplate.js +5 -17
- package/build/src/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +509 -95
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.js +93 -51
- package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
- package/build/src/components/OverLay/type.d.ts +1 -0
- package/build/src/components/OverLay/type.d.ts.map +1 -1
- package/build/src/components/OverLay/type.js.map +1 -1
- package/build/src/components/WebBarcodeScanner.d.ts +9 -0
- package/build/src/components/WebBarcodeScanner.d.ts.map +1 -0
- package/build/src/components/WebBarcodeScanner.js +152 -0
- package/build/src/components/WebBarcodeScanner.js.map +1 -0
- package/build/src/config/KYCConfigs.js +2 -2
- package/build/src/config/KYCConfigs.js.map +1 -1
- package/build/src/config/region_mapping.json +0 -14
- package/build/src/modules/api/CardAuthentification.d.ts +2 -0
- package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/src/modules/api/CardAuthentification.js +22 -39
- package/build/src/modules/api/CardAuthentification.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts +7 -2
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +163 -57
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/build/src/types/KYC.types.d.ts +1 -0
- package/build/src/types/KYC.types.d.ts.map +1 -1
- package/build/src/types/KYC.types.js.map +1 -1
- package/build/src/utils/aamvaParser.d.ts +62 -0
- package/build/src/utils/aamvaParser.d.ts.map +1 -0
- package/build/src/utils/aamvaParser.js +91 -0
- package/build/src/utils/aamvaParser.js.map +1 -0
- package/build/src/utils/cropByObb.d.ts.map +1 -1
- package/build/src/utils/cropByObb.js +4 -3
- package/build/src/utils/cropByObb.js.map +1 -1
- package/build/src/web/WebKYCEntry.d.ts +1 -1
- package/build/src/web/WebKYCEntry.d.ts.map +1 -1
- package/build/src/web/WebKYCEntry.js +105 -239
- package/build/src/web/WebKYCEntry.js.map +1 -1
- package/package.json +5 -2
- package/src/components/EnhancedCameraView.tsx +116 -42
- package/src/components/EnhancedCameraView.web.tsx +98 -338
- package/src/components/KYCElements/CameraCapture.tsx +1 -1
- package/src/components/KYCElements/CountrySelection.tsx +60 -240
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +7 -20
- package/src/components/KYCElements/IDCardCapture.tsx +672 -151
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +112 -65
- package/src/components/OverLay/type.ts +1 -1
- package/src/components/WebBarcodeScanner.tsx +273 -0
- package/src/config/KYCConfigs.ts +2 -2
- package/src/config/region_mapping.json +0 -14
- package/src/modules/api/CardAuthentification.ts +41 -56
- package/src/modules/api/KYCService.ts +236 -82
- package/src/types/KYC.types.ts +1 -0
- package/src/utils/aamvaParser.ts +102 -0
- package/src/utils/cropByObb.ts +5 -3
- package/src/web/WebKYCEntry.tsx +125 -301
package/src/web/WebKYCEntry.tsx
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useState, useCallback, JSX } from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, SafeAreaView, ActivityIndicator } from 'react-native';
|
|
3
|
-
// import { TemplateKYCFlow } from '../components/TemplateKYCFlowRefactored';
|
|
4
|
-
// import { KYCTemplate } from '../types/KYC.types';
|
|
5
3
|
import { useI18n } from '../hooks/useI18n';
|
|
6
4
|
import { TemplateKYCExample } from '../components/TemplateKYCExample';
|
|
7
5
|
import { isCallbackUrlAllowed } from '../config/allowedDomains';
|
|
@@ -24,15 +22,18 @@ interface URLParams {
|
|
|
24
22
|
lang?: string;
|
|
25
23
|
theme?: string;
|
|
26
24
|
kyc_id?: string;
|
|
27
|
-
secret?: string;
|
|
28
|
-
env?: 'PRODUCTION' | 'SANDBOX';
|
|
29
|
-
template_id?: string;
|
|
30
|
-
server_env?: BackendEnvironment;
|
|
31
|
-
step?: string;
|
|
32
|
-
component_index?: string;
|
|
33
|
-
country?: string;
|
|
34
|
-
document_type?: string;
|
|
35
|
-
region?: string;
|
|
25
|
+
secret?: string;
|
|
26
|
+
env?: 'PRODUCTION' | 'SANDBOX';
|
|
27
|
+
template_id?: string;
|
|
28
|
+
server_env?: BackendEnvironment;
|
|
29
|
+
step?: string;
|
|
30
|
+
component_index?: string;
|
|
31
|
+
country?: string;
|
|
32
|
+
document_type?: string;
|
|
33
|
+
region?: string;
|
|
34
|
+
// 🚨 NEW: Added explicitly for the mobile handoff
|
|
35
|
+
session_id?: string;
|
|
36
|
+
resume?: boolean;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
interface VerificationSteps {
|
|
@@ -43,159 +44,91 @@ interface VerificationSteps {
|
|
|
43
44
|
|
|
44
45
|
const postWebhook = async (url: string, payload: unknown, timeoutMs: number = 5000) => {
|
|
45
46
|
const body = JSON.stringify(payload);
|
|
46
|
-
|
|
47
47
|
const postWithTimeout = async (init: RequestInit) => {
|
|
48
48
|
const controller = new AbortController();
|
|
49
49
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
50
50
|
try {
|
|
51
|
-
return await fetch(url, {
|
|
52
|
-
method: 'POST',
|
|
53
|
-
...init,
|
|
54
|
-
signal: controller.signal,
|
|
55
|
-
});
|
|
51
|
+
return await fetch(url, { method: 'POST', ...init, signal: controller.signal });
|
|
56
52
|
} finally {
|
|
57
53
|
clearTimeout(timeoutId);
|
|
58
54
|
}
|
|
59
55
|
};
|
|
60
56
|
|
|
61
|
-
// In browser, first try a regular JSON POST so servers validating Content-Type
|
|
62
|
-
// behave the same as Postman/cURL.
|
|
63
57
|
if (typeof window !== 'undefined') {
|
|
64
58
|
try {
|
|
65
|
-
const response = await postWithTimeout({
|
|
66
|
-
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
},
|
|
69
|
-
body,
|
|
70
|
-
keepalive: true,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (!response.ok) {
|
|
74
|
-
throw new Error(`Webhook failed with status ${response.status}`);
|
|
75
|
-
}
|
|
59
|
+
const response = await postWithTimeout({ headers: { 'Content-Type': 'application/json' }, body, keepalive: true });
|
|
60
|
+
if (!response.ok) throw new Error(`Webhook failed with status ${response.status}`);
|
|
76
61
|
return;
|
|
77
62
|
} catch (error) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
await postWithTimeout({
|
|
85
|
-
mode: 'no-cors',
|
|
86
|
-
headers: {
|
|
87
|
-
'Content-Type': 'text/plain;charset=UTF-8',
|
|
88
|
-
},
|
|
89
|
-
body,
|
|
90
|
-
keepalive: true,
|
|
91
|
-
});
|
|
63
|
+
if (error instanceof Error && error.name === 'AbortError') throw error;
|
|
64
|
+
await postWithTimeout({ mode: 'no-cors', headers: { 'Content-Type': 'text/plain;charset=UTF-8' }, body, keepalive: true });
|
|
92
65
|
return;
|
|
93
66
|
}
|
|
94
67
|
}
|
|
95
68
|
|
|
96
|
-
const response = await postWithTimeout({
|
|
97
|
-
|
|
98
|
-
'Content-Type': 'application/json',
|
|
99
|
-
},
|
|
100
|
-
body,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
if (!response.ok) {
|
|
104
|
-
throw new Error(`Webhook failed with status ${response.status}`);
|
|
105
|
-
}
|
|
69
|
+
const response = await postWithTimeout({ headers: { 'Content-Type': 'application/json' }, body });
|
|
70
|
+
if (!response.ok) throw new Error(`Webhook failed with status ${response.status}`);
|
|
106
71
|
};
|
|
107
72
|
|
|
108
|
-
export function WebKYCEntry({
|
|
109
|
-
onComplete,
|
|
110
|
-
onError,
|
|
111
|
-
onCancel,
|
|
112
|
-
onSessionSave,
|
|
113
|
-
}: WebKYCEntryProps): JSX.Element {
|
|
114
|
-
console.log('first')
|
|
73
|
+
export function WebKYCEntry({ onComplete, onError, onCancel, onSessionSave }: WebKYCEntryProps): JSX.Element {
|
|
115
74
|
const { setLocale } = useI18n();
|
|
116
75
|
const [urlParams, setUrlParams] = useState<URLParams>({});
|
|
117
76
|
const [isLoading, setIsLoading] = useState(true);
|
|
118
77
|
const [error, setError] = useState<string | null>(null);
|
|
119
78
|
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
|
120
79
|
|
|
121
|
-
// Parse URL parameters
|
|
122
80
|
const parseUrlParams = useCallback((): URLParams => {
|
|
123
81
|
if (typeof window === 'undefined') return {};
|
|
124
82
|
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
83
|
+
const rawUrl = window.location.href;
|
|
84
|
+
console.log("Raw URL intercepted:", rawUrl);
|
|
85
|
+
|
|
86
|
+
const paramsString = rawUrl.replace(/^[^?#]*[?#]/, '').replace(/[?#]/g, '&');
|
|
128
87
|
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
88
|
+
const urlParams = new URLSearchParams(paramsString);
|
|
89
|
+
|
|
90
|
+
const getParam = (key: string) => urlParams.get(key) || undefined;
|
|
91
|
+
|
|
92
|
+
const envParam = getParam('env');
|
|
93
|
+
const validEnv = envParam === 'SANDBOX' || envParam === 'PRODUCTION' ? envParam : 'PRODUCTION';
|
|
94
|
+
const serverEnvParam = getParam('server_env');
|
|
95
|
+
const validServerEnv: BackendEnvironment = serverEnvParam === 'TEST' || serverEnvParam === 'PRODUCTION' ? (serverEnvParam as BackendEnvironment) : 'PRODUCTION';
|
|
133
96
|
|
|
134
97
|
return {
|
|
135
|
-
token:
|
|
136
|
-
return_url:
|
|
137
|
-
push_url:
|
|
138
|
-
save_session_url:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
theme: urlParams.get('theme') || 'light',
|
|
145
|
-
kyc_id: urlParams.get('kyc_id') || undefined,
|
|
146
|
-
secret: urlParams.get('secret') || undefined,
|
|
98
|
+
token: getParam('token'),
|
|
99
|
+
return_url: getParam('return_url'),
|
|
100
|
+
push_url: getParam('push_url'),
|
|
101
|
+
save_session_url: getParam('save_session_url') || getParam('saveSessionUrl'),
|
|
102
|
+
reference_id: getParam('reference_id'),
|
|
103
|
+
lang: getParam('lang') || 'en',
|
|
104
|
+
theme: getParam('theme') || 'light',
|
|
105
|
+
kyc_id: getParam('kyc_id') || getParam('session_id'),
|
|
106
|
+
secret: getParam('secret'),
|
|
147
107
|
env: validEnv,
|
|
148
|
-
template_id:
|
|
108
|
+
template_id: getParam('template_id'),
|
|
149
109
|
server_env: validServerEnv,
|
|
150
|
-
step:
|
|
151
|
-
component_index:
|
|
152
|
-
country:
|
|
153
|
-
document_type:
|
|
154
|
-
region:
|
|
110
|
+
step: getParam('step'),
|
|
111
|
+
component_index: getParam('component_index'),
|
|
112
|
+
country: getParam('country'),
|
|
113
|
+
document_type: getParam('document_type'),
|
|
114
|
+
region: getParam('region'),
|
|
115
|
+
resume: getParam('resume') === 'true',
|
|
155
116
|
};
|
|
156
117
|
}, []);
|
|
157
118
|
|
|
158
|
-
|
|
159
|
-
const redirectToReturnUrl = useCallback(async (params: {
|
|
160
|
-
status: 'completed' | 'cancelled' | 'error';
|
|
161
|
-
kyc_id?: string;
|
|
162
|
-
message?: string;
|
|
163
|
-
processing_state?: 'analyzing' | 'completed' | 'pending';
|
|
164
|
-
verification_steps?: VerificationSteps;
|
|
165
|
-
}) => {
|
|
119
|
+
const redirectToReturnUrl = useCallback(async (params: { status: 'completed' | 'cancelled' | 'error'; kyc_id?: string; message?: string; processing_state?: 'analyzing' | 'completed' | 'pending'; verification_steps?: VerificationSteps; }) => {
|
|
166
120
|
const { return_url } = urlParams;
|
|
167
|
-
|
|
168
|
-
if (!return_url) {
|
|
169
|
-
console.warn('No return_url provided');
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
|
|
121
|
+
if (!return_url) { console.warn('No return_url provided'); return; }
|
|
173
122
|
try {
|
|
174
|
-
// Enhanced URL validation with domain whitelist
|
|
175
123
|
const validation = isCallbackUrlAllowed(return_url);
|
|
176
124
|
if (!validation.allowed) {
|
|
177
125
|
console.error('Callback URL validation failed:', validation.reason);
|
|
178
126
|
setError(`Security Error: ${validation.reason}`);
|
|
179
|
-
|
|
180
|
-
// Log suspicious redirect attempt
|
|
181
|
-
console.warn('Suspicious redirect attempt blocked:', {
|
|
182
|
-
url: return_url,
|
|
183
|
-
reason: validation.reason,
|
|
184
|
-
timestamp: new Date().toISOString(),
|
|
185
|
-
});
|
|
186
127
|
return;
|
|
187
128
|
}
|
|
188
|
-
// Send postMessage to parent if in iframe
|
|
189
129
|
if (window.parent !== window) {
|
|
190
130
|
const targetOrigin = new URL(return_url).origin;
|
|
191
|
-
window.parent.postMessage({
|
|
192
|
-
type: 'kyc_result',
|
|
193
|
-
status: params.status,
|
|
194
|
-
kyc_id: params.kyc_id,
|
|
195
|
-
message: params.message,
|
|
196
|
-
processing_state: params.processing_state,
|
|
197
|
-
verification_steps: params.verification_steps,
|
|
198
|
-
}, targetOrigin);
|
|
131
|
+
window.parent.postMessage({ type: 'kyc_result', status: params.status, kyc_id: params.kyc_id, message: params.message, processing_state: params.processing_state, verification_steps: params.verification_steps }, targetOrigin);
|
|
199
132
|
} else {
|
|
200
133
|
window.location.href = return_url;
|
|
201
134
|
}
|
|
@@ -206,190 +139,127 @@ export function WebKYCEntry({
|
|
|
206
139
|
}, [urlParams]);
|
|
207
140
|
|
|
208
141
|
const handleComplete = useCallback(async (data: any) => {
|
|
209
|
-
console.log('KYC completed:', data);
|
|
210
|
-
|
|
211
|
-
// Check if still processing/analyzing
|
|
212
|
-
const isStillProcessing = data.isProcessing || data.session?.isProcessing;
|
|
142
|
+
console.log('KYC completed, raw state:', data);
|
|
143
|
+
const isStillProcessing = data?.isProcessing || data?.session?.isProcessing;
|
|
213
144
|
|
|
214
|
-
// Handle Push URL webhook if provided
|
|
215
145
|
if (urlParams.push_url) {
|
|
216
146
|
try {
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
console.warn('Failed parsing inner stringified JSON string:', parseErr);
|
|
238
|
-
parsedKycData = rawData;
|
|
239
|
-
}
|
|
240
|
-
} else {
|
|
241
|
-
parsedKycData = rawData;
|
|
242
|
-
}
|
|
147
|
+
// 1. Locate the actual backend payload from the Context state
|
|
148
|
+
const backendResult = data?.verification?.result || data?.result?.result || data?.result || data;
|
|
149
|
+
|
|
150
|
+
// 2. Safely extract session ID
|
|
151
|
+
const finalSessionId =
|
|
152
|
+
data?.session?.session_id ||
|
|
153
|
+
backendResult?.session_id ||
|
|
154
|
+
backendResult?.data?.session_id ||
|
|
155
|
+
urlParams.kyc_id ||
|
|
156
|
+
'unknown';
|
|
157
|
+
|
|
158
|
+
// 3. Extract the core data payload
|
|
159
|
+
let parsedKycData: any = {};
|
|
160
|
+
if (backendResult?.data) {
|
|
161
|
+
try {
|
|
162
|
+
parsedKycData = typeof backendResult.data === 'string'
|
|
163
|
+
? JSON.parse(backendResult.data)
|
|
164
|
+
: backendResult.data;
|
|
165
|
+
} catch (e) {
|
|
166
|
+
console.warn("Could not parse backendResult.data JSON");
|
|
243
167
|
}
|
|
244
168
|
}
|
|
245
169
|
|
|
246
|
-
|
|
170
|
+
if (parsedKycData && typeof parsedKycData === 'object' && parsedKycData[finalSessionId]) {
|
|
171
|
+
const nested = parsedKycData[finalSessionId];
|
|
172
|
+
if (nested?.data) {
|
|
173
|
+
parsedKycData = typeof nested.data === 'string' ? JSON.parse(nested.data) : nested.data;
|
|
174
|
+
} else {
|
|
175
|
+
parsedKycData = nested;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
247
178
|
|
|
248
|
-
// 3. Build your exact un-nested payload signature
|
|
249
179
|
const verificationState = {
|
|
250
180
|
session_id: finalSessionId,
|
|
251
|
-
|
|
252
|
-
|
|
181
|
+
reference_id: urlParams.reference_id || backendResult?.referenceId || null,
|
|
182
|
+
status: backendResult?.status || data?.verification?.status || 'completed',
|
|
183
|
+
message: backendResult?.message || backendResult?.reason || backendResult?.issues?.[0] || 'Verification finished.',
|
|
253
184
|
data: parsedKycData || {}
|
|
254
185
|
};
|
|
255
186
|
|
|
256
|
-
|
|
257
|
-
|
|
187
|
+
const hasValidSessionId = finalSessionId && finalSessionId !== 'unknown';
|
|
188
|
+
const hasValidData = parsedKycData && Object.keys(parsedKycData).length > 0;
|
|
189
|
+
|
|
190
|
+
if (!hasValidSessionId || !hasValidData) {
|
|
191
|
+
console.log("Webhook Blocked: Payload contains no data or session_id is unknown.", JSON.stringify(verificationState));
|
|
192
|
+
} else {
|
|
193
|
+
console.log("Pushing Webhook Payload:", JSON.stringify(verificationState, null, 2));
|
|
194
|
+
await postWebhook(urlParams.push_url, verificationState, 5000);
|
|
195
|
+
}
|
|
196
|
+
|
|
258
197
|
} catch (err) {
|
|
259
|
-
console.warn('Failed to push data to webhook URL:', err);
|
|
198
|
+
console.warn('Failed to construct or push data to webhook URL:', err);
|
|
260
199
|
}
|
|
261
200
|
}
|
|
262
201
|
|
|
202
|
+
setIsAnalyzing(false);
|
|
203
|
+
|
|
204
|
+
// Always redirect the user back to your main app/website, regardless of webhook success/block
|
|
263
205
|
redirectToReturnUrl({
|
|
264
|
-
status: 'completed',
|
|
265
|
-
kyc_id: data
|
|
266
|
-
message: 'KYC process
|
|
206
|
+
status: data?.verification?.status === 'failed' ? 'error' : 'completed',
|
|
207
|
+
kyc_id: data?.session?.session_id || urlParams.kyc_id,
|
|
208
|
+
message: 'KYC process concluded',
|
|
267
209
|
processing_state: isStillProcessing ? 'analyzing' : 'completed',
|
|
268
210
|
verification_steps: {
|
|
269
|
-
document_analyzed:
|
|
270
|
-
selfie_analyzed:
|
|
271
|
-
liveness_checked:
|
|
211
|
+
document_analyzed: true,
|
|
212
|
+
selfie_analyzed: true,
|
|
213
|
+
liveness_checked: true
|
|
272
214
|
},
|
|
273
215
|
});
|
|
274
|
-
|
|
216
|
+
|
|
275
217
|
onComplete?.(data);
|
|
276
218
|
}, [redirectToReturnUrl, urlParams, onComplete]);
|
|
277
219
|
|
|
278
|
-
// Handle KYC error
|
|
279
220
|
const handleError = useCallback((error: string) => {
|
|
280
221
|
console.error('KYC error:', error);
|
|
281
222
|
setIsAnalyzing(false);
|
|
282
|
-
redirectToReturnUrl({
|
|
283
|
-
status: 'error',
|
|
284
|
-
kyc_id: urlParams.kyc_id,
|
|
285
|
-
message: error,
|
|
286
|
-
processing_state: 'pending',
|
|
287
|
-
});
|
|
223
|
+
redirectToReturnUrl({ status: 'error', kyc_id: urlParams.kyc_id, message: error, processing_state: 'pending' });
|
|
288
224
|
onError?.(error);
|
|
289
225
|
}, [redirectToReturnUrl, urlParams.kyc_id, onError]);
|
|
290
226
|
|
|
291
|
-
// Handle KYC cancellation
|
|
292
227
|
const handleCancel = useCallback(async () => {
|
|
293
|
-
console.log('KYC cancelled', urlParams.push_url, urlParams);
|
|
294
228
|
setIsAnalyzing(false);
|
|
295
229
|
if (urlParams.push_url) {
|
|
296
230
|
try {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
result: null, // pass null as result
|
|
300
|
-
reference_id: urlParams.reference_id,
|
|
301
|
-
};
|
|
302
|
-
await postWebhook(urlParams.push_url, verificationState, 5000);
|
|
303
|
-
console.log('Successfully pushed KYC cancellation data to webhook');
|
|
304
|
-
} catch (err) {
|
|
305
|
-
// Fail open - redirect anyway even if webhook fails
|
|
306
|
-
console.warn('Failed to push data to webhook URL:', err);
|
|
307
|
-
}
|
|
231
|
+
await postWebhook(urlParams.push_url, { status: 'cancelled', result: null, reference_id: urlParams.reference_id }, 5000);
|
|
232
|
+
} catch (err) { console.warn('Failed to push data to webhook URL:', err); }
|
|
308
233
|
}
|
|
309
|
-
redirectToReturnUrl({
|
|
310
|
-
status: 'cancelled',
|
|
311
|
-
kyc_id: urlParams.kyc_id,
|
|
312
|
-
message: 'KYC process was cancelled',
|
|
313
|
-
processing_state: 'pending',
|
|
314
|
-
});
|
|
234
|
+
redirectToReturnUrl({ status: 'cancelled', kyc_id: urlParams.kyc_id, message: 'KYC process was cancelled', processing_state: 'pending' });
|
|
315
235
|
onCancel?.();
|
|
316
236
|
}, [redirectToReturnUrl, urlParams.kyc_id, urlParams.push_url, urlParams.reference_id, onCancel]);
|
|
317
237
|
|
|
318
238
|
const handleSessionSave = useCallback(async (sessionId: string) => {
|
|
319
|
-
console.log('KYC session created:', sessionId);
|
|
320
|
-
|
|
321
239
|
if (urlParams.save_session_url) {
|
|
322
240
|
try {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
reference_id: urlParams.reference_id,
|
|
326
|
-
result: {
|
|
327
|
-
session_id: sessionId,
|
|
328
|
-
kyc_id: sessionId,
|
|
329
|
-
},
|
|
330
|
-
};
|
|
331
|
-
await postWebhook(urlParams.save_session_url, payload, 5000);
|
|
332
|
-
console.log('Successfully pushed session data to save_session_url');
|
|
333
|
-
} catch (err) {
|
|
334
|
-
// Fail open - do not block the KYC flow if save_session webhook fails
|
|
335
|
-
console.warn('Failed to push session data to save_session_url:', err);
|
|
336
|
-
}
|
|
337
|
-
} else {
|
|
338
|
-
console.warn('save_session_url is missing in URL params, webhook not called');
|
|
241
|
+
await postWebhook(urlParams.save_session_url, { status: 'session_created', reference_id: urlParams.reference_id, result: { session_id: sessionId, kyc_id: sessionId } }, 5000);
|
|
242
|
+
} catch (err) { console.warn('Failed to push session data:', err); }
|
|
339
243
|
}
|
|
340
|
-
|
|
341
244
|
onSessionSave?.(sessionId);
|
|
342
245
|
}, [urlParams.save_session_url, urlParams.reference_id, onSessionSave]);
|
|
343
246
|
|
|
344
|
-
// Initialize component
|
|
345
247
|
useEffect(() => {
|
|
346
248
|
try {
|
|
347
249
|
const params = parseUrlParams();
|
|
348
|
-
|
|
349
250
|
setUrlParams(params);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if (params.lang) {
|
|
353
|
-
setLocale(params.lang);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Configure backend environment
|
|
357
|
-
if (params.server_env) {
|
|
358
|
-
KYCConfig.setBackendEnvironment(params.server_env);
|
|
359
|
-
}
|
|
360
|
-
|
|
251
|
+
if (params.lang) setLocale(params.lang);
|
|
252
|
+
if (params.server_env) KYCConfig.setBackendEnvironment(params.server_env);
|
|
361
253
|
setIsLoading(false);
|
|
362
254
|
} catch (error) {
|
|
363
|
-
console.error('Error parsing URL parameters:', error);
|
|
364
255
|
setError('Error parsing URL parameters');
|
|
365
256
|
setIsLoading(false);
|
|
366
257
|
}
|
|
367
258
|
}, [parseUrlParams, setLocale]);
|
|
368
259
|
|
|
369
|
-
|
|
370
|
-
if (
|
|
371
|
-
|
|
372
|
-
<View style={styles.container}>
|
|
373
|
-
<Text style={styles.loadingText}>Loading KYC process...</Text>
|
|
374
|
-
</View>
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (error) {
|
|
379
|
-
return (
|
|
380
|
-
<View style={styles.container}>
|
|
381
|
-
<Text style={styles.errorText}>Error: {error}</Text>
|
|
382
|
-
</View>
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (!urlParams.token) {
|
|
387
|
-
return (
|
|
388
|
-
<View style={styles.container}>
|
|
389
|
-
<Text style={styles.errorText}>No token provided in URL parameters</Text>
|
|
390
|
-
</View>
|
|
391
|
-
);
|
|
392
|
-
}
|
|
260
|
+
if (isLoading) return <View style={styles.container}><Text style={styles.loadingText}>Loading KYC process...</Text></View>;
|
|
261
|
+
if (error) return <View style={styles.container}><Text style={styles.errorText}>Error: {error}</Text></View>;
|
|
262
|
+
if (!urlParams.token) return <View style={styles.container}><Text style={styles.errorText}>No token provided in URL parameters</Text></View>;
|
|
393
263
|
|
|
394
264
|
return (
|
|
395
265
|
<SafeAreaView style={styles.container}>
|
|
@@ -411,7 +281,7 @@ export function WebKYCEntry({
|
|
|
411
281
|
templateId={urlParams.template_id}
|
|
412
282
|
env={urlParams.env || 'PRODUCTION'}
|
|
413
283
|
serverEnv={urlParams.server_env}
|
|
414
|
-
existingSessionId={urlParams.kyc_id}
|
|
284
|
+
existingSessionId={urlParams.kyc_id} // Successfully passes session_id down
|
|
415
285
|
initialComponentIndex={(() => {
|
|
416
286
|
const raw = urlParams.component_index ?? urlParams.step;
|
|
417
287
|
if (raw == null || raw === '') return undefined;
|
|
@@ -424,62 +294,16 @@ export function WebKYCEntry({
|
|
|
424
294
|
/>
|
|
425
295
|
</SafeAreaView>
|
|
426
296
|
);
|
|
427
|
-
}
|
|
297
|
+
}
|
|
428
298
|
|
|
429
299
|
const styles = StyleSheet.create({
|
|
430
|
-
container: {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
},
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
textAlign: 'center',
|
|
438
|
-
marginTop: 50,
|
|
439
|
-
color: '#666',
|
|
440
|
-
},
|
|
441
|
-
errorText: {
|
|
442
|
-
fontSize: 16,
|
|
443
|
-
textAlign: 'center',
|
|
444
|
-
marginTop: 50,
|
|
445
|
-
color: '#dc2626',
|
|
446
|
-
paddingHorizontal: 20,
|
|
447
|
-
},
|
|
448
|
-
analyzingOverlay: {
|
|
449
|
-
position: 'absolute',
|
|
450
|
-
top: 0,
|
|
451
|
-
left: 0,
|
|
452
|
-
right: 0,
|
|
453
|
-
bottom: 0,
|
|
454
|
-
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
455
|
-
justifyContent: 'center',
|
|
456
|
-
alignItems: 'center',
|
|
457
|
-
zIndex: 9999,
|
|
458
|
-
},
|
|
459
|
-
analyzingContainer: {
|
|
460
|
-
backgroundColor: 'white',
|
|
461
|
-
borderRadius: 12,
|
|
462
|
-
padding: 32,
|
|
463
|
-
alignItems: 'center',
|
|
464
|
-
shadowColor: '#000',
|
|
465
|
-
shadowOffset: { width: 0, height: 4 },
|
|
466
|
-
shadowOpacity: 0.3,
|
|
467
|
-
shadowRadius: 8,
|
|
468
|
-
elevation: 8,
|
|
469
|
-
},
|
|
470
|
-
analyzingText: {
|
|
471
|
-
fontSize: 18,
|
|
472
|
-
fontWeight: '600',
|
|
473
|
-
color: '#333',
|
|
474
|
-
marginTop: 16,
|
|
475
|
-
textAlign: 'center',
|
|
476
|
-
},
|
|
477
|
-
analyzingSubtext: {
|
|
478
|
-
fontSize: 14,
|
|
479
|
-
color: '#666',
|
|
480
|
-
marginTop: 8,
|
|
481
|
-
textAlign: 'center',
|
|
482
|
-
},
|
|
300
|
+
container: { flex: 1, backgroundColor: '#f5f5f5', overflow: 'visible' as any },
|
|
301
|
+
loadingText: { fontSize: 18, textAlign: 'center', marginTop: 50, color: '#666' },
|
|
302
|
+
errorText: { fontSize: 16, textAlign: 'center', marginTop: 50, color: '#dc2626', paddingHorizontal: 20 },
|
|
303
|
+
analyzingOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.7)', justifyContent: 'center', alignItems: 'center', zIndex: 9999 },
|
|
304
|
+
analyzingContainer: { backgroundColor: 'white', borderRadius: 12, padding: 32, alignItems: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.3, shadowRadius: 8, elevation: 8 },
|
|
305
|
+
analyzingText: { fontSize: 18, fontWeight: '600', color: '#333', marginTop: 16, textAlign: 'center' },
|
|
306
|
+
analyzingSubtext: { fontSize: 14, color: '#666', marginTop: 8, textAlign: 'center' },
|
|
483
307
|
});
|
|
484
308
|
|
|
485
309
|
export default WebKYCEntry;
|