@finspringinnovations/fdsdk 0.0.3 → 0.0.5

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.
@@ -12,17 +12,35 @@ export const masterDataApi = baseApi.injectEndpoints({
12
12
  const request = {
13
13
  url: 'masterdata',
14
14
  method: 'GET' as const,
15
+ cache: 'no-store' as const,
15
16
  headers: {
16
17
  workflowInstanceId: '{{workflowInstanceId}}',
17
18
  'x-api-key': '{{X-API-KEY}}',
18
19
  encryptdecrypt: 'false',
20
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
21
+ Pragma: 'no-cache',
22
+ Expires: '0',
19
23
  provider: providerId || '{{shriramprovider}}',
20
24
  },
21
25
  };
22
26
  return request;
23
27
  },
24
- transformResponse: (response) => {
25
- return response;
28
+ transformResponse: (response: any) => {
29
+ if (response == null) return response;
30
+ // If API returns a JSON string, parse it
31
+ let data = response;
32
+ if (typeof response === 'string') {
33
+ try {
34
+ data = JSON.parse(response);
35
+ } catch {
36
+ return response;
37
+ }
38
+ }
39
+ // Unwrap { data: ... } so consumers get a consistent shape
40
+ if (typeof data === 'object' && data !== null && 'data' in data && Object.keys(data).length === 1) {
41
+ return data.data;
42
+ }
43
+ return data;
26
44
  },
27
45
  }),
28
46
  }),
