@zezosoft/react-native-zezopay 1.0.0

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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -0
  3. package/lib/module/ZezoPay/Payments/Providers/PaymentGateways.types.d.js +2 -0
  4. package/lib/module/ZezoPay/Payments/Providers/PaymentGateways.types.d.js.map +1 -0
  5. package/lib/module/ZezoPay/Payments/Providers/Razorpay/Razorpay.js +39 -0
  6. package/lib/module/ZezoPay/Payments/Providers/Razorpay/Razorpay.js.map +1 -0
  7. package/lib/module/ZezoPay/Payments/Providers/index.js +10 -0
  8. package/lib/module/ZezoPay/Payments/Providers/index.js.map +1 -0
  9. package/lib/module/ZezoPay/ZezoPay.js +176 -0
  10. package/lib/module/ZezoPay/ZezoPay.js.map +1 -0
  11. package/lib/module/ZezoPay/components/Header.js +97 -0
  12. package/lib/module/ZezoPay/components/Header.js.map +1 -0
  13. package/lib/module/ZezoPay/components/PayButton.js +143 -0
  14. package/lib/module/ZezoPay/components/PayButton.js.map +1 -0
  15. package/lib/module/ZezoPay/components/PaymentMethod.js +250 -0
  16. package/lib/module/ZezoPay/components/PaymentMethod.js.map +1 -0
  17. package/lib/module/ZezoPay/components/Summary.js +184 -0
  18. package/lib/module/ZezoPay/components/Summary.js.map +1 -0
  19. package/lib/module/ZezoPay/components/VoucherBox.js +124 -0
  20. package/lib/module/ZezoPay/components/VoucherBox.js.map +1 -0
  21. package/lib/module/ZezoPay/index.js +5 -0
  22. package/lib/module/ZezoPay/index.js.map +1 -0
  23. package/lib/module/ZezoPay/types/index.js +4 -0
  24. package/lib/module/ZezoPay/types/index.js.map +1 -0
  25. package/lib/module/ZezoPay/utils/hooks/useAsync.js +32 -0
  26. package/lib/module/ZezoPay/utils/hooks/useAsync.js.map +1 -0
  27. package/lib/module/ZezoPay/utils/hooks/useZezoPay.js +270 -0
  28. package/lib/module/ZezoPay/utils/hooks/useZezoPay.js.map +1 -0
  29. package/lib/module/ZezoPay/utils/index.js +15 -0
  30. package/lib/module/ZezoPay/utils/index.js.map +1 -0
  31. package/lib/module/index.js +4 -0
  32. package/lib/module/index.js.map +1 -0
  33. package/lib/module/package.json +1 -0
  34. package/lib/typescript/package.json +1 -0
  35. package/lib/typescript/src/ZezoPay/Payments/Providers/Razorpay/Razorpay.d.ts +7 -0
  36. package/lib/typescript/src/ZezoPay/Payments/Providers/Razorpay/Razorpay.d.ts.map +1 -0
  37. package/lib/typescript/src/ZezoPay/Payments/Providers/index.d.ts +7 -0
  38. package/lib/typescript/src/ZezoPay/Payments/Providers/index.d.ts.map +1 -0
  39. package/lib/typescript/src/ZezoPay/ZezoPay.d.ts +5 -0
  40. package/lib/typescript/src/ZezoPay/ZezoPay.d.ts.map +1 -0
  41. package/lib/typescript/src/ZezoPay/components/Header.d.ts +12 -0
  42. package/lib/typescript/src/ZezoPay/components/Header.d.ts.map +1 -0
  43. package/lib/typescript/src/ZezoPay/components/PayButton.d.ts +12 -0
  44. package/lib/typescript/src/ZezoPay/components/PayButton.d.ts.map +1 -0
  45. package/lib/typescript/src/ZezoPay/components/PaymentMethod.d.ts +12 -0
  46. package/lib/typescript/src/ZezoPay/components/PaymentMethod.d.ts.map +1 -0
  47. package/lib/typescript/src/ZezoPay/components/Summary.d.ts +7 -0
  48. package/lib/typescript/src/ZezoPay/components/Summary.d.ts.map +1 -0
  49. package/lib/typescript/src/ZezoPay/components/VoucherBox.d.ts +9 -0
  50. package/lib/typescript/src/ZezoPay/components/VoucherBox.d.ts.map +1 -0
  51. package/lib/typescript/src/ZezoPay/index.d.ts +3 -0
  52. package/lib/typescript/src/ZezoPay/index.d.ts.map +1 -0
  53. package/lib/typescript/src/ZezoPay/types/index.d.ts +65 -0
  54. package/lib/typescript/src/ZezoPay/types/index.d.ts.map +1 -0
  55. package/lib/typescript/src/ZezoPay/utils/hooks/useAsync.d.ts +6 -0
  56. package/lib/typescript/src/ZezoPay/utils/hooks/useAsync.d.ts.map +1 -0
  57. package/lib/typescript/src/ZezoPay/utils/hooks/useZezoPay.d.ts +46 -0
  58. package/lib/typescript/src/ZezoPay/utils/hooks/useZezoPay.d.ts.map +1 -0
  59. package/lib/typescript/src/ZezoPay/utils/index.d.ts +6 -0
  60. package/lib/typescript/src/ZezoPay/utils/index.d.ts.map +1 -0
  61. package/lib/typescript/src/index.d.ts +2 -0
  62. package/lib/typescript/src/index.d.ts.map +1 -0
  63. package/package.json +181 -0
  64. package/src/ZezoPay/Payments/Providers/PaymentGateways.types.d.ts +48 -0
  65. package/src/ZezoPay/Payments/Providers/Razorpay/Razorpay.ts +46 -0
  66. package/src/ZezoPay/Payments/Providers/index.ts +8 -0
  67. package/src/ZezoPay/ZezoPay.tsx +174 -0
  68. package/src/ZezoPay/components/Header.tsx +107 -0
  69. package/src/ZezoPay/components/PayButton.tsx +132 -0
  70. package/src/ZezoPay/components/PaymentMethod.tsx +259 -0
  71. package/src/ZezoPay/components/Summary.tsx +188 -0
  72. package/src/ZezoPay/components/VoucherBox.tsx +133 -0
  73. package/src/ZezoPay/index.ts +2 -0
  74. package/src/ZezoPay/types/index.ts +69 -0
  75. package/src/ZezoPay/utils/hooks/useAsync.ts +38 -0
  76. package/src/ZezoPay/utils/hooks/useZezoPay.ts +325 -0
  77. package/src/ZezoPay/utils/index.ts +16 -0
  78. package/src/index.tsx +1 -0
