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