@finspringinnovations/fdsdk 0.0.7 → 0.0.8

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.
@@ -9,12 +9,18 @@ export declare const WORKFLOW_STATES: {
9
9
  readonly PAYMENT: 410;
10
10
  readonly END: 403;
11
11
  };
12
+ export declare const WORKFLOW_STATE_CAPTIONS: Record<string, number>;
12
13
  export declare const WORKFLOW_TASKS: {
13
14
  readonly RESEND_OTP: 11045;
14
15
  readonly VALIDATE_OTP: 11047;
15
16
  };
16
17
  export type WorkflowState = typeof WORKFLOW_STATES[keyof typeof WORKFLOW_STATES];
17
18
  export type WorkflowTask = typeof WORKFLOW_TASKS[keyof typeof WORKFLOW_TASKS];
19
+ /**
20
+ * Resolve workflow state from either current_state_caption (string) or current_state (number).
21
+ * Prefers caption when available so navigation is based on current_state_caption.
22
+ */
23
+ export declare function resolveWorkflowState(caption: string | null | undefined, numericState: number | null | undefined): WorkflowState | null;
18
24
  export declare const getWorkflowStateName: (stateId: number) => string | undefined;
19
25
  export declare const getWorkflowTaskName: (taskId: number) => string | undefined;
