@finspringinnovations/fdsdk 0.0.2 → 0.0.4

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 (38) hide show
  1. package/lib/assets/images/images.d.ts +1 -0
  2. package/lib/assets/images/images.js +1 -0
  3. package/lib/components/AmountInput.js +2 -1
  4. package/lib/components/CheckboxOption.js +3 -3
  5. package/lib/components/FDCard.js +9 -0
  6. package/lib/components/OTPInput.js +1 -1
  7. package/lib/components/PendingFDBottomSheet.js +8 -8
  8. package/lib/components/TextFieldWithLabel.js +2 -2
  9. package/lib/config/appDataConfig.d.ts +3 -2
  10. package/lib/config/appDataConfig.js +5 -5
  11. package/lib/hooks/usePaymentSSE.d.ts +8 -11
  12. package/lib/hooks/usePaymentSSE.js +31 -147
  13. package/lib/navigation/RootNavigator.d.ts +1 -0
  14. package/lib/navigation/RootNavigator.js +2 -2
  15. package/lib/navigation/index.d.ts +1 -0
  16. package/lib/navigation/index.js +2 -2
  17. package/lib/screens/FDCalculator.d.ts +2 -0
  18. package/lib/screens/FDCalculator.js +32 -14
  19. package/lib/screens/NomineeDetail.js +8 -8
  20. package/lib/screens/Payment.js +43 -69
  21. package/lib/screens/ReviewKYC.js +2 -2
  22. package/package.json +1 -1
  23. package/src/assets/images/images.js +1 -0
  24. package/src/assets/images/shriram.png +0 -0
  25. package/src/components/AmountInput.tsx +1 -5
  26. package/src/components/CheckboxOption.tsx +3 -3
  27. package/src/components/FDCard.tsx +15 -0
  28. package/src/components/OTPInput.tsx +1 -1
  29. package/src/components/PendingFDBottomSheet.tsx +8 -8
  30. package/src/components/TextFieldWithLabel.tsx +2 -2
  31. package/src/config/appDataConfig.ts +6 -5
  32. package/src/hooks/usePaymentSSE.ts +36 -167
  33. package/src/navigation/RootNavigator.tsx +5 -1
  34. package/src/navigation/index.tsx +3 -1
  35. package/src/screens/FDCalculator.tsx +30 -2
  36. package/src/screens/NomineeDetail.tsx +7 -8
  37. package/src/screens/Payment.tsx +52 -77
  38. package/src/screens/ReviewKYC.tsx +2 -2
@@ -1,4 +1,5 @@
1
1
  export namespace images {
2
2
  let arrowLeft: any;
3
3
  let chevronDown: any;
4
+ let shriramLogo: any;
4
5
  }
@@ -7,4 +7,5 @@ exports.images = void 0;
7
7
  exports.images = {
8
8
  arrowLeft: require('./arrow-left.png'),
9
9
  chevronDown: require('./chevron-down.png'),
10
+ shriramLogo: require('./shriram.png'),
10
11
  };
@@ -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(react_native_1.Image, { source: { uri: base64Images_1.base64Images.info }, style: [styles.noteIcon, { width: 14, height: 14, tintColor: colors.textLight }], resizeMode: "contain" }),
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.inputBorder || colors.border,
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
- borderColor: colors.inputBorder || colors.border,
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
  });
@@ -7,6 +7,7 @@ const react_1 = __importDefault(require("react"));
7
7
  const react_native_1 = require("react-native");
8
8
  const ThemeContext_1 = require("../theme/ThemeContext");
9
9
  const theme_1 = require("../theme");
10
+ const base64Images_1 = require("../constants/strings/base64Images");
10
11
  const FDCard = ({ id, name, accountNumber, roi, tenure, amount, maturityDate, status, creditRating, onPress, customStyles = {}, }) => {
11
12
  const colors = (0, ThemeContext_1.useColors)();
12
13
  const typography = (0, ThemeContext_1.useTypography)();
@@ -31,6 +32,8 @@ const FDCard = ({ id, name, accountNumber, roi, tenure, amount, maturityDate, st
31
32
  };
32
33
  return (react_1.default.createElement(react_native_1.TouchableOpacity, { style: [styles.fdCard, customStyles.container], onPress: () => onPress === null || onPress === void 0 ? void 0 : onPress(id), activeOpacity: 0.8 },
33
34
  react_1.default.createElement(react_native_1.View, { style: styles.fdCardHeader },
35
+ react_1.default.createElement(react_native_1.View, null,
36
+ react_1.default.createElement(react_native_1.Image, { source: { uri: base64Images_1.base64Images.shriramLogo }, style: styles.logo, resizeMode: "contain" })),
34
37
  react_1.default.createElement(react_native_1.Text, { style: [styles.fdName, customStyles.name] }, name)),
35
38
  react_1.default.createElement(react_native_1.View, { style: styles.fdDetailsRow },
36
39
  react_1.default.createElement(react_native_1.View, { style: styles.fdDetailItem },
@@ -91,6 +94,12 @@ const createStyles = (colors, typography, spacing, shadows, themeName) => {
91
94
  roiText: {
92
95
  color: colors.success,
93
96
  },
97
+ logo: {
98
+ width: 25,
99
+ height: 25,
100
+ marginRight: 5,
101
+ borderRadius: 15,
102
+ },
94
103
  });
95
104
  };
96
105
  exports.default = FDCard;
@@ -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 || colors.border, borderRadius: 8 }, typography.styles.bodyLarge), { color: colors.text, backgroundColor: themeName === 'dark' ? colors.inputBackground : 'white' }),
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' ? colors.border : 'transparent',
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.text,
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: colors.surface,
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.text, marginBottom: spacing.md }),
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.text }),
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.inputBorder || colors.border, borderWidth: 1 };
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: '#000000',
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: string;
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, _f;
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
- if (!((_f = appData.panNumber) === null || _f === void 0 ? void 0 : _f.trim()))
222
- errors.push('PAN number is required');
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
- * Mirrors the web app's usePaymentSSE (FDWebApplication/src/hooks/usePaymentSSE.ts).
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, { onPaymentStatus: (status, payload) => { ... } });
16
- * stop();
9
+ * start(applicationId, {
10
+ * onPaymentStatus: (status, payload) => { ... },
11
+ * onError: (error) => { ... },
12
+ * });
13
+ * stop(); // to cancel
17
14
  */
