@finspringinnovations/fdsdk 0.0.1 → 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.
Files changed (53) hide show
  1. package/lib/components/CompanyHeader.js +40 -36
  2. package/lib/components/InterestRateCard.js +3 -2
  3. package/lib/components/PaymentDetailsCard.js +8 -7
  4. package/lib/components/PendingFDBottomSheet.js +2 -2
  5. package/lib/components/TextFieldWithLabel.js +1 -1
  6. package/lib/components/TrustBox.js +2 -2
  7. package/lib/config/appDataConfig.d.ts +53 -2
  8. package/lib/config/appDataConfig.js +39 -6
  9. package/lib/hooks/usePaymentSSE.d.ts +24 -0
  10. package/lib/hooks/usePaymentSSE.js +135 -0
  11. package/lib/hooks/usePaymentStatusTimer.d.ts +25 -0
  12. package/lib/hooks/usePaymentStatusTimer.js +122 -0
  13. package/lib/index.d.ts +2 -2
  14. package/lib/index.js +5 -2
  15. package/lib/navigation/RootNavigator.js +6 -1
  16. package/lib/navigation/index.d.ts +2 -0
  17. package/lib/navigation/index.js +13 -11
  18. package/lib/screens/FDCalculator.d.ts +1 -0
  19. package/lib/screens/FDCalculator.js +64 -48
  20. package/lib/screens/FDList.js +10 -12
  21. package/lib/screens/NomineeDetail.js +21 -15
  22. package/lib/screens/PayNow.js +6 -6
  23. package/lib/screens/Payment.d.ts +1 -0
  24. package/lib/screens/Payment.js +34 -13
  25. package/lib/screens/PaymentStatus.js +33 -42
  26. package/lib/theme/ThemeContext.d.ts +2 -0
  27. package/lib/theme/ThemeContext.js +4 -2
  28. package/lib/theme/index.d.ts +6 -1
  29. package/lib/theme/index.js +24 -8
  30. package/lib/utils/sseParser.d.ts +7 -0
  31. package/lib/utils/sseParser.js +27 -0
  32. package/package.json +2 -2
  33. package/src/components/CompanyHeader.tsx +50 -44
  34. package/src/components/InterestRateCard.tsx +3 -2
  35. package/src/components/PaymentDetailsCard.tsx +45 -40
  36. package/src/components/PendingFDBottomSheet.tsx +2 -2
  37. package/src/components/TextFieldWithLabel.tsx +1 -1
  38. package/src/components/TrustBox.tsx +2 -2
  39. package/src/config/appDataConfig.ts +70 -5
  40. package/src/hooks/usePaymentSSE.ts +155 -0
  41. package/src/hooks/usePaymentStatusTimer.ts +169 -0
  42. package/src/index.tsx +4 -1
  43. package/src/navigation/RootNavigator.tsx +7 -0
  44. package/src/navigation/index.tsx +16 -17
  45. package/src/screens/FDCalculator.tsx +64 -40
  46. package/src/screens/FDList.tsx +11 -11
  47. package/src/screens/NomineeDetail.tsx +20 -14
  48. package/src/screens/PayNow.tsx +7 -7
  49. package/src/screens/Payment.tsx +45 -14
  50. package/src/screens/PaymentStatus.tsx +44 -57
  51. package/src/theme/ThemeContext.tsx +6 -1
  52. package/src/theme/index.ts +30 -8
  53. package/src/utils/sseParser.ts +40 -0
@@ -10,7 +10,8 @@ const base64Images_1 = require("../constants/strings/base64Images");
10
10
  const CompanyHeader = ({ companyName, rating }) => {
11
11
  const colors = (0, ThemeContext_1.useColors)();
12
12
  const typography = (0, ThemeContext_1.useTypography)();
13
- const styles = createStyles(colors, typography);
13
+ const { themeName } = (0, ThemeContext_1.useTheme)();
14
+ const styles = createStyles(colors, typography, themeName);
14
15
  return (react_1.default.createElement(react_native_1.View, { style: styles.wrapper },
15
16
  react_1.default.createElement(react_native_1.View, { style: styles.card },
16
17
  react_1.default.createElement(react_native_1.View, { style: styles.innerBorder },
@@ -19,39 +20,42 @@ const CompanyHeader = ({ companyName, rating }) => {
19
20
  react_1.default.createElement(react_native_1.Text, { style: styles.name }, companyName),
20
21
  react_1.default.createElement(react_native_1.Text, { style: styles.rating }, rating))))));
21
22
  };
