@finspringinnovations/fdsdk 0.0.2 → 0.0.3
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/lib/components/AmountInput.js +2 -1
- package/lib/components/CheckboxOption.js +3 -3
- package/lib/components/OTPInput.js +1 -1
- package/lib/components/PendingFDBottomSheet.js +8 -8
- package/lib/components/TextFieldWithLabel.js +2 -2
- package/lib/config/appDataConfig.d.ts +3 -2
- package/lib/config/appDataConfig.js +5 -5
- package/lib/hooks/usePaymentSSE.d.ts +8 -11
- package/lib/hooks/usePaymentSSE.js +31 -147
- package/lib/navigation/RootNavigator.js +1 -1
- package/lib/screens/FDCalculator.d.ts +1 -0
- package/lib/screens/FDCalculator.js +29 -14
- package/lib/screens/NomineeDetail.js +8 -8
- package/lib/screens/Payment.js +43 -69
- package/lib/screens/ReviewKYC.js +2 -2
- package/package.json +1 -1
- package/src/components/AmountInput.tsx +1 -5
- package/src/components/CheckboxOption.tsx +3 -3
- package/src/components/OTPInput.tsx +1 -1
- package/src/components/PendingFDBottomSheet.tsx +8 -8
- package/src/components/TextFieldWithLabel.tsx +2 -2
- package/src/config/appDataConfig.ts +6 -5
- package/src/hooks/usePaymentSSE.ts +36 -167
- package/src/navigation/RootNavigator.tsx +1 -0
- package/src/screens/FDCalculator.tsx +27 -2
- package/src/screens/NomineeDetail.tsx +7 -8
- package/src/screens/Payment.tsx +52 -77
- package/src/screens/ReviewKYC.tsx +2 -2
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const react_1 = __importDefault(require("react"));
|
|
7
7
|
const react_native_1 = require("react-native");
|
|
8
|
+
const Ionicons_1 = __importDefault(require("react-native-vector-icons/Ionicons"));
|
|
8
9
|
const base64Images_1 = require("../constants/strings/base64Images");
|
|
9
10
|
const ThemeContext_1 = require("../theme/ThemeContext");
|
|
10
11
|
const theme_1 = require("../theme");
|
|
@@ -39,7 +40,7 @@ const AmountInput = ({ label, value, onChangeText, onBlur, onFocus, placeholder
|
|
|
39
40
|
react_1.default.createElement(react_native_1.Image, { source: { uri: base64Images_1.base64Images.error }, style: { width: 16, height: 16, marginRight: 6 }, resizeMode: "contain" }),
|
|
40
41
|
react_1.default.createElement(react_native_1.Text, { style: styles.errorText }, errorMessage))),
|
|
41
42
|
noteText && (react_1.default.createElement(react_native_1.View, { style: styles.noteRow },
|
|
42
|
-
react_1.default.createElement(
|
|
43
|
+
react_1.default.createElement(Ionicons_1.default, { name: "information-circle-outline", size: 14, color: themeName === 'dark' ? colors.labelColor : colors.textLight, style: styles.noteIcon }),
|
|
43
44
|
react_1.default.createElement(react_native_1.Text, { style: styles.noteText }, noteText))),
|
|
44
45
|
minMaxText && (react_1.default.createElement(react_native_1.View, { style: styles.limitContainer },
|
|
45
46
|
react_1.default.createElement(react_native_1.Text, { style: styles.limitLabel }, "Min: "),
|
|
@@ -21,20 +21,20 @@ const createStyles = (colors, typography, themeName) => react_native_1.StyleShee
|
|
|
21
21
|
flexDirection: 'row',
|
|
22
22
|
alignItems: 'center',
|
|
23
23
|
paddingVertical: 0,
|
|
24
|
-
paddingHorizontal: 5
|
|
25
24
|
},
|
|
26
25
|
checkbox: {
|
|
27
26
|
width: 18,
|
|
28
27
|
height: 18,
|
|
29
28
|
borderRadius: 3,
|
|
30
29
|
borderWidth: 1.5,
|
|
31
|
-
borderColor: colors.
|
|
30
|
+
borderColor: themeName === 'dark' ? colors.tabSelected : colors.primary,
|
|
32
31
|
marginRight: 12,
|
|
33
32
|
alignItems: 'center',
|
|
34
33
|
justifyContent: 'center',
|
|
35
34
|
},
|
|
36
35
|
checkboxChecked: {
|
|
37
|
-
|
|
36
|
+
// backgroundColor: colors.tabSelected,
|
|
37
|
+
borderColor: colors.tabSelected,
|
|
38
38
|
},
|
|
39
39
|
label: Object.assign(Object.assign({}, typography.styles.bodyMedium), { color: colors.text, flex: 1 }),
|
|
40
40
|
});
|
|
@@ -117,7 +117,7 @@ const createStyles = (colors, typography, themeName) => react_native_1.StyleShee
|
|
|
117
117
|
marginBottom: 16,
|
|
118
118
|
gap: 8,
|
|
119
119
|
},
|
|
120
|
-
otpInput: Object.assign(Object.assign({ width: 48, height: 48, borderWidth: 1, borderColor: colors.inputBorder
|
|
120
|
+
otpInput: Object.assign(Object.assign({ width: 48, height: 48, borderWidth: 1, borderColor: themeName === 'dark' ? colors.inputBorder : colors.primary, borderRadius: 8 }, typography.styles.bodyLarge), { color: colors.text, backgroundColor: themeName === 'dark' ? colors.inputBackground : 'white' }),
|
|
121
121
|
otpInputError: {
|
|
122
122
|
borderColor: colors.error,
|
|
123
123
|
borderWidth: 2,
|
|
@@ -47,11 +47,11 @@ const createStyles = (colors, typography, spacing, themeName) => react_native_1.
|
|
|
47
47
|
},
|
|
48
48
|
bottomSheet: {
|
|
49
49
|
padding: spacing.sm,
|
|
50
|
-
backgroundColor: colors.background,
|
|
50
|
+
backgroundColor: themeName === 'dark' ? colors.background : 'white',
|
|
51
51
|
borderTopLeftRadius: 20,
|
|
52
52
|
borderTopRightRadius: 20,
|
|
53
53
|
borderWidth: themeName === 'dark' ? 1 : 0,
|
|
54
|
-
borderColor: themeName === 'dark' ?
|
|
54
|
+
borderColor: themeName === 'dark' ? '#ffffff' : 'transparent',
|
|
55
55
|
maxHeight: react_native_1.Dimensions.get('window').height * 0.8,
|
|
56
56
|
minHeight: react_native_1.Platform.OS === 'ios' ? 430 : 400,
|
|
57
57
|
},
|
|
@@ -71,7 +71,7 @@ const createStyles = (colors, typography, spacing, themeName) => react_native_1.
|
|
|
71
71
|
title: {
|
|
72
72
|
fontSize: 17,
|
|
73
73
|
fontWeight: '500',
|
|
74
|
-
color: colors.
|
|
74
|
+
color: themeName === 'dark' ? colors.labelColor : '#333',
|
|
75
75
|
flex: 1,
|
|
76
76
|
marginRight: spacing.md,
|
|
77
77
|
lineHeight: 24,
|
|
@@ -80,7 +80,7 @@ const createStyles = (colors, typography, spacing, themeName) => react_native_1.
|
|
|
80
80
|
width: 32,
|
|
81
81
|
height: 32,
|
|
82
82
|
borderRadius: 16,
|
|
83
|
-
backgroundColor:
|
|
83
|
+
backgroundColor: '#f5f5f5',
|
|
84
84
|
justifyContent: 'center',
|
|
85
85
|
alignItems: 'center',
|
|
86
86
|
},
|
|
@@ -90,13 +90,13 @@ const createStyles = (colors, typography, spacing, themeName) => react_native_1.
|
|
|
90
90
|
paddingTop: spacing.lg,
|
|
91
91
|
paddingBottom: react_native_1.Platform.OS === 'ios' ? 20 : 0,
|
|
92
92
|
},
|
|
93
|
-
sectionTitle: Object.assign(Object.assign({}, typography.styles.h3), { color: colors.
|
|
93
|
+
sectionTitle: Object.assign(Object.assign({}, typography.styles.h3), { color: themeName === 'dark' ? colors.labelColor : '#333', marginBottom: spacing.md }),
|
|
94
94
|
bookNewButton: {
|
|
95
95
|
marginTop: spacing.xxxl,
|
|
96
96
|
marginBottom: spacing.xxxl,
|
|
97
97
|
alignItems: 'center',
|
|
98
98
|
},
|
|
99
|
-
bookNewText: Object.assign(Object.assign({}, typography.styles.body2), { color: colors.
|
|
99
|
+
bookNewText: Object.assign(Object.assign({}, typography.styles.body2), { color: themeName === 'dark' ? colors.labelColor : '#000000' }),
|
|
100
100
|
disabledButton: {
|
|
101
101
|
opacity: 0.6,
|
|
102
102
|
},
|
|
@@ -110,13 +110,13 @@ const createStyles = (colors, typography, spacing, themeName) => react_native_1.
|
|
|
110
110
|
},
|
|
111
111
|
continueButton: {
|
|
112
112
|
height: 50,
|
|
113
|
-
backgroundColor: colors.buttonBackground || colors.headerBg,
|
|
113
|
+
backgroundColor: colors.buttonBackground || colors.headerBg || '#007AFF',
|
|
114
114
|
borderRadius: 25,
|
|
115
115
|
paddingHorizontal: spacing.lg,
|
|
116
116
|
alignItems: 'center',
|
|
117
117
|
justifyContent: 'center',
|
|
118
118
|
marginBottom: react_native_1.Platform.OS === 'ios' ? spacing.lg + 20 : spacing.lg,
|
|
119
119
|
},
|
|
120
|
-
continueButtonText: Object.assign(Object.assign({}, typography.styles.button), { color: colors.buttonTextColor || colors.headerText, fontWeight: '600' }),
|
|
120
|
+
continueButtonText: Object.assign(Object.assign({}, typography.styles.button), { color: colors.buttonTextColor || colors.headerText || '#FFFFFF', fontWeight: '600' }),
|
|
121
121
|
});
|
|
122
122
|
exports.default = PendingFDBottomSheet;
|
|
@@ -91,7 +91,7 @@ const TextFieldWithLabel = ({ label, value, onChangeText, placeholder, placehold
|
|
|
91
91
|
return { borderColor: colors.success, borderWidth: 1 };
|
|
92
92
|
}
|
|
93
93
|
if (isFocused) {
|
|
94
|
-
return { borderColor: colors.
|
|
94
|
+
return { borderColor: colors.primary, borderWidth: 1 };
|
|
95
95
|
}
|
|
96
96
|
if (themeName === 'dark') {
|
|
97
97
|
return {
|
|
@@ -238,7 +238,7 @@ const createStyles = (colors, typography, themeName) => react_native_1.StyleShee
|
|
|
238
238
|
height: '100%',
|
|
239
239
|
},
|
|
240
240
|
disabledTextInput: {
|
|
241
|
-
color:
|
|
241
|
+
color: colors.text,
|
|
242
242
|
},
|
|
243
243
|
leftIcon: {
|
|
244
244
|
marginRight: 12,
|
|
@@ -41,7 +41,7 @@ export interface AppData {
|
|
|
41
41
|
gender: string;
|
|
42
42
|
mobNo: string;
|
|
43
43
|
email: string;
|
|
44
|
-
panNumber
|
|
44
|
+
panNumber?: string;
|
|
45
45
|
address?: string;
|
|
46
46
|
area?: string;
|
|
47
47
|
city?: string;
|
|
@@ -55,6 +55,7 @@ export interface AppData {
|
|
|
55
55
|
maritalStatus?: string;
|
|
56
56
|
userReferenceId?: string;
|
|
57
57
|
eventNotifyUrl?: string;
|
|
58
|
+
startFDAlertMessage?: string;
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
60
61
|
* Initialize the SDK with app data from the main application
|
|
@@ -90,7 +91,7 @@ export declare const getUserInfoForAPI: () => {
|
|
|
90
91
|
name: string;
|
|
91
92
|
dob: string;
|
|
92
93
|
gender: string;
|
|
93
|
-
panNumber: string;
|
|
94
|
+
panNumber: string | undefined;
|
|
94
95
|
mobileNumber: string;
|
|
95
96
|
email: string;
|
|
96
97
|
eventNotifyUrl: string | undefined;
|
|
@@ -54,7 +54,7 @@ const initializeSDK = (appData, onValidationError) => {
|
|
|
54
54
|
{ field: 'gender', label: 'Gender' },
|
|
55
55
|
{ field: 'mobNo', label: 'Mobile Number' },
|
|
56
56
|
{ field: 'email', label: 'Email' },
|
|
57
|
-
{ field: 'panNumber', label: 'PAN Number' },
|
|
57
|
+
// { field: 'panNumber', label: 'PAN Number' },
|
|
58
58
|
];
|
|
59
59
|
const missingFields = [];
|
|
60
60
|
requiredFields.forEach(({ field, label }) => {
|
|
@@ -203,7 +203,7 @@ exports.getUserDemographics = getUserDemographics;
|
|
|
203
203
|
* Validate app data
|
|
204
204
|
*/
|
|
205
205
|
const validateAppData = (appData) => {
|
|
206
|
-
var _a, _b, _c, _d, _e
|
|
206
|
+
var _a, _b, _c, _d, _e;
|
|
207
207
|
const errors = [];
|
|
208
208
|
// Required fields validation
|
|
209
209
|
if (!((_a = appData.id) === null || _a === void 0 ? void 0 : _a.trim()))
|
|
@@ -218,13 +218,13 @@ const validateAppData = (appData) => {
|
|
|
218
218
|
errors.push('Mobile number is required');
|
|
219
219
|
if (!((_e = appData.email) === null || _e === void 0 ? void 0 : _e.trim()))
|
|
220
220
|
errors.push('Email is required');
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
// PAN is optional — validated later on FD Calculator screen
|
|
222
|
+
// if (!appData.panNumber?.trim()) errors.push('PAN number is required');
|
|
223
223
|
// Format validation (only if fields are provided)
|
|
224
224
|
if (appData.dob && !/^\d{4}-\d{2}-\d{2}$/.test(appData.dob)) {
|
|
225
225
|
errors.push('Date of birth must be in YYYY-MM-DD format');
|
|
226
226
|
}
|
|
227
|
-
if (appData.panNumber && !/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(appData.panNumber)) {
|
|
227
|
+
if (appData.panNumber && appData.panNumber.trim() && !/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(appData.panNumber)) {
|
|
228
228
|
errors.push('PAN number must be in valid format (e.g., ABCDE1234F)');
|
|
229
229
|
}
|
|
230
230
|
if (appData.mobNo && !/^[6-9]\d{9}$/.test(appData.mobNo)) {
|
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* usePaymentSSE - React Native SSE hook for payment status tracking.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Uses XMLHttpRequest instead of fetch+ReadableStream because
|
|
4
|
+
* Uses XMLHttpRequest instead of fetch streams because
|
|
6
5
|
* React Native does not support ReadableStream / getReader().
|
|
7
6
|
*
|
|
8
|
-
* Backend spec:
|
|
9
|
-
* Endpoint: GET /events?applicationId=<id>
|
|
10
|
-
* Events: fd_payment_status, digilocker_verification_status
|
|
11
|
-
* Format: Standard SSE → event: fd_payment_status\ndata: {"paymentStatus":"FAILED"}\n\n
|
|
12
|
-
*
|
|
13
7
|
* Usage:
|
|
14
8
|
* const { start, stop } = usePaymentSSE();
|
|
15
|
-
* start(applicationId, {
|
|
16
|
-
*
|
|
9
|
+
* start(applicationId, {
|
|
10
|
+
* onPaymentStatus: (status, payload) => { ... },
|
|
11
|
+
* onError: (error) => { ... },
|
|
12
|
+
* });
|
|
13
|
+
* stop(); // to cancel
|
|
17
14
|
*/
|
|
18
|
-
|
|
15
|
+
interface UsePaymentSSECallbacks {
|
|
19
16
|
onPaymentStatus: (status: string, payload: Record<string, unknown>) => void;
|
|
20
|
-
onDigilockerStatus?: (payload: Record<string, unknown>) => void;
|
|
21
17
|
onError?: (error: any) => void;
|
|
22
18
|
onConnected?: () => void;
|
|
23
19
|
}
|
|
@@ -25,3 +21,4 @@ export declare function usePaymentSSE(): {
|
|
|
25
21
|
start: (applicationId: string, callbacks: UsePaymentSSECallbacks) => void;
|
|
26
22
|
stop: () => void;
|
|
27
23
|
};
|
|
24
|
+
export {};
|
|
@@ -2,85 +2,34 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* usePaymentSSE - React Native SSE hook for payment status tracking.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* Uses XMLHttpRequest instead of fetch+ReadableStream because
|
|
5
|
+
* Uses XMLHttpRequest instead of fetch streams because
|
|
7
6
|
* React Native does not support ReadableStream / getReader().
|
|
8
7
|
*
|
|
9
|
-
* Backend spec:
|
|
10
|
-
* Endpoint: GET /events?applicationId=<id>
|
|
11
|
-
* Events: fd_payment_status, digilocker_verification_status
|
|
12
|
-
* Format: Standard SSE → event: fd_payment_status\ndata: {"paymentStatus":"FAILED"}\n\n
|
|
13
|
-
*
|
|
14
8
|
* Usage:
|
|
15
9
|
* const { start, stop } = usePaymentSSE();
|
|
16
|
-
* start(applicationId, {
|
|
17
|
-
*
|
|
10
|
+
* start(applicationId, {
|
|
11
|
+
* onPaymentStatus: (status, payload) => { ... },
|
|
12
|
+
* onError: (error) => { ... },
|
|
13
|
+
* });
|
|
14
|
+
* stop(); // to cancel
|
|
18
15
|
*/
|
|
19
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
17
|
exports.usePaymentSSE = usePaymentSSE;
|
|
21
18
|
const react_1 = require("react");
|
|
19
|
+
const sseParser_1 = require("../utils/sseParser");
|
|
22
20
|
const appDataConfig_1 = require("../config/appDataConfig");
|
|
23
21
|
const apiConfig_1 = require("../config/apiConfig");
|
|
24
|
-
|
|
25
|
-
const encryption_1 = require("../utils/encryption");
|
|
26
|
-
const encryptionConfig_1 = require("../config/encryptionConfig");
|
|
27
|
-
// ─── SSE Parser (standard format: event:/data: lines separated by \n\n) ─────
|
|
28
|
-
function parseSSEBuffer(buffer, chunk) {
|
|
29
|
-
const results = [];
|
|
30
|
-
buffer.value += chunk;
|
|
31
|
-
const delim = '\n\n';
|
|
32
|
-
let idx;
|
|
33
|
-
while ((idx = buffer.value.indexOf(delim)) >= 0) {
|
|
34
|
-
const block = buffer.value.slice(0, idx);
|
|
35
|
-
buffer.value = buffer.value.slice(idx + delim.length);
|
|
36
|
-
let eventType = 'message';
|
|
37
|
-
const dataParts = [];
|
|
38
|
-
for (const line of block.split(/\r?\n/)) {
|
|
39
|
-
if (line.startsWith('event:')) {
|
|
40
|
-
eventType = line.slice(6).trim();
|
|
41
|
-
}
|
|
42
|
-
else if (line.startsWith('data:')) {
|
|
43
|
-
dataParts.push(line.slice(5).trim());
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const data = dataParts.join('\n').trim();
|
|
47
|
-
if (data)
|
|
48
|
-
results.push({ eventType, data });
|
|
49
|
-
}
|
|
50
|
-
return results;
|
|
51
|
-
}
|
|
52
|
-
// ─── SSE Data Parser (handles encrypted responses) ──────────────────────────
|
|
53
|
-
async function parseSSEData(raw) {
|
|
54
|
-
const parsed = JSON.parse(raw || '{}');
|
|
55
|
-
const encrypted = parsed === null || parsed === void 0 ? void 0 : parsed.encryptedResponse;
|
|
56
|
-
if (typeof encrypted === 'string' && encrypted.length > 0) {
|
|
57
|
-
try {
|
|
58
|
-
const encryptionConfig = (0, encryptionConfig_1.getEncryptionConfig)();
|
|
59
|
-
const decrypted = await (0, encryption_1.decryptResponse)({ encryptedResponse: encrypted }, encryptionConfig);
|
|
60
|
-
return (decrypted || {});
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
if (__DEV__) {
|
|
64
|
-
console.warn('[usePaymentSSE] Failed to decrypt SSE data, using raw:', e);
|
|
65
|
-
}
|
|
66
|
-
return parsed;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return parsed;
|
|
70
|
-
}
|
|
71
|
-
// ─── Hook ───────────────────────────────────────────────────────────────────
|
|
22
|
+
// -- Hook --
|
|
72
23
|
function usePaymentSSE() {
|
|
24
|
+
// Store the XMLHttpRequest so we can abort it later
|
|
73
25
|
const xhrRef = (0, react_1.useRef)(null);
|
|
26
|
+
// Track how much of responseText we've already parsed
|
|
74
27
|
const lastIndexRef = (0, react_1.useRef)(0);
|
|
28
|
+
// SSE text buffer (persists across onprogress calls)
|
|
75
29
|
const bufferRef = (0, react_1.useRef)({ value: '' });
|
|
76
30
|
const start = (0, react_1.useCallback)((applicationId, callbacks) => {
|
|
77
|
-
var _a
|
|
78
|
-
|
|
79
|
-
if (__DEV__)
|
|
80
|
-
console.warn('[usePaymentSSE] start() skipped: applicationId is empty');
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
// Clean up any existing connection
|
|
31
|
+
var _a;
|
|
32
|
+
// Clean up any existing connection first
|
|
84
33
|
if (xhrRef.current) {
|
|
85
34
|
xhrRef.current.abort();
|
|
86
35
|
xhrRef.current = null;
|
|
@@ -88,113 +37,50 @@ function usePaymentSSE() {
|
|
|
88
37
|
// Reset parsing state
|
|
89
38
|
lastIndexRef.current = 0;
|
|
90
39
|
bufferRef.current = { value: '' };
|
|
91
|
-
// 1. Build URL
|
|
40
|
+
// 1. Build the SSE endpoint URL
|
|
92
41
|
const envData = (0, appDataConfig_1.getEnvironmentData)();
|
|
93
42
|
const apiConfig = (0, apiConfig_1.getApiConfig)();
|
|
94
43
|
const baseUrl = (envData === null || envData === void 0 ? void 0 : envData.apiBaseUrl) || (apiConfig === null || apiConfig === void 0 ? void 0 : apiConfig.baseUrl) || '';
|
|
95
44
|
const url = `${baseUrl}/events?applicationId=${encodeURIComponent(applicationId)}`;
|
|
96
|
-
// 2. Create
|
|
45
|
+
// 2. Create XMLHttpRequest
|
|
97
46
|
const xhr = new XMLHttpRequest();
|
|
98
47
|
xhrRef.current = xhr;
|
|
99
48
|
xhr.open('GET', url);
|
|
100
|
-
// 3. Set headers
|
|
101
|
-
// (Content-Type, x-api-key, onboarding IDs, userReferenceId, provider)
|
|
102
|
-
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
103
|
-
xhr.setRequestHeader('Accept', 'text/event-stream');
|
|
104
|
-
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
|
105
|
-
// API key
|
|
49
|
+
// 3. Set headers (same headers your baseApi interceptor uses)
|
|
106
50
|
const apiKey = (envData === null || envData === void 0 ? void 0 : envData.apiKey) || ((_a = apiConfig === null || apiConfig === void 0 ? void 0 : apiConfig.headers) === null || _a === void 0 ? void 0 : _a['X-API-Key']) || '';
|
|
107
51
|
if (apiKey) {
|
|
108
52
|
xhr.setRequestHeader('x-api-key', apiKey);
|
|
109
53
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (storeRef) {
|
|
113
|
-
const state = storeRef.getState();
|
|
114
|
-
const onboarding = state === null || state === void 0 ? void 0 : state.onboarding;
|
|
115
|
-
if (onboarding === null || onboarding === void 0 ? void 0 : onboarding.workflowInstanceId) {
|
|
116
|
-
xhr.setRequestHeader('workflowInstanceId', onboarding.workflowInstanceId);
|
|
117
|
-
}
|
|
118
|
-
if (onboarding === null || onboarding === void 0 ? void 0 : onboarding.applicationId) {
|
|
119
|
-
xhr.setRequestHeader('applicationId', onboarding.applicationId);
|
|
120
|
-
}
|
|
121
|
-
if (onboarding === null || onboarding === void 0 ? void 0 : onboarding.entityid) {
|
|
122
|
-
// Web app sends BOTH casings
|
|
123
|
-
xhr.setRequestHeader('entityid', onboarding.entityid);
|
|
124
|
-
xhr.setRequestHeader('entityId', onboarding.entityid);
|
|
125
|
-
}
|
|
126
|
-
if (onboarding === null || onboarding === void 0 ? void 0 : onboarding.providerId) {
|
|
127
|
-
xhr.setRequestHeader('provider', onboarding.providerId);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
// userReferenceId from app data
|
|
131
|
-
const appData = (0, appDataConfig_1.getAppData)();
|
|
132
|
-
if (appData === null || appData === void 0 ? void 0 : appData.id) {
|
|
133
|
-
xhr.setRequestHeader('userReferenceId', appData.id);
|
|
134
|
-
}
|
|
54
|
+
xhr.setRequestHeader('Accept', 'text/event-stream');
|
|
55
|
+
xhr.setRequestHeader('Cache-Control', 'no-cache');
|
|
135
56
|
// 4. Handle incoming data
|
|
136
57
|
xhr.onprogress = () => {
|
|
58
|
+
var _a;
|
|
59
|
+
// responseText contains ALL text received so far
|
|
60
|
+
// Only parse the NEW text since last call
|
|
137
61
|
const newText = xhr.responseText.slice(lastIndexRef.current);
|
|
138
62
|
lastIndexRef.current = xhr.responseText.length;
|
|
139
63
|
if (!newText)
|
|
140
64
|
return;
|
|
141
|
-
|
|
65
|
+
// Parse new text into SSE events
|
|
66
|
+
const events = (0, sseParser_1.parseSSEBuffer)(bufferRef.current, newText);
|
|
67
|
+
// Handle each event
|
|
142
68
|
for (const { eventType, data } of events) {
|
|
143
|
-
if (
|
|
144
|
-
continue;
|
|
145
|
-
const handlePaymentStatus = async () => {
|
|
146
|
-
var _a;
|
|
69
|
+
if (eventType === 'fd_payment_status') {
|
|
147
70
|
try {
|
|
148
|
-
const parsed =
|
|
71
|
+
const parsed = JSON.parse(data);
|
|
149
72
|
const status = ((_a = parsed.paymentStatus) !== null && _a !== void 0 ? _a : '').toUpperCase();
|
|
150
|
-
if (__DEV__) {
|
|
151
|
-
console.log('[usePaymentSSE] fd_payment_status:', { status, payload: parsed });
|
|
152
|
-
}
|
|
153
73
|
callbacks.onPaymentStatus(status, parsed);
|
|
154
74
|
}
|
|
155
|
-
catch (
|
|
156
|
-
if (__DEV__) {
|
|
157
|
-
console.warn('[usePaymentSSE] Failed to parse fd_payment_status:', err);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
const handleDigilockerStatus = async () => {
|
|
162
|
-
var _a;
|
|
163
|
-
try {
|
|
164
|
-
const parsed = await parseSSEData(data);
|
|
165
|
-
(_a = callbacks.onDigilockerStatus) === null || _a === void 0 ? void 0 : _a.call(callbacks, parsed);
|
|
166
|
-
}
|
|
167
|
-
catch (err) {
|
|
75
|
+
catch (parseError) {
|
|
168
76
|
if (__DEV__) {
|
|
169
|
-
console.
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
// Match event type — same logic as web app
|
|
174
|
-
if (eventType === 'fd_payment_status') {
|
|
175
|
-
handlePaymentStatus();
|
|
176
|
-
}
|
|
177
|
-
else if (eventType === 'digilocker_verification_status') {
|
|
178
|
-
handleDigilockerStatus();
|
|
179
|
-
}
|
|
180
|
-
else if (eventType === 'message') {
|
|
181
|
-
// Fallback: check "event" field inside the JSON data
|
|
182
|
-
try {
|
|
183
|
-
const parsed = JSON.parse(data);
|
|
184
|
-
if (parsed.event === 'fd_payment_status') {
|
|
185
|
-
handlePaymentStatus();
|
|
77
|
+
console.error('[usePaymentSSE] Failed to parse event data:', parseError);
|
|
186
78
|
}
|
|
187
|
-
else if (parsed.event === 'digilocker_verification_status') {
|
|
188
|
-
handleDigilockerStatus();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
catch (_a) {
|
|
192
|
-
// ignore
|
|
193
79
|
}
|
|
194
80
|
}
|
|
195
81
|
}
|
|
196
82
|
};
|
|
197
|
-
// 5. Handle connection
|
|
83
|
+
// 5. Handle successful connection
|
|
198
84
|
xhr.onreadystatechange = () => {
|
|
199
85
|
var _a, _b;
|
|
200
86
|
if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
|
|
@@ -228,12 +114,10 @@ function usePaymentSSE() {
|
|
|
228
114
|
}
|
|
229
115
|
(_a = callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, new Error('SSE connection timed out'));
|
|
230
116
|
};
|
|
231
|
-
// 8. Send
|
|
117
|
+
// 8. Send the request (connection stays open)
|
|
232
118
|
xhr.send();
|
|
233
119
|
if (__DEV__) {
|
|
234
|
-
|
|
235
|
-
console.log('[usePaymentSSE] Connecting to:', url);
|
|
236
|
-
console.log('[usePaymentSSE] Headers → apiKey:', !!apiKey, '| entityid:', onboarding === null || onboarding === void 0 ? void 0 : onboarding.entityid, '| applicationId:', onboarding === null || onboarding === void 0 ? void 0 : onboarding.applicationId, '| workflowInstanceId:', onboarding === null || onboarding === void 0 ? void 0 : onboarding.workflowInstanceId, '| providerId:', onboarding === null || onboarding === void 0 ? void 0 : onboarding.providerId, '| userReferenceId:', appData === null || appData === void 0 ? void 0 : appData.id);
|
|
120
|
+
console.log('[usePaymentSSE] Starting SSE connection to:', url);
|
|
237
121
|
}
|
|
238
122
|
}, []);
|
|
239
123
|
const stop = (0, react_1.useCallback)(() => {
|
|
@@ -131,7 +131,7 @@ const RootNavigator = ({ config = {}, onExit }) => {
|
|
|
131
131
|
} }, props)))),
|
|
132
132
|
react_1.default.createElement(Stack.Screen, { name: "FDCalculator", options: { title: 'FD Calculator' } }, (props) => {
|
|
133
133
|
var _a;
|
|
134
|
-
return (react_1.default.createElement(FDCalculator_1.default, Object.assign({ onGoBack: () => (0, helpers_2.goBack)(), onNavigateToReviewKYC: () => { var _a; return (0, helpers_2.navigate)('ReviewKYC', { fdData: (_a = props.route.params) === null || _a === void 0 ? void 0 : _a.fdData }); }, fdData: (_a = props.route.params) === null || _a === void 0 ? void 0 : _a.fdData }, props)));
|
|
134
|
+
return (react_1.default.createElement(FDCalculator_1.default, Object.assign({ onGoBack: () => (0, helpers_2.goBack)(), onExitSDK: () => onExit === null || onExit === void 0 ? void 0 : onExit(), onNavigateToReviewKYC: () => { var _a; return (0, helpers_2.navigate)('ReviewKYC', { fdData: (_a = props.route.params) === null || _a === void 0 ? void 0 : _a.fdData }); }, fdData: (_a = props.route.params) === null || _a === void 0 ? void 0 : _a.fdData }, props)));
|
|
135
135
|
}),
|
|
136
136
|
react_1.default.createElement(Stack.Screen, { name: "AadhaarVerification", options: { title: 'Aadhaar Verification' } }, (props) => (react_1.default.createElement(AadhaarVerification_1.default, Object.assign({ onGoBack: () => (0, helpers_2.goBack)(), onVerificationComplete: (aadhaarNumber) => {
|
|
137
137
|
// Navigate to Employee (occupation) screen after verification
|
|
@@ -55,7 +55,7 @@ const fdListSelectedSlice_1 = require("../store/fdListSelectedSlice");
|
|
|
55
55
|
const helpers_1 = require("../navigation/helpers");
|
|
56
56
|
const strings_1 = require("../constants/strings");
|
|
57
57
|
const base64Images_1 = require("../constants/strings/base64Images");
|
|
58
|
-
const FDCalculator = ({ onGoBack, onNavigateToReviewKYC, fdData }) => {
|
|
58
|
+
const FDCalculator = ({ onGoBack, onExitSDK, onNavigateToReviewKYC, fdData }) => {
|
|
59
59
|
const typography = (0, ThemeContext_1.useTypography)();
|
|
60
60
|
const colors = (0, ThemeContext_1.useColors)();
|
|
61
61
|
const { themeName } = (0, ThemeContext_1.useTheme)();
|
|
@@ -312,7 +312,7 @@ const FDCalculator = ({ onGoBack, onNavigateToReviewKYC, fdData }) => {
|
|
|
312
312
|
}, [debounceTimer, handleCalculateFD, payoutValue]);
|
|
313
313
|
// Function to call onboarding API
|
|
314
314
|
const handleStartOnboarding = react_1.default.useCallback(async (selectedPayout) => {
|
|
315
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
315
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
316
316
|
if (isBooking)
|
|
317
317
|
return;
|
|
318
318
|
setIsBooking(true);
|
|
@@ -331,20 +331,35 @@ const FDCalculator = ({ onGoBack, onNavigateToReviewKYC, fdData }) => {
|
|
|
331
331
|
if (amountValue < 5000) {
|
|
332
332
|
return;
|
|
333
333
|
}
|
|
334
|
+
const appData = (0, appDataConfig_1.getAppData)();
|
|
335
|
+
const hasEssentialData = (_a = appData === null || appData === void 0 ? void 0 : appData.panNumber) === null || _a === void 0 ? void 0 : _a.trim();
|
|
336
|
+
if (!hasEssentialData) {
|
|
337
|
+
const alertMessage = (appData === null || appData === void 0 ? void 0 : appData.startFDAlertMessage) || 'Please complete your KYC details to start an FD, Update your profile in the app and try again.';
|
|
338
|
+
react_native_1.Alert.alert('Action Required', alertMessage, [
|
|
339
|
+
{
|
|
340
|
+
text: 'OK',
|
|
341
|
+
onPress: () => {
|
|
342
|
+
if (onExitSDK) {
|
|
343
|
+
onExitSDK();
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
], { cancelable: false });
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
334
350
|
// Get providerId from interest rates data
|
|
335
|
-
const sdr = ((
|
|
336
|
-
const fdr = ((
|
|
351
|
+
const sdr = ((_b = interestRates === null || interestRates === void 0 ? void 0 : interestRates.data) === null || _b === void 0 ? void 0 : _b.sdrScheme) || [];
|
|
352
|
+
const fdr = ((_c = interestRates === null || interestRates === void 0 ? void 0 : interestRates.data) === null || _c === void 0 ? void 0 : _c.fdrScheme) || [];
|
|
337
353
|
const firstRate = sdr[0] || fdr[0];
|
|
338
354
|
const providerId = firstRate === null || firstRate === void 0 ? void 0 : firstRate.providerId;
|
|
339
355
|
// Get user data for all required fields
|
|
340
|
-
const appData = (0, appDataConfig_1.getAppData)();
|
|
341
356
|
const userDob = (appData === null || appData === void 0 ? void 0 : appData.dob) || '1990-01-01';
|
|
342
357
|
const isWomenDepositor = (appData === null || appData === void 0 ? void 0 : appData.gender) === 'F';
|
|
343
358
|
// Get calculated values from API response based on investment type
|
|
344
359
|
const investmentType = payoutValue === 'On Maturity' ? 'SDR' : 'FDR';
|
|
345
360
|
const calcData = investmentType === 'SDR'
|
|
346
|
-
? (
|
|
347
|
-
: (
|
|
361
|
+
? (_e = (_d = calculationResult === null || calculationResult === void 0 ? void 0 : calculationResult.data) === null || _d === void 0 ? void 0 : _d.sdrCalc) === null || _e === void 0 ? void 0 : _e[0]
|
|
362
|
+
: (_g = (_f = calculationResult === null || calculationResult === void 0 ? void 0 : calculationResult.data) === null || _f === void 0 ? void 0 : _f.fdrCalc) === null || _g === void 0 ? void 0 : _g[0];
|
|
348
363
|
const interestRate = calcData === null || calcData === void 0 ? void 0 : calcData.wRoi;
|
|
349
364
|
const maturityAmount = calcData === null || calcData === void 0 ? void 0 : calcData.maturityAmount;
|
|
350
365
|
// Calculate maturity date
|
|
@@ -356,8 +371,8 @@ const FDCalculator = ({ onGoBack, onNavigateToReviewKYC, fdData }) => {
|
|
|
356
371
|
mobile: (appData === null || appData === void 0 ? void 0 : appData.mobNo) || '',
|
|
357
372
|
prefix: (appData === null || appData === void 0 ? void 0 : appData.gender) === 'M' ? 'Mr.' : ((appData === null || appData === void 0 ? void 0 : appData.gender) === 'F' ? 'Ms.' : 'Mr.'),
|
|
358
373
|
fullName: (appData === null || appData === void 0 ? void 0 : appData.name) || '',
|
|
359
|
-
firstName: ((
|
|
360
|
-
lastName: ((
|
|
374
|
+
firstName: ((_h = appData === null || appData === void 0 ? void 0 : appData.name) === null || _h === void 0 ? void 0 : _h.split(' ')[0]) || '',
|
|
375
|
+
lastName: ((_j = appData === null || appData === void 0 ? void 0 : appData.name) === null || _j === void 0 ? void 0 : _j.split(' ').slice(1).join(' ')) || '',
|
|
361
376
|
userReferenceId: (appData === null || appData === void 0 ? void 0 : appData.id) || '',
|
|
362
377
|
dob: userDob,
|
|
363
378
|
email: (appData === null || appData === void 0 ? void 0 : appData.email) || '',
|
|
@@ -403,11 +418,11 @@ const FDCalculator = ({ onGoBack, onNavigateToReviewKYC, fdData }) => {
|
|
|
403
418
|
// Persist onboarding identifiers globally for subsequent API calls
|
|
404
419
|
try {
|
|
405
420
|
const ids = {
|
|
406
|
-
workflowInstanceId: (
|
|
407
|
-
applicationId: (
|
|
408
|
-
entityid: (
|
|
409
|
-
customerId: (
|
|
410
|
-
fdId: (
|
|
421
|
+
workflowInstanceId: (_k = result === null || result === void 0 ? void 0 : result.data) === null || _k === void 0 ? void 0 : _k.workflowInstanceId,
|
|
422
|
+
applicationId: (_l = result === null || result === void 0 ? void 0 : result.data) === null || _l === void 0 ? void 0 : _l.applicationId,
|
|
423
|
+
entityid: (_m = result === null || result === void 0 ? void 0 : result.data) === null || _m === void 0 ? void 0 : _m.entityid,
|
|
424
|
+
customerId: (_o = result === null || result === void 0 ? void 0 : result.data) === null || _o === void 0 ? void 0 : _o.customerId,
|
|
425
|
+
fdId: (_p = result === null || result === void 0 ? void 0 : result.data) === null || _p === void 0 ? void 0 : _p.fdId,
|
|
411
426
|
};
|
|
412
427
|
if (dispatch && setOnboardingIds) {
|
|
413
428
|
dispatch(setOnboardingIds(ids));
|