18
- export interface UsePaymentSSECallbacks {
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
- * Mirrors the web app's usePaymentSSE (FDWebApplication/src/hooks/usePaymentSSE.ts).
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, { onPaymentStatus: (status, payload) => { ... } });
17
- * stop();
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
- const baseApi_1 = require("../api/baseApi");
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, _b;
78
- if (!(applicationId === null || applicationId === void 0 ? void 0 : applicationId.trim())) {
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 XHR
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 mirror web app's getApiHeaders() exactly
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
- // Onboarding identifiers from Redux store
111
- const storeRef = (0, baseApi_1.getSDKStoreRef)();
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
- const events = parseSSEBuffer(bufferRef.current, newText);
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 (!data)
144
- continue;
145
- const handlePaymentStatus = async () => {
146
- var _a;
69
+ if (eventType === 'fd_payment_status') {
147
70
  try {
148
- const parsed = await parseSSEData(data);
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 (err) {
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.warn('[usePaymentSSE] Failed to parse digilocker_verification_status:', err);
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 status
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
- const onboarding = (_b = storeRef === null || storeRef === void 0 ? void 0 : storeRef.getState()) === null || _b === void 0 ? void 0 : _b.onboarding;
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)(() => {
@@ -3,6 +3,7 @@ import type { SDKNavigationConfig } from './types';
3
3
  interface RootNavigatorProps {
4
4
  config?: SDKNavigationConfig;
5
5
  onExit?: (fdDetails?: any) => void;
6
+ onPanRequired?: () => void;
6
7
  }
7
8
  declare const RootNavigator: React.FC<RootNavigatorProps>;
8
9
  export default RootNavigator;
@@ -57,7 +57,7 @@ const PaymentStatus_1 = __importDefault(require("../screens/PaymentStatus"));
57
57
  const helpers_2 = require("./helpers");
58
58
  const paymentSession_1 = require("../state/paymentSession");
59
59
  const Stack = (0, stack_1.createStackNavigator)();
60
- const RootNavigator = ({ config = {}, onExit }) => {
60
+ const RootNavigator = ({ config = {}, onExit, onPanRequired, }) => {
61
61
  // Handle Android hardware back button
62
62
  (0, react_1.useEffect)(() => {
63
63
  if (react_native_1.Platform.OS !== 'android')
@@ -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(), onPanRequired: onPanRequired, 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
@@ -6,6 +6,7 @@ import { type CustomColors } from '../config/appDataConfig';
6
6
  interface SDKNavigationContainerProps {
7
7
  config?: SDKNavigationConfig;
8
8
  onExit?: (fdDetails?: any) => void;
9
+ onPanRequired?: () => void;
9
10
  children?: React.ReactNode;
10
11
  theme?: ThemeName | Theme;
11
12
  colors?: CustomColors;
@@ -15,7 +15,7 @@ const MasterDataProvider_1 = require("../providers/MasterDataProvider");
15
15
  const helpers_1 = require("./helpers");
16
16
  const appDataConfig_1 = require("../config/appDataConfig");
17
17
  // Main navigation container for the SDK
18
- const SDKNavigationContainer = ({ config, onExit, children, theme, colors: propColors, useReactNavigation = true, // Default to React Navigation
18
+ const SDKNavigationContainer = ({ config, onExit, onPanRequired, children, theme, colors: propColors, useReactNavigation = true, // Default to React Navigation
19
19
  }) => {
20
20
  // Merge color overrides: prop-level colors take priority over global SDK colors
21
21
  const globalColors = (0, appDataConfig_1.getSDKColors)();
@@ -23,7 +23,7 @@ const SDKNavigationContainer = ({ config, onExit, children, theme, colors: propC
23
23
  ? Object.assign(Object.assign({}, (globalColors || {})), (propColors || {})) : null;
24
24
  // Choose navigator based on flag
25
25
  const navigator = useReactNavigation
26
- ? react_1.default.createElement(RootNavigator_1.default, { config: config, onExit: onExit })
26
+ ? react_1.default.createElement(RootNavigator_1.default, { config: config, onExit: onExit, onPanRequired: onPanRequired })
27
27
  : react_1.default.createElement(SimpleNavigator_1.default, { config: config, onExit: onExit });
28
28
  const content = children || (useReactNavigation ? (react_1.default.createElement(native_1.NavigationContainer, { ref: helpers_1.navigationRef }, navigator)) : (navigator));
29
29
  // Build ThemeProvider props
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
  export interface FDCalculatorProps {
3
3
  onGoBack?: () => void;
4
+ onExitSDK?: () => void;
5
+ onPanRequired?: () => void;
4
6
  onNavigateToReviewKYC?: () => void;
5
7
  fdData?: {
6
8
  id: string;