22
- const createStyles = (colors, typography) => react_native_1.StyleSheet.create({
23
- wrapper: {
24
- paddingHorizontal: 2,
25
- paddingTop: 12,
26
- paddingBottom: 8,
27
- },
28
- card: {
29
- backgroundColor: '#0B2940',
30
- borderRadius: 24,
31
- padding: 4,
32
- borderWidth: 1,
33
- borderColor: '#124061',
34
- shadowColor: '#000',
35
- shadowOpacity: 0.2,
36
- shadowOffset: { width: 0, height: 4 },
37
- shadowRadius: 12,
38
- elevation: 4,
39
- },
40
- innerBorder: {
41
- borderRadius: 20,
42
- padding: 16,
43
- flexDirection: 'row',
44
- alignItems: 'center',
45
- },
46
- logo: {
47
- width: 45,
48
- height: 45,
49
- marginRight: 12,
50
- },
51
- info: {
52
- flex: 1,
53
- },
54
- name: Object.assign(Object.assign({}, typography.styles.h2), { color: '#FFFFFF', marginBottom: 6 }),
55
- rating: Object.assign(Object.assign({}, typography.styles.text14Medium), { color: '#BFD5F6' }),
56
- });
23
+ const createStyles = (colors, typography, themeName) => {
24
+ const isDark = themeName === 'dark';
25
+ return react_native_1.StyleSheet.create({
26
+ wrapper: {
27
+ paddingHorizontal: 2,
28
+ paddingTop: 12,
29
+ paddingBottom: 8,
30
+ },
31
+ card: {
32
+ backgroundColor: isDark ? colors.headerBg : '#FFFFFF',
33
+ borderRadius: 24,
34
+ padding: 4,
35
+ borderWidth: 1,
36
+ borderColor: isDark ? colors.headerBg + 'AA' : colors.border + '40',
37
+ shadowColor: colors.shadow || '#000',
38
+ shadowOpacity: isDark ? 0.2 : 0.1,
39
+ shadowOffset: { width: 0, height: 4 },
40
+ shadowRadius: 12,
41
+ elevation: 4,
42
+ },
43
+ innerBorder: {
44
+ borderRadius: 20,
45
+ padding: 16,
46
+ flexDirection: 'row',
47
+ alignItems: 'center',
48
+ },
49
+ logo: {
50
+ width: 45,
51
+ height: 45,
52
+ marginRight: 12,
53
+ },
54
+ info: {
55
+ flex: 1,
56
+ },
57
+ name: Object.assign(Object.assign({}, typography.styles.h2), { color: isDark ? colors.headerText : colors.text, marginBottom: 6 }),
58
+ rating: Object.assign(Object.assign({}, typography.styles.text14Medium), { color: isDark ? colors.headerText : colors.primary, opacity: isDark ? 0.8 : 1 }),
59
+ });
60
+ };
57
61
  exports.default = CompanyHeader;