@@ -0,0 +1,259 @@
1
+ import type { PaymentProviderData } from '@zezosoft/zezopay-client';
2
+ import React, { memo, useCallback } from 'react';
3
+ import { Pressable, StyleSheet, Text, View, Animated } from 'react-native';
4
+ import { scale, moderateScale, verticalScale } from 'react-native-size-matters';
5
+
6
+ interface PaymentMethodProps {
7
+ providers: PaymentProviderData[];
8
+ selectedProvider: string | null;
9
+ onProviderChange: (provider: string) => void;
10
+ isLoading?: boolean;
11
+ error?: string | null;
12
+ }
13
+
14
+ const COLORS = {
15
+ background: '#ffffff',
16
+ lightGray: '#f9fafb',
17
+ grayBorder: '#e5e7eb',
18
+ grayText: '#6b7280',
19
+ darkText: '#111827',
20
+ logoFallback: '#9ca3af',
21
+ skeleton: '#d1d5db',
22
+ error: '#dc2626',
23
+ selectedBorder: '#2563EB',
24
+ selectedBackground: '#eff6ff',
25
+ radioBorder: '#9ca3af',
26
+ };
27
+
28
+ const ProviderLogo: React.FC<{
29
+ name: string;
30
+ logo?: string;
31
+ isLoading?: boolean;
32
+ }> = ({ name, logo, isLoading }) => {
33
+ if (isLoading) return <View style={styles.skeletonLogo} />;
34
+
35
+ if (logo) {
36
+ return (
37
+ <View style={styles.logoWrapper}>
38
+ <Animated.Image
39
+ source={{ uri: logo }}
40
+ style={styles.logo}
41
+ accessibilityLabel={`${name} logo`}
42
+ />
43
+ </View>
44
+ );
45
+ }
46
+
47
+ return (
48
+ <View style={styles.logoFallback}>
49
+ <Text style={styles.logoFallbackText}>{name[0]}</Text>
50
+ </View>
51
+ );
52
+ };
53
+
54
+ const ProviderItem: React.FC<{
55
+ provider: PaymentProviderData;
56
+ isProviderSelected: boolean;
57
+ isLoading: boolean;
58
+ onPress: (provider: string) => void;
59
+ }> = memo(({ provider, isProviderSelected, isLoading, onPress }) => (
60
+ <Pressable
61
+ onPress={() => onPress(provider.provider)}
62
+ key={provider.provider}
63
+ style={[
64
+ styles.gatewayItem,
65
+ isProviderSelected && styles.gatewaySelected,
66
+ isLoading && styles.disabled,
67
+ ]}
68
+ disabled={isLoading}
69
+ accessibilityState={{ selected: isProviderSelected, disabled: isLoading }}
70
+ >
71
+ <View style={styles.providerContainer}>
72
+ <ProviderLogo
73
+ name={provider.provider}
74
+ logo={provider.logo}
75
+ isLoading={isLoading}
76
+ />
77
+ {isLoading ? (
78
+ <View style={styles.skeletonText} />
79
+ ) : (
80
+ <Text style={styles.gatewayText}>{provider.provider}</Text>
81
+ )}
82
+ </View>
83
+ <View style={[styles.radio, isProviderSelected && styles.radioSelected]}>
84
+ {isProviderSelected && <View style={styles.radioDot} />}
85
+ </View>
86
+ </Pressable>
87
+ ));
88
+
89
+ const PaymentMethod: React.FC<PaymentMethodProps> = ({
90
+ providers,
91
+ selectedProvider,
92
+ onProviderChange,
93
+ isLoading = false,
94
+ error = null,
95
+ }) => {
96
+ const handleProviderChange = useCallback(
97
+ (provider: string) => onProviderChange(provider),
98
+ [onProviderChange]
99
+ );
100
+
101
+ const skeletonItems = Array.from({ length: 3 }, (_, i) => ({
102
+ provider: `loading-${i}`,
103
+ logo: '',
104
+ }));
105
+
106
+ return (
107
+ <View style={styles.container}>
108
+ <Text style={styles.title} accessibilityRole="header">
109
+ Payment Gateways
110
+ </Text>
111
+
112
+ {(isLoading ? skeletonItems : providers).map((provider) => (
113
+ <ProviderItem
114
+ key={provider.provider}
115
+ provider={provider as PaymentProviderData}
116
+ isProviderSelected={provider.provider === selectedProvider}
117
+ isLoading={isLoading}
118
+ onPress={handleProviderChange}
119
+ />
120
+ ))}
121
+
122
+ {error && (
123
+ <View style={styles.noProvidersWrapper}>
124
+ <Text style={styles.errorText}>{error}</Text>
125
+ </View>
126
+ )}
127
+
128
+ {!isLoading && !error && providers.length === 0 && (
129
+ <View style={styles.noProvidersWrapper}>
130
+ <Text style={styles.noProvidersText}>
131
+ No payment gateways available
132
+ </Text>
133
+ </View>
134
+ )}
135
+ </View>
136
+ );
137
+ };
138
+
139
+ const styles = StyleSheet.create({
140
+ container: {
141
+ padding: scale(16),
142
+ backgroundColor: COLORS.background,
143
+ borderRadius: scale(12),
144
+ elevation: 4,
145
+ marginBottom: verticalScale(16),
146
+ shadowColor: '#000',
147
+ shadowOpacity: 0.05,
148
+ shadowRadius: 10,
149
+ shadowOffset: { width: 0, height: 2 },
150
+ maxHeight: 250,
151
+ },
152
+ title: {
153
+ fontSize: moderateScale(16),
154
+ fontWeight: '700',
155
+ marginBottom: verticalScale(8),
156
+ color: COLORS.darkText,
157
+ },
158
+ errorText: {
159
+ fontSize: moderateScale(14),
160
+ color: COLORS.error,
161
+ marginBottom: verticalScale(8),
162
+ },
163
+ noProvidersWrapper: {
164
+ flex: 1,
165
+ justifyContent: 'center',
166
+ alignItems: 'center',
167
+ minHeight: 150,
168
+ },
169
+ noProvidersText: {
170
+ fontSize: moderateScale(14),
171
+ color: COLORS.grayText,
172
+ textAlign: 'center',
173
+ },
174
+ gatewayItem: {
175
+ flexDirection: 'row',
176
+ justifyContent: 'space-between',
177
+ alignItems: 'center',
178
+ padding: scale(10),
179
+ borderRadius: scale(10),
180
+ backgroundColor: COLORS.lightGray,
181
+ borderWidth: 1,
182
+ borderColor: COLORS.grayBorder,
183
+ marginBottom: verticalScale(8),
184
+ },
185
+ gatewaySelected: {
186
+ borderColor: COLORS.selectedBorder,
187
+ backgroundColor: COLORS.selectedBackground,
188
+ },
189
+ disabled: { opacity: 0.6 },
190
+ providerContainer: { flexDirection: 'row', alignItems: 'center' },
191
+ logoWrapper: {
192
+ width: scale(32),
193
+ height: scale(32),
194
+ backgroundColor: COLORS.background,
195
+ borderRadius: scale(6),
196
+ padding: scale(3),
197
+ alignItems: 'center',
198
+ justifyContent: 'center',
199
+ marginRight: scale(12),
200
+ },
201
+ logo: {
202
+ width: '100%',
203
+ height: '100%',
204
+ resizeMode: 'contain',
205
+ borderRadius: scale(4),
206
+ },
207
+ skeletonLogo: {
208
+ width: scale(28),
209
+ height: scale(28),
210
+ borderRadius: scale(14),
211
+ backgroundColor: COLORS.skeleton,
212
+ marginRight: scale(12),
213
+ },
214
+ logoFallback: {
215
+ width: scale(28),
216
+ height: scale(28),
217
+ borderRadius: scale(14),
218
+ backgroundColor: COLORS.logoFallback,
219
+ alignItems: 'center',
220
+ justifyContent: 'center',
221
+ marginRight: scale(12),
222
+ },
223
+ logoFallbackText: {
224
+ fontSize: moderateScale(12),
225
+ fontWeight: '700',
226
+ color: COLORS.background,
227
+ textTransform: 'uppercase',
228
+ },
229
+ gatewayText: {
230
+ fontSize: moderateScale(15),
231
+ fontWeight: '600',
232
+ textTransform: 'capitalize',
233
+ color: COLORS.darkText,
234
+ },
235
+ skeletonText: {
236
+ width: scale(80),
237
+ height: verticalScale(14),
238
+ backgroundColor: COLORS.skeleton,
239
+ borderRadius: scale(4),
240
+ },
241
+ radio: {
242
+ width: scale(20),
243
+ height: scale(20),
244
+ borderRadius: scale(10),
245
+ borderWidth: scale(2),
246
+ borderColor: COLORS.radioBorder,
247
+ alignItems: 'center',
248
+ justifyContent: 'center',
249
+ },
250
+ radioSelected: { borderColor: COLORS.selectedBorder },
251
+ radioDot: {
252
+ width: scale(10),
253
+ height: scale(10),
254
+ borderRadius: scale(5),
255
+ backgroundColor: COLORS.selectedBorder,
256
+ },
257
+ });
258
+
259
+ export default memo(PaymentMethod);
@@ -0,0 +1,188 @@
1
+ import { View, Text, Image, TouchableOpacity, StyleSheet } from 'react-native';
2
+ import { X } from 'lucide-react-native';
3
+ import type { ISummaryItem } from '@zezosoft/zezopay-client';
4
+ import { formatCurrency } from '../utils';
5
+
6
+ export const Summary = ({
7
+ items = [],
8
+ displayCurrency = 'INR',
9
+ onRemoveItem,
10
+ }: {
11
+ items: ISummaryItem[];
12
+ displayCurrency?: string;
13
+ onRemoveItem?: (id: ISummaryItem['id']) => void;
14
+ }) => {
15
+ // Show error if no items
16
+ if (!items || items.length === 0) {
17
+ return (
18
+ <View style={styles.errorWrapper}>
19
+ <Text style={styles.errorText}>No items in summary.</Text>
20
+ </View>
21
+ );
22
+ }
23
+
24
+ return (
25
+ <View>
26
+ <Text style={styles.title}>Summary</Text>
27
+ <View style={styles.list}>
28
+ {items.map((item) => {
29
+ let discountedPrice = item.price;
30
+ if (item.discount) {
31
+ if (typeof item.discount === 'number') {
32
+ discountedPrice = (item.price * (100 - item.discount)) / 100;
33
+ } else if (item.discount.type === 'percentage') {
34
+ discountedPrice =
35
+ (item.price * (100 - item.discount.amount)) / 100;
36
+ } else if (item.discount.type === 'fixed') {
37
+ discountedPrice = item.price - item.discount.amount;
38
+ }
39
+ }
40
+
41
+ return (
42
+ <View key={`summary-${item.id}`} style={styles.itemBox}>
43
+ <View style={styles.row}>
44
+ <View style={styles.left}>
45
+ {item.image ? (
46
+ <Image source={{ uri: item.image }} style={styles.logo} />
47
+ ) : (
48
+ <View style={[styles.logo, styles.logoTextContainer]}>
49
+ <Text style={styles.logoText}>
50
+ {item.name.slice(0, 2).toUpperCase()}
51
+ </Text>
52
+ </View>
53
+ )}
54
+
55
+ <View style={styles.info}>
56
+ <Text style={styles.itemName}>{item.name}</Text>
57
+
58
+ {item.discount ? (
59
+ <Text style={styles.subText}>
60
+ <Text style={styles.strike}>
61
+ {formatCurrency({
62
+ amount: item.price,
63
+ currency: displayCurrency,
64
+ })}
65
+ </Text>
66
+ <Text> </Text>
67
+ <Text style={styles.discounted}>
68
+ {formatCurrency({
69
+ amount: discountedPrice,
70
+ currency: displayCurrency,
71
+ })}
72
+ </Text>
73
+ {item.duration ? (
74
+ <Text>{` /${item.duration}`}</Text>
75
+ ) : null}
76
+ </Text>
77
+ ) : (
78
+ <Text style={styles.subText}>
79
+ {formatCurrency({
80
+ amount: discountedPrice,
81
+ currency: displayCurrency,
82
+ })}
83
+ {item.duration ? (
84
+ <Text>{` /${item.duration}`}</Text>
85
+ ) : null}
86
+ </Text>
87
+ )}
88
+ </View>
89
+ </View>
90
+
91
+ {onRemoveItem && (
92
+ <TouchableOpacity
93
+ onPress={() => onRemoveItem(item.id)}
94
+ style={styles.removeBtn}
95
+ >
96
+ <X size={20} />
97
+ </TouchableOpacity>
98
+ )}
99
+ </View>
100
+ </View>
101
+ );
102
+ })}
103
+ </View>
104
+ </View>
105
+ );
106
+ };
107
+
108
+ const styles = StyleSheet.create({
109
+ title: {
110
+ fontSize: 18,
111
+ fontWeight: '700',
112
+ marginBottom: 10,
113
+ },
114
+ list: {
115
+ gap: 10,
116
+ },
117
+ itemBox: {
118
+ padding: 12,
119
+ borderRadius: 12,
120
+ marginBottom: 10,
121
+ borderColor: '#a6e6cf',
122
+ borderWidth: 1,
123
+ backgroundColor: 'rgba(224, 243, 236, 0.68)',
124
+ },
125
+ row: {
126
+ flexDirection: 'row',
127
+ alignItems: 'center',
128
+ justifyContent: 'space-between',
129
+ },
130
+ left: {
131
+ flexDirection: 'row',
132
+ alignItems: 'center',
133
+ gap: 10,
134
+ flex: 1,
135
+ },
136
+ logo: {
137
+ width: 42,
138
+ height: 42,
139
+ borderRadius: 8,
140
+ backgroundColor: '#ddd',
141
+ justifyContent: 'center',
142
+ alignItems: 'center',
143
+ },
144
+ logoTextContainer: {
145
+ backgroundColor: '#444',
146
+ },
147
+ logoText: {
148
+ fontWeight: '700',
149
+ color: '#fff',
150
+ },
151
+ info: {
152
+ flexDirection: 'column',
153
+ },
154
+ itemName: {
155
+ fontSize: 15,
156
+ fontWeight: '600',
157
+ color: '#111',
158
+ },
159
+ subText: {
160
+ fontSize: 13,
161
+ color: '#222',
162
+ },
163
+ strike: {
164
+ textDecorationLine: 'line-through',
165
+ color: '#444',
166
+ },
167
+ discounted: {
168
+ fontWeight: '700',
169
+ color: '#000',
170
+ },
171
+ removeBtn: {
172
+ padding: 6,
173
+ borderRadius: 20,
174
+ },
175
+ errorWrapper: {
176
+ padding: 12,
177
+ borderRadius: 12,
178
+ backgroundColor: '#fee2e2',
179
+ borderWidth: 1,
180
+ borderColor: '#fca5a5',
181
+ marginVertical: 10,
182
+ },
183
+ errorText: {
184
+ color: '#b91c1c',
185
+ fontWeight: '600',
186
+ textAlign: 'center',
187
+ },
188
+ });
@@ -0,0 +1,133 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ TextInput,
6
+ StyleSheet,
7
+ TouchableOpacity,
8
+ } from 'react-native';
9
+ import LinearGradient from 'react-native-linear-gradient';
10
+ import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
11
+
12
+ // Centralized color definitions
13
+ const COLORS = {
14
+ primaryOrange: '#FF7847',
15
+ secondaryOrange: '#c24c21',
16
+ disabledOrangeStart: '#df7026ff',
17
+ disabledOrangeEnd: '#ce6320ff',
18
+ black: '#000',
19
+ white: '#fff',
20
+ };
21
+
22
+ interface VoucherBoxProps {
23
+ voucher: string;
24
+ setVoucher: (v: string) => void;
25
+ onApply?: () => void;
26
+ }
27
+
28
+ const VoucherBox: React.FC<VoucherBoxProps> = ({
29
+ voucher,
30
+ setVoucher,
31
+ onApply,
32
+ }) => {
33
+ return (
34
+ <View>
35
+ <Text style={styles.title}>Voucher</Text>
36
+ <View style={styles.inputWrapper}>
37
+ <TextInput
38
+ placeholder={'Enter code...'}
39
+ style={styles.input}
40
+ value={voucher}
41
+ onChangeText={setVoucher}
42
+ accessibilityLabel="Voucher code input"
43
+ />
44
+ <TouchableOpacity
45
+ style={styles.button}
46
+ onPress={onApply}
47
+ disabled={!voucher}
48
+ activeOpacity={0.8}
49
+ accessibilityRole="button"
50
+ accessibilityLabel="Apply voucher code"
51
+ >
52
+ <LinearGradient
53
+ colors={
54
+ voucher
55
+ ? [COLORS.primaryOrange, COLORS.secondaryOrange]
56
+ : [COLORS.disabledOrangeStart, COLORS.disabledOrangeEnd]
57
+ }
58
+ style={styles.gradient}
59
+ start={{ x: 0, y: 0 }}
60
+ end={{ x: 1, y: 0 }}
61
+ >
62
+ <Text
63
+ style={[styles.buttonText, !voucher && styles.disabledButtonText]}
64
+ >
65
+ Apply
66
+ </Text>
67
+ </LinearGradient>
68
+ </TouchableOpacity>
69
+ </View>
70
+ </View>
71
+ );
72
+ };
73
+
74
+ const styles = StyleSheet.create({
75
+ title: {
76
+ fontSize: moderateScale(16),
77
+ fontWeight: '700',
78
+ color: COLORS.black,
79
+ marginBottom: verticalScale(8),
80
+ },
81
+ inputWrapper: {
82
+ flexDirection: 'row',
83
+ alignItems: 'center',
84
+ backgroundColor: COLORS.white,
85
+ height: moderateScale(47),
86
+ minHeight: verticalScale(45),
87
+ maxHeight: verticalScale(50),
88
+ borderRadius: scale(8),
89
+ borderWidth: 1,
90
+ borderColor: COLORS.primaryOrange,
91
+ width: '100%',
92
+ shadowColor: COLORS.black,
93
+ shadowOffset: { width: 0, height: 2 },
94
+ paddingVertical: verticalScale(2),
95
+ marginBottom: verticalScale(8),
96
+ },
97
+ input: {
98
+ flex: 1,
99
+ height: '100%',
100
+ fontSize: moderateScale(14),
101
+ paddingHorizontal: scale(14),
102
+ paddingVertical: verticalScale(8),
103
+ borderRadius: scale(12),
104
+ },
105
+ button: {
106
+ minWidth: scale(80),
107
+ height: moderateScale(45),
108
+ borderRadius: scale(10),
109
+ overflow: 'hidden',
110
+ justifyContent: 'center',
111
+ alignItems: 'center',
112
+ marginRight: scale(3),
113
+ },
114
+ gradient: {
115
+ width: '100%',
116
+ height: '100%',
117
+ alignItems: 'center',
118
+ justifyContent: 'center',
119
+ borderRadius: scale(10),
120
+ },
121
+ buttonText: {
122
+ color: COLORS.white,
123
+ fontWeight: '700',
124
+ fontSize: moderateScale(15),
125
+ textTransform: 'uppercase',
126
+ },
127
+ disabledButtonText: {
128
+ color: COLORS.white,
129
+ opacity: 0.7,
130
+ },
131
+ });
132
+
133
+ export default VoucherBox;
@@ -0,0 +1,2 @@
1
+ export * from './types';
2
+ export { default as ZezoPay } from './ZezoPay';
@@ -0,0 +1,69 @@
1
+ // types.ts
2
+
3
+ import type { UserInfo } from '@zezosoft/zezopay-client';
4
+
5
+ export type PaymentDetails = {
6
+ userId: string;
7
+ price: number;
8
+ metadata?: {
9
+ isPaymentInitiatedEnabled?: boolean;
10
+ userInfo?: UserInfo;
11
+ };
12
+ };
13
+
14
+ export type ZezoPayProps = {
15
+ publicKey: string;
16
+ subscriptionId?: string;
17
+ digitalProductId?: string;
18
+ summaryItems: SummaryItem[];
19
+ userInfo: UserInfo;
20
+ title: string;
21
+ onBack: () => void;
22
+ callback?: ZezoPayCallbacks;
23
+ };
24
+
25
+ export type ZezoPayCallbacks = {
26
+ onSuccess?: (data: any) => void;
27
+ onFailure?: (error: any) => void;
28
+ };
29
+
30
+ export type PaymentProviderType =
31
+ | 'razorpay'
32
+ | 'stripe'
33
+ | 'ccavenue'
34
+ | 'phonepe';
35
+
36
+ export type ZezoPayHookProps = {
37
+ publicKey: string;
38
+ userInfo: {
39
+ _id: string;
40
+ name: string;
41
+ email?: string;
42
+ phone?: string;
43
+ };
44
+ handlePayment?: (args: {
45
+ provider: PaymentProviderType;
46
+ subscriptionId?: string;
47
+ digitalProductId?: string;
48
+ userInfo: { _id: string; name: string; email?: string; phone?: string };
49
+ }) => Promise<any>;
50
+ subscriptionId?: string;
51
+ digitalProductId?: string;
52
+ summaryItems?: SummaryItem[];
53
+ validateVoucherCode?: (code: string) => number;
54
+ callbacks?: ZezoPayCallbacks;
55
+ };
56
+
57
+ export type Discount = {
58
+ type: 'percentage' | 'fixed';
59
+ amount: number;
60
+ };
61
+
62
+ export type SummaryItem = {
63
+ id: string;
64
+ name: string;
65
+ price: number;
66
+ discount?: number | { type: 'percentage' | 'fixed'; amount: number };
67
+ duration?: string;
68
+ image?: string;
69
+ };
@@ -0,0 +1,38 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ export function useAsync<T>(asyncFn: () => Promise<T>, deps: any[] = []) {
4
+ const [result, setResult] = useState<T | null>(null);
5
+ const [loading, setLoading] = useState(true);
6
+ const [error, setError] = useState<Error | null>(null);
7
+
8
+ useEffect(() => {
9
+ let mounted = true;
10
+ setLoading(true);
11
+
12
+ asyncFn()
13
+ .then((res) => {
14
+ if (mounted) {
15
+ setResult(res);
16
+ setError(null);
17
+ }
18
+ })
19
+ .catch((err) => {
20
+ if (mounted)
21
+ setError(
22
+ err instanceof Error
23
+ ? err
24
+ : new Error(err.msg || err.message || 'Unknown error')
25
+ );
26
+ })
27
+ .finally(() => {
28
+ if (mounted) setLoading(false);
29
+ });
30
+
31
+ return () => {
32
+ mounted = false;
33
+ };
34
+ // eslint-disable-next-line react-hooks/exhaustive-deps
35
+ }, deps);
36
+
37
+ return { result, loading, error };
38
+ }