@tagadapay/plugin-sdk 1.0.2

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 (63) hide show
  1. package/README.md +475 -0
  2. package/dist/data/currencies.json +2410 -0
  3. package/dist/index.d.ts +33 -0
  4. package/dist/index.js +37 -0
  5. package/dist/react/components/DebugDrawer.d.ts +7 -0
  6. package/dist/react/components/DebugDrawer.js +368 -0
  7. package/dist/react/components/OffersDemo.d.ts +1 -0
  8. package/dist/react/components/OffersDemo.js +50 -0
  9. package/dist/react/components/index.d.ts +1 -0
  10. package/dist/react/components/index.js +1 -0
  11. package/dist/react/config/environment.d.ts +22 -0
  12. package/dist/react/config/environment.js +132 -0
  13. package/dist/react/config/payment.d.ts +23 -0
  14. package/dist/react/config/payment.js +52 -0
  15. package/dist/react/hooks/useAuth.d.ts +4 -0
  16. package/dist/react/hooks/useAuth.js +12 -0
  17. package/dist/react/hooks/useCheckout.d.ts +262 -0
  18. package/dist/react/hooks/useCheckout.js +325 -0
  19. package/dist/react/hooks/useCurrency.d.ts +4 -0
  20. package/dist/react/hooks/useCurrency.js +640 -0
  21. package/dist/react/hooks/useCustomer.d.ts +7 -0
  22. package/dist/react/hooks/useCustomer.js +14 -0
  23. package/dist/react/hooks/useEnvironment.d.ts +7 -0
  24. package/dist/react/hooks/useEnvironment.js +18 -0
  25. package/dist/react/hooks/useLocale.d.ts +2 -0
  26. package/dist/react/hooks/useLocale.js +43 -0
  27. package/dist/react/hooks/useOffers.d.ts +99 -0
  28. package/dist/react/hooks/useOffers.js +115 -0
  29. package/dist/react/hooks/useOrder.d.ts +44 -0
  30. package/dist/react/hooks/useOrder.js +77 -0
  31. package/dist/react/hooks/usePayment.d.ts +60 -0
  32. package/dist/react/hooks/usePayment.js +343 -0
  33. package/dist/react/hooks/usePaymentPolling.d.ts +45 -0
  34. package/dist/react/hooks/usePaymentPolling.js +146 -0
  35. package/dist/react/hooks/useProducts.d.ts +95 -0
  36. package/dist/react/hooks/useProducts.js +120 -0
  37. package/dist/react/hooks/useSession.d.ts +10 -0
  38. package/dist/react/hooks/useSession.js +17 -0
  39. package/dist/react/hooks/useThreeds.d.ts +38 -0
  40. package/dist/react/hooks/useThreeds.js +162 -0
  41. package/dist/react/hooks/useThreedsModal.d.ts +16 -0
  42. package/dist/react/hooks/useThreedsModal.js +328 -0
  43. package/dist/react/index.d.ts +26 -0
  44. package/dist/react/index.js +27 -0
  45. package/dist/react/providers/TagadaProvider.d.ts +55 -0
  46. package/dist/react/providers/TagadaProvider.js +471 -0
  47. package/dist/react/services/apiService.d.ts +149 -0
  48. package/dist/react/services/apiService.js +168 -0
  49. package/dist/react/types.d.ts +151 -0
  50. package/dist/react/types.js +4 -0
  51. package/dist/react/utils/__tests__/urlUtils.test.d.ts +1 -0
  52. package/dist/react/utils/__tests__/urlUtils.test.js +189 -0
  53. package/dist/react/utils/deviceInfo.d.ts +39 -0
  54. package/dist/react/utils/deviceInfo.js +163 -0
  55. package/dist/react/utils/jwtDecoder.d.ts +14 -0
  56. package/dist/react/utils/jwtDecoder.js +86 -0
  57. package/dist/react/utils/money.d.ts +2273 -0
  58. package/dist/react/utils/money.js +104 -0
  59. package/dist/react/utils/tokenStorage.d.ts +16 -0
  60. package/dist/react/utils/tokenStorage.js +52 -0
  61. package/dist/react/utils/urlUtils.d.ts +239 -0
  62. package/dist/react/utils/urlUtils.js +449 -0
  63. package/package.json +64 -0