20
26
  export declare const WORKFLOW_CONSTANTS: {
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WORKFLOW_CONSTANTS = exports.getWorkflowTaskName = exports.getWorkflowStateName = exports.WORKFLOW_TASKS = exports.WORKFLOW_STATES = void 0;
4
- // Workflow State Constants
3
+ exports.WORKFLOW_CONSTANTS = exports.getWorkflowTaskName = exports.getWorkflowStateName = exports.WORKFLOW_TASKS = exports.WORKFLOW_STATE_CAPTIONS = exports.WORKFLOW_STATES = void 0;
4
+ exports.resolveWorkflowState = resolveWorkflowState;
5
+ // Workflow State Constants (numeric)
5
6
  exports.WORKFLOW_STATES = {
6
7
  INITIATION: 404,
7
8
  START: 405,
@@ -13,11 +14,39 @@ exports.WORKFLOW_STATES = {
13
14
  PAYMENT: 410,
14
15
  END: 403,
15
16
  };
17
+ // Workflow state captions from backend (current_state_caption) → numeric state
18
+ exports.WORKFLOW_STATE_CAPTIONS = {
19
+ SHRIRAM_V1_S1_START: 405,
20
+ SHRIRAM_V1_S2_INITIATION: 404,
21
+ SHRIRAM_V1_S3_CUSTOMER_INFO_KYC: 406,
22
+ SHRIRAM_V1_S4_OCCUPATION: 409,
23
+ SHRIRAM_V1_S5_NOMINEE_DETAILS: 407,
24
+ SHRIRAM_V1_S6_BANK_DETAILS: 408,
25
+ SHRIRAM_V1_S7_FD_CREATION: 411,
26
+ SHRIRAM_V1_S8_PAYMENT: 410,
27
+ SHRIRAM_V1_S9_END: 403,
28
+ };
16
29
  // Workflow Task Constants
17
30
  exports.WORKFLOW_TASKS = {
18
31
  RESEND_OTP: 11045,
19
32
  VALIDATE_OTP: 11047,
20
33
  };
34
+ /**
35
+ * Resolve workflow state from either current_state_caption (string) or current_state (number).
36
+ * Prefers caption when available so navigation is based on current_state_caption.
37
+ */
38
+ function resolveWorkflowState(caption, numericState) {
39
+ const captionStr = typeof caption === 'string' ? caption.trim() : '';
40
+ if (captionStr && exports.WORKFLOW_STATE_CAPTIONS[captionStr] !== undefined) {
41
+ return exports.WORKFLOW_STATE_CAPTIONS[captionStr];
42
+ }
43
+ const num = numericState != null ? Number(numericState) : NaN;
44
+ const values = Object.values(exports.WORKFLOW_STATES);
45
+ if (!Number.isNaN(num) && values.includes(num)) {
46
+ return num;
47
+ }
48
+ return null;
49
+ }
21
50
  // Helper function to get workflow state name
22
51
  const getWorkflowStateName = (stateId) => {
23
52
  const entry = Object.entries(exports.WORKFLOW_STATES).find(([_, value]) => value === stateId);
@@ -105,7 +105,7 @@ export declare const FD_STRINGS: {
105
105
  readonly LESS_THAN_1_3Y_TAB: "<1-3Y";
106
106
  readonly LESS_THAN_3_5Y_TAB: "<3-5Y";
107
107
  readonly GREATER_THAN_5Y_TAB: ">5Y";
108
- readonly ACTIVE_FDS_SECTION: "Active FDs";
108
+ readonly ACTIVE_FDS_SECTION: "Pending FDs";
109
109
  readonly LOADING_RATES: "Loading rates...";
110
110
  readonly LOADING_APPLICATIONS: "Loading applications...";
111
111
  readonly LOADING_FD_DATA: "Searching for the best F.D. deals...";
@@ -131,7 +131,7 @@ exports.FD_STRINGS = {
131
131
  LESS_THAN_1_3Y_TAB: '<1-3Y',
132
132
  LESS_THAN_3_5Y_TAB: '<3-5Y',
133
133
  GREATER_THAN_5Y_TAB: '>5Y',
134
- ACTIVE_FDS_SECTION: 'Active FDs',
134
+ ACTIVE_FDS_SECTION: 'Pending FDs',
135
135
  // Loading and status messages
136
136
  LOADING_RATES: 'Loading rates...',
137
137
  LOADING_APPLICATIONS: 'Loading applications...',
@@ -46,9 +46,11 @@ const customerApi_1 = require("../api/customerApi");
46
46
  const masterDataApi_1 = require("../api/masterDataApi");
47
47
  const MasterDataProvider_1 = require("../providers/MasterDataProvider");
48
48
  const workflowApi_1 = require("../api/workflowApi");
49
+ const fdApi_1 = require("../api/fdApi");
49
50
  const SafeAreaWrapper_1 = __importDefault(require("../components/SafeAreaWrapper"));
50
51
  const appDataConfig_1 = require("../config/appDataConfig");
51
52
  const helpers_1 = require("../navigation/helpers");
53
+ const paymentSession_1 = require("../state/paymentSession");
52
54
  const store_1 = require("../store");
53
55
  const onboardingSlice_1 = require("../store/onboardingSlice");
54
56
  const fdListSelectedSlice_1 = require("../store/fdListSelectedSlice");
@@ -68,12 +70,15 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
68
70
  const spacing = (0, ThemeContext_1.useSpacing)();
69
71
  const { themeName } = (0, ThemeContext_1.useTheme)();
70
72
  const dispatch = (0, store_1.useAppDispatch)();
73
+ const onboardingIds = (0, store_1.useAppSelector)((state) => state === null || state === void 0 ? void 0 : state.onboarding);
71
74
  const hasFetchedRatesRef = react_1.default.useRef(false);
72
75
  const isCallingInterestRatesRef = react_1.default.useRef(false);
73
76
  // API calls to fetch
74
77
  const [getInterestRates, { data: interestRates, error: interestRatesError, isLoading: isLoadingRates, }] = (0, interestRateApi_1.useGetInterestRatesMutation)();
75
78
  const [getCustomerApplications, { data: customerApplications, error: customerApplicationsError, isLoading: isLoadingApplications, }] = (0, customerApi_1.useGetCustomerApplicationsMutation)();
76
79
  const [terminateWorkflow, { data: terminateWorkflowData, error: terminateWorkflowError, isLoading: isTerminatingWorkflow, }] = (0, workflowApi_1.useTerminateWorkflowMutation)();
80
+ const [paymentReverseFeed, { isLoading: isCheckingPaymentStatus }] = (0, fdApi_1.usePaymentReverseFeedMutation)();
81
+ const [paymentRetry, { isLoading: isRetryingPayment }] = (0, fdApi_1.usePaymentRetryMutation)();
77
82
  const styles = createStyles(colors, typography, spacing, themeName);
78
83
  const { setMasterData } = (0, MasterDataProvider_1.useMasterData)();
79
84
  // Helper function to check if customer applications is empty
@@ -466,6 +471,7 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
466
471
  }, [customerApplications, dispatch]);
467
472
  // Unified handler: behave like bottom sheet Continue flow
468
473
  const handlePendingFDContinue = async () => {
474
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
469
475
  // Persist active FD data to store for use across screens
470
476
  try {
471
477
  if (activeFD) {
@@ -490,9 +496,14 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
490
496
  catch (e) {
491
497
  // Handle error silently
492
498
  }
493
- // Get current state from customer applications for workflow navigation
494
- let currentState = null;
499
+ // Get current state (from current_state_caption first) and identifiers from customer applications for workflow navigation
500
+ let currentStateCaption = undefined;
501
+ let currentStateNumeric = undefined;
495
502
  let transactionId = undefined;
503
+ let applicationId = undefined;
504
+ let workflowInstanceId = undefined;
505
+ let entityId = undefined;
506
+ let providerIdFromApp = undefined;
496
507
  try {
497
508
  if (customerApplications && !isCustomerApplicationsEmpty()) {
498
509
  // Normalize list
@@ -512,30 +523,85 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
512
523
  const s = (a.wf_status || a.status || '').toString();
513
524
  return s === 'Active';
514
525
  }) || applicationsData[0];
515
- currentState = appData.current_state;
516
- // Extract transactionId from application data
526
+ currentStateCaption = (_a = appData.current_state_caption) !== null && _a !== void 0 ? _a : appData.currentStateCaption;
527
+ currentStateNumeric = (_b = appData.current_state) !== null && _b !== void 0 ? _b : appData.currentState;
517
528
  transactionId = appData.transaction_id || appData.transactionId;
529
+ applicationId = appData.application_id || appData.applicationId || appData.id;
530
+ workflowInstanceId = appData.workflow_instance_id || appData.workflowInstanceId || ((_c = appData.workflow) === null || _c === void 0 ? void 0 : _c.instanceId);
531
+ entityId = appData.entity_id || appData.entityId || ((_d = appData.entity) === null || _d === void 0 ? void 0 : _d.id);
532
+ providerIdFromApp = appData.provider_id || appData.providerId || appData.fd_provider_id || appData.fdProviderId;
518
533
  }
519
534
  }
520
535
  }
521
536
  catch (error) {
522
537
  // Handle error silently
523
538
  }
524
- // If current state is payment and transactionId is available,
525
- // open PaymentStatus and let SSE (/events) drive status updates.
526
- if (currentState === workflowConstants_1.WORKFLOW_STATES.PAYMENT && transactionId) {
527
- const fdDataParam = activeFD ? {
528
- companyName: activeFD.name,
529
- amount: Number(activeFD.invested) || 0,
530
- fdRate: `${activeFD.returns}% p.a.`,
531
- tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
532
- interestPayout: activeFD.interestPayout || 'Yearly',
533
- } : undefined;
534
- (0, helpers_1.navigate)('PaymentStatus', { status: 'pending', transactionId, fdData: fdDataParam });
539
+ // Resolve state from current_state_caption (preferred) or current_state
540
+ const currentState = (0, workflowConstants_1.resolveWorkflowState)(currentStateCaption, currentStateNumeric);
541
+ const fdDataParam = activeFD ? {
542
+ companyName: activeFD.name,
543
+ amount: Number(activeFD.invested) || 0,
544
+ fdRate: `${activeFD.returns}% p.a.`,
545
+ tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
546
+ interestPayout: activeFD.interestPayout || 'Yearly',
547
+ } : undefined;
548
+ // If FD status is payment (410 or SHRIRAM_V1_S8_PAYMENT): run reverse feed first, then navigate by result or retry and go to Payment
549
+ const isPaymentState = currentState === workflowConstants_1.WORKFLOW_STATES.PAYMENT || currentStateCaption === 'SHRIRAM_V1_S8_PAYMENT';
550
+ const resolvedAppId = applicationId || (onboardingIds === null || onboardingIds === void 0 ? void 0 : onboardingIds.applicationId);
551
+ const resolvedWfId = workflowInstanceId || (onboardingIds === null || onboardingIds === void 0 ? void 0 : onboardingIds.workflowInstanceId);
552
+ if (isPaymentState && transactionId && resolvedAppId && resolvedWfId) {
553
+ const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
554
+ const userRefId = (_e = userInfo === null || userInfo === void 0 ? void 0 : userInfo.userReferenceId) !== null && _e !== void 0 ? _e : userInfo === null || userInfo === void 0 ? void 0 : userInfo.id;
555
+ const providerId = providerIdFromApp || (onboardingIds === null || onboardingIds === void 0 ? void 0 : onboardingIds.providerId);
556
+ const resolvedEntityId = (_f = entityId !== null && entityId !== void 0 ? entityId : onboardingIds === null || onboardingIds === void 0 ? void 0 : onboardingIds.entityid) !== null && _f !== void 0 ? _f : '';
557
+ try {
558
+ const reverseFeedRes = await paymentReverseFeed({
559
+ providerId,
560
+ workflowInstanceId: resolvedWfId,
561
+ userreferenceid: userRefId,
562
+ applicationid: resolvedAppId,
563
+ entityid: resolvedEntityId,
564
+ transactionId,
565
+ }).unwrap();
566
+ const paymentStatus = String((_j = (_h = (_g = reverseFeedRes === null || reverseFeedRes === void 0 ? void 0 : reverseFeedRes.data) === null || _g === void 0 ? void 0 : _g.paymentStatus) !== null && _h !== void 0 ? _h : reverseFeedRes === null || reverseFeedRes === void 0 ? void 0 : reverseFeedRes.paymentStatus) !== null && _j !== void 0 ? _j : '').toUpperCase();
567
+ if (paymentStatus === 'SUCCESS') {
568
+ (0, helpers_1.navigate)('PaymentStatus', { status: 'success', transactionId, fdData: fdDataParam });
569
+ return;
570
+ }
571
+ if (paymentStatus === 'FAILED') {
572
+ (0, helpers_1.navigate)('PaymentStatus', { status: 'failed', transactionId, fdData: fdDataParam });
573
+ return;
574
+ }
575
+ }
576
+ catch (e) {
577
+ // Reverse feed error or no conclusive status → fall through to retry
578
+ }
579
+ // No response / still pending: hit retry payment API and navigate to Payment screen
580
+ try {
581
+ const retryRes = await paymentRetry({
582
+ providerId,
583
+ workflowInstanceId: resolvedWfId,
584
+ userreferenceid: userRefId,
585
+ applicationid: resolvedAppId,
586
+ entityid: resolvedEntityId,
587
+ transactionId,
588
+ }).unwrap();
589
+ const payload = Array.isArray(retryRes === null || retryRes === void 0 ? void 0 : retryRes.data) ? retryRes.data[0] : (_k = retryRes === null || retryRes === void 0 ? void 0 : retryRes.data) !== null && _k !== void 0 ? _k : retryRes;
590
+ const newPaymentUrl = (payload === null || payload === void 0 ? void 0 : payload.PaymentUrl) || (payload === null || payload === void 0 ? void 0 : payload.paymentUrl);
591
+ const newTransactionId = (payload === null || payload === void 0 ? void 0 : payload.Transactionid) || (payload === null || payload === void 0 ? void 0 : payload.transactionId) || transactionId;
592
+ if (newPaymentUrl) {
593
+ (0, paymentSession_1.setPaymentSession)({ paymentUrl: newPaymentUrl, transactionId: newTransactionId });
594
+ dispatch((0, onboardingSlice_1.setOnboardingIds)({ workflowInstanceId: resolvedWfId, applicationId: resolvedAppId, entityid: resolvedEntityId || undefined, providerId }));
595
+ }
596
+ }
597
+ catch (e) {
598
+ // Retry API failed; still navigate to Payment so user can try
599
+ }
600
+ (0, helpers_1.navigate)('Payment');
535
601
  return;
536
602
  }
537
- // Use workflow navigation based on current state (non-payment states or no transactionId)
538
- if (currentState && Object.values(workflowConstants_1.WORKFLOW_STATES).includes(currentState)) {
603
+ // Use workflow navigation based on resolved current state (from current_state_caption or current_state)
604
+ if (currentState != null && Object.values(workflowConstants_1.WORKFLOW_STATES).includes(currentState)) {
539
605
  (0, workflowNavigator_1.handleWorkflowNavigation)({
540
606
  workflowState: currentState,
541
607
  investmentData: undefined,
@@ -46,6 +46,7 @@ const ActionButton_1 = __importDefault(require("../components/ActionButton"));
46
46
  const ThemeContext_1 = require("../theme/ThemeContext");
47
47
  const fdApi_1 = require("../api/fdApi");
48
48
  const customerApi_1 = require("../api/customerApi");
49
+ const workflowApi_1 = require("../api/workflowApi");
49
50
  const store_1 = require("../store");
50
51
  const appDataConfig_1 = require("../config/appDataConfig");
51
52
  const apiConfig_1 = require("../config/apiConfig");
@@ -77,6 +78,10 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
77
78
  const applicationId = (0, store_1.useAppSelector)((state) => { var _a; return (_a = state === null || state === void 0 ? void 0 : state.onboarding) === null || _a === void 0 ? void 0 : _a.applicationId; });
78
79
  const entityId = (0, store_1.useAppSelector)((state) => { var _a; return (_a = state === null || state === void 0 ? void 0 : state.onboarding) === null || _a === void 0 ? void 0 : _a.entityid; });
79
80
  const providerId = (0, store_1.useAppSelector)((state) => { var _a; return (_a = state === null || state === void 0 ? void 0 : state.onboarding) === null || _a === void 0 ? void 0 : _a.providerId; });
81
+ const defaultProviderId = (0, store_1.useAppSelector)((state) => { var _a; return (_a = state === null || state === void 0 ? void 0 : state.onboarding) === null || _a === void 0 ? void 0 : _a.providerId; });
82
+ // Previous State API (same as Occupation / Employee screen - move FD to previous state)
83
+ const [previousState] = (0, workflowApi_1.usePreviousStateMutation)();
84
+ const [isGoingBack, setIsGoingBack] = (0, react_1.useState)(false);
80
85
  const sseXhrRef = (0, react_1.useRef)(null);
81
86
  const sseLastIndexRef = (0, react_1.useRef)(0);
82
87
  const sseBufferRef = (0, react_1.useRef)({ value: '' });
@@ -276,6 +281,13 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
276
281
  }
277
282
  }
278
283
  catch (error) {
284
+ // Fallback: go to Payment screen so user can retry (e.g. when status is pending)
285
+ if (onRetry) {
286
+ onRetry();
287
+ }
288
+ else {
289
+ (0, helpers_1.navigate)('Payment');
290
+ }
279
291
  }
280
292
  };
281
293
  // Handle exit button click - fetch FD details and pass to main app
@@ -374,20 +386,46 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
374
386
  }
375
387
  }
376
388
  };
377
- // Disable hardware back button on PaymentStatus screen
389
+ // Back handler for pending: same as Occupation screen - call previousState API then navigate to Payment
390
+ const handleBackPress = (0, react_1.useCallback)(async () => {
391
+ var _a;
392
+ setIsGoingBack(true);
393
+ try {
394
+ const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
395
+ await previousState({
396
+ providerId: defaultProviderId,
397
+ workflowInstanceId,
398
+ userreferenceid: (_a = userInfo === null || userInfo === void 0 ? void 0 : userInfo.userReferenceId) !== null && _a !== void 0 ? _a : userInfo === null || userInfo === void 0 ? void 0 : userInfo.id,
399
+ applicationid: applicationId,
400
+ entityid: entityId !== null && entityId !== void 0 ? entityId : '',
401
+ });
402
+ }
403
+ catch (e) {
404
+ // Handle error silently
405
+ }
406
+ finally {
407
+ setIsGoingBack(false);
408
+ (0, helpers_1.navigate)('PayNow');
409
+ }
410
+ }, [defaultProviderId, workflowInstanceId, applicationId, entityId, previousState]);
411
+ // On pending: allow back (header + hardware) using same handleBackPress as Occupation. On success/failed: block hardware back.
378
412
  (0, react_1.useEffect)(() => {
379
413
  if (react_native_1.Platform.OS !== 'android')
380
414
  return;
381
415
  const onBackPress = () => {
382
- // Return true to prevent default behavior (block back button)
383
- return true;
416
+ if (currentStatus === 'pending') {
417
+ handleBackPress();
418
+ return true;
419
+ }
420
+ return true; // block back on success/failed
384
421
  };
385
422
  const backHandler = react_native_1.BackHandler.addEventListener('hardwareBackPress', onBackPress);
386
423
  return () => backHandler.remove();
387
- }, []);
424
+ }, [currentStatus, handleBackPress]);
388
425
  return (react_1.default.createElement(SafeAreaWrapper_1.default, { includeTop: true, bottomPadding: 0, statusBarColor: colors.background, statusBarStyle: themeName === 'dark' ? 'light-content' : 'dark-content' },
389
426
  react_native_1.Platform.OS === 'ios' && react_1.default.createElement(react_native_1.StatusBar, { barStyle: themeName === 'dark' ? 'light-content' : 'dark-content' }),
390
427
  react_1.default.createElement(react_native_1.View, { style: styles.container },
428
+ currentStatus === 'pending' && (react_1.default.createElement(components_1.Header, { title: bank_1.BANK_STRINGS.PAYMENT_STATUS_TITLE, onBackPress: handleBackPress, backgroundColor: colors.primary })),
391
429
  react_1.default.createElement(react_native_1.ScrollView, { showsVerticalScrollIndicator: false, contentContainerStyle: styles.scrollContent },
392
430
  react_1.default.createElement(react_native_1.View, { style: styles.iconContainer },
393
431
  react_1.default.createElement(react_native_1.View, { style: styles.iconCircleOuter },
@@ -408,7 +446,8 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
408
446
  react_1.default.createElement(react_native_1.Text, { style: styles.disclaimerText }, bank_1.BANK_STRINGS.PAYMENT_SUCCESS_DISCLAIMER)))),
409
447
  react_1.default.createElement(react_native_1.View, { style: styles.footer },
410
448
  currentStatus === 'success' && (react_1.default.createElement(ActionButton_1.default, { title: isLoadingCustomerApplications ? common_1.COMMON_STRINGS.LOADING : common_1.COMMON_STRINGS.EXIT, onPress: handleExit, disabled: isLoadingCustomerApplications })),
411
- currentStatus === 'failed' && (react_1.default.createElement(ActionButton_1.default, { title: isLoadingPaymentRetry ? common_1.COMMON_STRINGS.RETRYING : bank_1.BANK_STRINGS.RETRY_PAYMENT_BUTTON, onPress: handlePaymentRetry, disabled: isLoadingPaymentRetry }))))));
449
+ currentStatus === 'failed' && (react_1.default.createElement(ActionButton_1.default, { title: isLoadingPaymentRetry ? common_1.COMMON_STRINGS.RETRYING : bank_1.BANK_STRINGS.RETRY_PAYMENT_BUTTON, onPress: handlePaymentRetry, disabled: isLoadingPaymentRetry })),
450
+ currentStatus === 'pending' && (react_1.default.createElement(ActionButton_1.default, { title: isLoadingPaymentRetry ? common_1.COMMON_STRINGS.RETRYING : bank_1.BANK_STRINGS.RETRY_PAYMENT_BUTTON, onPress: handlePaymentRetry, disabled: isLoadingPaymentRetry }))))));
412
451
  };