@@ -24,9 +24,10 @@ const InterestRateCard = ({ interestRate, maturityAmount, children, }) => {
24
24
  const createStyles = (colors, typography, themeName) => react_native_1.StyleSheet.create({
25
25
  card: {
26
26
  backgroundColor: themeName === 'dark' ? colors.inputBackground : 'rgba(0,235,180,0.1)',
27
- padding: 16,
27
+ paddingVertical: 16,
28
28
  borderRadius: 4,
29
29
  marginTop: 25,
30
+ paddingHorizontal: 12,
30
31
  },
31
32
  row: {
32
33
  flexDirection: 'row',
@@ -39,7 +40,7 @@ const createStyles = (colors, typography, themeName) => react_native_1.StyleShee
39
40
  flex: 1,
40
41
  alignItems: 'flex-end',
41
42
  },
42
- label: Object.assign(Object.assign({}, typography.styles.bodySmall), { color: themeName === 'dark' ? colors.labelColor : colors.textLight, marginBottom: 4 }),
43
+ label: Object.assign(Object.assign({}, typography.styles.bodySmall), { color: colors.textLight, marginBottom: 4 }),
43
44
  value: Object.assign(Object.assign({}, typography.styles.bodyLarge), { fontWeight: '600', color: colors.text }),
44
45
  valueRight: Object.assign(Object.assign({}, typography.styles.bodyLarge), { fontWeight: '700', color: colors.text }),
45
46
  });
@@ -35,20 +35,21 @@ const PaymentDetailsCard = ({ companyName, amount, fdRate, tenure, interestPayou
35
35
  };
36
36
  const createStyles = (colors, typography, themeName) => {
37
37
  var _a, _b, _c, _d;
38
+ const isDark = themeName === 'dark';
38
39
  return react_native_1.StyleSheet.create({
39
40
  card: {
40
- backgroundColor: themeName === 'dark' ? '#0B2940' : '#0B2940',
41
+ backgroundColor: isDark ? colors.headerBg : 'rgba(0,235,180,0.1)',
41
42
  borderRadius: 16,
42
43
  padding: 20,
43
44
  alignItems: 'flex-start',
44
45
  marginHorizontal: 16,
45
- borderWidth: 1,
46
- borderColor: '#1F4C73',
46
+ borderWidth: isDark ? 1 : 0,
47
+ borderColor: isDark ? colors.headerBg + 'AA' : 'transparent',
47
48
  },
48
- companyName: Object.assign(Object.assign({}, typography.styles.h3), { color: '#FFFFFF', marginBottom: 12 }),
49
+ companyName: Object.assign(Object.assign({}, typography.styles.h3), { color: isDark ? colors.headerText : colors.text, marginBottom: 12 }),
49
50
  companyLine: {
50
51
  height: 1,
51
- backgroundColor: '#1F4C73',
52
+ backgroundColor: isDark ? colors.headerText + '33' : colors.border + '40',
52
53
  marginBottom: 12,
53
54
  width: '100%',
54
55
  },
@@ -61,8 +62,8 @@ const createStyles = (colors, typography, themeName) => {
61
62
  alignItems: 'center',
62
63
  paddingVertical: 8,
63
64
  },
64
- detailLabel: Object.assign(Object.assign({}, typography.styles.text12Regular), { color: '#BFD5F6' }),
65
- detailValue: Object.assign(Object.assign({}, typography.styles.text14Medium), { color: '#FFFFFF', lineHeight: ((_d = (_b = (_a = typography.styles.text14Medium) === null || _a === void 0 ? void 0 : _a.lineHeight) !== null && _b !== void 0 ? _b : (_c = typography.styles.text14Medium) === null || _c === void 0 ? void 0 : _c.fontSize) !== null && _d !== void 0 ? _d : 14) + 3 }),
65
+ detailLabel: Object.assign(Object.assign({}, typography.styles.text12Regular), { color: isDark ? colors.headerText : colors.text, opacity: isDark ? 0.7 : 1 }),
66
+ detailValue: Object.assign(Object.assign({}, typography.styles.text14Medium), { color: isDark ? colors.headerText : colors.text, lineHeight: ((_d = (_b = (_a = typography.styles.text14Medium) === null || _a === void 0 ? void 0 : _a.lineHeight) !== null && _b !== void 0 ? _b : (_c = typography.styles.text14Medium) === null || _c === void 0 ? void 0 : _c.fontSize) !== null && _d !== void 0 ? _d : 14) + 3 }),
66
67
  });
67
68
  };
68
69
  exports.default = PaymentDetailsCard;
@@ -110,13 +110,13 @@ const createStyles = (colors, typography, spacing, themeName) => react_native_1.
110
110
  },
111
111
  continueButton: {
112
112
  height: 50,
113
- backgroundColor: '#007AFF',
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: 'white', 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;
@@ -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,
@@ -39,7 +39,7 @@ const createStyles = (colors, typography, themeName) => react_native_1.StyleShee
39
39
  flexDirection: 'column',
40
40
  flex: 1,
41
41
  },
42
- titleLight: Object.assign(Object.assign({}, typography.styles.bodyMedium), { color: themeName === 'dark' ? '#C4CDD3' : colors.textLight, marginBottom: 0 }),
43
- title: Object.assign(Object.assign({}, typography.styles.bodyMedium), { color: themeName === 'dark' ? colors.labelColor : colors.textLight, marginBottom: 0 }),
42
+ titleLight: Object.assign(Object.assign({}, typography.styles.bodyMedium), { color: colors.textLight, marginBottom: 0 }),
43
+ title: Object.assign(Object.assign({}, typography.styles.bodyMedium), { color: colors.text, marginBottom: 0 }),
44
44
  });
