@finspringinnovations/fdsdk 0.0.5 → 0.0.7

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.
@@ -2,6 +2,10 @@ import React from 'react';
2
2
  interface InterestRateCardProps {
3
3
  interestRate: string;
4
4
  maturityAmount: string;
5
+ /** Interest payout amount (e.g. from sdrCalc[0].intPayAmt), shown below Interest Rate */
6
+ intPayAmt?: string;
7
+ /** Total interest earnings (e.g. from sdrCalc[0].totalInterestEarnings), shown below On Maturity */
8
+ totalInterestEarnings?: string;
5
9
  children?: React.ReactNode;
6
10
  }
7
11
  declare const InterestRateCard: React.FC<InterestRateCardProps>;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const react_1 = __importDefault(require("react"));
7
7
  const react_native_1 = require("react-native");
8
8
  const ThemeContext_1 = require("../theme/ThemeContext");
9
- const InterestRateCard = ({ interestRate, maturityAmount, children, }) => {
9
+ const InterestRateCard = ({ interestRate, maturityAmount, intPayAmt, totalInterestEarnings, children, }) => {
10
10
  const colors = (0, ThemeContext_1.useColors)();
11
11
  const typography = (0, ThemeContext_1.useTypography)();
12
12
  const { themeName } = (0, ThemeContext_1.useTheme)();
@@ -17,8 +17,15 @@ const InterestRateCard = ({ interestRate, maturityAmount, children, }) => {
17
17
  react_1.default.createElement(react_native_1.Text, { style: styles.label }, "Interest Rate"),
18
18
  react_1.default.createElement(react_native_1.Text, { style: styles.value }, interestRate)),
19
19
  react_1.default.createElement(react_native_1.View, { style: styles.right },
20
- react_1.default.createElement(react_native_1.Text, { style: styles.label }, "On Maturity"),
20
+ react_1.default.createElement(react_native_1.Text, { style: styles.label }, "Amount On Maturity"),
21
21
  react_1.default.createElement(react_native_1.Text, { style: styles.valueRight }, maturityAmount))),
22
+ (intPayAmt != null || totalInterestEarnings != null) && (react_1.default.createElement(react_native_1.View, { style: [styles.row, styles.secondRow] },
23
+ react_1.default.createElement(react_native_1.View, { style: styles.left }, intPayAmt != null && (react_1.default.createElement(react_1.default.Fragment, null,
24
+ react_1.default.createElement(react_native_1.Text, { style: styles.label }, "Total Interest"),
25
+ react_1.default.createElement(react_native_1.Text, { style: styles.value }, intPayAmt)))),
26
+ react_1.default.createElement(react_native_1.View, { style: styles.right }, totalInterestEarnings != null && (react_1.default.createElement(react_1.default.Fragment, null,
27
+ react_1.default.createElement(react_native_1.Text, { style: styles.label }, "On Maturity Interest"),
28
+ react_1.default.createElement(react_native_1.Text, { style: styles.valueRight }, totalInterestEarnings)))))),
22
29
  children));
23
30
  };
24
31
  const createStyles = (colors, typography, themeName) => react_native_1.StyleSheet.create({
@@ -33,6 +40,9 @@ const createStyles = (colors, typography, themeName) => react_native_1.StyleShee
33
40
  flexDirection: 'row',
34
41
  justifyContent: 'space-between',
35
42
  },
43
+ secondRow: {
44
+ marginTop: 12,
45
+ },
36
46
  left: {
37
47
  flex: 1,
38
48
  },
@@ -42,6 +52,6 @@ const createStyles = (colors, typography, themeName) => react_native_1.StyleShee
42
52
  },
43
53
  label: Object.assign(Object.assign({}, typography.styles.bodySmall), { color: colors.textLight, marginBottom: 4 }),
44
54
  value: Object.assign(Object.assign({}, typography.styles.bodyLarge), { fontWeight: '600', color: colors.text }),