@@ -31,4 +49,4 @@ export const masterDataApi = baseApi.injectEndpoints({
31
49
 
32
50
  export const {
33
51
  useGetMasterDataQuery
34
- } = masterDataApi;
52
+ } = masterDataApi;
@@ -9,6 +9,38 @@ export interface TerminateWorkflowRequest {
9
9
 
10
10
  export type TerminateWorkflowResponse = any; // Replace with concrete type when available
11
11
 
12
+ const TASK_ID_TO_CAPTION: Record<string, string> = {
13
+ '11042': 'SHRIRAM_V1_S1_T1_START',
14
+ '11043': 'SHRIRAM_V1_S1_T2_GET_PERSONAL_DETAILS_AND_BASIC_FD_INFO',
15
+ '11044': 'SHRIRAM_V1_S1_T3_END',
16
+ '11050': 'SHRIRAM_V1_S2_T1_KYC_START',
17
+ '11051': 'SHRIRAM_V1_S2_T2_CHECK_FOR_PAN_RAPID_STATUS',
18
+ '11052': 'SHRIRAM_V1_S2_T3_CHECK_IF_AADHAAR_ALREADY_VERIFIED',
19
+ '11045': 'SHRIRAM_V1_S2_T4_SEND_AADHAR_OTP',
20
+ '11046': 'SHRIRAM_V1_S2_T5_CHECK_FOR_OTP',
21
+ '11047': 'SHRIRAM_V1_S2_T6_VALIDATE_AADHAR_OTP',
22
+ '11053': 'SHRIRAM_V1_S2_T7_AADHAAR_VERIFIED',
23
+ '11049': 'SHRIRAM_V1_S2_T8_TERMINATE_TASK_AND_WORKFLOW_AFTER_KYC_RETRIES',
24
+ '11048': 'SHRIRAM_V1_S2_T9_KYC_END',
25
+ '11054': 'SHRIRAM_V1_S3_T1_OCCUPATION_START',
26
+ '11055': 'SHRIRAM_V1_S3_T2_CAPTURE_OCCUPATION_DETAILS',
27
+ '11056': 'SHRIRAM_V1_S3_T3_OCCUPATION_END',
28
+ '11057': 'SHRIRAM_V1_S4_T1_NOMINEE_START',
29
+ '11058': 'SHRIRAM_V1_S4_T2_CAPTURE_NOMINEE_DETAILS',
30
+ '11059': 'SHRIRAM_V1_S4_T3_NOMINEE_END',
31
+ '11060': 'SHRIRAM_V1_S5_T1_BANK_DETAILS_START',
32
+ '11061': 'SHRIRAM_V1_S5_T2_CAPTURE_BANK_DETAILS',
33
+ '11062': 'SHRIRAM_V1_S5_T3_BANK_DETAILS_END',
34
+ '11063': 'SHRIRAM_V1_S6_T1_FD_CREATION_START',
35
+ '11064': 'SHRIRAM_V1_S6_T2_CREATE_FD',
36
+ '11065': 'SHRIRAM_V1_S6_T3_FD_CREATION_END',
37
+ '11069': 'SHRIRAM_V1_S7_T1_PAYMENT_START',
38
+ '11068': 'SHRIRAM_V1_S7_T2_PAYMENT',
39
+ '11067': 'SHRIRAM_V1_S7_T3_CHECK_FOR_PAYMENT_STATUS',
40
+ '11070': 'SHRIRAM_V1_S7_T4_TERMINATE_TASK_AND_WORKFLOW_AFTER_PAYMENT_RETRIES',
41
+ '11066': 'SHRIRAM_V1_S7_T5_PAYMENT_END',
42
+ };
43
+
12
44
  export const workflowApi = baseApi.injectEndpoints({
13
45
  endpoints: (builder) => ({
14
46
  terminateWorkflow: builder.mutation<
@@ -37,11 +69,28 @@ export const workflowApi = baseApi.injectEndpoints({
37
69
  updateTask: builder.mutation<any, any>({
38
70
  query: (body) => {
39
71
  const { providerId, workflowInstanceId, userreferenceid, applicationid, entityid, ...requestBody } = body;
72
+ const normalizedRequestBody = { ...requestBody } as Record<string, any>;
73
+
74
+ // Backend now expects caption-based field name.
75
+ // Keep compatibility if any caller still passes targetTaskId.
76
+ if (!normalizedRequestBody.targetTaskCaption && normalizedRequestBody.targetTaskId) {
77
+ normalizedRequestBody.targetTaskCaption = normalizedRequestBody.targetTaskId;
78
+ delete normalizedRequestBody.targetTaskId;
79
+ }
80
+
81
+ // Normalize numeric task IDs to caption values if provided.
82
+ if (normalizedRequestBody.targetTaskCaption !== undefined && normalizedRequestBody.targetTaskCaption !== null) {
83
+ const key = String(normalizedRequestBody.targetTaskCaption).trim();
84
+ if (TASK_ID_TO_CAPTION[key]) {
85
+ normalizedRequestBody.targetTaskCaption = TASK_ID_TO_CAPTION[key];
86
+ }
87
+ }
88
+
40
89
  return {
41
90
  url: 'taskflow/update',
42
91
  method: 'POST',
43
92
  body: {
44
- ...requestBody,
93
+ ...normalizedRequestBody,
45
94
  workflowInstanceId: workflowInstanceId,
46
95
  },
47
96
  headers: {
@@ -5,4 +5,5 @@
5
5
  export const images = {
6
6
  arrowLeft: require('./arrow-left.png'),
7
7
  chevronDown: require('./chevron-down.png'),
8
+ shriramLogo: require('./shriram.png'),
8
9
  };
Binary file
@@ -5,10 +5,12 @@ import {
5
5
  StyleSheet,
6
6
  TouchableOpacity,
7
7
  Platform,
8
+ Image,
8
9
  } from 'react-native';
9
10
  import { useColors, useTypography, useSpacing, useShadows, useTheme } from '../theme/ThemeContext';
10
11
  import { getBorderColor } from '../theme';
11
12
  import type { ColorScheme, ThemeName } from '../theme';
13
+ import { base64Images } from '../constants/strings/base64Images';
12
14
 
13
15
  interface FDCardProps {
14
16
  id: string;
@@ -74,6 +76,13 @@ const FDCard: React.FC<FDCardProps> = ({
74
76
  activeOpacity={0.8}
75
77
  >
76
78
  <View style={styles.fdCardHeader}>
79
+ <View>
80
+ <Image
81
+ source={{ uri: base64Images.shriramLogo }}
82
+ style={styles.logo}
83
+ resizeMode="contain"
84
+ />
85
+ </View>
77
86
  <Text style={[styles.fdName, customStyles.name]}>{name}</Text>
78
87
  </View>
79
88
 
@@ -160,6 +169,12 @@ const createStyles = (colors: ColorScheme, typography: any, spacing: any, shadow
160
169
  roiText: {
161
170
  color: colors.success,
162
171
  },
172
+ logo: {
173
+ width: 25,
174
+ height: 25,
175
+ marginRight: 5,
176
+ borderRadius: 15,
177
+ },
163
178
  });
164
179
 
165
180
  export default FDCard;
@@ -14,9 +14,11 @@
14
14
  */
15
15
 
16
16
  import { useCallback, useRef } from 'react';
17
- import { parseSSEBuffer, type SSEEvent } from '../utils/sseParser';
18
- import { getEnvironmentData } from '../config/appDataConfig';
19
- import { getApiConfig } from '../config/apiConfig';
17
+ import { parseSSEBuffer } from '../utils/sseParser';
18
+ import { getEnvironmentData, getUserInfoForAPI } from '../config/appDataConfig';
19
+ import { getApiConfig, getSecureHeaders } from '../config/apiConfig';
20
+ import { decryptResponse } from '../utils/encryption';
21
+ import { getEncryptionConfig } from '../config/encryptionConfig';
20
22
 
21
23
  // -- Types --
22
24
 
@@ -26,6 +28,13 @@ interface UsePaymentSSECallbacks {
26
28
  onConnected?: () => void;
27
29
  }
28
30
 
31
+ export interface PaymentSSEExtraHeaders {
32
+ workflowInstanceId?: string;
33
+ applicationId?: string;
34
+ entityid?: string;
35
+ providerId?: string;
36
+ }
37
+
29
38
  // -- Hook --
30
39
 
31
40
  export function usePaymentSSE() {
@@ -37,7 +46,7 @@ export function usePaymentSSE() {
37
46
  const bufferRef = useRef<{ value: string }>({ value: '' });
38
47
 
39
48
  const start = useCallback(
40
- (applicationId: string, callbacks: UsePaymentSSECallbacks) => {
49
+ (applicationId: string, callbacks: UsePaymentSSECallbacks, extraHeaders?: PaymentSSEExtraHeaders) => {
41
50
  // Clean up any existing connection first
42
51
  if (xhrRef.current) {
43
52
  xhrRef.current.abort();
@@ -51,8 +60,13 @@ export function usePaymentSSE() {
51
60
  // 1. Build the SSE endpoint URL
52
61
  const envData = getEnvironmentData();
53
62
  const apiConfig = getApiConfig();
54
- const baseUrl = envData?.apiBaseUrl || apiConfig?.baseUrl || '';
55
- const url = `${baseUrl}/events?applicationId=${encodeURIComponent(applicationId)}`;
63
+ const baseUrl = (envData?.apiBaseUrl || apiConfig?.baseUrl || '').replace(/\/$/, '');
64
+ const url = baseUrl ? `${baseUrl}/events?applicationId=${encodeURIComponent(applicationId)}` : '';
65
+
66
+ if (!url) {
67
+ if (__DEV__) console.warn('[usePaymentSSE] No base URL, skipping SSE');
68
+ return;
69
+ }
56
70
 
57
71
  // 2. Create XMLHttpRequest
58
72
  const xhr = new XMLHttpRequest();
@@ -65,8 +79,50 @@ export function usePaymentSSE() {
65
79
  if (apiKey) {
66
80
  xhr.setRequestHeader('x-api-key', apiKey);
67
81
  }
82
+
83
+ // Add secure headers (Content-Type, User-Agent, etc.)
84
+ const secureHeaders = getSecureHeaders();
85
+ Object.entries(secureHeaders).forEach(([key, value]) => {
86
+ if (key.toLowerCase() !== 'content-type') {
87
+ xhr.setRequestHeader(key, value);
88
+ }
89
+ });
90
+
91
+ // SSE-specific headers
68
92
  xhr.setRequestHeader('Accept', 'text/event-stream');
69
- xhr.setRequestHeader('Cache-Control', 'no-cache');
93
+ xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
94
+ xhr.setRequestHeader('Pragma', 'no-cache');
95
+ xhr.setRequestHeader('Expires', '0');
96
+
97
+ // Add userreferenceid header (required for authentication) - both cases
98
+ try {
99
+ const userInfo = getUserInfoForAPI();
100
+ const userRefId = userInfo?.userReferenceId || userInfo?.id;
101
+ if (userRefId) {
102
+ xhr.setRequestHeader('userreferenceid', userRefId);
103
+ xhr.setRequestHeader('userReferenceId', userRefId);
104
+ }
105
+ } catch {
106
+ if (__DEV__) console.warn('[usePaymentSSE] Could not get userReferenceId');
107
+ }
108
+
109
+ // Add provider header (required for authentication)
110
+ if (extraHeaders?.providerId) {
111
+ xhr.setRequestHeader('provider', extraHeaders.providerId);
112
+ }
113
+
114
+ // Add onboarding headers - both camelCase and lowercase variants
115
+ if (extraHeaders?.workflowInstanceId) {
116
+ xhr.setRequestHeader('workflowInstanceId', extraHeaders.workflowInstanceId);
117
+ }
118
+ if (extraHeaders?.applicationId) {
119
+ xhr.setRequestHeader('applicationId', extraHeaders.applicationId);
120
+ xhr.setRequestHeader('applicationid', extraHeaders.applicationId);
121
+ }
122
+ if (extraHeaders?.entityid) {
123
+ xhr.setRequestHeader('entityid', extraHeaders.entityid);
124
+ xhr.setRequestHeader('entityId', extraHeaders.entityid);
125
+ }
70
126
 
71
127
  // 4. Handle incoming data
72
128
  xhr.onprogress = () => {
@@ -80,17 +136,40 @@ export function usePaymentSSE() {
80
136
  // Parse new text into SSE events
81
137
  const events = parseSSEBuffer(bufferRef.current, newText);
82
138
 
83
- // Handle each event
139
+ // Handle each event (support encrypted payload like web)
140
+ const handleEventData = async (rawData: string) => {
141
+ try {
142
+ const parsed = JSON.parse(rawData || '{}') as Record<string, unknown>;
143
+ let payload = parsed;
144
+ if (typeof parsed?.encryptedResponse === 'string' && parsed.encryptedResponse) {
145
+ try {
146
+ const config = getEncryptionConfig();
147
+ const decrypted = await decryptResponse(
148
+ { encryptedResponse: parsed.encryptedResponse },
149
+ config
150
+ );
151
+ payload = (decrypted || {}) as Record<string, unknown>;
152
+ } catch {
153
+ if (__DEV__) console.warn('[usePaymentSSE] Decrypt failed, using raw');
154
+ }
155
+ }
156
+ const status = String(payload?.paymentStatus ?? payload?.payment_status ?? '').toUpperCase();
157
+ if (status) callbacks.onPaymentStatus(status, payload);
158
+ } catch (parseError) {
159
+ if (__DEV__) console.error('[usePaymentSSE] Failed to parse event data:', parseError);
160
+ }
161
+ };
162
+
84
163
  for (const { eventType, data } of events) {
164
+ if (!data) continue;
85
165
  if (eventType === 'fd_payment_status') {
166
+ handleEventData(data);
167
+ } else if (eventType === 'message') {
86
168
  try {
87
- const parsed = JSON.parse(data);
88
- const status = (parsed.paymentStatus ?? '').toUpperCase();
89
- callbacks.onPaymentStatus(status, parsed);
90
- } catch (parseError) {
91
- if (__DEV__) {
92
- console.error('[usePaymentSSE] Failed to parse event data:', parseError);
93
- }
169
+ const parsed = JSON.parse(data) as { event?: string; [k: string]: unknown };
170
+ if (parsed?.event === 'fd_payment_status') handleEventData(data);
171
+ } catch {
172
+ // ignore
94
173
  }
95
174
  }
96
175
  }
@@ -134,6 +213,21 @@ export function usePaymentSSE() {
134
213
 
135
214
  if (__DEV__) {
136
215
  console.log('[usePaymentSSE] Starting SSE connection to:', url);
216
+ console.log('[usePaymentSSE] Headers:', {
217
+ 'x-api-key': apiKey ? '***' : 'missing',
218
+ 'userreferenceid': (() => {
219
+ try {
220
+ const userInfo = getUserInfoForAPI();
221
+ return userInfo?.userReferenceId || userInfo?.id || 'missing';
222
+ } catch {
223
+ return 'missing';
224
+ }
225
+ })(),
226
+ 'provider': extraHeaders?.providerId || 'missing',
227
+ 'workflowInstanceId': extraHeaders?.workflowInstanceId || 'missing',
228
+ 'applicationId': extraHeaders?.applicationId || 'missing',
229
+ 'entityid': extraHeaders?.entityid || 'missing',
230
+ });
137
231
  }
138
232
  },
139
233
  []
@@ -27,11 +27,13 @@ const Stack = createStackNavigator<RootStackParamList>();
27
27
  interface RootNavigatorProps {
28
28
  config?: SDKNavigationConfig;
29
29
  onExit?: (fdDetails?: any) => void;
30
+ onPanRequired?: () => void;
30
31
  }
31
32
 
32
33
  const RootNavigator: React.FC<RootNavigatorProps> = ({
33
34
  config = {},
34
- onExit
35
+ onExit,
36
+ onPanRequired,
35
37
  }) => {
36
38
  // Handle Android hardware back button
37
39
  useEffect(() => {
@@ -151,6 +153,7 @@ const RootNavigator: React.FC<RootNavigatorProps> = ({
151
153
  <FDCalculatorScreen
152
154
  onGoBack={() => goBack()}
153
155
  onExitSDK={() => onExit?.()}
156
+ onPanRequired={onPanRequired} // <── add this
154
157
  onNavigateToReviewKYC={() => navigate('ReviewKYC', { fdData: props.route.params?.fdData })}
155
158
  fdData={(props.route.params as any)?.fdData}
156
159
  {...props}
@@ -309,15 +312,21 @@ const RootNavigator: React.FC<RootNavigatorProps> = ({
309
312
  onGoBack={() => goBack()}
310
313
  paymentUrl={(getPaymentSession().paymentUrl) || ''}
311
314
  onPaymentSuccess={(data) => {
315
+ const payload = data && typeof data === 'object' ? data : {};
316
+ const transactionId = (payload as any)?.transactionId ?? (payload as any)?.transaction_id ?? (payload as any)?.TransactionId;
312
317
  navigate('PaymentStatus', {
313
318
  status: 'success',
314
- paymentData: data
319
+ paymentData: data,
320
+ transactionId,
315
321
  } as any);
316
322
  }}
317
323
  onPaymentFailure={(error) => {
324
+ const payload = error && typeof error === 'object' ? error : {};
325
+ const transactionId = (payload as any)?.transactionId ?? (payload as any)?.transaction_id ?? (payload as any)?.TransactionId;
318
326
  navigate('PaymentStatus', {
319
327
  status: 'failed',
320
- paymentData: error
328
+ paymentData: error,
329
+ transactionId,
321
330
  } as any);
322
331
  }}
323
332
  onPaymentPending={(info) => {
@@ -14,6 +14,7 @@ import { getSDKColors, type CustomColors } from '../config/appDataConfig';
14
14
  interface SDKNavigationContainerProps {
15
15
  config?: SDKNavigationConfig;
16
16
  onExit?: (fdDetails?: any) => void;
17
+ onPanRequired?: () => void;
17
18
  children?: React.ReactNode;
18
19
  theme?: ThemeName | Theme;
19
20
  colors?: CustomColors; // Custom color overrides (prop-level, highest priority)
@@ -24,6 +25,7 @@ interface SDKNavigationContainerProps {
24
25
  export const SDKNavigationContainer: React.FC<SDKNavigationContainerProps> = ({
25
26
  config,
26
27
  onExit,
28
+ onPanRequired,
27
29
  children,
28
30
  theme,
29
31
  colors: propColors,
@@ -37,7 +39,7 @@ export const SDKNavigationContainer: React.FC<SDKNavigationContainerProps> = ({
37
39
 
38
40
  // Choose navigator based on flag
39
41
  const navigator = useReactNavigation
40
- ? <RootNavigator config={config} onExit={onExit} />
42
+ ? <RootNavigator config={config} onExit={onExit} onPanRequired={onPanRequired} />
41
43
  : <SimpleNavigator config={config as SimpleNavigationConfig} onExit={onExit} />;
42
44
 
43
45
  const content = children || (
@@ -295,7 +295,7 @@ const AadhaarVerification: React.FC<AadhaarVerificationProps> = ({
295
295
  applicationid: applicationId,
296
296
  entityid: entityId,
297
297
  // Request params/body
298
- targetTaskId: WORKFLOW_TASKS.VALIDATE_OTP,
298
+ targetTaskCaption: WORKFLOW_TASKS.VALIDATE_OTP,
299
299
  }).unwrap();
300
300
  setIsValidateOtpTaskCalled(true);
301
301
  } else {
@@ -357,7 +357,7 @@ const AadhaarVerification: React.FC<AadhaarVerificationProps> = ({
357
357
  applicationid: applicationId,
358
358
  entityid: entityId,
359
359
  // Request params/body
360
- targetTaskId: WORKFLOW_TASKS.RESEND_OTP,
360
+ targetTaskCaption: WORKFLOW_TASKS.RESEND_OTP,
361
361
  }).unwrap();
362
362
 
363
363
 
@@ -42,6 +42,7 @@ import { base64Images } from '../constants/strings/base64Images';
42
42
  export interface FDCalculatorProps {
43
43
  onGoBack?: () => void;
44
44
  onExitSDK?: () => void;
45
+ onPanRequired?: () => void;
45
46
  onNavigateToReviewKYC?: () => void;
46
47
  fdData?: {
47
48
  id: string;
@@ -56,7 +57,7 @@ export interface FDCalculatorProps {
56
57
  };
57
58
  }
58
59
 
59
- const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onExitSDK, onNavigateToReviewKYC, fdData }) => {
60
+ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onExitSDK, onPanRequired, onNavigateToReviewKYC, fdData }) => {
60
61
  const typography = useTypography();
61
62
  const colors = useColors();
62
63
  const { themeName } = useTheme();
@@ -139,7 +140,12 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onExitSDK, onNavi
139
140
 
140
141
  const { data: fallbackMasterData, isLoading: isLoadingFallback, error: fallbackError, refetch: refetchMasterData } = useGetMasterDataQuery(
141
142
  { providerId: defaultProviderId }, // Use default provider ID
142
- { skip: !!masterData } // Skip if master data is already available
143
+ {
144
+ skip: !!masterData || !defaultProviderId,
145
+ refetchOnMountOrArgChange: true,
146
+ refetchOnFocus: true,
147
+ refetchOnReconnect: true,
148
+ }
143
149
  );
144
150
 
145
151
  // Use fallback master data if global master data is not available
@@ -376,7 +382,9 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onExitSDK, onNavi
376
382
  {
377
383
  text: 'OK',
378
384
  onPress: () => {
379
- if (onExitSDK) {
385
+ if (onPanRequired) {
386
+ onPanRequired();
387
+ } else if (onExitSDK) {
380
388
  onExitSDK();
381
389
  }
382
390
  },
@@ -1008,4 +1016,4 @@ const createStyles = (typography: any, colors: any, themeName: string) => StyleS
1008
1016
  backgroundColor: 'rgba(0, 0, 0, 0.3)',
1009
1017
  zIndex: 1000,
1010
1018
  },
1011
- });
1019
+ });
@@ -20,7 +20,6 @@ import { useGetCustomerApplicationsMutation } from '../api/customerApi';
20
20
  import { useGetMasterDataQuery } from '../api/masterDataApi';
21
21
  import { useMasterData } from '../providers/MasterDataProvider';
22
22
  import { useTerminateWorkflowMutation } from '../api/workflowApi';
23
- import { usePaymentReverseFeedMutation } from '../api/fdApi';
24
23
  import type { ColorScheme, ThemeName } from '../theme';
25
24
  import SafeAreaWrapper from '../components/SafeAreaWrapper';
26
25
  import { getUserInfoForAPI, getAppData } from '../config/appDataConfig';
@@ -99,22 +98,9 @@ const FDList: React.FC<FDListProps> = ({
99
98
  isLoading: isTerminatingWorkflow,
100
99
  }] = useTerminateWorkflowMutation();
101
100
 
102
- // Payment Reverse Feed API
103
- const [paymentReverseFeed, {
104
- data: paymentReverseFeedResponse,
105
- error: paymentReverseFeedError,
106
- isLoading: isLoadingPaymentReverseFeed,
107
- }] = usePaymentReverseFeedMutation();
108
-
109
101
  const styles = createStyles(colors, typography, spacing, themeName);
110
102
  const { setMasterData } = useMasterData();
111
103
 
112
- // Redux selectors for workflow IDs
113
- const workflowInstanceId = useAppSelector((state: any) => state?.onboarding?.workflowInstanceId);
114
- const applicationId = useAppSelector((state: any) => state?.onboarding?.applicationId);
115
- const entityId = useAppSelector((state: any) => state?.onboarding?.entityid);
116
- const providerId = useAppSelector((state: any) => state?.onboarding?.providerId);
117
-
118
104
  // Helper function to check if customer applications is empty
119
105
  const isCustomerApplicationsEmpty = () => {
120
106
  if (!customerApplications) {
@@ -223,7 +209,12 @@ const FDList: React.FC<FDListProps> = ({
223
209
 
224
210
  const { data: masterData, isLoading: isLoadingMaster } = useGetMasterDataQuery(
225
211
  { providerId: inferredProviderId },
226
- { skip: !inferredProviderId }
212
+ {
213
+ skip: !inferredProviderId,
214
+ refetchOnMountOrArgChange: true,
215
+ refetchOnFocus: true,
216
+ refetchOnReconnect: true,
217
+ }
227
218
  );
228
219
 
229
220
  // Only render once all three API calls have completed (success or error)
@@ -491,26 +482,34 @@ const FDList: React.FC<FDListProps> = ({
491
482
 
492
483
  // Persist onboarding identifiers globally for subsequent API calls (like FDCalculator)
493
484
  try {
494
- // Extract workflow parameters from response
485
+ // Extract workflow parameters from response (support array / data[] / applications[])
495
486
  const responseData = customerApplications?.data || customerApplications;
496
- let applicationData = Array.isArray(responseData) ? responseData[0] : responseData;
487
+ let applicationsData: any[] = [];
488
+ if (Array.isArray(responseData)) {
489
+ applicationsData = responseData;
490
+ } else if (responseData?.applications && Array.isArray(responseData.applications)) {
491
+ applicationsData = responseData.applications;
492
+ } else if (Array.isArray(customerApplications?.applications)) {
493
+ applicationsData = customerApplications.applications;
494
+ }
495
+
496
+ let applicationData = applicationsData.length > 0 ? applicationsData[0] : (Array.isArray(responseData) ? responseData[0] : responseData);
497
497
 
498
498
  // Prefer the application with wf_status === 'Active'
499
- if (Array.isArray(responseData)) {
500
- const activeOnly = responseData.find((app: any) => {
499
+ if (applicationsData.length > 0) {
500
+ const activeOnly = applicationsData.find((app: any) => {
501
501
  const status = (app.wf_status || app.status || '').toString();
502
502
  return status === 'Active';
503
503
  });
504
504
  if (activeOnly) applicationData = activeOnly;
505
505
  }
506
506
 
507
- if (Array.isArray(responseData)) {
508
- completeFDData = responseData.find((app: any) => {
509
- const status = (app.wf_status || app.status || '').toString();
510
- return status === 'Completed';
511
- });
512
- setGlobalData({ completeFDData: !!completeFDData });
513
- }
507
+ // Set completed flag robustly for downstream ReviewKYC/Aadhaar flow decisions
508
+ completeFDData = applicationsData.find((app: any) => {
509
+ const status = (app.wf_status || app.status || '').toString().toLowerCase();
510
+ return status === 'completed';
511
+ });
512
+ setGlobalData({ completeFDData: !!completeFDData });
514
513
  if (applicationData) {
515
514
  const ids = {
516
515
  workflowInstanceId: applicationData.workflow_instance_id,
@@ -593,56 +592,18 @@ const FDList: React.FC<FDListProps> = ({
593
592
  // Handle error silently
594
593
  }
595
594
 
596
- // If current state is payment and transactionId is available, call payment reverse feed API
595
+ // If current state is payment and transactionId is available,
596
+ // open PaymentStatus and let SSE (/events) drive status updates.
597
597
  if (currentState === WORKFLOW_STATES.PAYMENT && transactionId) {
598
- try {
599
- // Get user info and required IDs
600
- const userInfo = getUserInfoForAPI();
601
-
602
- // Prepare payment reverse feed request
603
- const paymentReverseFeedRequest = {
604
- // Headers
605
- providerId: providerId,
606
- workflowInstanceId: workflowInstanceId,
607
- userreferenceid: userInfo.id,
608
- applicationid: applicationId,
609
- entityid: entityId,
610
- // Body
611
- transactionId: transactionId,
612
- };
613
-
614
- const response = await paymentReverseFeed(paymentReverseFeedRequest).unwrap();
615
-
616
- // Handle the response based on payment status
617
- const paymentStatus = (response?.data?.paymentStatus || '').toLowerCase();
618
- const statusParam = paymentStatus === 'success' ? 'success' : paymentStatus === 'failed' ? 'failed' : 'pending';
619
-
620
- // Build fdData for display
621
- const fdDataParam = activeFD ? {
622
- companyName: activeFD.name,
623
- amount: Number(activeFD.invested) || 0,
624
- fdRate: `${activeFD.returns}% p.a.`,
625
- tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
626
- interestPayout: activeFD.interestPayout || 'Yearly',
627
- } : undefined;
628
-
629
- navigate('PaymentStatus', { status: statusParam, transactionId, fdData: fdDataParam } as any);
630
- return;
631
-
632
- } catch (error) {
633
- // Handle error silently
634
-
635
- // On error, navigate with pending status as fallback
636
- const fdDataParam = activeFD ? {
637
- companyName: activeFD.name,
638
- amount: Number(activeFD.invested) || 0,
639
- fdRate: `${activeFD.returns}% p.a.`,
640
- tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
641
- interestPayout: activeFD.interestPayout || 'Yearly',
642
- } : undefined;
643
- navigate('PaymentStatus', { status: 'pending', transactionId, fdData: fdDataParam } as any);
644
- return;
645
- }
598
+ const fdDataParam = activeFD ? {
599
+ companyName: activeFD.name,
600
+ amount: Number(activeFD.invested) || 0,
601
+ fdRate: `${activeFD.returns}% p.a.`,
602
+ tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
603
+ interestPayout: activeFD.interestPayout || 'Yearly',
604
+ } : undefined;
605
+ navigate('PaymentStatus', { status: 'pending', transactionId, fdData: fdDataParam } as any);
606
+ return;
646
607
  }
647
608
 
648
609
  // Use workflow navigation based on current state (non-payment states or no transactionId)
@@ -1281,4 +1242,4 @@ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeN
1281
1242
  },
1282
1243
  });
1283
1244
 
1284
- export default FDList;
1245
+ export default FDList;