@@ -0,0 +1,325 @@
1
+ import { useState, useCallback, useEffect, useRef } from 'react';
2
+ import { useTagadaContext } from '../providers/TagadaProvider';
3
+ import { getCheckoutToken } from '../utils/urlUtils';
4
+ import { useCurrency } from '../hooks/useCurrency';
5
+ export function useCheckout(options = {}) {
6
+ const { apiService } = useTagadaContext();
7
+ const { code: currentCurrency } = useCurrency();
8
+ const [checkout, setCheckout] = useState(null);
9
+ const [isLoading, setIsLoading] = useState(false);
10
+ const [error, setError] = useState(null);
11
+ const [isInitialized, setIsInitialized] = useState(false);
12
+ const { autoRefresh = false, refreshInterval = 30000, autoInitFromUrl = false, checkoutToken: providedToken, fallbackToken, autoLoadFromToken = true, } = options;
13
+ const refreshTimeoutRef = useRef(null);
14
+ const currentCheckoutTokenRef = useRef(null);
15
+ const hasAutoLoadedRef = useRef(false);
16
+ // Clear refresh timeout on unmount
17
+ useEffect(() => {
18
+ return () => {
19
+ if (refreshTimeoutRef.current) {
20
+ clearTimeout(refreshTimeoutRef.current);
21
+ }
22
+ };
23
+ }, []);
24
+ const init = useCallback(async (params) => {
25
+ // Don't allow init if we already have a checkout token
26
+ if (providedToken) {
27
+ console.warn('[Checkout] Cannot init - checkoutToken already provided:', providedToken.substring(0, 8) + '...');
28
+ throw new Error('Cannot initialize new checkout when checkoutToken is already provided. The existing checkout will be auto-loaded.');
29
+ }
30
+ setIsLoading(true);
31
+ setError(null);
32
+ try {
33
+ const requestBody = {
34
+ ...params,
35
+ returnUrl: params.returnUrl || window.location.origin,
36
+ customer: {
37
+ ...params.customer,
38
+ currency: params.customer?.currency || currentCurrency,
39
+ },
40
+ };
41
+ const response = await apiService.fetch('/api/v1/checkout/session/init', {
42
+ method: 'POST',
43
+ body: requestBody,
44
+ });
45
+ // Get the full checkout data
46
+ const checkoutData = await getCheckout(response.checkoutToken);
47
+ setCheckout(checkoutData);
48
+ setIsInitialized(true);
49
+ // Update URL with checkout token
50
+ if (typeof window !== 'undefined' && response.checkoutToken) {
51
+ const currentUrl = new URL(window.location.href);
52
+ if (!currentUrl.searchParams.has('checkoutToken')) {
53
+ const newUrl = new URL(window.location.href);
54
+ newUrl.searchParams.set('checkoutToken', response.checkoutToken);
55
+ window.history.replaceState(null, '', newUrl.toString());
56
+ }
57
+ }
58
+ return {
59
+ checkoutUrl: response.checkoutUrl,
60
+ checkoutSession: checkoutData.checkoutSession,
61
+ checkoutToken: response.checkoutToken,
62
+ };
63
+ }
64
+ catch (err) {
65
+ const error = err instanceof Error ? err : new Error('Failed to initialize checkout');
66
+ setError(error);
67
+ throw error;
68
+ }
69
+ finally {
70
+ setIsLoading(false);
71
+ }
72
+ }, [apiService, currentCurrency, providedToken]);
73
+ const getCheckout = useCallback(async (checkoutToken) => {
74
+ setIsLoading(true);
75
+ setError(null);
76
+ try {
77
+ const queryParams = new URLSearchParams();
78
+ if (currentCurrency) {
79
+ queryParams.set('currency', currentCurrency);
80
+ }
81
+ const url = `/api/v1/checkout-sessions/${checkoutToken}/v2${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
82
+ const response = await apiService.fetch(url, {
83
+ method: 'GET',
84
+ });
85
+ setCheckout(response);
86
+ currentCheckoutTokenRef.current = checkoutToken;
87
+ setIsInitialized(true);
88
+ return response;
89
+ }
90
+ catch (err) {
91
+ const error = err instanceof Error ? err : new Error('Failed to get checkout session');
92
+ setError(error);
93
+ throw error;
94
+ }
95
+ finally {
96
+ setIsLoading(false);
97
+ }
98
+ }, [apiService, currentCurrency]);
99
+ const refresh = useCallback(async () => {
100
+ if (!currentCheckoutTokenRef.current) {
101
+ throw new Error('No checkout session to refresh');
102
+ }
103
+ await getCheckout(currentCheckoutTokenRef.current);
104
+ }, [getCheckout]);
105
+ const updateAddress = useCallback(async (data) => {
106
+ if (!checkout?.checkoutSession.id) {
107
+ throw new Error('No checkout session available');
108
+ }
109
+ try {
110
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/address`, {
111
+ method: 'POST',
112
+ body: { data },
113
+ });
114
+ await refresh();
115
+ return response;
116
+ }
117
+ catch (err) {
118
+ const error = err instanceof Error ? err : new Error('Failed to update address');
119
+ throw error;
120
+ }
121
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
122
+ const setCheckoutInfo = useCallback(async (data) => {
123
+ if (!checkout?.checkoutSession.id) {
124
+ throw new Error('No checkout session available');
125
+ }
126
+ try {
127
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/checkout-info`, {
128
+ method: 'POST',
129
+ body: {
130
+ shippingAddress: data.shippingAddress,
131
+ billingAddress: data.differentBillingAddress ? data.billingAddress : data.shippingAddress,
132
+ differentBillingAddress: data.differentBillingAddress || false,
133
+ },
134
+ });
135
+ if (response.success) {
136
+ await refresh();
137
+ }
138
+ return response;
139
+ }
140
+ catch (err) {
141
+ const error = err instanceof Error ? err : new Error('Failed to set checkout info');
142
+ throw error;
143
+ }
144
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
145
+ const applyPromotionCode = useCallback(async (code) => {
146
+ if (!checkout?.checkoutSession.id) {
147
+ throw new Error('No checkout session available');
148
+ }
149
+ try {
150
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/promotions/apply`, {
151
+ method: 'POST',
152
+ body: { code },
153
+ });
154
+ if (response.success) {
155
+ await refresh();
156
+ }
157
+ return response;
158
+ }
159
+ catch (err) {
160
+ const error = err instanceof Error ? err : new Error('Failed to apply promotion code');
161
+ throw error;
162
+ }
163
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
164
+ const removePromotion = useCallback(async (promotionId) => {
165
+ if (!checkout?.checkoutSession.id) {
166
+ throw new Error('No checkout session available');
167
+ }
168
+ try {
169
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/promotions/${promotionId}`, {
170
+ method: 'DELETE',
171
+ });
172
+ if (response.success) {
173
+ await refresh();
174
+ }
175
+ return response;
176
+ }
177
+ catch (err) {
178
+ const error = err instanceof Error ? err : new Error('Failed to remove promotion');
179
+ throw error;
180
+ }
181
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
182
+ const getAppliedPromotions = useCallback(async () => {
183
+ if (!checkout?.checkoutSession.id) {
184
+ throw new Error('No checkout session available');
185
+ }
186
+ try {
187
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/promotions`, {
188
+ method: 'GET',
189
+ });
190
+ return response;
191
+ }
192
+ catch (err) {
193
+ const error = err instanceof Error ? err : new Error('Failed to get applied promotions');
194
+ throw error;
195
+ }
196
+ }, [apiService, checkout?.checkoutSession.id]);
197
+ const updateLineItems = useCallback(async (lineItems) => {
198
+ if (!checkout?.checkoutSession.id) {
199
+ throw new Error('No checkout session available');
200
+ }
201
+ try {
202
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/line-items`, {
203
+ method: 'POST',
204
+ body: { lineItems },
205
+ });
206
+ if (response.success) {
207
+ await refresh();
208
+ }
209
+ return response;
210
+ }
211
+ catch (err) {
212
+ const error = err instanceof Error ? err : new Error('Failed to update line items');
213
+ throw error;
214
+ }
215
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
216
+ const toggleOrderBump = useCallback(async (orderBumpOfferId, selected) => {
217
+ if (!checkout?.checkoutSession.id) {
218
+ throw new Error('No checkout session available');
219
+ }
220
+ try {
221
+ const response = await apiService.fetch(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/toggle-order-bump`, {
222
+ method: 'POST',
223
+ body: { orderBumpOfferId, selected },
224
+ });
225
+ if (response.success) {
226
+ await refresh();
227
+ }
228
+ return response;
229
+ }
230
+ catch (err) {
231
+ const error = err instanceof Error ? err : new Error('Failed to toggle order bump');
232
+ throw error;
233
+ }
234
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
235
+ const updateCustomer = useCallback(async (data) => {
236
+ if (!checkout?.checkoutSession.customerId) {
237
+ throw new Error('No customer ID available');
238
+ }
239
+ try {
240
+ const response = await apiService.updateCustomer(checkout.checkoutSession.customerId, data);
241
+ await refresh();
242
+ return { success: true };
243
+ }
244
+ catch (err) {
245
+ const error = err instanceof Error ? err : new Error('Failed to update customer');
246
+ console.error('Customer update failed:', error);
247
+ return { success: false, error: error.message };
248
+ }
249
+ }, [apiService, checkout?.checkoutSession.customerId, refresh]);
250
+ const updateCustomerAndSessionInfo = useCallback(async (data) => {
251
+ if (!checkout?.checkoutSession.id) {
252
+ throw new Error('No checkout session available');
253
+ }
254
+ try {
255
+ const response = await apiService.updateCustomerAndSessionInfo(checkout.checkoutSession.id, {
256
+ customerData: data.customerData,
257
+ shippingAddress: data.shippingAddress,
258
+ billingAddress: data.differentBillingAddress ? data.billingAddress : data.shippingAddress,
259
+ differentBillingAddress: data.differentBillingAddress || false,
260
+ });
261
+ if (response.success) {
262
+ await refresh();
263
+ }
264
+ return response;
265
+ }
266
+ catch (err) {
267
+ const error = err instanceof Error ? err : new Error('Failed to update customer and session info');
268
+ throw error;
269
+ }
270
+ }, [apiService, checkout?.checkoutSession.id, refresh]);
271
+ const clear = useCallback(() => {
272
+ setCheckout(null);
273
+ setError(null);
274
+ setIsInitialized(false);
275
+ currentCheckoutTokenRef.current = null;
276
+ hasAutoLoadedRef.current = false;
277
+ if (refreshTimeoutRef.current) {
278
+ clearTimeout(refreshTimeoutRef.current);
279
+ }
280
+ }, []);
281
+ // Auto-load existing checkout session from provided token or URL token
282
+ useEffect(() => {
283
+ if (!autoLoadFromToken || hasAutoLoadedRef.current || isInitialized) {
284
+ return;
285
+ }
286
+ // Prioritize provided checkoutToken, then URL token, then fallback
287
+ const token = providedToken ||
288
+ getCheckoutToken({
289
+ currentUrl: true,
290
+ fallbackToken,
291
+ });
292
+ if (token && !isLoading) {
293
+ console.debug('[Checkout] Auto-loading from token:', {
294
+ source: providedToken ? 'provided' : 'url/fallback',
295
+ tokenPreview: token.substring(0, 8) + '...',
296
+ });
297
+ hasAutoLoadedRef.current = true;
298
+ getCheckout(token).catch((err) => {
299
+ console.error('Auto-load failed:', err);
300
+ setError(err instanceof Error ? err : new Error('Auto-load failed'));
301
+ hasAutoLoadedRef.current = false; // Reset to allow retry
302
+ });
303
+ }
304
+ }, [autoLoadFromToken, providedToken, fallbackToken, isInitialized, isLoading, getCheckout]);
305
+ return {
306
+ checkout,
307
+ isLoading,
308
+ error,
309
+ isInitialized,
310
+ initialized: !!checkout?.checkoutSession,
311
+ init,
312
+ getCheckout,
313
+ refresh,
314
+ updateAddress,
315
+ setCheckoutInfo,
316
+ applyPromotionCode,
317
+ removePromotion,
318
+ getAppliedPromotions,
319
+ updateLineItems,
320
+ toggleOrderBump,
321
+ updateCustomer,
322
+ updateCustomerAndSessionInfo,
323
+ clear,
324
+ };
325
+ }
@@ -0,0 +1,4 @@
1
+ import { Currency } from '../types';
2
+ export declare function useCurrency(): Currency & {
3
+ format: (amount: number) => string;
4
+ };