@finspringinnovations/fdsdk 0.0.4 → 0.0.6
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.
- package/lib/api/baseApi.js +17 -6
- package/lib/api/masterDataApi.js +21 -1
- package/lib/api/workflowApi.js +46 -1
- package/lib/config/appDataConfig.d.ts +5 -0
- package/lib/hooks/usePaymentSSE.d.ts +7 -1
- package/lib/hooks/usePaymentSSE.js +287 -63
- package/lib/navigation/RootNavigator.js +10 -2
- package/lib/screens/AadhaarVerification.js +2 -2
- package/lib/screens/FDCalculator.js +6 -2
- package/lib/screens/FDList.js +37 -64
- package/lib/screens/Payment.js +159 -27
- package/lib/screens/PaymentStatus.js +139 -25
- package/lib/screens/ReviewKYC.js +45 -97
- package/lib/utils/sseParser.js +19 -3
- package/package.json +1 -1
- package/src/api/baseApi.ts +19 -6
- package/src/api/masterDataApi.ts +21 -3
- package/src/api/workflowApi.ts +50 -1
- package/src/config/appDataConfig.ts +5 -0
- package/src/hooks/usePaymentSSE.ts +307 -66
- package/src/navigation/RootNavigator.tsx +8 -2
- package/src/screens/AadhaarVerification.tsx +2 -2
- package/src/screens/FDCalculator.tsx +7 -2
- package/src/screens/FDList.tsx +37 -76
- package/src/screens/Payment.tsx +181 -38
- package/src/screens/PaymentStatus.tsx +158 -43
- package/src/screens/ReviewKYC.tsx +94 -170
- package/src/utils/sseParser.ts +20 -4
|
@@ -127,8 +127,12 @@ const FDCalculator = ({ onGoBack, onExitSDK, onPanRequired, onNavigateToReviewKY
|
|
|
127
127
|
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; }); // Default Shriram provider ID
|
|
128
128
|
// Default Shriram provider ID
|
|
129
129
|
const { data: fallbackMasterData, isLoading: isLoadingFallback, error: fallbackError, refetch: refetchMasterData } = (0, masterDataApi_1.useGetMasterDataQuery)({ providerId: defaultProviderId }, // Use default provider ID
|
|
130
|
-
{
|
|
131
|
-
|
|
130
|
+
{
|
|
131
|
+
skip: !!masterData || !defaultProviderId,
|
|
132
|
+
refetchOnMountOrArgChange: true,
|
|
133
|
+
refetchOnFocus: true,
|
|
134
|
+
refetchOnReconnect: true,
|
|
135
|
+
});
|
|
132
136
|
// Use fallback master data if global master data is not available
|
|
133
137
|
// Store only the data parameter after stringifying instead of complete response
|
|
134
138
|
const effectiveMasterData = react_1.default.useMemo(() => {
|
package/lib/screens/FDList.js
CHANGED
|
@@ -46,7 +46,6 @@ 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");
|
|
50
49
|
const SafeAreaWrapper_1 = __importDefault(require("../components/SafeAreaWrapper"));
|
|
51
50
|
const appDataConfig_1 = require("../config/appDataConfig");
|
|
52
51
|
const helpers_1 = require("../navigation/helpers");
|
|
@@ -75,15 +74,8 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
|
|
|
75
74
|
const [getInterestRates, { data: interestRates, error: interestRatesError, isLoading: isLoadingRates, }] = (0, interestRateApi_1.useGetInterestRatesMutation)();
|
|
76
75
|
const [getCustomerApplications, { data: customerApplications, error: customerApplicationsError, isLoading: isLoadingApplications, }] = (0, customerApi_1.useGetCustomerApplicationsMutation)();
|
|
77
76
|
const [terminateWorkflow, { data: terminateWorkflowData, error: terminateWorkflowError, isLoading: isTerminatingWorkflow, }] = (0, workflowApi_1.useTerminateWorkflowMutation)();
|
|
78
|
-
// Payment Reverse Feed API
|
|
79
|
-
const [paymentReverseFeed, { data: paymentReverseFeedResponse, error: paymentReverseFeedError, isLoading: isLoadingPaymentReverseFeed, }] = (0, fdApi_1.usePaymentReverseFeedMutation)();
|
|
80
77
|
const styles = createStyles(colors, typography, spacing, themeName);
|
|
81
78
|
const { setMasterData } = (0, MasterDataProvider_1.useMasterData)();
|
|
82
|
-
// Redux selectors for workflow IDs
|
|
83
|
-
const workflowInstanceId = (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.workflowInstanceId; });
|
|
84
|
-
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; });
|
|
85
|
-
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; });
|
|
86
|
-
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; });
|
|
87
79
|
// Helper function to check if customer applications is empty
|
|
88
80
|
const isCustomerApplicationsEmpty = () => {
|
|
89
81
|
if (!customerApplications) {
|
|
@@ -179,7 +171,12 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
|
|
|
179
171
|
return interestRates.data.fdrScheme[0].providerId;
|
|
180
172
|
return defaultProviderId; // Use default if no rates data yet
|
|
181
173
|
}, [interestRates]);
|
|
182
|
-
const { data: masterData, isLoading: isLoadingMaster } = (0, masterDataApi_1.useGetMasterDataQuery)({ providerId: inferredProviderId }, {
|
|
174
|
+
const { data: masterData, isLoading: isLoadingMaster } = (0, masterDataApi_1.useGetMasterDataQuery)({ providerId: inferredProviderId }, {
|
|
175
|
+
skip: !inferredProviderId,
|
|
176
|
+
refetchOnMountOrArgChange: true,
|
|
177
|
+
refetchOnFocus: true,
|
|
178
|
+
refetchOnReconnect: true,
|
|
179
|
+
});
|
|
183
180
|
// Only render once all three API calls have completed (success or error)
|
|
184
181
|
// Show loading initially until all APIs have completed
|
|
185
182
|
const isAllDataReady = react_1.default.useMemo(() => {
|
|
@@ -413,25 +410,34 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
|
|
|
413
410
|
// Customer applications loaded successfully
|
|
414
411
|
// Persist onboarding identifiers globally for subsequent API calls (like FDCalculator)
|
|
415
412
|
try {
|
|
416
|
-
// Extract workflow parameters from response
|
|
413
|
+
// Extract workflow parameters from response (support array / data[] / applications[])
|
|
417
414
|
const responseData = (customerApplications === null || customerApplications === void 0 ? void 0 : customerApplications.data) || customerApplications;
|
|
418
|
-
let
|
|
419
|
-
// Prefer the application with wf_status === 'Active'
|
|
415
|
+
let applicationsData = [];
|
|
420
416
|
if (Array.isArray(responseData)) {
|
|
421
|
-
|
|
417
|
+
applicationsData = responseData;
|
|
418
|
+
}
|
|
419
|
+
else if ((responseData === null || responseData === void 0 ? void 0 : responseData.applications) && Array.isArray(responseData.applications)) {
|
|
420
|
+
applicationsData = responseData.applications;
|
|
421
|
+
}
|
|
422
|
+
else if (Array.isArray(customerApplications === null || customerApplications === void 0 ? void 0 : customerApplications.applications)) {
|
|
423
|
+
applicationsData = customerApplications.applications;
|
|
424
|
+
}
|
|
425
|
+
let applicationData = applicationsData.length > 0 ? applicationsData[0] : (Array.isArray(responseData) ? responseData[0] : responseData);
|
|
426
|
+
// Prefer the application with wf_status === 'Active'
|
|
427
|
+
if (applicationsData.length > 0) {
|
|
428
|
+
const activeOnly = applicationsData.find((app) => {
|
|
422
429
|
const status = (app.wf_status || app.status || '').toString();
|
|
423
430
|
return status === 'Active';
|
|
424
431
|
});
|
|
425
432
|
if (activeOnly)
|
|
426
433
|
applicationData = activeOnly;
|
|
427
434
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
+
// Set completed flag robustly for downstream ReviewKYC/Aadhaar flow decisions
|
|
436
|
+
completeFDData = applicationsData.find((app) => {
|
|
437
|
+
const status = (app.wf_status || app.status || '').toString().toLowerCase();
|
|
438
|
+
return status === 'completed';
|
|
439
|
+
});
|
|
440
|
+
(0, globalData_1.setGlobalData)({ completeFDData: !!completeFDData });
|
|
435
441
|
if (applicationData) {
|
|
436
442
|
const ids = {
|
|
437
443
|
workflowInstanceId: applicationData.workflow_instance_id,
|
|
@@ -460,7 +466,6 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
|
|
|
460
466
|
}, [customerApplications, dispatch]);
|
|
461
467
|
// Unified handler: behave like bottom sheet Continue flow
|
|
462
468
|
const handlePendingFDContinue = async () => {
|
|
463
|
-
var _a;
|
|
464
469
|
// Persist active FD data to store for use across screens
|
|
465
470
|
try {
|
|
466
471
|
if (activeFD) {
|
|
@@ -516,50 +521,18 @@ const FDList = ({ onGoBack, onSelectFD, onNavigateToFDCalculator, customStyles =
|
|
|
516
521
|
catch (error) {
|
|
517
522
|
// Handle error silently
|
|
518
523
|
}
|
|
519
|
-
// If current state is payment and transactionId is available,
|
|
524
|
+
// If current state is payment and transactionId is available,
|
|
525
|
+
// open PaymentStatus and let SSE (/events) drive status updates.
|
|
520
526
|
if (currentState === workflowConstants_1.WORKFLOW_STATES.PAYMENT && transactionId) {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
applicationid: applicationId,
|
|
531
|
-
entityid: entityId,
|
|
532
|
-
// Body
|
|
533
|
-
transactionId: transactionId,
|
|
534
|
-
};
|
|
535
|
-
const response = await paymentReverseFeed(paymentReverseFeedRequest).unwrap();
|
|
536
|
-
// Handle the response based on payment status
|
|
537
|
-
const paymentStatus = (((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.paymentStatus) || '').toLowerCase();
|
|
538
|
-
const statusParam = paymentStatus === 'success' ? 'success' : paymentStatus === 'failed' ? 'failed' : 'pending';
|
|
539
|
-
// Build fdData for display
|
|
540
|
-
const fdDataParam = activeFD ? {
|
|
541
|
-
companyName: activeFD.name,
|
|
542
|
-
amount: Number(activeFD.invested) || 0,
|
|
543
|
-
fdRate: `${activeFD.returns}% p.a.`,
|
|
544
|
-
tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
|
|
545
|
-
interestPayout: activeFD.interestPayout || 'Yearly',
|
|
546
|
-
} : undefined;
|
|
547
|
-
(0, helpers_1.navigate)('PaymentStatus', { status: statusParam, transactionId, fdData: fdDataParam });
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
catch (error) {
|
|
551
|
-
// Handle error silently
|
|
552
|
-
// On error, navigate with pending status as fallback
|
|
553
|
-
const fdDataParam = activeFD ? {
|
|
554
|
-
companyName: activeFD.name,
|
|
555
|
-
amount: Number(activeFD.invested) || 0,
|
|
556
|
-
fdRate: `${activeFD.returns}% p.a.`,
|
|
557
|
-
tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
|
|
558
|
-
interestPayout: activeFD.interestPayout || 'Yearly',
|
|
559
|
-
} : undefined;
|
|
560
|
-
(0, helpers_1.navigate)('PaymentStatus', { status: 'pending', transactionId, fdData: fdDataParam });
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
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 });
|
|
535
|
+
return;
|
|
563
536
|
}
|
|
564
537
|
// Use workflow navigation based on current state (non-payment states or no transactionId)
|
|
565
538
|
if (currentState && Object.values(workflowConstants_1.WORKFLOW_STATES).includes(currentState)) {
|
package/lib/screens/Payment.js
CHANGED
|
@@ -42,35 +42,35 @@ const react_native_webview_1 = require("react-native-webview");
|
|
|
42
42
|
const SafeAreaWrapper_1 = __importDefault(require("../components/SafeAreaWrapper"));
|
|
43
43
|
const ThemeContext_1 = require("../theme/ThemeContext");
|
|
44
44
|
const encryption_1 = require("../utils/encryption");
|
|
45
|
+
const sseParser_1 = require("../utils/sseParser");
|
|
45
46
|
const encryptionConfig_1 = require("../config/encryptionConfig");
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const
|
|
47
|
+
const appDataConfig_1 = require("../config/appDataConfig");
|
|
48
|
+
const apiConfig_1 = require("../config/apiConfig");
|
|
49
|
+
const store_1 = require("../store");
|
|
49
50
|
const Payment = ({ onGoBack, onPaymentSuccess, onPaymentFailure, onPaymentPending, paymentUrl, successUrl = 'payment/success', failureUrl = 'payment/failure', }) => {
|
|
50
51
|
const colors = (0, ThemeContext_1.useColors)();
|
|
51
52
|
const styles = createStyles(colors);
|
|
52
53
|
const webViewRef = (0, react_1.useRef)(null);
|
|
53
54
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
55
|
+
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; });
|
|
56
|
+
const workflowInstanceId = (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.workflowInstanceId; });
|
|
57
|
+
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; });
|
|
58
|
+
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; });
|
|
59
|
+
const sseXhrRef = (0, react_1.useRef)(null);
|
|
60
|
+
const sseLastIndexRef = (0, react_1.useRef)(0);
|
|
61
|
+
const sseBufferRef = (0, react_1.useRef)({ value: '' });
|
|
62
|
+
const onSuccessRef = (0, react_1.useRef)(onPaymentSuccess);
|
|
63
|
+
const onFailureRef = (0, react_1.useRef)(onPaymentFailure);
|
|
64
|
+
onSuccessRef.current = onPaymentSuccess;
|
|
65
|
+
onFailureRef.current = onPaymentFailure;
|
|
66
|
+
const stopPaymentSSE = (0, react_1.useCallback)(() => {
|
|
67
|
+
if (sseXhrRef.current) {
|
|
68
|
+
sseXhrRef.current.abort();
|
|
69
|
+
sseXhrRef.current = null;
|
|
69
70
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}, [startTimer, stopTimer]));
|
|
71
|
+
sseLastIndexRef.current = 0;
|
|
72
|
+
sseBufferRef.current = { value: '' };
|
|
73
|
+
}, []);
|
|
74
74
|
const handleNavigationStateChange = async (navState) => {
|
|
75
75
|
const { url } = navState;
|
|
76
76
|
if (url.includes('payment/status')) {
|
|
@@ -92,6 +92,143 @@ const Payment = ({ onGoBack, onPaymentSuccess, onPaymentFailure, onPaymentPendin
|
|
|
92
92
|
const handleLoadEnd = () => {
|
|
93
93
|
setLoading(false);
|
|
94
94
|
};
|
|
95
|
+
// SSE: listen for payment status while user is on Payment WebView (same as web integration)
|
|
96
|
+
(0, react_1.useEffect)(() => {
|
|
97
|
+
var _a;
|
|
98
|
+
if (!applicationId || !(paymentUrl === null || paymentUrl === void 0 ? void 0 : paymentUrl.trim())) {
|
|
99
|
+
stopPaymentSSE();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const envData = (0, appDataConfig_1.getEnvironmentData)();
|
|
103
|
+
const apiConfig = (0, apiConfig_1.getApiConfig)();
|
|
104
|
+
const baseUrl = ((envData === null || envData === void 0 ? void 0 : envData.apiBaseUrl) || (apiConfig === null || apiConfig === void 0 ? void 0 : apiConfig.baseUrl) || '').replace(/\/$/, '');
|
|
105
|
+
const url = baseUrl ? `${baseUrl}/events?applicationId=${encodeURIComponent(applicationId)}` : '';
|
|
106
|
+
if (!url)
|
|
107
|
+
return;
|
|
108
|
+
stopPaymentSSE();
|
|
109
|
+
const xhr = new XMLHttpRequest();
|
|
110
|
+
sseXhrRef.current = xhr;
|
|
111
|
+
xhr.open('GET', url);
|
|
112
|
+
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']) || '';
|
|
113
|
+
if (apiKey) {
|
|
114
|
+
xhr.setRequestHeader('x-api-key', apiKey);
|
|
115
|
+
xhr.setRequestHeader('X-API-Key', apiKey);
|
|
116
|
+
}
|
|
117
|
+
const secureHeaders = (0, apiConfig_1.getSecureHeaders)();
|
|
118
|
+
Object.entries(secureHeaders).forEach(([key, value]) => {
|
|
119
|
+
if (key.toLowerCase() !== 'content-type') {
|
|
120
|
+
xhr.setRequestHeader(key, value);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
xhr.setRequestHeader('Accept', 'text/event-stream');
|
|
124
|
+
xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
125
|
+
xhr.setRequestHeader('Pragma', 'no-cache');
|
|
126
|
+
xhr.setRequestHeader('Expires', '0');
|
|
127
|
+
try {
|
|
128
|
+
const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
|
|
129
|
+
const userRefId = (userInfo === null || userInfo === void 0 ? void 0 : userInfo.userReferenceId) || (userInfo === null || userInfo === void 0 ? void 0 : userInfo.id);
|
|
130
|
+
if (userRefId) {
|
|
131
|
+
xhr.setRequestHeader('userreferenceid', userRefId);
|
|
132
|
+
xhr.setRequestHeader('userReferenceId', userRefId);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (_b) {
|
|
136
|
+
// ignore
|
|
137
|
+
}
|
|
138
|
+
if (providerId) {
|
|
139
|
+
xhr.setRequestHeader('provider', providerId);
|
|
140
|
+
xhr.setRequestHeader('providerid', providerId);
|
|
141
|
+
xhr.setRequestHeader('providerId', providerId);
|
|
142
|
+
}
|
|
143
|
+
if (workflowInstanceId) {
|
|
144
|
+
xhr.setRequestHeader('workflowInstanceId', workflowInstanceId);
|
|
145
|
+
xhr.setRequestHeader('workflowinstanceid', workflowInstanceId);
|
|
146
|
+
}
|
|
147
|
+
if (applicationId) {
|
|
148
|
+
xhr.setRequestHeader('applicationId', applicationId);
|
|
149
|
+
xhr.setRequestHeader('applicationid', applicationId);
|
|
150
|
+
}
|
|
151
|
+
if (entityId) {
|
|
152
|
+
xhr.setRequestHeader('entityid', entityId);
|
|
153
|
+
xhr.setRequestHeader('entityId', entityId);
|
|
154
|
+
}
|
|
155
|
+
const handleEventData = async (rawData) => {
|
|
156
|
+
var _a, _b, _c, _d, _e, _f;
|
|
157
|
+
try {
|
|
158
|
+
const parsed = JSON.parse(rawData || '{}');
|
|
159
|
+
let payload = parsed;
|
|
160
|
+
if (typeof (parsed === null || parsed === void 0 ? void 0 : parsed.encryptedResponse) === 'string' && parsed.encryptedResponse) {
|
|
161
|
+
try {
|
|
162
|
+
const config = (0, encryptionConfig_1.getEncryptionConfig)();
|
|
163
|
+
const decrypted = await (0, encryption_1.decryptResponse)({ encryptedResponse: parsed.encryptedResponse }, config);
|
|
164
|
+
payload = (decrypted || {});
|
|
165
|
+
}
|
|
166
|
+
catch (_g) {
|
|
167
|
+
// ignore and use raw payload
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const payloadData = (payload === null || payload === void 0 ? void 0 : payload.data) || {};
|
|
171
|
+
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();
|
|
172
|
+
if (status === 'SUCCESS') {
|
|
173
|
+
stopPaymentSSE();
|
|
174
|
+
(_e = onSuccessRef.current) === null || _e === void 0 ? void 0 : _e.call(onSuccessRef, payload);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (status === 'FAILED') {
|
|
178
|
+
stopPaymentSSE();
|
|
179
|
+
(_f = onFailureRef.current) === null || _f === void 0 ? void 0 : _f.call(onFailureRef, payload);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (_h) {
|
|
183
|
+
// ignore parse errors
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
xhr.onprogress = () => {
|
|
187
|
+
setLoading(false);
|
|
188
|
+
const newText = xhr.responseText.slice(sseLastIndexRef.current);
|
|
189
|
+
sseLastIndexRef.current = xhr.responseText.length;
|
|
190
|
+
if (!newText)
|
|
191
|
+
return;
|
|
192
|
+
const events = (0, sseParser_1.parseSSEBuffer)(sseBufferRef.current, newText);
|
|
193
|
+
for (const { eventType, data } of events) {
|
|
194
|
+
if (!data)
|
|
195
|
+
continue;
|
|
196
|
+
const normalizedEventType = String(eventType || '').trim().toLowerCase();
|
|
197
|
+
if (normalizedEventType === 'fd_payment_status') {
|
|
198
|
+
handleEventData(data);
|
|
199
|
+
}
|
|
200
|
+
else if (normalizedEventType === 'message') {
|
|
201
|
+
try {
|
|
202
|
+
const parsed = JSON.parse(data);
|
|
203
|
+
const embeddedEvent = String((parsed === null || parsed === void 0 ? void 0 : parsed.event) || '').trim().toLowerCase();
|
|
204
|
+
if (embeddedEvent === 'fd_payment_status') {
|
|
205
|
+
handleEventData(data);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (_a) {
|
|
209
|
+
// ignore
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
xhr.onreadystatechange = () => {
|
|
215
|
+
if ((xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED ||
|
|
216
|
+
xhr.readyState === XMLHttpRequest.LOADING) &&
|
|
217
|
+
xhr.status === 200) {
|
|
218
|
+
setLoading(false);
|
|
219
|
+
if (__DEV__)
|
|
220
|
+
console.log('[Payment] SSE connected for payment status');
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
xhr.onerror = () => {
|
|
224
|
+
if (__DEV__)
|
|
225
|
+
console.warn('[Payment] SSE connection error');
|
|
226
|
+
};
|
|
227
|
+
xhr.send();
|
|
228
|
+
return () => {
|
|
229
|
+
stopPaymentSSE();
|
|
230
|
+
};
|
|
231
|
+
}, [applicationId, paymentUrl, workflowInstanceId, entityId, providerId, stopPaymentSSE]);
|
|
95
232
|
const handleMessage = async (event) => {
|
|
96
233
|
var _a, _b, _c;
|
|
97
234
|
try {
|
|
@@ -130,13 +267,9 @@ const Payment = ({ onGoBack, onPaymentSuccess, onPaymentFailure, onPaymentPendin
|
|
|
130
267
|
// Check payment status in data
|
|
131
268
|
const paymentStatus = (_c = (_b = response.data) === null || _b === void 0 ? void 0 : _b.paymentStatus) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
132
269
|
if (paymentStatus === 'success') {
|
|
133
|
-
currentStatusRef.current = "success";
|
|
134
|
-
stopTimer();
|
|
135
270
|
onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
|
|
136
271
|
}
|
|
137
272
|
else if (paymentStatus === 'failed') {
|
|
138
|
-
currentStatusRef.current = "failed";
|
|
139
|
-
stopTimer();
|
|
140
273
|
onPaymentFailure === null || onPaymentFailure === void 0 ? void 0 : onPaymentFailure(response);
|
|
141
274
|
}
|
|
142
275
|
else {
|
|
@@ -144,7 +277,6 @@ const Payment = ({ onGoBack, onPaymentSuccess, onPaymentFailure, onPaymentPendin
|
|
|
144
277
|
}
|
|
145
278
|
}
|
|
146
279
|
else {
|
|
147
|
-
currentStatusRef.current = "pending";
|
|
148
280
|
onPaymentPending === null || onPaymentPending === void 0 ? void 0 : onPaymentPending(response);
|
|
149
281
|
}
|
|
150
282
|
setLoading(false);
|
|
@@ -48,13 +48,15 @@ const fdApi_1 = require("../api/fdApi");
|
|
|
48
48
|
const customerApi_1 = require("../api/customerApi");
|
|
49
49
|
const store_1 = require("../store");
|
|
50
50
|
const appDataConfig_1 = require("../config/appDataConfig");
|
|
51
|
+
const apiConfig_1 = require("../config/apiConfig");
|
|
51
52
|
const helpers_1 = require("../navigation/helpers");
|
|
52
53
|
const paymentSession_1 = require("../state/paymentSession");
|
|
53
54
|
const native_1 = require("@react-navigation/native");
|
|
54
55
|
const bank_1 = require("../constants/strings/bank");
|
|
55
56
|
const common_1 = require("../constants/strings/common");
|
|
56
|
-
const
|
|
57
|
-
const
|
|
57
|
+
const sseParser_1 = require("../utils/sseParser");
|
|
58
|
+
const encryption_1 = require("../utils/encryption");
|
|
59
|
+
const encryptionConfig_1 = require("../config/encryptionConfig");
|
|
58
60
|
const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) => {
|
|
59
61
|
var _a;
|
|
60
62
|
const colors = (0, ThemeContext_1.useColors)();
|
|
@@ -75,18 +77,9 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
|
|
|
75
77
|
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; });
|
|
76
78
|
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; });
|
|
77
79
|
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; });
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
providerId,
|
|
82
|
-
workflowInstanceId,
|
|
83
|
-
applicationid: applicationId,
|
|
84
|
-
entityid: entityId,
|
|
85
|
-
},
|
|
86
|
-
onStatusUpdate: (nextStatus) => {
|
|
87
|
-
setCurrentStatus(nextStatus);
|
|
88
|
-
},
|
|
89
|
-
});
|
|
80
|
+
const sseXhrRef = (0, react_1.useRef)(null);
|
|
81
|
+
const sseLastIndexRef = (0, react_1.useRef)(0);
|
|
82
|
+
const sseBufferRef = (0, react_1.useRef)({ value: '' });
|
|
90
83
|
// Payment Retry API
|
|
91
84
|
const [paymentRetry, { data: paymentRetryResponse, error: paymentRetryError, isLoading: isLoadingPaymentRetry, }] = (0, fdApi_1.usePaymentRetryMutation)();
|
|
92
85
|
// Get Customer Applications API
|
|
@@ -110,20 +103,143 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
|
|
|
110
103
|
const formatAmount = (amount) => {
|
|
111
104
|
return amount.toLocaleString('en-IN');
|
|
112
105
|
};
|
|
106
|
+
const stopPaymentSSE = (0, react_1.useCallback)(() => {
|
|
107
|
+
if (sseXhrRef.current) {
|
|
108
|
+
sseXhrRef.current.abort();
|
|
109
|
+
sseXhrRef.current = null;
|
|
110
|
+
}
|
|
111
|
+
sseLastIndexRef.current = 0;
|
|
112
|
+
sseBufferRef.current = { value: '' };
|
|
113
|
+
}, []);
|
|
113
114
|
(0, react_1.useEffect)(() => {
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
var _a;
|
|
116
|
+
if (currentStatus !== 'pending' || !applicationId) {
|
|
117
|
+
stopPaymentSSE();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const envData = (0, appDataConfig_1.getEnvironmentData)();
|
|
121
|
+
const apiConfig = (0, apiConfig_1.getApiConfig)();
|
|
122
|
+
const baseUrl = ((envData === null || envData === void 0 ? void 0 : envData.apiBaseUrl) || (apiConfig === null || apiConfig === void 0 ? void 0 : apiConfig.baseUrl) || '').replace(/\/$/, '');
|
|
123
|
+
const url = baseUrl ? `${baseUrl}/events?applicationId=${encodeURIComponent(applicationId)}` : '';
|
|
124
|
+
if (!url)
|
|
125
|
+
return;
|
|
126
|
+
stopPaymentSSE();
|
|
127
|
+
const xhr = new XMLHttpRequest();
|
|
128
|
+
sseXhrRef.current = xhr;
|
|
129
|
+
xhr.open('GET', url);
|
|
130
|
+
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']) || '';
|
|
131
|
+
if (apiKey) {
|
|
132
|
+
xhr.setRequestHeader('x-api-key', apiKey);
|
|
133
|
+
xhr.setRequestHeader('X-API-Key', apiKey);
|
|
134
|
+
}
|
|
135
|
+
const secureHeaders = (0, apiConfig_1.getSecureHeaders)();
|
|
136
|
+
Object.entries(secureHeaders).forEach(([key, value]) => {
|
|
137
|
+
if (key.toLowerCase() !== 'content-type') {
|
|
138
|
+
xhr.setRequestHeader(key, value);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
xhr.setRequestHeader('Accept', 'text/event-stream');
|
|
142
|
+
xhr.setRequestHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
143
|
+
xhr.setRequestHeader('Pragma', 'no-cache');
|
|
144
|
+
xhr.setRequestHeader('Expires', '0');
|
|
145
|
+
try {
|
|
146
|
+
const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
|
|
147
|
+
const userRefId = (userInfo === null || userInfo === void 0 ? void 0 : userInfo.userReferenceId) || (userInfo === null || userInfo === void 0 ? void 0 : userInfo.id);
|
|
148
|
+
if (userRefId) {
|
|
149
|
+
xhr.setRequestHeader('userreferenceid', userRefId);
|
|
150
|
+
xhr.setRequestHeader('userReferenceId', userRefId);
|
|
151
|
+
}
|
|
116
152
|
}
|
|
117
|
-
|
|
118
|
-
|
|
153
|
+
catch (_b) {
|
|
154
|
+
// ignore
|
|
119
155
|
}
|
|
156
|
+
if (providerId) {
|
|
157
|
+
xhr.setRequestHeader('provider', providerId);
|
|
158
|
+
xhr.setRequestHeader('providerid', providerId);
|
|
159
|
+
xhr.setRequestHeader('providerId', providerId);
|
|
160
|
+
}
|
|
161
|
+
if (workflowInstanceId) {
|
|
162
|
+
xhr.setRequestHeader('workflowInstanceId', workflowInstanceId);
|
|
163
|
+
xhr.setRequestHeader('workflowinstanceid', workflowInstanceId);
|
|
164
|
+
}
|
|
165
|
+
if (applicationId) {
|
|
166
|
+
xhr.setRequestHeader('applicationId', applicationId);
|
|
167
|
+
xhr.setRequestHeader('applicationid', applicationId);
|
|
168
|
+
}
|
|
169
|
+
if (entityId) {
|
|
170
|
+
xhr.setRequestHeader('entityid', entityId);
|
|
171
|
+
xhr.setRequestHeader('entityId', entityId);
|
|
172
|
+
}
|
|
173
|
+
const handleEventData = async (rawData) => {
|
|
174
|
+
var _a, _b, _c, _d;
|
|
175
|
+
try {
|
|
176
|
+
const parsed = JSON.parse(rawData || '{}');
|
|
177
|
+
let payload = parsed;
|
|
178
|
+
if (typeof (parsed === null || parsed === void 0 ? void 0 : parsed.encryptedResponse) === 'string' && parsed.encryptedResponse) {
|
|
179
|
+
try {
|
|
180
|
+
const config = (0, encryptionConfig_1.getEncryptionConfig)();
|
|
181
|
+
const decrypted = await (0, encryption_1.decryptResponse)({ encryptedResponse: parsed.encryptedResponse }, config);
|
|
182
|
+
payload = (decrypted || {});
|
|
183
|
+
}
|
|
184
|
+
catch (_e) {
|
|
185
|
+
// ignore and use raw payload
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const payloadData = (payload === null || payload === void 0 ? void 0 : payload.data) || {};
|
|
189
|
+
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();
|
|
190
|
+
if (status === 'SUCCESS') {
|
|
191
|
+
setCurrentStatus('success');
|
|
192
|
+
stopPaymentSSE();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (status === 'FAILED') {
|
|
196
|
+
setCurrentStatus('failed');
|
|
197
|
+
stopPaymentSSE();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (_f) {
|
|
201
|
+
// ignore parse errors
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
xhr.onprogress = () => {
|
|
205
|
+
const newText = xhr.responseText.slice(sseLastIndexRef.current);
|
|
206
|
+
sseLastIndexRef.current = xhr.responseText.length;
|
|
207
|
+
if (!newText)
|
|
208
|
+
return;
|
|
209
|
+
const events = (0, sseParser_1.parseSSEBuffer)(sseBufferRef.current, newText);
|
|
210
|
+
for (const { eventType, data } of events) {
|
|
211
|
+
if (!data)
|
|
212
|
+
continue;
|
|
213
|
+
const normalizedEventType = String(eventType || '').trim().toLowerCase();
|
|
214
|
+
if (normalizedEventType === 'fd_payment_status') {
|
|
215
|
+
handleEventData(data);
|
|
216
|
+
}
|
|
217
|
+
else if (normalizedEventType === 'message') {
|
|
218
|
+
try {
|
|
219
|
+
const parsed = JSON.parse(data);
|
|
220
|
+
const embeddedEvent = String((parsed === null || parsed === void 0 ? void 0 : parsed.event) || '').trim().toLowerCase();
|
|
221
|
+
if (embeddedEvent === 'fd_payment_status') {
|
|
222
|
+
handleEventData(data);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (_a) {
|
|
226
|
+
// ignore
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
xhr.send();
|
|
120
232
|
return () => {
|
|
121
|
-
|
|
233
|
+
stopPaymentSSE();
|
|
122
234
|
};
|
|
123
|
-
}, [
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
235
|
+
}, [
|
|
236
|
+
applicationId,
|
|
237
|
+
currentStatus,
|
|
238
|
+
entityId,
|
|
239
|
+
providerId,
|
|
240
|
+
stopPaymentSSE,
|
|
241
|
+
workflowInstanceId
|
|
242
|
+
]);
|
|
127
243
|
// Handle payment retry when status is failed
|
|
128
244
|
const handlePaymentRetry = async () => {
|
|
129
245
|
var _a;
|
|
@@ -167,8 +283,6 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
|
|
|
167
283
|
try {
|
|
168
284
|
// Get user info from app data
|
|
169
285
|
const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
|
|
170
|
-
// Get API configuration
|
|
171
|
-
const apiConfig = (0, apiConfig_1.getApiConfig)();
|
|
172
286
|
// Prepare request payload
|
|
173
287
|
const requestPayload = {
|
|
174
288
|
userReferenceId: userInfo.id
|