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