413
452
  const createStyles = (colors, typography, status, themeName) => {
414
453
  const isDark = themeName === 'dark';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finspringinnovations/fdsdk",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "FD SDK for React Native applications",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,4 +1,4 @@
1
- // Workflow State Constants
1
+ // Workflow State Constants (numeric)
2
2
  export const WORKFLOW_STATES = {
3
3
  INITIATION: 404,
4
4
  START: 405,
@@ -11,6 +11,19 @@ export const WORKFLOW_STATES = {
11
11
  END: 403,
12
12
  } as const;
13
13
 
14
+ // Workflow state captions from backend (current_state_caption) → numeric state
15
+ export const WORKFLOW_STATE_CAPTIONS: Record<string, number> = {
16
+ SHRIRAM_V1_S1_START: 405,
17
+ SHRIRAM_V1_S2_INITIATION: 404,
18
+ SHRIRAM_V1_S3_CUSTOMER_INFO_KYC: 406,
19
+ SHRIRAM_V1_S4_OCCUPATION: 409,
20
+ SHRIRAM_V1_S5_NOMINEE_DETAILS: 407,
21
+ SHRIRAM_V1_S6_BANK_DETAILS: 408,
22
+ SHRIRAM_V1_S7_FD_CREATION: 411,
23
+ SHRIRAM_V1_S8_PAYMENT: 410,
24
+ SHRIRAM_V1_S9_END: 403,
25
+ };
26
+
14
27
  // Workflow Task Constants
15
28
  export const WORKFLOW_TASKS = {
16
29
  RESEND_OTP: 11045,
@@ -21,6 +34,23 @@ export const WORKFLOW_TASKS = {
21
34
  export type WorkflowState = typeof WORKFLOW_STATES[keyof typeof WORKFLOW_STATES];
22
35
  export type WorkflowTask = typeof WORKFLOW_TASKS[keyof typeof WORKFLOW_TASKS];
23
36
 
37
+ /**
38
+ * Resolve workflow state from either current_state_caption (string) or current_state (number).
39
+ * Prefers caption when available so navigation is based on current_state_caption.
40
+ */
41
+ export function resolveWorkflowState(caption: string | null | undefined, numericState: number | null | undefined): WorkflowState | null {
42
+ const captionStr = typeof caption === 'string' ? caption.trim() : '';
43
+ if (captionStr && WORKFLOW_STATE_CAPTIONS[captionStr] !== undefined) {
44
+ return WORKFLOW_STATE_CAPTIONS[captionStr] as WorkflowState;
45
+ }
46
+ const num = numericState != null ? Number(numericState) : NaN;
47
+ const values = Object.values(WORKFLOW_STATES) as number[];
48
+ if (!Number.isNaN(num) && values.includes(num)) {
49
+ return num as WorkflowState;
50
+ }
51
+ return null;
52
+ }
53
+
24
54
  // Helper function to get workflow state name
25
55
  export const getWorkflowStateName = (stateId: number): string | undefined => {
26
56
  const entry = Object.entries(WORKFLOW_STATES).find(([_, value]) => value === stateId);
@@ -149,7 +149,7 @@ export const FD_STRINGS = {
149
149
  LESS_THAN_1_3Y_TAB: '<1-3Y',
150
150
  LESS_THAN_3_5Y_TAB: '<3-5Y',
151
151
  GREATER_THAN_5Y_TAB: '>5Y',
152
- ACTIVE_FDS_SECTION: 'Active FDs',
152
+ ACTIVE_FDS_SECTION: 'Pending FDs',
153
153
 
154
154
  // Loading and status messages
155
155
  LOADING_RATES: 'Loading rates...',
@@ -20,15 +20,17 @@ 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, usePaymentRetryMutation } from '../api/fdApi';
23
24
  import type { ColorScheme, ThemeName } from '../theme';
24
25
  import SafeAreaWrapper from '../components/SafeAreaWrapper';
25
26
  import { getUserInfoForAPI, getAppData } from '../config/appDataConfig';
26
27
  import { navigate } from '../navigation/helpers';
28
+ import { setPaymentSession } from '../state/paymentSession';
27
29
  import { useAppDispatch, useAppSelector } from '../store';
28
30
  import { setOnboardingIds } from '../store/onboardingSlice';
29
31
  import { setFDListSelected } from '../store/fdListSelectedSlice';
30
32
  import { handleWorkflowNavigation } from '../navigation/workflowNavigator';
31
- import { WORKFLOW_STATES } from '../config/workflowConstants';
33
+ import { WORKFLOW_STATES, WORKFLOW_STATE_CAPTIONS, resolveWorkflowState } from '../config/workflowConstants';
32
34
  import { setGlobalData } from '../utils/globalData';
33
35
  import { FD_STRINGS } from '../constants/strings';
34
36
  import { base64Images } from '../constants/strings/base64Images';
@@ -76,6 +78,7 @@ const FDList: React.FC<FDListProps> = ({
76
78
  const spacing = useSpacing();
77
79
  const { themeName } = useTheme();
78
80
  const dispatch = useAppDispatch();
81
+ const onboardingIds = useAppSelector((state: any) => state?.onboarding);
79
82
  const hasFetchedRatesRef = React.useRef(false);
80
83
  const isCallingInterestRatesRef = React.useRef(false);
81
84
 
@@ -98,6 +101,9 @@ const FDList: React.FC<FDListProps> = ({
98
101
  isLoading: isTerminatingWorkflow,
99
102
  }] = useTerminateWorkflowMutation();
100
103
 
104
+ const [paymentReverseFeed, { isLoading: isCheckingPaymentStatus }] = usePaymentReverseFeedMutation();
105
+ const [paymentRetry, { isLoading: isRetryingPayment }] = usePaymentRetryMutation();
106
+
101
107
  const styles = createStyles(colors, typography, spacing, themeName);
102
108
  const { setMasterData } = useMasterData();
103
109
 
@@ -561,9 +567,14 @@ const FDList: React.FC<FDListProps> = ({
561
567
  // Handle error silently
562
568
  }
563
569
 
564
- // Get current state from customer applications for workflow navigation
565
- let currentState: any = null;
570
+ // Get current state (from current_state_caption first) and identifiers from customer applications for workflow navigation
571
+ let currentStateCaption: string | null | undefined = undefined;
572
+ let currentStateNumeric: number | null | undefined = undefined;
566
573
  let transactionId: string | undefined = undefined;
574
+ let applicationId: string | undefined = undefined;
575
+ let workflowInstanceId: string | undefined = undefined;
576
+ let entityId: string | undefined = undefined;
577
+ let providerIdFromApp: string | undefined = undefined;
567
578
  try {
568
579
  if (customerApplications && !isCustomerApplicationsEmpty()) {
569
580
  // Normalize list
@@ -582,32 +593,90 @@ const FDList: React.FC<FDListProps> = ({
582
593
  const s = (a.wf_status || a.status || '').toString();
583
594
  return s === 'Active';
584
595
  }) || applicationsData[0];
585
- currentState = appData.current_state;
586
-
587
- // Extract transactionId from application data
596
+ currentStateCaption = appData.current_state_caption ?? appData.currentStateCaption;
597
+ currentStateNumeric = appData.current_state ?? appData.currentState;
588
598
  transactionId = appData.transaction_id || appData.transactionId;
599
+ applicationId = appData.application_id || appData.applicationId || appData.id;
600
+ workflowInstanceId = appData.workflow_instance_id || appData.workflowInstanceId || appData.workflow?.instanceId;
601
+ entityId = appData.entity_id || appData.entityId || appData.entity?.id;
602
+ providerIdFromApp = appData.provider_id || appData.providerId || appData.fd_provider_id || appData.fdProviderId;
589
603
  }
590
604
  }
591
605
  } catch (error) {
592
606
  // Handle error silently
593
607
  }
594
608
 
595
- // If current state is payment and transactionId is available,
596
- // open PaymentStatus and let SSE (/events) drive status updates.
597
- if (currentState === WORKFLOW_STATES.PAYMENT && transactionId) {
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);
609
+ // Resolve state from current_state_caption (preferred) or current_state
610
+ const currentState = resolveWorkflowState(currentStateCaption, currentStateNumeric);
611
+
612
+ const fdDataParam = activeFD ? {
613
+ companyName: activeFD.name,
614
+ amount: Number(activeFD.invested) || 0,
615
+ fdRate: `${activeFD.returns}% p.a.`,
616
+ tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
617
+ interestPayout: activeFD.interestPayout || 'Yearly',
618
+ } : undefined;
619
+
620
+ // If FD status is payment (410 or SHRIRAM_V1_S8_PAYMENT): run reverse feed first, then navigate by result or retry and go to Payment
621
+ const isPaymentState = currentState === WORKFLOW_STATES.PAYMENT || currentStateCaption === 'SHRIRAM_V1_S8_PAYMENT';
622
+ const resolvedAppId = applicationId || onboardingIds?.applicationId;
623
+ const resolvedWfId = workflowInstanceId || onboardingIds?.workflowInstanceId;
624
+ if (isPaymentState && transactionId && resolvedAppId && resolvedWfId) {
625
+ const userInfo = getUserInfoForAPI();
626
+ const userRefId = userInfo?.userReferenceId ?? userInfo?.id;
627
+ const providerId = providerIdFromApp || onboardingIds?.providerId;
628
+ const resolvedEntityId = entityId ?? onboardingIds?.entityid ?? '';
629
+
630
+ try {
631
+ const reverseFeedRes = await paymentReverseFeed({
632
+ providerId,
633
+ workflowInstanceId: resolvedWfId,
634
+ userreferenceid: userRefId,
635
+ applicationid: resolvedAppId,
636
+ entityid: resolvedEntityId,
637
+ transactionId,
638
+ }).unwrap();
639
+
640
+ const paymentStatus = String(reverseFeedRes?.data?.paymentStatus ?? reverseFeedRes?.paymentStatus ?? '').toUpperCase();
641
+ if (paymentStatus === 'SUCCESS') {
642
+ navigate('PaymentStatus', { status: 'success', transactionId, fdData: fdDataParam } as any);
643
+ return;
644
+ }
645
+ if (paymentStatus === 'FAILED') {
646
+ navigate('PaymentStatus', { status: 'failed', transactionId, fdData: fdDataParam } as any);
647
+ return;
648
+ }
649
+ } catch (e) {
650
+ // Reverse feed error or no conclusive status → fall through to retry
651
+ }
652
+
653
+ // No response / still pending: hit retry payment API and navigate to Payment screen
654
+ try {
655
+ const retryRes = await paymentRetry({
656
+ providerId,
657
+ workflowInstanceId: resolvedWfId,
658
+ userreferenceid: userRefId,
659
+ applicationid: resolvedAppId,
660
+ entityid: resolvedEntityId,
661
+ transactionId,
662
+ }).unwrap();
663
+
664
+ const payload: any = Array.isArray((retryRes as any)?.data) ? (retryRes as any).data[0] : (retryRes as any)?.data ?? retryRes;
665
+ const newPaymentUrl = payload?.PaymentUrl || payload?.paymentUrl;
666
+ const newTransactionId = payload?.Transactionid || payload?.transactionId || transactionId;
667
+ if (newPaymentUrl) {
668
+ setPaymentSession({ paymentUrl: newPaymentUrl, transactionId: newTransactionId });
669
+ dispatch(setOnboardingIds({ workflowInstanceId: resolvedWfId, applicationId: resolvedAppId, entityid: resolvedEntityId || undefined, providerId }));
670
+ }
671
+ } catch (e) {
672
+ // Retry API failed; still navigate to Payment so user can try
673
+ }
674
+ navigate('Payment');
606
675
  return;
607
676
  }
608
677
 
609
- // Use workflow navigation based on current state (non-payment states or no transactionId)
610
- if (currentState && Object.values(WORKFLOW_STATES).includes(currentState)) {
678
+ // Use workflow navigation based on resolved current state (from current_state_caption or current_state)
679
+ if (currentState != null && Object.values(WORKFLOW_STATES).includes(currentState)) {
611
680
  handleWorkflowNavigation({
612
681
  workflowState: currentState,
613
682
  investmentData: undefined,
@@ -3,11 +3,12 @@ import { View, Text, StyleSheet, ScrollView, Alert, Image, BackHandler, Platform
3
3
  import Icon from 'react-native-vector-icons/Ionicons';
4
4
  import { base64Images } from '../constants/strings/base64Images';
5
5
  import SafeAreaWrapper from '../components/SafeAreaWrapper';
6
- import { PaymentDetailsCard } from '../components';
6
+ import { Header, PaymentDetailsCard } from '../components';
7
7
  import ActionButton from '../components/ActionButton';
8
8
  import { useColors, useTypography, useTheme } from '../theme/ThemeContext';
9
9
  import { usePaymentRetryMutation } from '../api/fdApi';
10
10
  import { useGetCustomerApplicationsMutation } from '../api/customerApi';
11
+ import { usePreviousStateMutation } from '../api/workflowApi';
11
12
  import { useAppSelector } from '../store';
12
13
  import { getEnvironmentData, getUserInfoForAPI } from '../config/appDataConfig';
13
14
  import { getApiConfig, getSecureHeaders } from '../config/apiConfig';
@@ -65,6 +66,12 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
65
66
  const applicationId = useAppSelector((state: any) => state?.onboarding?.applicationId);
66
67
  const entityId = useAppSelector((state: any) => state?.onboarding?.entityid);
67
68
  const providerId = useAppSelector((state: any) => state?.onboarding?.providerId);
69
+ const defaultProviderId = useAppSelector((state: any) => state?.onboarding?.providerId);
70
+
71
+ // Previous State API (same as Occupation / Employee screen - move FD to previous state)
72
+ const [previousState] = usePreviousStateMutation();
73
+ const [isGoingBack, setIsGoingBack] = useState(false);
74
+
68
75
  const sseXhrRef = useRef<XMLHttpRequest | null>(null);
69
76
  const sseLastIndexRef = useRef<number>(0);
70
77
  const sseBufferRef = useRef<{ value: string }>({ value: '' });
@@ -298,6 +305,12 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
298
305
  Alert.alert(BANK_STRINGS.PAYMENT_FAILED, BANK_STRINGS.PAYMENT_GATEWAY_ERROR);
299
306
  }
300
307
  } catch (error) {
308
+ // Fallback: go to Payment screen so user can retry (e.g. when status is pending)
309
+ if (onRetry) {
310
+ onRetry();
311
+ } else {
312
+ navigate('Payment');
313
+ }
301
314
  }
302
315
  };
303
316
 
@@ -412,13 +425,36 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
412
425
  }
413
426
  };
414
427
 
415
- // Disable hardware back button on PaymentStatus screen
428
+ // Back handler for pending: same as Occupation screen - call previousState API then navigate to Payment
429
+ const handleBackPress = useCallback(async () => {
430
+ setIsGoingBack(true);
431
+ try {
432
+ const userInfo = getUserInfoForAPI();
433
+ await previousState({
434
+ providerId: defaultProviderId,
435
+ workflowInstanceId,
436
+ userreferenceid: userInfo?.userReferenceId ?? userInfo?.id,
437
+ applicationid: applicationId,
438
+ entityid: entityId ?? '',
439
+ });
440
+ } catch (e) {
441
+ // Handle error silently
442
+ } finally {
443
+ setIsGoingBack(false);
444
+ navigate('PayNow');
445
+ }
446
+ }, [defaultProviderId, workflowInstanceId, applicationId, entityId, previousState]);
447
+
448
+ // On pending: allow back (header + hardware) using same handleBackPress as Occupation. On success/failed: block hardware back.
416
449
  useEffect(() => {
417
450
  if (Platform.OS !== 'android') return;
418
451
 
419
452
  const onBackPress = () => {
420
- // Return true to prevent default behavior (block back button)
421
- return true;
453
+ if (currentStatus === 'pending') {
454
+ handleBackPress();
455
+ return true;
456
+ }
457
+ return true; // block back on success/failed
422
458
  };
423
459
 
424
460
  const backHandler = BackHandler.addEventListener(
@@ -427,7 +463,7 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
427
463
  );
428
464
 
429
465
  return () => backHandler.remove();
430
- }, []);
466
+ }, [currentStatus, handleBackPress]);
431
467
 
432
468
  return (
433
469
  <SafeAreaWrapper
@@ -439,9 +475,13 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
439
475
  {Platform.OS === 'ios' && <StatusBar barStyle={themeName === 'dark' ? 'light-content' : 'dark-content'} />}
440
476
 
441
477
  <View style={styles.container}>
442
- {/* <Text style={styles.headerTitle}>
443
- {status === 'success' ? 'PAYMENT SUCCESSFUL' : 'PAYMENT FAILED'}
444
- </Text> */}
478
+ {currentStatus === 'pending' && (
479
+ <Header
480
+ title={BANK_STRINGS.PAYMENT_STATUS_TITLE}
481
+ onBackPress={handleBackPress}
482
+ backgroundColor={colors.primary}
483
+ />
484
+ )}
445
485
 
446
486
  <ScrollView
447
487
  showsVerticalScrollIndicator={false}
@@ -535,6 +575,13 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
535
575
  disabled={isLoadingPaymentRetry}
536
576
  />
537
577
  )}
578
+ {currentStatus === 'pending' && (
579
+ <ActionButton
580
+ title={isLoadingPaymentRetry ? COMMON_STRINGS.RETRYING : BANK_STRINGS.RETRY_PAYMENT_BUTTON}
581
+ onPress={handlePaymentRetry}
582
+ disabled={isLoadingPaymentRetry}
583
+ />
584
+ )}
538
585
  </View>
539
586
  </View>
540
587
  </SafeAreaWrapper>