45
- valueRight: Object.assign(Object.assign({}, typography.styles.bodyLarge), { fontWeight: '700', color: colors.text }),
55
+ valueRight: Object.assign(Object.assign({}, typography.styles.bodyLarge), { fontWeight: '700', color: colors.text, alignItems: 'center', justifyContent: 'center', marginRight: 10 }),
46
56
  });
47
57
  exports.default = InterestRateCard;
@@ -4,6 +4,11 @@ export interface EnvironmentData {
4
4
  apiKey?: string;
5
5
  encryptionKey?: string;
6
6
  enableLogging?: boolean;
7
+ sseMinimalLogs?: boolean;
8
+ enableVerboseSSELogging?: boolean;
9
+ sseReconnectAttempts?: number;
10
+ sseReconnectDelayMs?: number;
11
+ sseConnectTimeoutMs?: number;
7
12
  }
8
13
  /**
9
14
  * Custom color overrides that can be passed during SDK initialization.
@@ -16,6 +16,7 @@
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.usePaymentSSE = usePaymentSSE;
18
18
  const react_1 = require("react");
19
+ const react_native_1 = require("react-native");
19
20
  const sseParser_1 = require("../utils/sseParser");
20
21
  const appDataConfig_1 = require("../config/appDataConfig");
21
22
  const apiConfig_1 = require("../config/apiConfig");
@@ -25,17 +26,32 @@ const encryptionConfig_1 = require("../config/encryptionConfig");
25
26
  function usePaymentSSE() {
26
27
  // Store the XMLHttpRequest so we can abort it later
27
28
  const xhrRef = (0, react_1.useRef)(null);
29
+ const reconnectTimerRef = (0, react_1.useRef)(null);
30
+ const connectWatchdogTimerRef = (0, react_1.useRef)(null);
31
+ const stopRequestedRef = (0, react_1.useRef)(false);
32
+ const connectionIdRef = (0, react_1.useRef)(0);
28
33
  // Track how much of responseText we've already parsed
29
34
  const lastIndexRef = (0, react_1.useRef)(0);
30
35
  // SSE text buffer (persists across onprogress calls)
31
36
  const bufferRef = (0, react_1.useRef)({ value: '' });
32
37
  const start = (0, react_1.useCallback)((applicationId, callbacks, extraHeaders) => {
33
- var _a;
38
+ var _a, _b, _c;
39
+ connectionIdRef.current += 1;
40
+ const connectionId = connectionIdRef.current;
41
+ stopRequestedRef.current = false;
34
42
  // Clean up any existing connection first
35
43
  if (xhrRef.current) {
36
44
  xhrRef.current.abort();
37
45
  xhrRef.current = null;
38
46
  }
47
+ if (reconnectTimerRef.current) {
48
+ clearTimeout(reconnectTimerRef.current);
49
+ reconnectTimerRef.current = null;
50
+ }
51
+ if (connectWatchdogTimerRef.current) {
52
+ clearTimeout(connectWatchdogTimerRef.current);
53
+ connectWatchdogTimerRef.current = null;
54
+ }
39
55
  // Reset parsing state
40
56
  lastIndexRef.current = 0;
41
57
  bufferRef.current = { value: '' };
@@ -44,172 +60,291 @@ function usePaymentSSE() {
44
60
  const apiConfig = (0, apiConfig_1.getApiConfig)();
45
61
  const baseUrl = ((envData === null || envData === void 0 ? void 0 : envData.apiBaseUrl) || (apiConfig === null || apiConfig === void 0 ? void 0 : apiConfig.baseUrl) || '').replace(/\/$/, '');
46
62
  const url = baseUrl ? `${baseUrl}/events?applicationId=${encodeURIComponent(applicationId)}` : '';
63
+ const minimalLogs = (envData === null || envData === void 0 ? void 0 : envData.sseMinimalLogs) === true;
64
+ const verboseSSE = (envData === null || envData === void 0 ? void 0 : envData.enableVerboseSSELogging) !== false;
65
+ const maxReconnectAttempts = Number((_a = envData === null || envData === void 0 ? void 0 : envData.sseReconnectAttempts) !== null && _a !== void 0 ? _a : 4);
66
+ const reconnectDelayMs = Number((_b = envData === null || envData === void 0 ? void 0 : envData.sseReconnectDelayMs) !== null && _b !== void 0 ? _b : 2000);
67
+ const connectWatchdogMs = Number((_c = envData === null || envData === void 0 ? void 0 : envData.sseConnectTimeoutMs) !== null && _c !== void 0 ? _c : 15000);
68
+ const rnVersion = (() => {
69
+ var _a;
70
+ const v = (_a = react_native_1.Platform === null || react_native_1.Platform === void 0 ? void 0 : react_native_1.Platform.constants) === null || _a === void 0 ? void 0 : _a.reactNativeVersion;
71
+ return v ? `${v.major}.${v.minor}.${v.patch}` : 'unknown';
72
+ })();
73
+ const jsEngine = (global === null || global === void 0 ? void 0 : global.HermesInternal) ? 'hermes' : 'jsc/other';
74
+ let reconnectAttempts = 0;
75
+ let connected = false;
47
76
  if (!url) {
48
- if (__DEV__)
77
+ if (__DEV__ && verboseSSE)
49
78
  console.warn('[usePaymentSSE] No base URL, skipping SSE');
50
79
  return;
51
80
  }
52
- // 2. Create XMLHttpRequest
53
- const xhr = new XMLHttpRequest();
54
- xhrRef.current = xhr;
55
- xhr.open('GET', url);
56
- // 3. Set headers (same headers your baseApi interceptor uses)
57
- 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']) || '';
58
- if (apiKey) {
59
- xhr.setRequestHeader('x-api-key', apiKey);
60
- }
61
- // Add secure headers (Content-Type, User-Agent, etc.)
62
- const secureHeaders = (0, apiConfig_1.getSecureHeaders)();
63
- Object.entries(secureHeaders).forEach(([key, value]) => {
64
- if (key.toLowerCase() !== 'content-type') {
65
- xhr.setRequestHeader(key, value);
81
+ const scheduleReconnect = (reason) => {
82
+ var _a;
83
+ if (stopRequestedRef.current || connectionIdRef.current !== connectionId)
84
+ return;
85
+ if (connectWatchdogTimerRef.current) {
86
+ clearTimeout(connectWatchdogTimerRef.current);
87
+ connectWatchdogTimerRef.current = null;
66
88
  }
67
- });
68
- // SSE-specific headers
69
- xhr.setRequestHeader('Accept', 'text/event-stream');
70
- xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
71
- xhr.setRequestHeader('Pragma', 'no-cache');
72
- xhr.setRequestHeader('Expires', '0');
73
- // Add userreferenceid header (required for authentication) - both cases
74
- try {
75
- const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
76
- const userRefId = (userInfo === null || userInfo === void 0 ? void 0 : userInfo.userReferenceId) || (userInfo === null || userInfo === void 0 ? void 0 : userInfo.id);
77
- if (userRefId) {
78
- xhr.setRequestHeader('userreferenceid', userRefId);
79
- xhr.setRequestHeader('userReferenceId', userRefId);
89
+ if (reconnectAttempts >= maxReconnectAttempts) {
90
+ (_a = callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, new Error(reason));
91
+ return;
80
92
  }
81
- }
82
- catch (_b) {
83
- if (__DEV__)
84
- console.warn('[usePaymentSSE] Could not get userReferenceId');
85
- }
86
- // Add provider header (required for authentication)
87
- if (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.providerId) {
88
- xhr.setRequestHeader('provider', extraHeaders.providerId);
89
- }
90
- // Add onboarding headers - both camelCase and lowercase variants
91
- if (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.workflowInstanceId) {
92
- xhr.setRequestHeader('workflowInstanceId', extraHeaders.workflowInstanceId);
93
- }
94
- if (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.applicationId) {
95
- xhr.setRequestHeader('applicationId', extraHeaders.applicationId);
96
- xhr.setRequestHeader('applicationid', extraHeaders.applicationId);
97
- }
98
- if (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.entityid) {
99
- xhr.setRequestHeader('entityid', extraHeaders.entityid);
100
- xhr.setRequestHeader('entityId', extraHeaders.entityid);
101
- }
102
- // 4. Handle incoming data
103
- xhr.onprogress = () => {
104
- // responseText contains ALL text received so far
105
- // Only parse the NEW text since last call
106
- const newText = xhr.responseText.slice(lastIndexRef.current);
107
- lastIndexRef.current = xhr.responseText.length;
108
- if (!newText)
93
+ reconnectAttempts += 1;
94
+ const delay = reconnectDelayMs * reconnectAttempts;
95
+ if (__DEV__ && !minimalLogs) {
96
+ console.warn(`[usePaymentSSE] Reconnecting (${reconnectAttempts}/${maxReconnectAttempts}) in ${delay}ms`);
97
+ console.log('[usePaymentSSE][runtime]', {
98
+ reason,
99
+ platform: react_native_1.Platform.OS,
100
+ osVersion: String(react_native_1.Platform.Version),
101
+ rnVersion,
102
+ jsEngine,
103
+ applicationId,
104
+ });
105
+ }
106
+ if (reconnectTimerRef.current) {
107
+ clearTimeout(reconnectTimerRef.current);
108
+ }
109
+ reconnectTimerRef.current = setTimeout(() => {
110
+ if (stopRequestedRef.current || connectionIdRef.current !== connectionId)
111
+ return;
112
+ openConnection();
113
+ }, delay);
114
+ };
115
+ const openConnection = () => {
116
+ var _a;
117
+ if (stopRequestedRef.current || connectionIdRef.current !== connectionId)
109
118
  return;
110
- // Parse new text into SSE events
111
- const events = (0, sseParser_1.parseSSEBuffer)(bufferRef.current, newText);
112
- // Handle each event (support encrypted payload like web)
113
- const handleEventData = async (rawData) => {
114
- var _a, _b;
115
- try {
116
- const parsed = JSON.parse(rawData || '{}');
117
- let payload = parsed;
118
- if (typeof (parsed === null || parsed === void 0 ? void 0 : parsed.encryptedResponse) === 'string' && parsed.encryptedResponse) {
119
- try {
120
- const config = (0, encryptionConfig_1.getEncryptionConfig)();
121
- const decrypted = await (0, encryption_1.decryptResponse)({ encryptedResponse: parsed.encryptedResponse }, config);
122
- payload = (decrypted || {});
123
- }
124
- catch (_c) {
125
- if (__DEV__)
126
- console.warn('[usePaymentSSE] Decrypt failed, using raw');
127
- }
128
- }
129
- const status = String((_b = (_a = payload === null || payload === void 0 ? void 0 : payload.paymentStatus) !== null && _a !== void 0 ? _a : payload === null || payload === void 0 ? void 0 : payload.payment_status) !== null && _b !== void 0 ? _b : '').toUpperCase();
130
- if (status)
131
- callbacks.onPaymentStatus(status, payload);
119
+ connected = false;
120
+ // 2. Create XMLHttpRequest
121
+ const xhr = new XMLHttpRequest();
122
+ xhrRef.current = xhr;
123
+ xhr.open('GET', url);
124
+ // 3. Set headers (same headers your baseApi interceptor uses)
125
+ 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']) || '';
126
+ if (apiKey) {
127
+ xhr.setRequestHeader('x-api-key', apiKey);
128
+ xhr.setRequestHeader('X-API-Key', apiKey);
129
+ }
130
+ // Add secure headers (Content-Type, User-Agent, etc.)
131
+ const secureHeaders = (0, apiConfig_1.getSecureHeaders)();
132
+ Object.entries(secureHeaders).forEach(([key, value]) => {
133
+ if (key.toLowerCase() !== 'content-type') {
134
+ xhr.setRequestHeader(key, value);
132
135
  }
133
- catch (parseError) {
134
- if (__DEV__)
135
- console.error('[usePaymentSSE] Failed to parse event data:', parseError);
136
+ });
137
+ // SSE-specific headers
138
+ xhr.setRequestHeader('Accept', 'text/event-stream');
139
+ xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
140
+ xhr.setRequestHeader('Pragma', 'no-cache');
141
+ xhr.setRequestHeader('Expires', '0');
142
+ // Add userreferenceid header (required for authentication) - both cases
143
+ try {
144
+ const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
145
+ const userRefId = (userInfo === null || userInfo === void 0 ? void 0 : userInfo.userReferenceId) || (userInfo === null || userInfo === void 0 ? void 0 : userInfo.id);
146
+ if (userRefId) {
147
+ xhr.setRequestHeader('userreferenceid', userRefId);
148
+ xhr.setRequestHeader('userReferenceId', userRefId);
149
+ }
150
+ }
151
+ catch (_b) {
152
+ if (__DEV__ && verboseSSE)
153
+ console.warn('[usePaymentSSE] Could not get userReferenceId');
154
+ }
155
+ // Add provider header variants
156
+ if (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.providerId) {
157
+ xhr.setRequestHeader('provider', extraHeaders.providerId);
158
+ xhr.setRequestHeader('providerid', extraHeaders.providerId);
159
+ xhr.setRequestHeader('providerId', extraHeaders.providerId);
160
+ }
161
+ // Add onboarding headers - both camelCase and lowercase variants
162
+ if (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.workflowInstanceId) {
163
+ xhr.setRequestHeader('workflowInstanceId', extraHeaders.workflowInstanceId);
164
+ xhr.setRequestHeader('workflowinstanceid', extraHeaders.workflowInstanceId);
165
+ }
166
+ if (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.applicationId) {
167
+ xhr.setRequestHeader('applicationId', extraHeaders.applicationId);
168
+ xhr.setRequestHeader('applicationid', extraHeaders.applicationId);
169
+ }
170
+ if (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.entityid) {
171
+ xhr.setRequestHeader('entityid', extraHeaders.entityid);
172
+ xhr.setRequestHeader('entityId', extraHeaders.entityid);
173
+ }
174
+ const markConnected = (path) => {
175
+ var _a;
176
+ if (connected)
177
+ return;
178
+ connected = true;
179
+ reconnectAttempts = 0;
180
+ if (connectWatchdogTimerRef.current) {
181
+ clearTimeout(connectWatchdogTimerRef.current);
182
+ connectWatchdogTimerRef.current = null;
183
+ }
184
+ (_a = callbacks.onConnected) === null || _a === void 0 ? void 0 : _a.call(callbacks);
185
+ if (__DEV__ && !minimalLogs) {
186
+ console.log('[usePaymentSSE] Connected to SSE stream');
187
+ console.log('[usePaymentSSE][runtime]', {
188
+ path,
189
+ platform: react_native_1.Platform.OS,
190
+ osVersion: String(react_native_1.Platform.Version),
191
+ rnVersion,
192
+ jsEngine,
193
+ applicationId,
194
+ });
136
195
  }
137
196
  };
138
- for (const { eventType, data } of events) {
139
- if (!data)
140
- continue;
141
- if (eventType === 'fd_payment_status') {
142
- handleEventData(data);
197
+ // 4. Handle incoming data
198
+ xhr.onprogress = () => {
199
+ if (xhr.responseText && xhr.responseText.length > 0) {
200
+ markConnected('progress');
143
201
  }
144
- else if (eventType === 'message') {
202
+ // responseText contains ALL text received so far
203
+ // Only parse the NEW text since last call
204
+ const newText = xhr.responseText.slice(lastIndexRef.current);
205
+ lastIndexRef.current = xhr.responseText.length;
206
+ if (!newText)
207
+ return;
208
+ // Parse new text into SSE events
209
+ const events = (0, sseParser_1.parseSSEBuffer)(bufferRef.current, newText);
210
+ // Handle each event (support encrypted payload like web)
211
+ const handleEventData = async (rawData) => {
212
+ var _a, _b, _c, _d;
145
213
  try {
146
- const parsed = JSON.parse(data);
147
- if ((parsed === null || parsed === void 0 ? void 0 : parsed.event) === 'fd_payment_status')
148
- handleEventData(data);
214
+ const parsed = JSON.parse(rawData || '{}');
215
+ let payload = parsed;
216
+ if (typeof (parsed === null || parsed === void 0 ? void 0 : parsed.encryptedResponse) === 'string' && parsed.encryptedResponse) {
217
+ try {
218
+ const config = (0, encryptionConfig_1.getEncryptionConfig)();
219
+ const decrypted = await (0, encryption_1.decryptResponse)({ encryptedResponse: parsed.encryptedResponse }, config);
220
+ payload = (decrypted || {});
221
+ }
222
+ catch (_e) {
223
+ if (__DEV__ && verboseSSE)
224
+ console.warn('[usePaymentSSE] Decrypt failed, using raw');
225
+ }
226
+ }
227
+ const payloadData = (payload === null || payload === void 0 ? void 0 : payload.data) || {};
228
+ const status = String((_d = (_c = (_b = (_a = payload === null || payload === void 0 ? void 0 : payload.paymentStatus) !== null && _a !== void 0 ? _a : payload === null || payload === void 0 ? void 0 : payload.payment_status) !== null && _b !== void 0 ? _b : payloadData === null || payloadData === void 0 ? void 0 : payloadData.paymentStatus) !== null && _c !== void 0 ? _c : payloadData === null || payloadData === void 0 ? void 0 : payloadData.payment_status) !== null && _d !== void 0 ? _d : '').toUpperCase();
229
+ if (status)
230
+ callbacks.onPaymentStatus(status, payload);
231
+ }
232
+ catch (parseError) {
233
+ if (__DEV__ && verboseSSE)
234
+ console.error('[usePaymentSSE] Failed to parse event data:', parseError);
149
235
  }
150
- catch (_a) {
151
- // ignore
236
+ };
237
+ for (const { eventType, data } of events) {
238
+ if (!data)
239
+ continue;
240
+ const normalizedEventType = String(eventType || '').trim().toLowerCase();
241
+ if (normalizedEventType === 'fd_payment_status') {
242
+ handleEventData(data);
243
+ }
244
+ else if (normalizedEventType === 'message') {
245
+ try {
246
+ const parsed = JSON.parse(data);
247
+ const embeddedEvent = String((parsed === null || parsed === void 0 ? void 0 : parsed.event) || '').trim().toLowerCase();
248
+ if (embeddedEvent === 'fd_payment_status')
249
+ handleEventData(data);
250
+ }
251
+ catch (_a) {
252
+ // ignore
253
+ }
152
254
  }
153
255
  }
154
- }
155
- };
156
- // 5. Handle successful connection
157
- xhr.onreadystatechange = () => {
158
- var _a, _b;
159
- if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
160
- if (xhr.status === 200) {
161
- (_a = callbacks.onConnected) === null || _a === void 0 ? void 0 : _a.call(callbacks);
162
- if (__DEV__) {
163
- console.log('[usePaymentSSE] Connected to SSE stream');
256
+ };
257
+ // 5. Handle connection status
258
+ xhr.onreadystatechange = () => {
259
+ if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED || xhr.readyState === XMLHttpRequest.LOADING) {
260
+ if (xhr.status === 200) {
261
+ markConnected('headers');
262
+ }
263
+ else if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED && xhr.status > 0) {
264
+ if (__DEV__) {
265
+ console.error('[usePaymentSSE] Bad status:', xhr.status);
266
+ }
267
+ scheduleReconnect(`SSE response status: ${xhr.status}`);
164
268
  }
165
269
  }
166
- else {
167
- if (__DEV__) {
168
- console.error('[usePaymentSSE] Bad status:', xhr.status);
270
+ if (xhr.readyState === XMLHttpRequest.DONE && !stopRequestedRef.current && connectionIdRef.current === connectionId) {
271
+ if (xhr.status !== 200) {
272
+ scheduleReconnect(`SSE closed with status: ${xhr.status}`);
169
273
  }
170
- (_b = callbacks.onError) === null || _b === void 0 ? void 0 : _b.call(callbacks, new Error(`SSE response status: ${xhr.status}`));
171
274
  }
275
+ };
276
+ // 6. Handle errors
277
+ xhr.onerror = () => {
278
+ if (__DEV__ && verboseSSE) {
279
+ console.error('[usePaymentSSE] Connection error');
280
+ }
281
+ scheduleReconnect('SSE connection failed');
282
+ };
283
+ // 7. Handle timeout
284
+ xhr.ontimeout = () => {
285
+ if (__DEV__) {
286
+ console.error('[usePaymentSSE] Connection timed out');
287
+ }
288
+ scheduleReconnect('SSE connection timed out');
289
+ };
290
+ // 8. Send the request (connection stays open)
291
+ xhr.send();
292
+ // If we never get connected callback/progress, restart connection.
293
+ if (connectWatchdogTimerRef.current) {
294
+ clearTimeout(connectWatchdogTimerRef.current);
172
295
  }
173
- };
174
- // 6. Handle errors
175
- xhr.onerror = () => {
176
- var _a;
177
- if (__DEV__) {
178
- console.error('[usePaymentSSE] Connection error');
179
- }
180
- (_a = callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, new Error('SSE connection failed'));
181
- };
182
- // 7. Handle timeout
183
- xhr.ontimeout = () => {
184
- var _a;
296
+ connectWatchdogTimerRef.current = setTimeout(() => {
297
+ if (connected || stopRequestedRef.current || connectionIdRef.current !== connectionId)
298
+ return;
299
+ try {
300
+ xhr.abort();
301
+ }
302
+ catch (_a) {
303
+ // ignore
304
+ }
305
+ scheduleReconnect('SSE connect watchdog timeout');
306
+ }, connectWatchdogMs);
185
307
  if (__DEV__) {
186
- console.error('[usePaymentSSE] Connection timed out');
308
+ console.log('[usePaymentSSE] Starting SSE connection to:', url);
309
+ console.log('[usePaymentSSE][runtime]', {
310
+ phase: 'start',
311
+ platform: react_native_1.Platform.OS,
312
+ osVersion: String(react_native_1.Platform.Version),
313
+ rnVersion,
314
+ jsEngine,
315
+ applicationId,
316
+ });
317
+ console.log('[usePaymentSSE] Headers:', {
318
+ 'x-api-key': apiKey ? '***' : 'missing',
319
+ 'userreferenceid': (() => {
320
+ try {
321
+ const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
322
+ return (userInfo === null || userInfo === void 0 ? void 0 : userInfo.userReferenceId) || (userInfo === null || userInfo === void 0 ? void 0 : userInfo.id) || 'missing';
323
+ }
324
+ catch (_a) {
325
+ return 'missing';
326
+ }
327
+ })(),
328
+ 'provider': (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.providerId) || 'missing',
329
+ 'workflowInstanceId': (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.workflowInstanceId) || 'missing',
330
+ 'applicationId': (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.applicationId) || 'missing',
331
+ 'entityid': (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.entityid) || 'missing',
332
+ });
187
333
  }
188
- (_a = callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, new Error('SSE connection timed out'));
189
334
  };
190
- // 8. Send the request (connection stays open)
191
- xhr.send();
192
- if (__DEV__) {
193
- console.log('[usePaymentSSE] Starting SSE connection to:', url);
194
- console.log('[usePaymentSSE] Headers:', {
195
- 'x-api-key': apiKey ? '***' : 'missing',
196
- 'userreferenceid': (() => {
197
- try {
198
- const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
199
- return (userInfo === null || userInfo === void 0 ? void 0 : userInfo.userReferenceId) || (userInfo === null || userInfo === void 0 ? void 0 : userInfo.id) || 'missing';
200
- }
201
- catch (_a) {
202
- return 'missing';
203
- }
204
- })(),
205
- 'provider': (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.providerId) || 'missing',
206
- 'workflowInstanceId': (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.workflowInstanceId) || 'missing',
207
- 'applicationId': (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.applicationId) || 'missing',
208
- 'entityid': (extraHeaders === null || extraHeaders === void 0 ? void 0 : extraHeaders.entityid) || 'missing',
209
- });
210
- }
335
+ openConnection();
211
336
  }, []);
212
337
  const stop = (0, react_1.useCallback)(() => {
338
+ stopRequestedRef.current = true;
339
+ connectionIdRef.current += 1;
340
+ if (reconnectTimerRef.current) {
341
+ clearTimeout(reconnectTimerRef.current);
342
+ reconnectTimerRef.current = null;
343
+ }
344
+ if (connectWatchdogTimerRef.current) {
345
+ clearTimeout(connectWatchdogTimerRef.current);
346
+ connectWatchdogTimerRef.current = null;
347
+ }
213
348
  if (xhrRef.current) {
214
349
  xhrRef.current.abort();
215
350
  xhrRef.current = null;
@@ -617,7 +617,31 @@ const FDCalculator = ({ onGoBack, onExitSDK, onPanRequired, onNavigateToReviewKY
617
617
  ? (_a = data === null || data === void 0 ? void 0 : data.sdrCalc) === null || _a === void 0 ? void 0 : _a[0]
618
618
  : (_b = data === null || data === void 0 ? void 0 : data.fdrCalc) === null || _b === void 0 ? void 0 : _b[0];
619
619
  const maturityAmount = (calcData === null || calcData === void 0 ? void 0 : calcData.maturityAmount) || (data === null || data === void 0 ? void 0 : data.maturityAmount) || (data === null || data === void 0 ? void 0 : data.totalAmount) || (data === null || data === void 0 ? void 0 : data.finalAmount) || (data === null || data === void 0 ? void 0 : data.amount);
620
- return maturityAmount ? `₹${maturityAmount.toLocaleString()}` : "";
620
+ return maturityAmount ? `₹${Number(maturityAmount).toLocaleString()}` : "";
621
+ })(), intPayAmt: (() => {
622
+ var _a, _b;
623
+ const data = calculationResult === null || calculationResult === void 0 ? void 0 : calculationResult.data;
624
+ const investmentType = payoutValue === 'On Maturity' ? 'SDR' : 'FDR';
625
+ const calcData = investmentType === 'SDR'
626
+ ? (_a = data === null || data === void 0 ? void 0 : data.sdrCalc) === null || _a === void 0 ? void 0 : _a[0]
627
+ : (_b = data === null || data === void 0 ? void 0 : data.fdrCalc) === null || _b === void 0 ? void 0 : _b[0];
628
+ const val = calcData === null || calcData === void 0 ? void 0 : calcData.intPayAmt;
629
+ if (val == null)
630
+ return undefined;
631
+ const num = Number(val);
632
+ return Number.isFinite(num) ? `₹ ${num.toLocaleString()}` : String(val);
633
+ })(), totalInterestEarnings: (() => {
634
+ var _a, _b;
635
+ const data = calculationResult === null || calculationResult === void 0 ? void 0 : calculationResult.data;
636
+ const investmentType = payoutValue === 'On Maturity' ? 'SDR' : 'FDR';
637
+ const calcData = investmentType === 'SDR'
638
+ ? (_a = data === null || data === void 0 ? void 0 : data.sdrCalc) === null || _a === void 0 ? void 0 : _a[0]
639
+ : (_b = data === null || data === void 0 ? void 0 : data.fdrCalc) === null || _b === void 0 ? void 0 : _b[0];
640
+ const val = calcData === null || calcData === void 0 ? void 0 : calcData.totalInterestEarnings;
641
+ if (val == null)
642
+ return undefined;
643
+ const num = Number(val);
644
+ return Number.isFinite(num) ? `₹ ${num.toLocaleString()}` : String(val);
621
645
  })() },
622
646
  react_1.default.createElement(components_1.CheckboxOption, { label: strings_1.FD_STRINGS.SENIOR_CITIZEN_BENEFIT, checked: seniorCitizen, onPress: () => setSeniorCitizen(!seniorCitizen), numberOfLines: 1, containerStyle: { marginTop: 12 }, disabled: true })),
623
647
  react_1.default.createElement(react_native_1.View, { style: [styles.checkboxCard, !taxResident && styles.checkboxCardError] },