@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.
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/lib/module/ZezoPay/Payments/Providers/PaymentGateways.types.d.js +2 -0
- package/lib/module/ZezoPay/Payments/Providers/PaymentGateways.types.d.js.map +1 -0
- package/lib/module/ZezoPay/Payments/Providers/Razorpay/Razorpay.js +39 -0
- package/lib/module/ZezoPay/Payments/Providers/Razorpay/Razorpay.js.map +1 -0
- package/lib/module/ZezoPay/Payments/Providers/index.js +10 -0
- package/lib/module/ZezoPay/Payments/Providers/index.js.map +1 -0
- package/lib/module/ZezoPay/ZezoPay.js +176 -0
- package/lib/module/ZezoPay/ZezoPay.js.map +1 -0
- package/lib/module/ZezoPay/components/Header.js +97 -0
- package/lib/module/ZezoPay/components/Header.js.map +1 -0
- package/lib/module/ZezoPay/components/PayButton.js +143 -0
- package/lib/module/ZezoPay/components/PayButton.js.map +1 -0
- package/lib/module/ZezoPay/components/PaymentMethod.js +250 -0
- package/lib/module/ZezoPay/components/PaymentMethod.js.map +1 -0
- package/lib/module/ZezoPay/components/Summary.js +184 -0
- package/lib/module/ZezoPay/components/Summary.js.map +1 -0
- package/lib/module/ZezoPay/components/VoucherBox.js +124 -0
- package/lib/module/ZezoPay/components/VoucherBox.js.map +1 -0
- package/lib/module/ZezoPay/index.js +5 -0
- package/lib/module/ZezoPay/index.js.map +1 -0
- package/lib/module/ZezoPay/types/index.js +4 -0
- package/lib/module/ZezoPay/types/index.js.map +1 -0
- package/lib/module/ZezoPay/utils/hooks/useAsync.js +32 -0
- package/lib/module/ZezoPay/utils/hooks/useAsync.js.map +1 -0
- package/lib/module/ZezoPay/utils/hooks/useZezoPay.js +270 -0
- package/lib/module/ZezoPay/utils/hooks/useZezoPay.js.map +1 -0
- package/lib/module/ZezoPay/utils/index.js +15 -0
- package/lib/module/ZezoPay/utils/index.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/ZezoPay/Payments/Providers/Razorpay/Razorpay.d.ts +7 -0
- package/lib/typescript/src/ZezoPay/Payments/Providers/Razorpay/Razorpay.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/Payments/Providers/index.d.ts +7 -0
- package/lib/typescript/src/ZezoPay/Payments/Providers/index.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/ZezoPay.d.ts +5 -0
- package/lib/typescript/src/ZezoPay/ZezoPay.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/components/Header.d.ts +12 -0
- package/lib/typescript/src/ZezoPay/components/Header.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/components/PayButton.d.ts +12 -0
- package/lib/typescript/src/ZezoPay/components/PayButton.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/components/PaymentMethod.d.ts +12 -0
- package/lib/typescript/src/ZezoPay/components/PaymentMethod.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/components/Summary.d.ts +7 -0
- package/lib/typescript/src/ZezoPay/components/Summary.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/components/VoucherBox.d.ts +9 -0
- package/lib/typescript/src/ZezoPay/components/VoucherBox.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/index.d.ts +3 -0
- package/lib/typescript/src/ZezoPay/index.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/types/index.d.ts +65 -0
- package/lib/typescript/src/ZezoPay/types/index.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/utils/hooks/useAsync.d.ts +6 -0
- package/lib/typescript/src/ZezoPay/utils/hooks/useAsync.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/utils/hooks/useZezoPay.d.ts +46 -0
- package/lib/typescript/src/ZezoPay/utils/hooks/useZezoPay.d.ts.map +1 -0
- package/lib/typescript/src/ZezoPay/utils/index.d.ts +6 -0
- package/lib/typescript/src/ZezoPay/utils/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +181 -0
- package/src/ZezoPay/Payments/Providers/PaymentGateways.types.d.ts +48 -0
- package/src/ZezoPay/Payments/Providers/Razorpay/Razorpay.ts +46 -0
- package/src/ZezoPay/Payments/Providers/index.ts +8 -0
- package/src/ZezoPay/ZezoPay.tsx +174 -0
- package/src/ZezoPay/components/Header.tsx +107 -0
- package/src/ZezoPay/components/PayButton.tsx +132 -0
- package/src/ZezoPay/components/PaymentMethod.tsx +259 -0
- package/src/ZezoPay/components/Summary.tsx +188 -0
- package/src/ZezoPay/components/VoucherBox.tsx +133 -0
- package/src/ZezoPay/index.ts +2 -0
- package/src/ZezoPay/types/index.ts +69 -0
- package/src/ZezoPay/utils/hooks/useAsync.ts +38 -0
- package/src/ZezoPay/utils/hooks/useZezoPay.ts +325 -0
- package/src/ZezoPay/utils/index.ts +16 -0
- package/src/index.tsx +1 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { useAsync } from './useAsync';
|
|
3
|
+
import {
|
|
4
|
+
ZezoPayClient,
|
|
5
|
+
type ICheckoutResult,
|
|
6
|
+
type ISummaryItem,
|
|
7
|
+
type PaymentProvider,
|
|
8
|
+
type ZezoPayCallbacks,
|
|
9
|
+
type HandlePayment,
|
|
10
|
+
type IReadyPaymentProvider,
|
|
11
|
+
type ICheckoutPayload,
|
|
12
|
+
type BaseCheckoutInput,
|
|
13
|
+
} from '@zezosoft/zezopay-client';
|
|
14
|
+
import { Platform } from 'react-native';
|
|
15
|
+
import { Razorpay } from '../../Payments/Providers';
|
|
16
|
+
|
|
17
|
+
export const allowedProviders: PaymentProvider[] = ['razorpay'];
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parameters for the useZezoPay hook
|
|
21
|
+
* @interface UseZezoPayParams
|
|
22
|
+
*/
|
|
23
|
+
interface UseZezoPayParams {
|
|
24
|
+
publicKey: string;
|
|
25
|
+
userInfo: { _id: string; name: string; email?: string; phone?: string };
|
|
26
|
+
subscriptionId?: string;
|
|
27
|
+
digitalProductId?: string;
|
|
28
|
+
summaryItems?: ISummaryItem[];
|
|
29
|
+
handlePayment?: HandlePayment;
|
|
30
|
+
callback?: ZezoPayCallbacks;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Custom hook to handle payments with ZezoPay SDK.
|
|
35
|
+
* Manages payment providers, summary items, and checkout process.
|
|
36
|
+
* @param params - Configuration for the ZezoPay client
|
|
37
|
+
* @returns Object with payment state and methods
|
|
38
|
+
*/
|
|
39
|
+
export const useZezoPay = ({
|
|
40
|
+
publicKey,
|
|
41
|
+
userInfo,
|
|
42
|
+
subscriptionId,
|
|
43
|
+
digitalProductId,
|
|
44
|
+
summaryItems: initialItems = [],
|
|
45
|
+
handlePayment,
|
|
46
|
+
callback,
|
|
47
|
+
}: UseZezoPayParams) => {
|
|
48
|
+
// Input validation
|
|
49
|
+
if (!publicKey) throw new Error('publicKey is required');
|
|
50
|
+
if (!userInfo?._id || !userInfo?.name)
|
|
51
|
+
throw new Error('userInfo must include _id and name');
|
|
52
|
+
|
|
53
|
+
// SDK client
|
|
54
|
+
const zezopay = useMemo(
|
|
55
|
+
() =>
|
|
56
|
+
new ZezoPayClient({
|
|
57
|
+
publicKey,
|
|
58
|
+
platform: Platform.OS === 'ios' ? 'ios' : 'android',
|
|
59
|
+
}),
|
|
60
|
+
[publicKey]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// States
|
|
64
|
+
const [selectedPayment, setSelectedPayment] =
|
|
65
|
+
useState<PaymentProvider | null>(null);
|
|
66
|
+
const [summaryItems, setSummaryItems] =
|
|
67
|
+
useState<ISummaryItem[]>(initialItems);
|
|
68
|
+
const [voucherCode, setVoucherCode] = useState('');
|
|
69
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
70
|
+
const [isSuccess, setIsSuccess] = useState(false);
|
|
71
|
+
const [error, setError] = useState('');
|
|
72
|
+
|
|
73
|
+
// Load payment providers
|
|
74
|
+
const {
|
|
75
|
+
result: providers,
|
|
76
|
+
loading,
|
|
77
|
+
error: providerError,
|
|
78
|
+
} = useAsync(async () => {
|
|
79
|
+
try {
|
|
80
|
+
const data = (await zezopay.payments.providers())?.data || [];
|
|
81
|
+
const filtered = data.filter((p: IReadyPaymentProvider) =>
|
|
82
|
+
allowedProviders.includes(p.provider as PaymentProvider)
|
|
83
|
+
);
|
|
84
|
+
if (!filtered.length) {
|
|
85
|
+
throw new Error('No supported payment providers available');
|
|
86
|
+
}
|
|
87
|
+
return filtered;
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.warn('Failed to load payment providers:', err);
|
|
90
|
+
throw new Error('Could not fetch payment providers');
|
|
91
|
+
}
|
|
92
|
+
}, [publicKey]);
|
|
93
|
+
|
|
94
|
+
// Auto-select first provider
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (providers?.length && !selectedPayment) {
|
|
97
|
+
setSelectedPayment(providers[0]?.provider as PaymentProvider);
|
|
98
|
+
}
|
|
99
|
+
}, [providers, selectedPayment]);
|
|
100
|
+
|
|
101
|
+
// Total price calculation
|
|
102
|
+
const totalPrice = useMemo(() => {
|
|
103
|
+
if (!summaryItems.length) return 0;
|
|
104
|
+
return Number(
|
|
105
|
+
summaryItems
|
|
106
|
+
.reduce((sum, item) => {
|
|
107
|
+
let price = item.price || 0;
|
|
108
|
+
if (item.discount) {
|
|
109
|
+
if (typeof item.discount === 'number') {
|
|
110
|
+
price = (price * (100 - item.discount)) / 100;
|
|
111
|
+
} else if (item.discount.type === 'percentage') {
|
|
112
|
+
price = (price * (100 - item.discount.amount)) / 100;
|
|
113
|
+
} else if (item.discount.type === 'fixed') {
|
|
114
|
+
price = Math.max(0, price - item.discount.amount); // Prevent negative prices
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return sum + price;
|
|
118
|
+
}, 0)
|
|
119
|
+
.toFixed(2)
|
|
120
|
+
);
|
|
121
|
+
}, [summaryItems]);
|
|
122
|
+
|
|
123
|
+
// Remove item from summary
|
|
124
|
+
const removeItem = useCallback((id: string | number) => {
|
|
125
|
+
setSummaryItems((prev) => prev.filter((item) => item.id !== id));
|
|
126
|
+
}, []);
|
|
127
|
+
|
|
128
|
+
// Default checkout handler
|
|
129
|
+
const defaultHandlePayment: HandlePayment = useCallback(
|
|
130
|
+
async ({
|
|
131
|
+
provider,
|
|
132
|
+
subscriptionId: subId,
|
|
133
|
+
digitalProductId: dpId,
|
|
134
|
+
userInfo: paymentUserInfo,
|
|
135
|
+
}) => {
|
|
136
|
+
if (!provider || !allowedProviders.includes(provider)) {
|
|
137
|
+
throw new Error(`Invalid payment provider: ${provider || 'none'}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Common payload properties
|
|
141
|
+
const basePayload: BaseCheckoutInput = {
|
|
142
|
+
provider,
|
|
143
|
+
userId: paymentUserInfo._id,
|
|
144
|
+
metadata: { userInfo: paymentUserInfo },
|
|
145
|
+
...(voucherCode && { coupon_code: voucherCode }), // Include coupon_code if provided
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Construct payload based on type
|
|
149
|
+
let payload: ICheckoutPayload;
|
|
150
|
+
|
|
151
|
+
if (subId) {
|
|
152
|
+
payload = {
|
|
153
|
+
...basePayload,
|
|
154
|
+
type: 'subscription',
|
|
155
|
+
subscriptionId: subId,
|
|
156
|
+
};
|
|
157
|
+
} else if (dpId) {
|
|
158
|
+
payload = {
|
|
159
|
+
...basePayload,
|
|
160
|
+
type: 'digital-product',
|
|
161
|
+
digitalProductId: dpId,
|
|
162
|
+
};
|
|
163
|
+
} else {
|
|
164
|
+
if (totalPrice <= 0) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
'Price must be greater than zero for normal payments'
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
payload = { ...basePayload, type: 'normal', price: totalPrice };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const result = await zezopay.payments.checkout(payload);
|
|
174
|
+
if (result.status !== 200) {
|
|
175
|
+
throw new Error(`Checkout failed with status: ${result.status}`);
|
|
176
|
+
}
|
|
177
|
+
return result.data;
|
|
178
|
+
} catch (err) {
|
|
179
|
+
console.warn('Checkout error:', err);
|
|
180
|
+
throw new Error('Failed to initiate checkout');
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
[voucherCode, totalPrice, zezopay.payments]
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const paymentFn = handlePayment || defaultHandlePayment;
|
|
187
|
+
|
|
188
|
+
// Payment handler
|
|
189
|
+
const paymentHandler = useCallback(
|
|
190
|
+
async (order: ICheckoutResult) => {
|
|
191
|
+
if (!selectedPayment) {
|
|
192
|
+
throw new Error('No payment provider selected');
|
|
193
|
+
}
|
|
194
|
+
if (!order?.orderId || !order?.price || !order?.currency) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
'Invalid order details: missing orderId, price, or currency'
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
if (selectedPayment === 'razorpay') {
|
|
202
|
+
const razorpay = new Razorpay();
|
|
203
|
+
await razorpay.open({
|
|
204
|
+
amount: order.price * 100,
|
|
205
|
+
currency: order.currency,
|
|
206
|
+
order_id: order.orderId,
|
|
207
|
+
publicKey: order.publicKey || publicKey, // Fallback to publicKey
|
|
208
|
+
handler(response) {
|
|
209
|
+
if (response.razorpay_payment_id) {
|
|
210
|
+
callback?.onSuccess?.({
|
|
211
|
+
status: 'success',
|
|
212
|
+
order_id: response.razorpay_order_id,
|
|
213
|
+
signature: response.razorpay_signature,
|
|
214
|
+
payment_id: response.razorpay_payment_id,
|
|
215
|
+
});
|
|
216
|
+
setIsSuccess(true);
|
|
217
|
+
setError('');
|
|
218
|
+
} else {
|
|
219
|
+
const err = new Error('Payment failed: No payment ID received');
|
|
220
|
+
setError(err.message);
|
|
221
|
+
callback?.onFailure?.(err);
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
OnError(err) {
|
|
225
|
+
const errorMessage =
|
|
226
|
+
err?.description ||
|
|
227
|
+
err?.message ||
|
|
228
|
+
err?.details?.error?.description ||
|
|
229
|
+
'Payment failed unexpectedly';
|
|
230
|
+
|
|
231
|
+
setError(errorMessage);
|
|
232
|
+
callback?.onError?.(err);
|
|
233
|
+
callback?.onFailure?.(err);
|
|
234
|
+
},
|
|
235
|
+
prefill: {
|
|
236
|
+
user_email: userInfo.email,
|
|
237
|
+
user_name: userInfo.name,
|
|
238
|
+
user_phone: userInfo.phone,
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
} else {
|
|
242
|
+
throw new Error(`Unsupported payment provider: ${selectedPayment}`);
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.warn('Payment handler error:', err);
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
[selectedPayment, userInfo, callback, publicKey]
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// Handle pay now
|
|
253
|
+
const handlePayNow = useCallback(async () => {
|
|
254
|
+
if (isProcessing) return; // Prevent multiple submissions
|
|
255
|
+
if (!selectedPayment) {
|
|
256
|
+
setError('Please select a payment provider');
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (!summaryItems.length) {
|
|
260
|
+
setError('Cart is empty. Add items to proceed.');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (totalPrice <= 0) {
|
|
264
|
+
setError('Total price must be greater than zero');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
setError('');
|
|
269
|
+
setIsProcessing(true); // ✅ Set processing to true at the start
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const order = await paymentFn({
|
|
273
|
+
provider: selectedPayment,
|
|
274
|
+
subscriptionId,
|
|
275
|
+
digitalProductId,
|
|
276
|
+
userInfo,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
if (!order) {
|
|
280
|
+
const err = new Error('No order data returned from checkout');
|
|
281
|
+
setError(err.message);
|
|
282
|
+
callback?.onFailure?.(err);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
await paymentHandler(order);
|
|
287
|
+
} catch (err: any) {
|
|
288
|
+
const errorMessage = err.message || 'Payment failed unexpectedly';
|
|
289
|
+
setError(errorMessage);
|
|
290
|
+
callback?.onFailure?.(err);
|
|
291
|
+
console.warn('Payment error:', err);
|
|
292
|
+
} finally {
|
|
293
|
+
setIsProcessing(false); // ✅ Reset processing state
|
|
294
|
+
}
|
|
295
|
+
}, [
|
|
296
|
+
isProcessing,
|
|
297
|
+
selectedPayment,
|
|
298
|
+
summaryItems,
|
|
299
|
+
totalPrice,
|
|
300
|
+
paymentFn,
|
|
301
|
+
subscriptionId,
|
|
302
|
+
digitalProductId,
|
|
303
|
+
userInfo,
|
|
304
|
+
paymentHandler,
|
|
305
|
+
callback,
|
|
306
|
+
]);
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
selectedPayment,
|
|
310
|
+
setSelectedPayment,
|
|
311
|
+
summaryItems,
|
|
312
|
+
setSummaryItems,
|
|
313
|
+
totalPrice,
|
|
314
|
+
voucherCode,
|
|
315
|
+
setVoucherCode,
|
|
316
|
+
isProcessing,
|
|
317
|
+
isSuccess,
|
|
318
|
+
error,
|
|
319
|
+
providers: providers || [],
|
|
320
|
+
loading,
|
|
321
|
+
providerError,
|
|
322
|
+
removeItem,
|
|
323
|
+
handlePayNow,
|
|
324
|
+
};
|
|
325
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function formatCurrency({
|
|
2
|
+
amount,
|
|
3
|
+
currency = 'INR',
|
|
4
|
+
minimumFractionDigits = 2,
|
|
5
|
+
}: {
|
|
6
|
+
amount: number;
|
|
7
|
+
currency?: string;
|
|
8
|
+
minimumFractionDigits?: number;
|
|
9
|
+
}): string {
|
|
10
|
+
return new Intl.NumberFormat('en-IN', {
|
|
11
|
+
style: 'currency',
|
|
12
|
+
currency,
|
|
13
|
+
minimumFractionDigits,
|
|
14
|
+
maximumFractionDigits: 2,
|
|
15
|
+
}).format(amount);
|
|
16
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ZezoPay';
|