@zezosoft/react-native-zezopay 1.0.2 → 1.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/README.md +4 -4
- package/lib/module/ZezoPay/ZezoPay.js +41 -27
- package/lib/module/ZezoPay/ZezoPay.js.map +1 -1
- package/lib/module/ZezoPay/components/PaymentMethod.js.map +1 -1
- package/lib/module/ZezoPay/components/Summary.js.map +1 -1
- package/lib/module/ZezoPay/components/VoucherBox.js +76 -27
- package/lib/module/ZezoPay/components/VoucherBox.js.map +1 -1
- package/lib/module/ZezoPay/http/api-sdk.js +14 -0
- package/lib/module/ZezoPay/http/api-sdk.js.map +1 -0
- package/lib/module/ZezoPay/http/services/baseService.js +28 -0
- package/lib/module/ZezoPay/http/services/baseService.js.map +1 -0
- package/lib/module/ZezoPay/http/services/payments/payments.js +51 -0
- package/lib/module/ZezoPay/http/services/payments/payments.js.map +1 -0
- package/lib/module/ZezoPay/http/services/payments/payments.types.js +2 -0
- package/lib/module/ZezoPay/http/services/payments/payments.types.js.map +1 -0
- package/lib/module/ZezoPay/http/utils/errorFormatter.js +49 -0
- package/lib/module/ZezoPay/http/utils/errorFormatter.js.map +1 -0
- package/lib/module/ZezoPay/utils/hooks/useZezoPay.js +187 -203
- package/lib/module/ZezoPay/utils/hooks/useZezoPay.js.map +1 -1
- package/lib/typescript/src/ZezoPay/ZezoPay.d.ts.map +1 -1
- package/lib/typescript/src/ZezoPay/components/PaymentMethod.d.ts +2 -2
- package/lib/typescript/src/ZezoPay/components/PaymentMethod.d.ts.map +1 -1
- package/lib/typescript/src/ZezoPay/components/Summary.d.ts +1 -1
- package/lib/typescript/src/ZezoPay/components/Summary.d.ts.map +1 -1
- package/lib/typescript/src/ZezoPay/components/VoucherBox.d.ts +6 -3
- package/lib/typescript/src/ZezoPay/components/VoucherBox.d.ts.map +1 -1
- package/lib/typescript/src/ZezoPay/http/api-sdk.d.ts +21 -0
- package/lib/typescript/src/ZezoPay/http/api-sdk.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/http/services/baseService.d.ts +9 -0
- package/lib/typescript/src/ZezoPay/http/services/baseService.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/http/services/payments/payments.d.ts +25 -0
- package/lib/typescript/src/ZezoPay/http/services/payments/payments.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/http/services/payments/payments.types.d.ts +100 -0
- package/lib/typescript/src/ZezoPay/http/services/payments/payments.types.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/http/utils/errorFormatter.d.ts +6 -0
- package/lib/typescript/src/ZezoPay/http/utils/errorFormatter.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/types/index.d.ts +27 -6
- package/lib/typescript/src/ZezoPay/types/index.d.ts.map +1 -1
- package/lib/typescript/src/ZezoPay/utils/hooks/useZezoPay.d.ts +23 -29
- package/lib/typescript/src/ZezoPay/utils/hooks/useZezoPay.d.ts.map +1 -1
- package/package.json +2 -3
- package/src/ZezoPay/ZezoPay.tsx +49 -28
- package/src/ZezoPay/components/PaymentMethod.tsx +4 -4
- package/src/ZezoPay/components/Summary.tsx +1 -1
- package/src/ZezoPay/components/VoucherBox.tsx +100 -51
- package/src/ZezoPay/http/api-sdk.ts +27 -0
- package/src/ZezoPay/http/services/baseService.ts +37 -0
- package/src/ZezoPay/http/services/payments/payments.ts +66 -0
- package/src/ZezoPay/http/services/payments/payments.types.ts +120 -0
- package/src/ZezoPay/http/utils/errorFormatter.ts +88 -0
- package/src/ZezoPay/types/index.ts +41 -7
- package/src/ZezoPay/utils/hooks/useZezoPay.ts +234 -231
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
TextInput,
|
|
6
6
|
StyleSheet,
|
|
7
7
|
TouchableOpacity,
|
|
8
|
+
ActivityIndicator,
|
|
8
9
|
} from 'react-native';
|
|
9
10
|
import LinearGradient from 'react-native-linear-gradient';
|
|
10
11
|
import { scale, verticalScale, moderateScale } from 'react-native-size-matters';
|
|
11
12
|
|
|
12
|
-
// Centralized color definitions
|
|
13
13
|
const COLORS = {
|
|
14
14
|
primaryOrange: '#FF7847',
|
|
15
15
|
secondaryOrange: '#c24c21',
|
|
@@ -20,58 +20,92 @@ const COLORS = {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
interface VoucherBoxProps {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
onApply?: ({ voucherCode }: { voucherCode: string }) => void;
|
|
24
|
+
loading?: boolean;
|
|
25
|
+
couponApplied?: boolean;
|
|
26
|
+
onRemove?: () => void;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
const VoucherBox: React.FC<VoucherBoxProps> = ({
|
|
29
|
-
voucher,
|
|
30
|
-
setVoucher,
|
|
31
30
|
onApply,
|
|
31
|
+
loading = false,
|
|
32
|
+
couponApplied = false,
|
|
33
|
+
onRemove,
|
|
32
34
|
}) => {
|
|
35
|
+
const [voucher, setVoucher] = React.useState('');
|
|
36
|
+
|
|
33
37
|
return (
|
|
34
|
-
<View>
|
|
38
|
+
<View style={styles.container}>
|
|
35
39
|
<Text style={styles.title}>Voucher</Text>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
style={styles.
|
|
59
|
-
|
|
60
|
-
|
|
40
|
+
|
|
41
|
+
{couponApplied ? (
|
|
42
|
+
// Display applied voucher with remove button
|
|
43
|
+
<View style={styles.appliedWrapper}>
|
|
44
|
+
<Text style={styles.appliedText}>{voucher}</Text>
|
|
45
|
+
<TouchableOpacity
|
|
46
|
+
style={styles.removeButton}
|
|
47
|
+
onPress={() => {
|
|
48
|
+
setVoucher('');
|
|
49
|
+
onRemove?.();
|
|
50
|
+
}}
|
|
51
|
+
accessibilityRole="button"
|
|
52
|
+
accessibilityLabel="Remove voucher"
|
|
53
|
+
>
|
|
54
|
+
<Text style={styles.removeText}>Remove</Text>
|
|
55
|
+
</TouchableOpacity>
|
|
56
|
+
</View>
|
|
57
|
+
) : (
|
|
58
|
+
// Input for new voucher
|
|
59
|
+
<View style={styles.inputWrapper}>
|
|
60
|
+
<TextInput
|
|
61
|
+
placeholder="Enter code..."
|
|
62
|
+
style={styles.input}
|
|
63
|
+
value={voucher}
|
|
64
|
+
onChangeText={(text) => setVoucher(text.toUpperCase())}
|
|
65
|
+
autoCapitalize="characters"
|
|
66
|
+
editable={!loading}
|
|
67
|
+
accessibilityLabel="Voucher code input"
|
|
68
|
+
/>
|
|
69
|
+
<TouchableOpacity
|
|
70
|
+
style={styles.button}
|
|
71
|
+
onPress={() => onApply?.({ voucherCode: voucher })}
|
|
72
|
+
disabled={!voucher || loading}
|
|
73
|
+
activeOpacity={0.8}
|
|
74
|
+
accessibilityRole="button"
|
|
75
|
+
accessibilityLabel="Apply voucher code"
|
|
61
76
|
>
|
|
62
|
-
<
|
|
63
|
-
|
|
77
|
+
<LinearGradient
|
|
78
|
+
colors={
|
|
79
|
+
voucher
|
|
80
|
+
? [COLORS.primaryOrange, COLORS.secondaryOrange]
|
|
81
|
+
: [COLORS.disabledOrangeStart, COLORS.disabledOrangeEnd]
|
|
82
|
+
}
|
|
83
|
+
style={styles.gradient}
|
|
84
|
+
start={{ x: 0, y: 0 }}
|
|
85
|
+
end={{ x: 1, y: 0 }}
|
|
64
86
|
>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
{loading ? (
|
|
88
|
+
<ActivityIndicator size="small" color={COLORS.white} />
|
|
89
|
+
) : (
|
|
90
|
+
<Text
|
|
91
|
+
style={[
|
|
92
|
+
styles.buttonText,
|
|
93
|
+
!voucher && styles.disabledButtonText,
|
|
94
|
+
]}
|
|
95
|
+
>
|
|
96
|
+
Apply
|
|
97
|
+
</Text>
|
|
98
|
+
)}
|
|
99
|
+
</LinearGradient>
|
|
100
|
+
</TouchableOpacity>
|
|
101
|
+
</View>
|
|
102
|
+
)}
|
|
70
103
|
</View>
|
|
71
104
|
);
|
|
72
105
|
};
|
|
73
106
|
|
|
74
107
|
const styles = StyleSheet.create({
|
|
108
|
+
container: { marginBottom: verticalScale(12) },
|
|
75
109
|
title: {
|
|
76
110
|
fontSize: moderateScale(16),
|
|
77
111
|
fontWeight: '700',
|
|
@@ -83,40 +117,33 @@ const styles = StyleSheet.create({
|
|
|
83
117
|
alignItems: 'center',
|
|
84
118
|
backgroundColor: COLORS.white,
|
|
85
119
|
height: moderateScale(47),
|
|
86
|
-
minHeight: verticalScale(45),
|
|
87
|
-
maxHeight: verticalScale(50),
|
|
88
120
|
borderRadius: scale(8),
|
|
89
121
|
borderWidth: 1,
|
|
90
122
|
borderColor: COLORS.primaryOrange,
|
|
91
123
|
width: '100%',
|
|
92
|
-
shadowColor: COLORS.black,
|
|
93
|
-
shadowOffset: { width: 0, height: 2 },
|
|
94
124
|
paddingVertical: verticalScale(2),
|
|
95
|
-
|
|
125
|
+
overflow: 'hidden',
|
|
96
126
|
},
|
|
97
127
|
input: {
|
|
98
128
|
flex: 1,
|
|
99
129
|
height: '100%',
|
|
100
130
|
fontSize: moderateScale(14),
|
|
101
131
|
paddingHorizontal: scale(14),
|
|
102
|
-
paddingVertical: verticalScale(8),
|
|
103
132
|
borderRadius: scale(12),
|
|
104
133
|
},
|
|
105
134
|
button: {
|
|
106
135
|
minWidth: scale(80),
|
|
107
136
|
height: moderateScale(45),
|
|
108
|
-
borderRadius: scale(10),
|
|
109
137
|
overflow: 'hidden',
|
|
110
138
|
justifyContent: 'center',
|
|
111
139
|
alignItems: 'center',
|
|
112
|
-
marginRight: scale(3),
|
|
113
140
|
},
|
|
114
141
|
gradient: {
|
|
115
142
|
width: '100%',
|
|
116
143
|
height: '100%',
|
|
117
144
|
alignItems: 'center',
|
|
118
145
|
justifyContent: 'center',
|
|
119
|
-
borderRadius: scale(10),
|
|
146
|
+
// borderRadius: scale(10),
|
|
120
147
|
},
|
|
121
148
|
buttonText: {
|
|
122
149
|
color: COLORS.white,
|
|
@@ -124,9 +151,31 @@ const styles = StyleSheet.create({
|
|
|
124
151
|
fontSize: moderateScale(15),
|
|
125
152
|
textTransform: 'uppercase',
|
|
126
153
|
},
|
|
127
|
-
disabledButtonText: {
|
|
128
|
-
|
|
129
|
-
|
|
154
|
+
disabledButtonText: { opacity: 0.7 },
|
|
155
|
+
appliedWrapper: {
|
|
156
|
+
flexDirection: 'row',
|
|
157
|
+
justifyContent: 'space-between',
|
|
158
|
+
alignItems: 'center',
|
|
159
|
+
borderColor: COLORS.primaryOrange,
|
|
160
|
+
borderWidth: 1,
|
|
161
|
+
backgroundColor: '#F3F4F6',
|
|
162
|
+
borderRadius: scale(8),
|
|
163
|
+
paddingHorizontal: scale(12),
|
|
164
|
+
paddingVertical: verticalScale(10),
|
|
165
|
+
},
|
|
166
|
+
appliedText: {
|
|
167
|
+
fontSize: moderateScale(14),
|
|
168
|
+
fontWeight: '600',
|
|
169
|
+
color: COLORS.black,
|
|
170
|
+
},
|
|
171
|
+
removeButton: {
|
|
172
|
+
paddingHorizontal: scale(5),
|
|
173
|
+
paddingVertical: verticalScale(4),
|
|
174
|
+
},
|
|
175
|
+
removeText: {
|
|
176
|
+
fontSize: moderateScale(14),
|
|
177
|
+
fontWeight: '700',
|
|
178
|
+
color: COLORS.primaryOrange,
|
|
130
179
|
},
|
|
131
180
|
});
|
|
132
181
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import PaymentService from './services/payments/payments';
|
|
2
|
+
|
|
3
|
+
export interface IOptions {
|
|
4
|
+
publicKey: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface APIError {
|
|
8
|
+
type: string;
|
|
9
|
+
status?: number | string;
|
|
10
|
+
message: string;
|
|
11
|
+
path?: string;
|
|
12
|
+
location?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class ZezoPayClient {
|
|
16
|
+
payments: PaymentService;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Constructor for the ZezoPay class.
|
|
20
|
+
* @param options
|
|
21
|
+
*/
|
|
22
|
+
constructor(options: IOptions) {
|
|
23
|
+
this.payments = new PaymentService(options);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export * from './services/payments/payments.types';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import axios, {
|
|
2
|
+
type AxiosInstance,
|
|
3
|
+
type AxiosRequestConfig,
|
|
4
|
+
type AxiosResponse,
|
|
5
|
+
} from 'axios';
|
|
6
|
+
import type { APIError, IOptions } from '../api-sdk';
|
|
7
|
+
import { formatAPIError } from '../utils/errorFormatter';
|
|
8
|
+
|
|
9
|
+
class BaseService {
|
|
10
|
+
protected client: AxiosInstance;
|
|
11
|
+
|
|
12
|
+
constructor(options: IOptions) {
|
|
13
|
+
this.client = axios.create({
|
|
14
|
+
baseURL: 'https://payapi.zezo.in',
|
|
15
|
+
// baseURL: 'http://localhost:5501',
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
'Accept': 'application/json',
|
|
19
|
+
'x-public-key': String(options.publicKey) || '',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected async request<T = any>(
|
|
25
|
+
config: AxiosRequestConfig
|
|
26
|
+
): Promise<AxiosResponse<T>> {
|
|
27
|
+
try {
|
|
28
|
+
return await this.client.request<T>(config);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
// 🔥 Always return consistent APIError format
|
|
31
|
+
const formattedError: APIError = formatAPIError(error);
|
|
32
|
+
throw formattedError;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default BaseService;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { AxiosResponse } from 'axios';
|
|
2
|
+
import type {
|
|
3
|
+
ICheckoutPayload,
|
|
4
|
+
ICheckoutResult,
|
|
5
|
+
ICouponResponse,
|
|
6
|
+
IOptions,
|
|
7
|
+
PaymentProviderList,
|
|
8
|
+
} from '../../api-sdk';
|
|
9
|
+
import BaseService from '../baseService';
|
|
10
|
+
|
|
11
|
+
class PaymentService extends BaseService {
|
|
12
|
+
constructor(options: IOptions) {
|
|
13
|
+
super(options);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get available payment providers
|
|
18
|
+
*/
|
|
19
|
+
async providers({
|
|
20
|
+
platform = 'web',
|
|
21
|
+
}: {
|
|
22
|
+
platform?: 'web' | 'ios' | 'android';
|
|
23
|
+
} = {}): Promise<AxiosResponse<PaymentProviderList>> {
|
|
24
|
+
return this.request<PaymentProviderList>({
|
|
25
|
+
method: 'GET',
|
|
26
|
+
url: '/api/v1/payments/ready',
|
|
27
|
+
headers: {
|
|
28
|
+
'x-platform': platform,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a checkout session
|
|
35
|
+
*/
|
|
36
|
+
async checkout(
|
|
37
|
+
payload: ICheckoutPayload,
|
|
38
|
+
platform?: 'web' | 'ios' | 'android'
|
|
39
|
+
): Promise<AxiosResponse<ICheckoutResult>> {
|
|
40
|
+
return this.request<ICheckoutResult>({
|
|
41
|
+
method: 'POST',
|
|
42
|
+
url: '/api/v1/payments/checkout',
|
|
43
|
+
data: payload,
|
|
44
|
+
headers: {
|
|
45
|
+
'x-platform': platform || 'web',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Verify a coupon
|
|
51
|
+
*/
|
|
52
|
+
async verifyCoupon(payload: {
|
|
53
|
+
code: string;
|
|
54
|
+
userId: string;
|
|
55
|
+
}): Promise<AxiosResponse<ICouponResponse>> {
|
|
56
|
+
return this.request<ICouponResponse>({
|
|
57
|
+
method: 'POST',
|
|
58
|
+
url: `/api/v1/payments/apply-coupon/${payload.code}`,
|
|
59
|
+
data: {
|
|
60
|
+
userId: payload.userId,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default PaymentService;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported payment providers
|
|
3
|
+
*/
|
|
4
|
+
export type PaymentProvider =
|
|
5
|
+
| 'stripe'
|
|
6
|
+
| 'razorpay'
|
|
7
|
+
| 'phonepe'
|
|
8
|
+
| 'ccavenue'
|
|
9
|
+
| string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Payment provider info used in UI
|
|
13
|
+
*/
|
|
14
|
+
export interface IReadyPaymentProvider {
|
|
15
|
+
logo: string;
|
|
16
|
+
provider: PaymentProvider;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Basic user info attached to payments
|
|
21
|
+
*/
|
|
22
|
+
export interface UserInfo {
|
|
23
|
+
_id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
email?: string;
|
|
26
|
+
phone?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extra metadata for payments
|
|
31
|
+
*/
|
|
32
|
+
export interface PaymentMeta {
|
|
33
|
+
isPaymentInitiatedEnabled?: boolean;
|
|
34
|
+
userInfo: UserInfo;
|
|
35
|
+
summary?: Record<string, any>;
|
|
36
|
+
[key: string]: any; // Allow flexible fields
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Base checkout input shared across types
|
|
41
|
+
*/
|
|
42
|
+
export interface BaseCheckoutInput {
|
|
43
|
+
userId: string;
|
|
44
|
+
provider: PaymentProvider;
|
|
45
|
+
metadata: PaymentMeta;
|
|
46
|
+
currency?: string;
|
|
47
|
+
coupon_code?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checkout payload variations
|
|
52
|
+
*/
|
|
53
|
+
export type ICheckoutPayload =
|
|
54
|
+
| (BaseCheckoutInput & { type: 'normal'; price: number })
|
|
55
|
+
| (BaseCheckoutInput & { type: 'subscription'; subscriptionId: string })
|
|
56
|
+
| (BaseCheckoutInput & { type: 'digital-product'; digitalProductId: string });
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Checkout API response
|
|
60
|
+
*/
|
|
61
|
+
export interface ICheckoutResult {
|
|
62
|
+
status: boolean;
|
|
63
|
+
price: number;
|
|
64
|
+
currency:
|
|
65
|
+
| 'INR'
|
|
66
|
+
| 'USD'
|
|
67
|
+
| 'EUR'
|
|
68
|
+
| 'GBP'
|
|
69
|
+
| 'JPY'
|
|
70
|
+
| 'AUD'
|
|
71
|
+
| 'CAD'
|
|
72
|
+
| 'CNY'
|
|
73
|
+
| 'SGD'
|
|
74
|
+
| string;
|
|
75
|
+
provider: PaymentProvider;
|
|
76
|
+
real_price: number;
|
|
77
|
+
orderId: string;
|
|
78
|
+
publicKey: string;
|
|
79
|
+
extraData?: Record<string, any>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Verification of payment status
|
|
84
|
+
*/
|
|
85
|
+
export interface IPaymentVerify {
|
|
86
|
+
_id: string;
|
|
87
|
+
userId: string;
|
|
88
|
+
orderId: string;
|
|
89
|
+
status: 'success' | 'failed' | 'pending';
|
|
90
|
+
meta_data: PaymentMeta;
|
|
91
|
+
[key: string]: any;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface IPaymentVerification {
|
|
95
|
+
message: string;
|
|
96
|
+
data: IPaymentVerify;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* List of supported providers
|
|
101
|
+
*/
|
|
102
|
+
export type PaymentProviderList = IReadyPaymentProvider[];
|
|
103
|
+
|
|
104
|
+
export interface ICouponValidity {
|
|
105
|
+
start_date: string; // ISO date string
|
|
106
|
+
end_date: string; // ISO date string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface ICouponData {
|
|
110
|
+
_id: string;
|
|
111
|
+
name: string;
|
|
112
|
+
discount: number; // percentage or fixed amount
|
|
113
|
+
type: 'percentage' | 'amount';
|
|
114
|
+
validity: ICouponValidity;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface ICouponResponse {
|
|
118
|
+
message: string;
|
|
119
|
+
data: ICouponData;
|
|
120
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// utils/formatAPIError.ts
|
|
2
|
+
|
|
3
|
+
import type { APIError } from '../api-sdk';
|
|
4
|
+
|
|
5
|
+
interface IBackendError {
|
|
6
|
+
response?: {
|
|
7
|
+
status?: number;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
data?: {
|
|
10
|
+
message?: string;
|
|
11
|
+
error?: {
|
|
12
|
+
type?: string;
|
|
13
|
+
message?: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
location?: string;
|
|
16
|
+
};
|
|
17
|
+
errors?: {
|
|
18
|
+
type?: string;
|
|
19
|
+
msg?: string;
|
|
20
|
+
path?: string;
|
|
21
|
+
location?: string;
|
|
22
|
+
}[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
config?: {
|
|
26
|
+
url?: string;
|
|
27
|
+
};
|
|
28
|
+
code?: string;
|
|
29
|
+
message?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Converts any kind of error (network, backend, unknown) into a consistent APIError format
|
|
34
|
+
*/
|
|
35
|
+
export function formatAPIError(err: unknown): APIError {
|
|
36
|
+
const error = err as IBackendError;
|
|
37
|
+
|
|
38
|
+
if (
|
|
39
|
+
error?.code === 'ECONNREFUSED' ||
|
|
40
|
+
error?.message?.includes('Network Error')
|
|
41
|
+
) {
|
|
42
|
+
return {
|
|
43
|
+
type: 'network_error',
|
|
44
|
+
status: error.code,
|
|
45
|
+
message: 'Network error: check your connection.',
|
|
46
|
+
path: error?.config?.url || '',
|
|
47
|
+
location: '',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const isHtml =
|
|
52
|
+
error?.response?.headers?.['content-type']?.includes('text/html') ||
|
|
53
|
+
(typeof error?.response?.data === 'string' &&
|
|
54
|
+
(error.response.data as string).startsWith('<!DOCTYPE html>'));
|
|
55
|
+
|
|
56
|
+
if (isHtml) {
|
|
57
|
+
return {
|
|
58
|
+
type: 'invalid_route',
|
|
59
|
+
status: error?.response?.status || 404,
|
|
60
|
+
message: 'This API endpoint does not exist or cannot be called.',
|
|
61
|
+
path: error?.config?.url || '',
|
|
62
|
+
location: '',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const firstError = error?.response?.data?.errors?.[0];
|
|
67
|
+
const errorObj = error?.response?.data?.error;
|
|
68
|
+
const message =
|
|
69
|
+
firstError?.msg || errorObj?.message || error?.response?.data?.message;
|
|
70
|
+
|
|
71
|
+
if (message) {
|
|
72
|
+
return {
|
|
73
|
+
type: firstError?.type || errorObj?.type || 'backend_error',
|
|
74
|
+
status: error?.response?.status,
|
|
75
|
+
message,
|
|
76
|
+
path: firstError?.path || errorObj?.path || error?.config?.url || '',
|
|
77
|
+
location: firstError?.location || errorObj?.location || '',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
type: 'unknown_error',
|
|
83
|
+
status: error?.response?.status,
|
|
84
|
+
message: error?.message || 'An unknown error occurred.',
|
|
85
|
+
path: error?.config?.url || '',
|
|
86
|
+
location: '',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
// types.ts
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ICheckoutResult,
|
|
5
|
+
PaymentMeta,
|
|
6
|
+
PaymentProvider,
|
|
7
|
+
UserInfo,
|
|
8
|
+
} from '../http/api-sdk';
|
|
9
|
+
|
|
10
|
+
/* Summary Item */
|
|
11
|
+
export interface ISummaryItem {
|
|
12
|
+
id: string | number;
|
|
13
|
+
name: string;
|
|
14
|
+
price: number;
|
|
15
|
+
duration?: string;
|
|
16
|
+
image?: string;
|
|
17
|
+
discount?: number | { amount: number; type: 'percentage' | 'fixed' };
|
|
18
|
+
}
|
|
19
|
+
/* Handle Payment Type */
|
|
20
|
+
export type HandlePayment = ({
|
|
21
|
+
provider,
|
|
22
|
+
subscriptionId,
|
|
23
|
+
digitalProductId,
|
|
24
|
+
userInfo,
|
|
25
|
+
coupon_code,
|
|
26
|
+
metadata,
|
|
27
|
+
}: {
|
|
28
|
+
provider: PaymentProvider;
|
|
29
|
+
subscriptionId?: string | null;
|
|
30
|
+
digitalProductId?: string | null;
|
|
31
|
+
userInfo: UserInfo;
|
|
32
|
+
coupon_code?: string | null;
|
|
33
|
+
metadata?: PaymentMeta | null;
|
|
34
|
+
}) => Promise<ICheckoutResult | null>;
|
|
35
|
+
|
|
36
|
+
/* Callbacks */
|
|
37
|
+
export interface ZezoPayCallbacks {
|
|
38
|
+
onSuccess?: (response: any) => void;
|
|
39
|
+
onFailure?: (error: any) => void;
|
|
40
|
+
onError?: (error: any) => void;
|
|
41
|
+
}
|
|
4
42
|
|
|
5
43
|
export type PaymentDetails = {
|
|
6
44
|
userId: string;
|
|
@@ -19,13 +57,9 @@ export type ZezoPayProps = {
|
|
|
19
57
|
userInfo: UserInfo;
|
|
20
58
|
title: string;
|
|
21
59
|
onBack: () => void;
|
|
22
|
-
|
|
60
|
+
callbacks?: ZezoPayCallbacks;
|
|
23
61
|
handlePayment?: HandlePayment;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export type ZezoPayCallbacks = {
|
|
27
|
-
onSuccess?: (data: any) => void;
|
|
28
|
-
onFailure?: (error: any) => void;
|
|
62
|
+
voucher?: boolean;
|
|
29
63
|
};
|
|
30
64
|
|
|
31
65
|
export type PaymentProviderType =
|