@fadyshawky/react-native-magic 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template/src/common/ImageResources.g.ts +33 -1
- package/template/src/common/components/Background.tsx +3 -1
- package/template/src/common/components/Container.tsx +1 -1
- package/template/src/common/components/OTPInput.tsx +3 -2
- package/template/src/common/components/PrimaryButton.tsx +23 -23
- package/template/src/common/components/PrimaryTextInput.tsx +189 -199
- package/template/src/common/components/RadioIcon.tsx +4 -4
- package/template/src/common/components/SafeText.tsx +41 -0
- package/template/src/common/components/SearchBar.tsx +19 -17
- package/template/src/common/components/TryAgain.tsx +3 -3
- package/template/src/common/localization/localization.ts +10 -0
- package/template/src/common/localization/translations/commonLocalization.ts +97 -0
- package/template/src/common/localization/translations/homeLocalization.ts +24 -0
- package/template/src/common/localization/translations/loginLocalization.ts +28 -2
- package/template/src/common/localization/translations/mainNavigationLocalization.ts +30 -0
- package/template/src/common/localization/translations/navigationLocalization.ts +48 -0
- package/template/src/common/localization/translations/otpLocalization.ts +28 -0
- package/template/src/common/localization/translations/passwordLocalization.ts +54 -0
- package/template/src/common/localization/translations/posLocalization.ts +196 -0
- package/template/src/common/utils/FeesCaalculation.tsx +37 -0
- package/template/src/common/utils/index.tsx +11 -0
- package/template/src/common/utils/printData.tsx +161 -0
- package/template/src/common/validations/errorValidations.ts +3 -2
- package/template/src/components/PrinterExample.js +226 -0
- package/template/src/core/api/serverHeaders.ts +62 -1
- package/template/src/core/store/Categories/categoryActions.ts +33 -0
- package/template/src/core/store/Categories/categorySlice.ts +75 -0
- package/template/src/core/store/Categories/categoryState.ts +41 -0
- package/template/src/core/store/Providers/providersActions.ts +102 -0
- package/template/src/core/store/Providers/providersSlice.ts +136 -0
- package/template/src/core/store/Providers/providersState.ts +37 -0
- package/template/src/core/store/Services/servicesActions.ts +191 -0
- package/template/src/core/store/Services/servicesSlice.ts +205 -0
- package/template/src/core/store/Services/servicesState.ts +466 -0
- package/template/src/core/store/app/appSlice.ts +13 -5
- package/template/src/core/store/app/appState.ts +10 -2
- package/template/src/core/store/rootReducer.ts +6 -1
- package/template/src/core/store/store.tsx +55 -2
- package/template/src/core/store/user/userActions.ts +164 -26
- package/template/src/core/store/user/userSlice.ts +193 -21
- package/template/src/core/store/user/userState.ts +148 -25
- package/template/src/core/theme/colors.ts +70 -94
- package/template/src/core/theme/commonConsts.ts +1 -1
- package/template/src/core/theme/commonSizes.ts +94 -119
- package/template/src/core/theme/commonStyles.ts +22 -22
- package/template/src/core/theme/fonts.ts +14 -13
- package/template/src/core/theme/themes.ts +75 -386
- package/template/src/core/theme/types.ts +15 -201
- package/template/src/core/utils/stringUtils.ts +114 -0
- package/template/src/modules/SunmiCard.js +212 -0
- package/template/src/modules/SunmiPrepaid.ts +122 -0
- package/template/src/navigation/AuthStack.tsx +8 -0
- package/template/src/navigation/HeaderComponents.tsx +76 -1
- package/template/src/navigation/MainNavigation.tsx +3 -1
- package/template/src/navigation/MainStack.tsx +130 -56
- package/template/src/navigation/TabBar.tsx +111 -59
- package/template/src/navigation/types.ts +24 -0
- package/template/src/screens/Categories/Categories.tsx +141 -0
- package/template/src/screens/Categories/hooks/useCategoriesData.ts +33 -0
- package/template/src/screens/Categories/types.ts +7 -0
- package/template/src/screens/Favorites/Favorites.tsx +130 -0
- package/template/src/screens/ForceChangePassword/ForceChangePasswordScreen.tsx +155 -0
- package/template/src/screens/History/History.tsx +430 -0
- package/template/src/screens/History/hooks/useHistoryData.ts +49 -0
- package/template/src/screens/History/types.ts +7 -0
- package/template/src/screens/InquiredBill/InquiredBill.tsx +443 -0
- package/template/src/screens/InquiredBill/hooks/useInquiredData.ts +91 -0
- package/template/src/screens/Login/Login.tsx +85 -85
- package/template/src/screens/OTP/OTPScreen.tsx +170 -0
- package/template/src/screens/PaymentConfirmation/PaymentConfirmation.tsx +326 -0
- package/template/src/screens/Providers/Providers.tsx +166 -0
- package/template/src/screens/Providers/hooks/useProvidersData.ts +33 -0
- package/template/src/screens/Providers/types.ts +7 -0
- package/template/src/screens/ReceiptScreen/ReceiptScreen.tsx +181 -0
- package/template/src/screens/ReceiptScreen/hooks/useReceiptData.ts +46 -0
- package/template/src/screens/ReceiptScreen/utils/utils.tsx +156 -0
- package/template/src/screens/Services/Services.tsx +144 -0
- package/template/src/screens/Services/hooks/useServicesData.ts +41 -0
- package/template/src/screens/SingleService/Components/FawryInputs.tsx +446 -0
- package/template/src/screens/SingleService/SingleService.tsx +229 -0
- package/template/src/screens/SingleService/hooks/useServiceData.ts +164 -0
- package/template/src/screens/home/Components/PayByCode.tsx +129 -0
- package/template/src/screens/home/HomeScreen.tsx +268 -77
- package/template/src/screens/home/hooks/useHomeData.ts +32 -38
- package/template/src/screens/index.tsx +24 -0
- package/template/src/screens/profile/Profile.tsx +290 -2
- package/template/src/services/SunmiPrinterInternal.js +268 -0
- package/template/src/types/sunmiPrepaid.d.ts +20 -0
- package/template/src/utils/SunmiPrinter.ts +442 -0
- package/template/src/utils/feesCalculator.ts +92 -0
- package/template/src/common/components/Stepper.tsx +0 -153
- package/template/src/common/components/Svg.tsx +0 -25
- package/template/src/common/hooks/useDebounce.ts +0 -17
- package/template/src/common/hooks/usePrevious.ts +0 -11
- package/template/src/common/localization/intlFormatter.ts +0 -37
- package/template/src/common/urls/emailUrl.ts +0 -20
- package/template/src/common/urls/mapUrl.ts +0 -22
- package/template/src/common/utils/listHandlers.ts +0 -30
- package/template/src/common/utils/serializeQueryParams.ts +0 -10
- package/template/src/common/validations/hooks/useDatesError.ts +0 -40
- package/template/src/common/validations/profileValidations.ts +0 -30
- package/template/src/core/theme/shadows.ts +0 -135
- package/template/src/navigation/TopTabBar.tsx +0 -77
- package/template/src/screens/Settings/Settings.tsx +0 -5
- package/template/src/screens/home/components/CarouselSection.tsx +0 -79
- package/template/src/screens/home/components/FeaturedCarousel.tsx +0 -128
- package/template/src/screens/main/Main.tsx +0 -5
- package/template/src/screens/registration/RegistrationScreen.tsx +0 -198
- package/template/src/screens/resetPassword/ForgotPasswordScreen.tsx +0 -129
|
@@ -1,170 +1,27 @@
|
|
|
1
1
|
import {TextStyle} from 'react-native';
|
|
2
|
-
import {ElevationShadow} from './shadows';
|
|
3
|
-
import {PrimaryColors, NaturalColors, AlertColors} from './colors';
|
|
4
2
|
|
|
5
3
|
export type ThemeMode = 'light' | 'dark';
|
|
6
4
|
|
|
7
|
-
export interface ISize {
|
|
8
|
-
font: {
|
|
9
|
-
// Almarai Design System Typography
|
|
10
|
-
bodySmall: number;
|
|
11
|
-
bodyMedium: number;
|
|
12
|
-
bodyLarge: number;
|
|
13
|
-
bodyXLarge: number;
|
|
14
|
-
heading6: number;
|
|
15
|
-
heading4: number;
|
|
16
|
-
heading3: number;
|
|
17
|
-
heading2: number;
|
|
18
|
-
heading1: number;
|
|
19
|
-
|
|
20
|
-
// Legacy support
|
|
21
|
-
extraSmall: number;
|
|
22
|
-
extraSmallPlus: number;
|
|
23
|
-
small: number;
|
|
24
|
-
smallPlus: number;
|
|
25
|
-
medium: number;
|
|
26
|
-
mediumPlus: number;
|
|
27
|
-
extraMedium: number;
|
|
28
|
-
large: number;
|
|
29
|
-
largePlus: number;
|
|
30
|
-
extraLarge: number;
|
|
31
|
-
extraLargePlus: number;
|
|
32
|
-
};
|
|
33
|
-
letterSpacing: {
|
|
34
|
-
// Almarai Design System
|
|
35
|
-
bodySmall: number;
|
|
36
|
-
bodyMedium: number;
|
|
37
|
-
bodyLarge: number;
|
|
38
|
-
bodyXLarge: number;
|
|
39
|
-
heading6: number;
|
|
40
|
-
heading4: number;
|
|
41
|
-
heading3: number;
|
|
42
|
-
heading2: number;
|
|
43
|
-
heading1: number;
|
|
44
|
-
|
|
45
|
-
// Legacy support
|
|
46
|
-
extraSmall: number;
|
|
47
|
-
extraSmallPlus: number;
|
|
48
|
-
small: number;
|
|
49
|
-
smallPlus: number;
|
|
50
|
-
medium: number;
|
|
51
|
-
mediumPlus: number;
|
|
52
|
-
large: number;
|
|
53
|
-
largePlus: number;
|
|
54
|
-
extraLarge: number;
|
|
55
|
-
extraLargePlus: number;
|
|
56
|
-
};
|
|
57
|
-
lineHeight: {
|
|
58
|
-
// Almarai Design System
|
|
59
|
-
bodySmall: number;
|
|
60
|
-
bodyMedium: number;
|
|
61
|
-
bodyLarge: number;
|
|
62
|
-
bodyXLarge: number;
|
|
63
|
-
heading6: number;
|
|
64
|
-
heading4: number;
|
|
65
|
-
heading3: number;
|
|
66
|
-
heading2: number;
|
|
67
|
-
heading1: number;
|
|
68
|
-
|
|
69
|
-
// Legacy support
|
|
70
|
-
extraSmall: number;
|
|
71
|
-
extraSmallPlus: number;
|
|
72
|
-
small: number;
|
|
73
|
-
smallPlus: number;
|
|
74
|
-
medium: number;
|
|
75
|
-
mediumPlus: number;
|
|
76
|
-
large: number;
|
|
77
|
-
largePlus: number;
|
|
78
|
-
extraLarge: number;
|
|
79
|
-
extraLargePlus: number;
|
|
80
|
-
};
|
|
81
|
-
spacing: {
|
|
82
|
-
// Design System Spacing Scale
|
|
83
|
-
none: number; // space / 0
|
|
84
|
-
xSmall: number; // space / 2
|
|
85
|
-
small: number; // space / 4
|
|
86
|
-
medium: number; // space / 8
|
|
87
|
-
large: number; // space / 12
|
|
88
|
-
xLarge: number; // space / 16
|
|
89
|
-
xxLarge: number; // space / 24 (2X-Large)
|
|
90
|
-
xxxLarge: number; // space / 32 (3X-Large)
|
|
91
|
-
xxxxLarge: number; // space / 48 (4X-Large)
|
|
92
|
-
xxxxxLarge: number; // space / 64 (5X-Large)
|
|
93
|
-
xxxxxxLarge: number; // space / 80 (6X-Large)
|
|
94
|
-
full: number; // space / 100 (Full)
|
|
95
|
-
|
|
96
|
-
// Legacy support
|
|
97
|
-
xs: number;
|
|
98
|
-
sm: number;
|
|
99
|
-
md: number;
|
|
100
|
-
lg: number;
|
|
101
|
-
xl: number;
|
|
102
|
-
};
|
|
103
|
-
borderRadius: {
|
|
104
|
-
// Design System Border Radius Scale
|
|
105
|
-
none: number; // Border / 0
|
|
106
|
-
xSmall: number; // Border / 2
|
|
107
|
-
small: number; // Border / 4
|
|
108
|
-
medium: number; // Border / 8
|
|
109
|
-
large: number; // Border / 12
|
|
110
|
-
xLarge: number; // Border / 16
|
|
111
|
-
xxLarge: number; // Border / 24 (2X-Large)
|
|
112
|
-
full: number; // Border / 1000 (Full)
|
|
113
|
-
|
|
114
|
-
// Legacy support
|
|
115
|
-
xs: number;
|
|
116
|
-
sm: number;
|
|
117
|
-
md: number;
|
|
118
|
-
lg: number;
|
|
119
|
-
xl: number;
|
|
120
|
-
};
|
|
121
|
-
borderWidth: {
|
|
122
|
-
// Design System Stroke Scale
|
|
123
|
-
none: number; // Stroke / 0
|
|
124
|
-
xSmall: number; // Stroke / 0.5
|
|
125
|
-
small: number; // Stroke / 1
|
|
126
|
-
medium: number; // Stroke / 1.5
|
|
127
|
-
large: number; // Stroke / 2
|
|
128
|
-
|
|
129
|
-
// Legacy support
|
|
130
|
-
extraSmall: number;
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
5
|
export interface Theme {
|
|
135
6
|
mode: ThemeMode;
|
|
136
|
-
colors:
|
|
7
|
+
colors: {
|
|
8
|
+
indigoBlue: string;
|
|
9
|
+
mutedLavender: string;
|
|
10
|
+
tintColor: string;
|
|
11
|
+
mutedLavender30: string;
|
|
12
|
+
balanceBackground: string;
|
|
13
|
+
white: string;
|
|
14
|
+
backgroundOpacity: string;
|
|
15
|
+
black: string;
|
|
16
|
+
background: string;
|
|
17
|
+
surface: string;
|
|
18
|
+
card: string;
|
|
19
|
+
shadow: string;
|
|
20
|
+
red: string;
|
|
21
|
+
};
|
|
137
22
|
text: {
|
|
138
|
-
// Almarai Design System Typography
|
|
139
|
-
// Headings
|
|
140
23
|
header1: TextStyle;
|
|
141
24
|
header2: TextStyle;
|
|
142
|
-
header3: TextStyle;
|
|
143
|
-
header4: TextStyle;
|
|
144
|
-
header6: TextStyle;
|
|
145
|
-
|
|
146
|
-
// Body Text with different weights
|
|
147
|
-
bodyXLargeLight: TextStyle;
|
|
148
|
-
bodyXLargeRegular: TextStyle;
|
|
149
|
-
bodyXLargeBold: TextStyle;
|
|
150
|
-
bodyXLargeExtraBold: TextStyle;
|
|
151
|
-
|
|
152
|
-
bodyLargeLight: TextStyle;
|
|
153
|
-
bodyLargeRegular: TextStyle;
|
|
154
|
-
bodyLargeBold: TextStyle;
|
|
155
|
-
bodyLargeExtraBold: TextStyle;
|
|
156
|
-
|
|
157
|
-
bodyMediumLight: TextStyle;
|
|
158
|
-
bodyMediumRegular: TextStyle;
|
|
159
|
-
bodyMediumBold: TextStyle;
|
|
160
|
-
bodyMediumExtraBold: TextStyle;
|
|
161
|
-
|
|
162
|
-
bodySmallLight: TextStyle;
|
|
163
|
-
bodySmallRegular: TextStyle;
|
|
164
|
-
bodySmallBold: TextStyle;
|
|
165
|
-
bodySmallExtraBold: TextStyle;
|
|
166
|
-
|
|
167
|
-
// Legacy styles for backward compatibility
|
|
168
25
|
balanceTitle: TextStyle;
|
|
169
26
|
balanceAmount: TextStyle;
|
|
170
27
|
balanceLabel: TextStyle;
|
|
@@ -178,21 +35,6 @@ export interface Theme {
|
|
|
178
35
|
navBar: TextStyle;
|
|
179
36
|
};
|
|
180
37
|
spacing: {
|
|
181
|
-
// Design System Spacing Scale
|
|
182
|
-
none: number;
|
|
183
|
-
xSmall: number;
|
|
184
|
-
small: number;
|
|
185
|
-
medium: number;
|
|
186
|
-
large: number;
|
|
187
|
-
xLarge: number;
|
|
188
|
-
xxLarge: number;
|
|
189
|
-
xxxLarge: number;
|
|
190
|
-
xxxxLarge: number;
|
|
191
|
-
xxxxxLarge: number;
|
|
192
|
-
xxxxxxLarge: number;
|
|
193
|
-
full: number;
|
|
194
|
-
|
|
195
|
-
// Legacy support
|
|
196
38
|
xs: number;
|
|
197
39
|
sm: number;
|
|
198
40
|
md: number;
|
|
@@ -200,38 +42,10 @@ export interface Theme {
|
|
|
200
42
|
xl: number;
|
|
201
43
|
};
|
|
202
44
|
borderRadius: {
|
|
203
|
-
// Design System Border Radius Scale
|
|
204
|
-
none: number;
|
|
205
|
-
xSmall: number;
|
|
206
|
-
small: number;
|
|
207
|
-
medium: number;
|
|
208
|
-
large: number;
|
|
209
|
-
xLarge: number;
|
|
210
|
-
xxLarge: number;
|
|
211
|
-
full: number;
|
|
212
|
-
|
|
213
|
-
// Legacy support
|
|
214
45
|
xs: number;
|
|
215
46
|
sm: number;
|
|
216
47
|
md: number;
|
|
217
48
|
lg: number;
|
|
218
49
|
xl: number;
|
|
219
50
|
};
|
|
220
|
-
borderWidth: {
|
|
221
|
-
// Design System Stroke Scale
|
|
222
|
-
none: number;
|
|
223
|
-
xSmall: number;
|
|
224
|
-
small: number;
|
|
225
|
-
medium: number;
|
|
226
|
-
large: number;
|
|
227
|
-
|
|
228
|
-
// Legacy support
|
|
229
|
-
extraSmall: number;
|
|
230
|
-
};
|
|
231
|
-
shadows: {
|
|
232
|
-
elevation1: ElevationShadow;
|
|
233
|
-
elevation2: ElevationShadow;
|
|
234
|
-
elevation3: ElevationShadow;
|
|
235
|
-
elevation4: ElevationShadow;
|
|
236
|
-
};
|
|
237
51
|
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Safely converts any value to a string
|
|
3
|
+
* This is especially useful for error messages that might be objects
|
|
4
|
+
*/
|
|
5
|
+
import CryptoJS from 'crypto-js';
|
|
6
|
+
|
|
7
|
+
export const ensureString = (value: any): string => {
|
|
8
|
+
if (value === null || value === undefined) {
|
|
9
|
+
return '';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (typeof value === 'string') {
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (typeof value === 'object') {
|
|
17
|
+
try {
|
|
18
|
+
// Try to extract a message property if it exists
|
|
19
|
+
if (value.message && typeof value.message === 'string') {
|
|
20
|
+
return value.message;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Otherwise stringify the object
|
|
24
|
+
return JSON.stringify(value);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
return '[Object]';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// For numbers, booleans, etc.
|
|
31
|
+
return String(value);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function decryptTripleDES(
|
|
35
|
+
encryptedBase64: string,
|
|
36
|
+
secretKeyBase64: string,
|
|
37
|
+
) {
|
|
38
|
+
try {
|
|
39
|
+
// Decode the key from base64
|
|
40
|
+
const decodedKey = CryptoJS.enc.Base64.parse(secretKeyBase64);
|
|
41
|
+
|
|
42
|
+
// Decode the encrypted value from base64
|
|
43
|
+
const encryptedWordArray = CryptoJS.enc.Base64.parse(encryptedBase64);
|
|
44
|
+
|
|
45
|
+
// Decrypt using Triple DES
|
|
46
|
+
const decrypted = CryptoJS.TripleDES.decrypt(
|
|
47
|
+
{ciphertext: encryptedWordArray},
|
|
48
|
+
decodedKey,
|
|
49
|
+
{
|
|
50
|
+
mode: CryptoJS.mode.ECB,
|
|
51
|
+
padding: CryptoJS.pad.Pkcs7,
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
let decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
|
|
56
|
+
|
|
57
|
+
if (!decryptedText) {
|
|
58
|
+
throw new Error('Decryption resulted in empty string');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
decryptedText = decryptedText.replace(/(.{4})/g, '$1-');
|
|
62
|
+
if (decryptedText.endsWith('-')) {
|
|
63
|
+
decryptedText = decryptedText.slice(0, -1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return decryptedText;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`Decryption failed`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Example usage:
|
|
73
|
+
// const decrypted = decryptBase64('encrypted_base64_string', 'your_secret_key');
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Converts a camelCase string to PascalCase with specific formatting
|
|
77
|
+
* Example: billerId -> BillerId
|
|
78
|
+
* @param str The camelCase string to convert
|
|
79
|
+
* @returns The converted PascalCase string
|
|
80
|
+
*/
|
|
81
|
+
export const toPascalCase = (str: string): string => {
|
|
82
|
+
if (!str) return '';
|
|
83
|
+
|
|
84
|
+
// Split by capital letters and convert to array
|
|
85
|
+
const words = str.split(/(?=[A-Z])/);
|
|
86
|
+
|
|
87
|
+
// Capitalize first letter of each word and join
|
|
88
|
+
return words
|
|
89
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
90
|
+
.join('');
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Example usage:
|
|
94
|
+
// toPascalCase('billerId') => 'BillerId'
|
|
95
|
+
// toPascalCase('customerName') => 'CustomerName'
|
|
96
|
+
// toPascalCase('accountNumber') => 'AccountNumber'
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Converts a PascalCase string to camelCase
|
|
100
|
+
* Example: BillerId -> billerId
|
|
101
|
+
* @param str The PascalCase string to convert
|
|
102
|
+
* @returns The converted camelCase string
|
|
103
|
+
*/
|
|
104
|
+
export const toCamelCase = (str: string): string => {
|
|
105
|
+
if (!str) return '';
|
|
106
|
+
|
|
107
|
+
// Convert first character to lowercase and keep the rest as is
|
|
108
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Example usage:
|
|
112
|
+
// toCamelCase('BillerId') => 'billerId'
|
|
113
|
+
// toCamelCase('CustomerName') => 'customerName'
|
|
114
|
+
// toCamelCase('AccountNumber') => 'accountNumber'
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import {NativeModules, Platform} from 'react-native';
|
|
2
|
+
|
|
3
|
+
const {SunmiCardModule} = NativeModules;
|
|
4
|
+
|
|
5
|
+
if (!SunmiCardModule) {
|
|
6
|
+
throw new Error('SunmiCardModule is not available on this device');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Interface for Sunmi Card Reader functionality
|
|
11
|
+
*/
|
|
12
|
+
class SunmiCard {
|
|
13
|
+
/**
|
|
14
|
+
* Card types constants
|
|
15
|
+
*/
|
|
16
|
+
static CARD_TYPE_MAGNETIC = SunmiCardModule.CARD_TYPE_MAGNETIC;
|
|
17
|
+
static CARD_TYPE_IC = SunmiCardModule.CARD_TYPE_IC;
|
|
18
|
+
static CARD_TYPE_NFC = SunmiCardModule.CARD_TYPE_NFC;
|
|
19
|
+
static CARD_TYPE_MAGNETIC_IC = SunmiCardModule.CARD_TYPE_MAGNETIC_IC;
|
|
20
|
+
static CARD_TYPE_MAGNETIC_NFC = SunmiCardModule.CARD_TYPE_MAGNETIC_NFC;
|
|
21
|
+
static CARD_TYPE_IC_NFC = SunmiCardModule.CARD_TYPE_IC_NFC;
|
|
22
|
+
static CARD_TYPE_MAGNETIC_IC_NFC = SunmiCardModule.CARD_TYPE_MAGNETIC_IC_NFC;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Card reader mode constants
|
|
26
|
+
*/
|
|
27
|
+
static READ_MODE_MAGNETIC = SunmiCardModule.READ_MODE_MAGNETIC;
|
|
28
|
+
static READ_MODE_IC = SunmiCardModule.READ_MODE_IC;
|
|
29
|
+
static READ_MODE_NFC = SunmiCardModule.READ_MODE_NFC;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Track error code constants
|
|
33
|
+
*/
|
|
34
|
+
static TRACK_ERROR_NONE = SunmiCardModule.TRACK_ERROR_NONE; // No error
|
|
35
|
+
static TRACK_ERROR_NO_DATA = SunmiCardModule.TRACK_ERROR_NO_DATA; // Track has no data
|
|
36
|
+
static TRACK_ERROR_PARITY = SunmiCardModule.TRACK_ERROR_PARITY; // Track parity check error
|
|
37
|
+
static TRACK_ERROR_LRC = SunmiCardModule.TRACK_ERROR_LRC; // Track LRC check error
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if the Sunmi Pay SDK is connected
|
|
41
|
+
* @returns {Promise<boolean>} - True if connected
|
|
42
|
+
*/
|
|
43
|
+
static isConnected() {
|
|
44
|
+
if (Platform.OS !== 'android') {
|
|
45
|
+
return Promise.reject(
|
|
46
|
+
new Error('SunmiCard is only available on Android'),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return SunmiCardModule.isConnected();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check for a card to be presented
|
|
55
|
+
* @param {number} cardType - Type of card to check for (use CARD_TYPE_* constants)
|
|
56
|
+
* @param {number} timeout - Timeout in seconds
|
|
57
|
+
* @returns {Promise<Object>} - Card information or timeout/error
|
|
58
|
+
*
|
|
59
|
+
* For magnetic cards, the result will include:
|
|
60
|
+
* - type: "magnetic"
|
|
61
|
+
* - track1: Track 1 data (if available)
|
|
62
|
+
* - track2: Track 2 data (if available)
|
|
63
|
+
* - track3: Track 3 data (if available)
|
|
64
|
+
* - track1ErrorCode: Error code for track 1 (0 = no error, -1 = no data, -2 = parity error, -3 = LRC error)
|
|
65
|
+
* - track2ErrorCode: Error code for track 2
|
|
66
|
+
* - track3ErrorCode: Error code for track 3
|
|
67
|
+
*
|
|
68
|
+
* For IC cards, the result will include:
|
|
69
|
+
* - type: "ic"
|
|
70
|
+
* - atr: ATR data
|
|
71
|
+
*
|
|
72
|
+
* For NFC cards, the result will include:
|
|
73
|
+
* - type: "nfc"
|
|
74
|
+
* - uuid: Card UUID
|
|
75
|
+
*
|
|
76
|
+
* For timeout, the result will include:
|
|
77
|
+
* - type: "timeout"
|
|
78
|
+
*/
|
|
79
|
+
static checkCard(cardType, timeout = 60) {
|
|
80
|
+
if (Platform.OS !== 'android') {
|
|
81
|
+
return Promise.reject(
|
|
82
|
+
new Error('SunmiCard is only available on Android'),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (typeof cardType !== 'number') {
|
|
87
|
+
return Promise.reject(new Error('cardType must be a number'));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (typeof timeout !== 'number') {
|
|
91
|
+
return Promise.reject(new Error('timeout must be a number'));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return SunmiCardModule.checkCard(cardType, timeout);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Cancel an ongoing card check operation
|
|
99
|
+
* @returns {Promise<boolean>} - True if successful
|
|
100
|
+
*/
|
|
101
|
+
static cancelCardCheck() {
|
|
102
|
+
if (Platform.OS !== 'android') {
|
|
103
|
+
return Promise.reject(
|
|
104
|
+
new Error('SunmiCard is only available on Android'),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return SunmiCardModule.cancelCardCheck();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Read data from an IC card
|
|
113
|
+
* Note: This method is provided for backward compatibility.
|
|
114
|
+
* It's recommended to use checkCard with CARD_TYPE_IC instead.
|
|
115
|
+
* @returns {Promise<Object>} - IC card information
|
|
116
|
+
*/
|
|
117
|
+
static readICCard() {
|
|
118
|
+
if (Platform.OS !== 'android') {
|
|
119
|
+
return Promise.reject(
|
|
120
|
+
new Error('SunmiCard is only available on Android'),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return SunmiCardModule.readICCard();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Read data from an NFC card
|
|
129
|
+
* Note: This method is provided for backward compatibility.
|
|
130
|
+
* It's recommended to use checkCard with CARD_TYPE_NFC instead.
|
|
131
|
+
* @returns {Promise<Object>} - NFC card information
|
|
132
|
+
*/
|
|
133
|
+
static readNFCCard() {
|
|
134
|
+
if (Platform.OS !== 'android') {
|
|
135
|
+
return Promise.reject(
|
|
136
|
+
new Error('SunmiCard is only available on Android'),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return SunmiCardModule.readNFCCard();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Transaction types
|
|
145
|
+
*/
|
|
146
|
+
static TRANS_TYPE_CONSUME = SunmiCardModule.TRANS_TYPE_CONSUME;
|
|
147
|
+
static TRANS_TYPE_REFUND = SunmiCardModule.TRANS_TYPE_REFUND;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Transaction result codes
|
|
151
|
+
*/
|
|
152
|
+
static TRANS_RESULT_SUCCESS = SunmiCardModule.TRANS_RESULT_SUCCESS;
|
|
153
|
+
static TRANS_RESULT_OFFLINE_APPROVED =
|
|
154
|
+
SunmiCardModule.TRANS_RESULT_OFFLINE_APPROVED;
|
|
155
|
+
static TRANS_RESULT_DECLINED = SunmiCardModule.TRANS_RESULT_DECLINED;
|
|
156
|
+
static TRANS_RESULT_TERMINATED = SunmiCardModule.TRANS_RESULT_TERMINATED;
|
|
157
|
+
static TRANS_RESULT_CANCEL = SunmiCardModule.TRANS_RESULT_CANCEL;
|
|
158
|
+
static TRANS_RESULT_TIMEOUT = SunmiCardModule.TRANS_RESULT_TIMEOUT;
|
|
159
|
+
static TRANS_RESULT_OTHER_ERROR = SunmiCardModule.TRANS_RESULT_OTHER_ERROR;
|
|
160
|
+
static TRANS_RESULT_MAC_ERROR = SunmiCardModule.TRANS_RESULT_MAC_ERROR;
|
|
161
|
+
static TRANS_RESULT_FALLBACK = SunmiCardModule.TRANS_RESULT_FALLBACK;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Start a card payment transaction
|
|
165
|
+
* @param {number} amount - The transaction amount in cents (e.g., 1000 for $10.00)
|
|
166
|
+
* @param {string} currency - The currency code (e.g., "USD")
|
|
167
|
+
* @param {string} transactionType - The transaction type ("consume" or "refund")
|
|
168
|
+
* @param {number} timeout - Timeout in seconds for card detection
|
|
169
|
+
* @returns {Promise<Object>} - Transaction result object
|
|
170
|
+
*/
|
|
171
|
+
static startCardPayment(
|
|
172
|
+
amount,
|
|
173
|
+
currency = 'USD',
|
|
174
|
+
transactionType = 'consume',
|
|
175
|
+
timeout = 60,
|
|
176
|
+
) {
|
|
177
|
+
if (typeof amount !== 'number' || amount <= 0) {
|
|
178
|
+
return Promise.reject(new Error('Amount must be a positive number'));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Convert transaction type string to integer
|
|
182
|
+
let transType = SunmiCard.TRANS_TYPE_CONSUME;
|
|
183
|
+
if (transactionType.toLowerCase() === 'refund') {
|
|
184
|
+
transType = SunmiCard.TRANS_TYPE_REFUND;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return SunmiCardModule.startCardPayment(
|
|
188
|
+
amount,
|
|
189
|
+
currency,
|
|
190
|
+
transactionType,
|
|
191
|
+
timeout,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Cancel an ongoing card payment transaction
|
|
197
|
+
* @returns {Promise<boolean>} - True if canceled successfully
|
|
198
|
+
*/
|
|
199
|
+
static cancelCardPayment() {
|
|
200
|
+
return SunmiCardModule.cancelCardPayment();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get the result of the last transaction
|
|
205
|
+
* @returns {Promise<Object|null>} - Transaction result object or null if no transaction
|
|
206
|
+
*/
|
|
207
|
+
static getLastTransactionResult() {
|
|
208
|
+
return SunmiCardModule.getLastTransactionResult();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export default SunmiCard;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {NativeModules} from 'react-native';
|
|
2
|
+
|
|
3
|
+
const {SunmiPrepaid} = NativeModules;
|
|
4
|
+
|
|
5
|
+
export interface PrepaidResponse {
|
|
6
|
+
success: boolean;
|
|
7
|
+
message: string;
|
|
8
|
+
billingAcct: string;
|
|
9
|
+
extraBillingAcctKey: any[];
|
|
10
|
+
customProperties: any[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ServiceStatus {
|
|
14
|
+
isInitialized: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function checkMeeterFeedBack(response: any): {Key: string; Value: string}[] {
|
|
18
|
+
let chunksNum;
|
|
19
|
+
if (response.meterFeedback.length % 1024 == 0)
|
|
20
|
+
chunksNum = response.meterFeedback.length / 1024;
|
|
21
|
+
else {
|
|
22
|
+
chunksNum = response.meterFeedback.length / 1024 + 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let tmp = [];
|
|
26
|
+
|
|
27
|
+
for (let i = 1; i <= chunksNum; i++) {
|
|
28
|
+
let meterFeedbackKey = 'ESCFBT0';
|
|
29
|
+
meterFeedbackKey += i;
|
|
30
|
+
let chunkLength = i * 1024;
|
|
31
|
+
let previousChunkLength = (i - 1) * 1024;
|
|
32
|
+
if (chunkLength > response.meterFeedback.length)
|
|
33
|
+
chunkLength = response.meterFeedback.length;
|
|
34
|
+
let chunkData = response.meterFeedback.substring(
|
|
35
|
+
previousChunkLength,
|
|
36
|
+
chunkLength,
|
|
37
|
+
);
|
|
38
|
+
tmp.push({Key: meterFeedbackKey, Value: chunkData});
|
|
39
|
+
}
|
|
40
|
+
return tmp;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class SunmiPrepaidModule {
|
|
44
|
+
async initializePrepaid(): Promise<PrepaidResponse> {
|
|
45
|
+
try {
|
|
46
|
+
return await SunmiPrepaid.initializePrepaid();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(`Failed to initialize prepaid service: ${error}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async forceCardFacadeDetection(): Promise<PrepaidResponse> {
|
|
53
|
+
try {
|
|
54
|
+
return await SunmiPrepaid.forceCardFacadeDetection();
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(`Failed to force card facade detection: ${error}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async readCardMetadata(
|
|
61
|
+
extraKeys: any,
|
|
62
|
+
type: string,
|
|
63
|
+
): Promise<PrepaidResponse> {
|
|
64
|
+
try {
|
|
65
|
+
const response = await SunmiPrepaid.readCardMetadata(type);
|
|
66
|
+
|
|
67
|
+
let keysArray = [
|
|
68
|
+
{Key: 'Key1', Value: response.vendor},
|
|
69
|
+
{Key: 'Key2', Value: response.sectorIdentifier},
|
|
70
|
+
{Key: 'Key3', Value: response.meterIdentifier},
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
let propertiesArray = [
|
|
74
|
+
{
|
|
75
|
+
Key:
|
|
76
|
+
type === 'SC' || type === 'GASSC'
|
|
77
|
+
? 'ElectrictyCompany'
|
|
78
|
+
: 'WaterCompany',
|
|
79
|
+
Value: response.companyIdentifier,
|
|
80
|
+
},
|
|
81
|
+
{Key: 'ClientIdentifier', Value: response.clientIdentifier},
|
|
82
|
+
{Key: 'CardMetadata', Value: response.metaData},
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
if (response.meterFeedback.length < 1024) {
|
|
86
|
+
propertiesArray.push({
|
|
87
|
+
Key: 'ESCFBT01',
|
|
88
|
+
Value: response.meterFeedback,
|
|
89
|
+
});
|
|
90
|
+
} else {
|
|
91
|
+
propertiesArray.push(...checkMeeterFeedBack(response));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (response.metaData) {
|
|
95
|
+
propertiesArray.push({Key: 'CardMetadata', Value: response.metaData});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
success: true,
|
|
100
|
+
message: 'success',
|
|
101
|
+
billingAcct: response.identifier,
|
|
102
|
+
extraBillingAcctKey: keysArray,
|
|
103
|
+
customProperties: propertiesArray,
|
|
104
|
+
};
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw new Error(`Failed to read card metadata: ${error}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async writeCardCharge(
|
|
111
|
+
billEncryptInfo: string,
|
|
112
|
+
cardMetadata: string,
|
|
113
|
+
): Promise<PrepaidResponse> {
|
|
114
|
+
try {
|
|
115
|
+
return await SunmiPrepaid.writeCardCharge(billEncryptInfo, cardMetadata);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
throw new Error(`Failed to write card charge: ${error}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default new SunmiPrepaidModule();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {createNativeStackNavigator} from '@react-navigation/native-stack';
|
|
2
2
|
import {useTranslation} from '../common/localization/LocalizationProvider';
|
|
3
3
|
import {Login} from '../screens/Login/Login';
|
|
4
|
+
import {OTPScreen} from '../screens/OTP/OTPScreen';
|
|
4
5
|
import {Splash} from '../screens/splash/Splash';
|
|
5
6
|
|
|
6
7
|
const Stack = createNativeStackNavigator();
|
|
@@ -23,6 +24,13 @@ export function AuthStack() {
|
|
|
23
24
|
headerShown: false,
|
|
24
25
|
},
|
|
25
26
|
},
|
|
27
|
+
{
|
|
28
|
+
id: 'OTP',
|
|
29
|
+
component: OTPScreen,
|
|
30
|
+
options: {
|
|
31
|
+
headerShown: false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
26
34
|
];
|
|
27
35
|
|
|
28
36
|
return (
|