45
45
  exports.default = TrustBox;
@@ -5,6 +5,35 @@ export interface EnvironmentData {
5
5
  encryptionKey?: string;
6
6
  enableLogging?: boolean;
7
7
  }
8
+ /**
9
+ * Custom color overrides that can be passed during SDK initialization.
10
+ * Any color defined here will override the corresponding color from the selected theme.
11
+ */
12
+ export interface CustomColors {
13
+ primary?: string;
14
+ tabSelected?: string;
15
+ headerBg?: string;
16
+ headerText?: string;
17
+ success?: string;
18
+ textSecondary?: string;
19
+ border?: string;
20
+ shadow?: string;
21
+ background?: string;
22
+ surface?: string;
23
+ text?: string;
24
+ textLight?: string;
25
+ error?: string;
26
+ warning?: string;
27
+ info?: string;
28
+ muted?: string;
29
+ inputBackground?: string;
30
+ inputBorder?: string;
31
+ placeholderColor?: string;
32
+ labelColor?: string;
33
+ buttonBackground?: string;
34
+ buttonTextColor?: string;
35
+ cancelButtonBg?: string;
36
+ }
8
37
  export interface AppData {
9
38
  id: string;
10
39
  name: string;
@@ -12,7 +41,7 @@ export interface AppData {
12
41
  gender: string;
13
42
  mobNo: string;
14
43
  email: string;
15
- panNumber: string;
44
+ panNumber?: string;
16
45
  address?: string;
17
46
  area?: string;
18
47
  city?: string;
@@ -26,6 +55,7 @@ export interface AppData {
26
55
  maritalStatus?: string;
27
56
  userReferenceId?: string;
28
57
  eventNotifyUrl?: string;
58
+ startFDAlertMessage?: string;
29
59
  }
30
60
  /**
31
61
  * Initialize the SDK with app data from the main application
@@ -61,7 +91,7 @@ export declare const getUserInfoForAPI: () => {
61
91
  name: string;
62
92
  dob: string;
63
93
  gender: string;
64
- panNumber: string;
94
+ panNumber: string | undefined;
65
95
  mobileNumber: string;
66
96
  email: string;
67
97
  eventNotifyUrl: string | undefined;
@@ -100,6 +130,27 @@ export declare const validateAppData: (appData: AppData) => {
100
130
  isValid: boolean;
101
131
  errors: string[];
102
132
  };
133
+ /**
134
+ * Set custom color overrides for the SDK theme.
135
+ * Colors defined here will override the corresponding color from the selected theme (primary/dark/corporate).
136
+ * Call this before rendering the SDK navigator.
137
+ *
138
+ * @example
139
+ * setSDKColors({
140
+ * primary: '#FF5722',
141
+ * headerBg: '#1976D2',
142
+ * buttonBackground: '#1976D2',
143
+ * });
144
+ */
145
+ export declare const setSDKColors: (customColors: CustomColors) => void;
146
+ /**
147
+ * Get the current custom color overrides
148
+ */
149
+ export declare const getSDKColors: () => CustomColors | null;
150
+ /**
151
+ * Clear custom color overrides
152
+ */
153
+ export declare const clearSDKColors: () => void;
103
154
  /**
104
155
  * Clear app data (for testing or logout scenarios)
105
156
  */
@@ -34,11 +34,13 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  };
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.clearAllData = exports.clearEnvironmentData = exports.clearAppData = exports.validateAppData = exports.getUserDemographics = exports.getUserInfoForAPI = exports.isEnvironmentInitialized = exports.isSDKInitialized = exports.getEnvironmentData = exports.getAppData = exports.initializeEnvironment = exports.initializeSDK = void 0;
37
+ exports.clearAllData = exports.clearEnvironmentData = exports.clearAppData = exports.clearSDKColors = exports.getSDKColors = exports.setSDKColors = exports.validateAppData = exports.getUserDemographics = exports.getUserInfoForAPI = exports.isEnvironmentInitialized = exports.isSDKInitialized = exports.getEnvironmentData = exports.getAppData = exports.initializeEnvironment = exports.initializeSDK = void 0;
38
38
  // Global app data storage
39
39
  let globalAppData = null;
40
40
  // Global environment data storage
41
41
  let globalEnvironmentData = null;
42
+ // Global custom color overrides
43
+ let globalCustomColors = null;
42
44
  /**
43
45
  * Initialize the SDK with app data from the main application
44
46
  * This should be called when the SDK starts
@@ -52,7 +54,7 @@ const initializeSDK = (appData, onValidationError) => {
52
54
  { field: 'gender', label: 'Gender' },
53
55
  { field: 'mobNo', label: 'Mobile Number' },
54
56
  { field: 'email', label: 'Email' },
55
- { field: 'panNumber', label: 'PAN Number' },
57
+ // { field: 'panNumber', label: 'PAN Number' },
56
58
  ];
57
59
  const missingFields = [];
58
60
  requiredFields.forEach(({ field, label }) => {
@@ -201,7 +203,7 @@ exports.getUserDemographics = getUserDemographics;
201
203
  * Validate app data
202
204
  */
203
205
  const validateAppData = (appData) => {
204
- var _a, _b, _c, _d, _e, _f;
206
+ var _a, _b, _c, _d, _e;
205
207
  const errors = [];
206
208
  // Required fields validation
207
209
  if (!((_a = appData.id) === null || _a === void 0 ? void 0 : _a.trim()))
@@ -216,13 +218,13 @@ const validateAppData = (appData) => {
216
218
  errors.push('Mobile number is required');
217
219
  if (!((_e = appData.email) === null || _e === void 0 ? void 0 : _e.trim()))
218
220
  errors.push('Email is required');
219
- if (!((_f = appData.panNumber) === null || _f === void 0 ? void 0 : _f.trim()))
220
- 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');
221
223
  // Format validation (only if fields are provided)
222
224
  if (appData.dob && !/^\d{4}-\d{2}-\d{2}$/.test(appData.dob)) {
223
225
  errors.push('Date of birth must be in YYYY-MM-DD format');
224
226
  }
225
- 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)) {
226
228
  errors.push('PAN number must be in valid format (e.g., ABCDE1234F)');
227
229
  }
228
230
  if (appData.mobNo && !/^[6-9]\d{9}$/.test(appData.mobNo)) {
@@ -240,6 +242,36 @@ const validateAppData = (appData) => {
240
242
  };
241
243
  };
242
244
  exports.validateAppData = validateAppData;
245
+ /**
246
+ * Set custom color overrides for the SDK theme.
247
+ * Colors defined here will override the corresponding color from the selected theme (primary/dark/corporate).
248
+ * Call this before rendering the SDK navigator.
249
+ *
250
+ * @example
251
+ * setSDKColors({
252
+ * primary: '#FF5722',
253
+ * headerBg: '#1976D2',
254
+ * buttonBackground: '#1976D2',
255
+ * });
256
+ */
257
+ const setSDKColors = (customColors) => {
258
+ globalCustomColors = customColors;
259
+ };
260
+ exports.setSDKColors = setSDKColors;
261
+ /**
262
+ * Get the current custom color overrides
263
+ */
264
+ const getSDKColors = () => {
265
+ return globalCustomColors;
266
+ };
267
+ exports.getSDKColors = getSDKColors;
268
+ /**
269
+ * Clear custom color overrides
270
+ */
271
+ const clearSDKColors = () => {
272
+ globalCustomColors = null;
273
+ };
274
+ exports.clearSDKColors = clearSDKColors;
243
275
  /**
244
276
  * Clear app data (for testing or logout scenarios)
245
277
  */
@@ -260,5 +292,6 @@ exports.clearEnvironmentData = clearEnvironmentData;
260
292
  const clearAllData = () => {
261
293
  globalAppData = null;
262
294
  globalEnvironmentData = null;
295
+ globalCustomColors = null;
263
296
  };
264
297
  exports.clearAllData = clearAllData;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * usePaymentSSE - React Native SSE hook for payment status tracking.
3
+ *
4
+ * Uses XMLHttpRequest instead of fetch streams because
5
+ * React Native does not support ReadableStream / getReader().
6
+ *
7
+ * Usage:
8
+ * const { start, stop } = usePaymentSSE();
9
+ * start(applicationId, {
10
+ * onPaymentStatus: (status, payload) => { ... },
11
+ * onError: (error) => { ... },
12
+ * });
13
+ * stop(); // to cancel
14
+ */
15
+ interface UsePaymentSSECallbacks {
16
+ onPaymentStatus: (status: string, payload: Record<string, unknown>) => void;
17
+ onError?: (error: any) => void;
18
+ onConnected?: () => void;
19
+ }
20
+ export declare function usePaymentSSE(): {
21
+ start: (applicationId: string, callbacks: UsePaymentSSECallbacks) => void;
22
+ stop: () => void;
23
+ };
24
+ export {};
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ /**
3
+ * usePaymentSSE - React Native SSE hook for payment status tracking.
4
+ *
5
+ * Uses XMLHttpRequest instead of fetch streams because
6
+ * React Native does not support ReadableStream / getReader().
7
+ *
8
+ * Usage:
9
+ * const { start, stop } = usePaymentSSE();
10
+ * start(applicationId, {
11
+ * onPaymentStatus: (status, payload) => { ... },
12
+ * onError: (error) => { ... },
13
+ * });
14
+ * stop(); // to cancel
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.usePaymentSSE = usePaymentSSE;
18
+ const react_1 = require("react");
19
+ const sseParser_1 = require("../utils/sseParser");
20
+ const appDataConfig_1 = require("../config/appDataConfig");
21
+ const apiConfig_1 = require("../config/apiConfig");
22
+ // -- Hook --
23
+ function usePaymentSSE() {
24
+ // Store the XMLHttpRequest so we can abort it later
25
+ const xhrRef = (0, react_1.useRef)(null);
26
+ // Track how much of responseText we've already parsed
27
+ const lastIndexRef = (0, react_1.useRef)(0);
28
+ // SSE text buffer (persists across onprogress calls)
29
+ const bufferRef = (0, react_1.useRef)({ value: '' });
30
+ const start = (0, react_1.useCallback)((applicationId, callbacks) => {
31
+ var _a;
32
+ // Clean up any existing connection first
33
+ if (xhrRef.current) {
34
+ xhrRef.current.abort();
35
+ xhrRef.current = null;
36
+ }
37
+ // Reset parsing state
38
+ lastIndexRef.current = 0;
39
+ bufferRef.current = { value: '' };
40
+ // 1. Build the SSE endpoint URL
41
+ const envData = (0, appDataConfig_1.getEnvironmentData)();
42
+ const apiConfig = (0, apiConfig_1.getApiConfig)();
43
+ const baseUrl = (envData === null || envData === void 0 ? void 0 : envData.apiBaseUrl) || (apiConfig === null || apiConfig === void 0 ? void 0 : apiConfig.baseUrl) || '';
44
+ const url = `${baseUrl}/events?applicationId=${encodeURIComponent(applicationId)}`;
45
+ // 2. Create XMLHttpRequest
46
+ const xhr = new XMLHttpRequest();
47
+ xhrRef.current = xhr;
48
+ xhr.open('GET', url);
49
+ // 3. Set headers (same headers your baseApi interceptor uses)
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']) || '';
51
+ if (apiKey) {
52
+ xhr.setRequestHeader('x-api-key', apiKey);
53
+ }
54
+ xhr.setRequestHeader('Accept', 'text/event-stream');
55
+ xhr.setRequestHeader('Cache-Control', 'no-cache');
56
+ // 4. Handle incoming data
57
+ xhr.onprogress = () => {
58
+ var _a;
59
+ // responseText contains ALL text received so far
60
+ // Only parse the NEW text since last call
61
+ const newText = xhr.responseText.slice(lastIndexRef.current);
62
+ lastIndexRef.current = xhr.responseText.length;
63
+ if (!newText)
64
+ return;
65
+ // Parse new text into SSE events
66
+ const events = (0, sseParser_1.parseSSEBuffer)(bufferRef.current, newText);
67
+ // Handle each event
68
+ for (const { eventType, data } of events) {
69
+ if (eventType === 'fd_payment_status') {
70
+ try {
71
+ const parsed = JSON.parse(data);
72
+ const status = ((_a = parsed.paymentStatus) !== null && _a !== void 0 ? _a : '').toUpperCase();
73
+ callbacks.onPaymentStatus(status, parsed);
74
+ }
75
+ catch (parseError) {
76
+ if (__DEV__) {
77
+ console.error('[usePaymentSSE] Failed to parse event data:', parseError);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ };
83
+ // 5. Handle successful connection
84
+ xhr.onreadystatechange = () => {
85
+ var _a, _b;
86
+ if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
87
+ if (xhr.status === 200) {
88
+ (_a = callbacks.onConnected) === null || _a === void 0 ? void 0 : _a.call(callbacks);
89
+ if (__DEV__) {
90
+ console.log('[usePaymentSSE] Connected to SSE stream');
91
+ }
92
+ }
93
+ else {
94
+ if (__DEV__) {
95
+ console.error('[usePaymentSSE] Bad status:', xhr.status);
96
+ }
97
+ (_b = callbacks.onError) === null || _b === void 0 ? void 0 : _b.call(callbacks, new Error(`SSE response status: ${xhr.status}`));
98
+ }
99
+ }
100
+ };
101
+ // 6. Handle errors
102
+ xhr.onerror = () => {
103
+ var _a;
104
+ if (__DEV__) {
105
+ console.error('[usePaymentSSE] Connection error');
106
+ }
107
+ (_a = callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, new Error('SSE connection failed'));
108
+ };
109
+ // 7. Handle timeout
110
+ xhr.ontimeout = () => {
111
+ var _a;
112
+ if (__DEV__) {
113
+ console.error('[usePaymentSSE] Connection timed out');
114
+ }
115
+ (_a = callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, new Error('SSE connection timed out'));
116
+ };
117
+ // 8. Send the request (connection stays open)
118
+ xhr.send();
119
+ if (__DEV__) {
120
+ console.log('[usePaymentSSE] Starting SSE connection to:', url);
121
+ }
122
+ }, []);
123
+ const stop = (0, react_1.useCallback)(() => {
124
+ if (xhrRef.current) {
125
+ xhrRef.current.abort();
126
+ xhrRef.current = null;
127
+ if (__DEV__) {
128
+ console.log('[usePaymentSSE] SSE connection stopped');
129
+ }
130
+ }
131
+ lastIndexRef.current = 0;
132
+ bufferRef.current = { value: '' };
133
+ }, []);
134
+ return { start, stop };
135
+ }
@@ -0,0 +1,25 @@
1
+ export type PaymentStatus = 'success' | 'failed' | 'pending';
2
+ interface ReverseFeedOverrides {
3
+ providerId?: string;
4
+ workflowInstanceId?: string;
5
+ userreferenceid?: string;
6
+ applicationid?: string;
7
+ entityid?: string;
8
+ transactionId?: string;
9
+ }
10
+ interface UsePaymentStatusTimerConfig {
11
+ transactionId?: string;
12
+ overrides?: ReverseFeedOverrides;
13
+ initialDelayMs?: number;
14
+ repeatDelayMs?: number;
15
+ maxDurationMs?: number;
16
+ onStatusUpdate?: (status: PaymentStatus, response?: any) => void;
17
+ }
18
+ export declare const usePaymentStatusTimer: ({ transactionId, overrides, initialDelayMs, repeatDelayMs, maxDurationMs, onStatusUpdate, }: UsePaymentStatusTimerConfig) => {
19
+ startTimer: () => void;
20
+ stopTimer: () => void;
21
+ triggerStatusCheck: () => Promise<PaymentStatus | undefined>;
22
+ isCheckingStatus: boolean;
23
+ latestStatusRef: import("react").MutableRefObject<PaymentStatus>;
24
+ };
25
+ export {};