@tagadapay/plugin-sdk 2.4.38 → 2.5.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 (112) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +2 -0
  3. package/dist/react/hooks/useCheckout.js +21 -33
  4. package/dist/react/hooks/useCheckoutSession.d.ts +19 -0
  5. package/dist/react/hooks/useCheckoutSession.js +108 -0
  6. package/dist/react/hooks/useCheckoutToken.d.ts +17 -0
  7. package/dist/react/hooks/useCheckoutToken.js +80 -0
  8. package/dist/react/hooks/useOrderBump.js +94 -29
  9. package/dist/react/hooks/useOrderBumpV2.d.ts +17 -0
  10. package/dist/react/hooks/useOrderBumpV2.js +95 -0
  11. package/dist/react/hooks/useOrderBumpV3.d.ts +23 -0
  12. package/dist/react/hooks/useOrderBumpV3.js +109 -0
  13. package/dist/react/hooks/usePayment.d.ts +1 -1
  14. package/dist/react/hooks/usePayment.js +2 -18
  15. package/dist/react/hooks/usePluginConfig.js +2 -13
  16. package/dist/react/hooks/usePostPurchases.js +11 -5
  17. package/dist/react/hooks/useProducts.js +2 -16
  18. package/dist/react/index.d.ts +9 -1
  19. package/dist/react/index.js +5 -1
  20. package/dist/react/providers/TagadaProvider.d.ts +0 -1
  21. package/dist/react/providers/TagadaProvider.js +16 -12
  22. package/dist/react/services/apiService.d.ts +1 -0
  23. package/dist/react/services/apiService.js +3 -0
  24. package/dist/v2/core/googleAutocomplete.d.ts +65 -0
  25. package/dist/v2/core/googleAutocomplete.js +94 -0
  26. package/dist/v2/core/index.d.ts +8 -0
  27. package/dist/v2/core/index.js +11 -0
  28. package/dist/v2/core/isoData.d.ts +50 -0
  29. package/dist/v2/core/isoData.js +103 -0
  30. package/dist/v2/core/resources/apiClient.d.ts +25 -0
  31. package/dist/v2/core/resources/apiClient.js +95 -0
  32. package/dist/v2/core/resources/checkout.d.ts +189 -0
  33. package/dist/v2/core/resources/checkout.js +119 -0
  34. package/dist/v2/core/resources/index.d.ts +13 -0
  35. package/dist/v2/core/resources/index.js +13 -0
  36. package/dist/v2/core/resources/offers.d.ts +98 -0
  37. package/dist/v2/core/resources/offers.js +115 -0
  38. package/dist/v2/core/resources/orders.d.ts +40 -0
  39. package/dist/v2/core/resources/orders.js +59 -0
  40. package/dist/v2/core/resources/payments.d.ts +140 -0
  41. package/dist/v2/core/resources/payments.js +126 -0
  42. package/dist/v2/core/resources/postPurchases.d.ts +182 -0
  43. package/dist/v2/core/resources/postPurchases.js +116 -0
  44. package/dist/v2/core/resources/products.d.ts +29 -0
  45. package/dist/v2/core/resources/products.js +49 -0
  46. package/dist/v2/core/resources/promotions.d.ts +45 -0
  47. package/dist/v2/core/resources/promotions.js +87 -0
  48. package/dist/v2/core/resources/threeds.d.ts +23 -0
  49. package/dist/v2/core/resources/threeds.js +15 -0
  50. package/dist/v2/core/utils/checkout.d.ts +24 -0
  51. package/dist/v2/core/utils/checkout.js +30 -0
  52. package/dist/v2/core/utils/currency.d.ts +28 -0
  53. package/dist/v2/core/utils/currency.js +272 -0
  54. package/dist/v2/core/utils/index.d.ts +12 -0
  55. package/dist/v2/core/utils/index.js +12 -0
  56. package/dist/v2/core/utils/order.d.ts +159 -0
  57. package/dist/v2/core/utils/order.js +42 -0
  58. package/dist/v2/core/utils/orderBump.d.ts +40 -0
  59. package/dist/v2/core/utils/orderBump.js +47 -0
  60. package/dist/v2/core/utils/pluginConfig.d.ts +43 -0
  61. package/dist/v2/core/utils/pluginConfig.js +155 -0
  62. package/dist/v2/core/utils/postPurchases.d.ts +32 -0
  63. package/dist/v2/core/utils/postPurchases.js +42 -0
  64. package/dist/v2/core/utils/products.d.ts +58 -0
  65. package/dist/v2/core/utils/products.js +64 -0
  66. package/dist/v2/core/utils/promotions.d.ts +24 -0
  67. package/dist/v2/core/utils/promotions.js +30 -0
  68. package/dist/v2/index.d.ts +19 -0
  69. package/dist/v2/index.js +15 -0
  70. package/dist/v2/react/components/DebugDrawer.d.ts +7 -0
  71. package/dist/v2/react/components/DebugDrawer.js +383 -0
  72. package/dist/v2/react/hooks/useApiQuery.d.ts +28 -0
  73. package/dist/v2/react/hooks/useApiQuery.js +84 -0
  74. package/dist/v2/react/hooks/useCheckoutQuery.d.ts +39 -0
  75. package/dist/v2/react/hooks/useCheckoutQuery.js +208 -0
  76. package/dist/v2/react/hooks/useCheckoutToken.d.ts +17 -0
  77. package/dist/v2/react/hooks/useCheckoutToken.js +80 -0
  78. package/dist/v2/react/hooks/useCurrency.d.ts +9 -0
  79. package/dist/v2/react/hooks/useCurrency.js +21 -0
  80. package/dist/v2/react/hooks/useGeoLocation.d.ts +138 -0
  81. package/dist/v2/react/hooks/useGeoLocation.js +126 -0
  82. package/dist/v2/react/hooks/useGoogleAutocomplete.d.ts +74 -0
  83. package/dist/v2/react/hooks/useGoogleAutocomplete.js +207 -0
  84. package/dist/v2/react/hooks/useISOData.d.ts +61 -0
  85. package/dist/v2/react/hooks/useISOData.js +176 -0
  86. package/dist/v2/react/hooks/useOffersQuery.d.ts +65 -0
  87. package/dist/v2/react/hooks/useOffersQuery.js +353 -0
  88. package/dist/v2/react/hooks/useOrderBumpQuery.d.ts +20 -0
  89. package/dist/v2/react/hooks/useOrderBumpQuery.js +88 -0
  90. package/dist/v2/react/hooks/useOrderQuery.d.ts +29 -0
  91. package/dist/v2/react/hooks/useOrderQuery.js +98 -0
  92. package/dist/v2/react/hooks/usePaymentPolling.d.ts +45 -0
  93. package/dist/v2/react/hooks/usePaymentPolling.js +153 -0
  94. package/dist/v2/react/hooks/usePaymentQuery.d.ts +19 -0
  95. package/dist/v2/react/hooks/usePaymentQuery.js +283 -0
  96. package/dist/v2/react/hooks/usePluginConfig.d.ts +16 -0
  97. package/dist/v2/react/hooks/usePluginConfig.js +36 -0
  98. package/dist/v2/react/hooks/usePostPurchasesQuery.d.ts +63 -0
  99. package/dist/v2/react/hooks/usePostPurchasesQuery.js +365 -0
  100. package/dist/v2/react/hooks/useProductsQuery.d.ts +31 -0
  101. package/dist/v2/react/hooks/useProductsQuery.js +102 -0
  102. package/dist/v2/react/hooks/usePromotionsQuery.d.ts +28 -0
  103. package/dist/v2/react/hooks/usePromotionsQuery.js +97 -0
  104. package/dist/v2/react/hooks/useThreeds.d.ts +36 -0
  105. package/dist/v2/react/hooks/useThreeds.js +166 -0
  106. package/dist/v2/react/hooks/useThreedsModal.d.ts +13 -0
  107. package/dist/v2/react/hooks/useThreedsModal.js +343 -0
  108. package/dist/v2/react/index.d.ts +38 -0
  109. package/dist/v2/react/index.js +27 -0
  110. package/dist/v2/react/providers/TagadaProvider.d.ts +63 -0
  111. package/dist/v2/react/providers/TagadaProvider.js +680 -0
  112. package/package.json +10 -3
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Offers Hook using TanStack Query
3
+ * Handles offers with automatic caching
4
+ */
5
+ import { useMemo, useCallback, useState } from 'react';
6
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
7
+ import { OffersResource } from '../../core/resources/offers';
8
+ import { useTagadaContext } from '../providers/TagadaProvider';
9
+ import { usePluginConfig } from './usePluginConfig';
10
+ import { getGlobalApiClient } from './useApiQuery';
11
+ export function useOffersQuery(options = {}) {
12
+ const { offerIds = [], enabled = true, returnUrl } = options;
13
+ const { storeId } = usePluginConfig();
14
+ const { isSessionInitialized, session } = useTagadaContext();
15
+ const _queryClient = useQueryClient();
16
+ // State for checkout sessions per offer (similar to postPurchases)
17
+ const [checkoutSessions, setCheckoutSessions] = useState({});
18
+ // Create offers resource client
19
+ const offersResource = useMemo(() => {
20
+ try {
21
+ return new OffersResource(getGlobalApiClient());
22
+ }
23
+ catch (error) {
24
+ throw new Error('Failed to initialize offers resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
25
+ }
26
+ }, []);
27
+ // Helper function for fetching order summary (similar to postPurchases)
28
+ const fetchOrderSummary = useCallback(async (offerId, sessionId) => {
29
+ try {
30
+ // Set updating state
31
+ setCheckoutSessions(prev => ({
32
+ ...prev,
33
+ [offerId]: {
34
+ ...prev[offerId],
35
+ isUpdatingSummary: true,
36
+ }
37
+ }));
38
+ const summaryResult = await offersResource.getOrderSummary(sessionId, true);
39
+ if (summaryResult) {
40
+ // Sort items by productId to ensure consistent order
41
+ const sortedItems = [...summaryResult.items].sort((a, b) => a.productId.localeCompare(b.productId));
42
+ const orderSummary = {
43
+ ...summaryResult,
44
+ items: sortedItems,
45
+ };
46
+ // Initialize selected variants based on the summary
47
+ const initialVariants = {};
48
+ sortedItems.forEach((item) => {
49
+ if (item.productId && item.variantId) {
50
+ initialVariants[item.productId] = item.variantId;
51
+ }
52
+ });
53
+ setCheckoutSessions(prev => ({
54
+ ...prev,
55
+ [offerId]: {
56
+ ...prev[offerId],
57
+ orderSummary,
58
+ selectedVariants: initialVariants,
59
+ isUpdatingSummary: false,
60
+ }
61
+ }));
62
+ }
63
+ }
64
+ catch (error) {
65
+ console.error(`[SDK] Failed to fetch order summary for offer ${offerId}:`, error);
66
+ setCheckoutSessions(prev => ({
67
+ ...prev,
68
+ [offerId]: {
69
+ ...prev[offerId],
70
+ isUpdatingSummary: false,
71
+ }
72
+ }));
73
+ throw error;
74
+ }
75
+ }, [offersResource]);
76
+ // Create query key based on options
77
+ const queryKey = useMemo(() => ['offers', { storeId, offerIds }], [storeId, offerIds]);
78
+ // Use TanStack Query for fetching offers
79
+ const { data: offers = [], isLoading, error, refetch } = useQuery({
80
+ queryKey,
81
+ enabled: enabled && !!storeId && isSessionInitialized,
82
+ queryFn: async () => {
83
+ if (!storeId) {
84
+ throw new Error('Store ID not found. Make sure the TagadaProvider is properly configured.');
85
+ }
86
+ if (offerIds.length > 0) {
87
+ // Fetch specific offers by IDs
88
+ return await offersResource.getOffersByIds(storeId, offerIds);
89
+ }
90
+ else {
91
+ // Fetch all offers for the store
92
+ return await offersResource.getOffers(storeId);
93
+ }
94
+ },
95
+ staleTime: 5 * 60 * 1000, // 5 minutes
96
+ refetchOnWindowFocus: false,
97
+ });
98
+ // Mutations for offer actions
99
+ const createCheckoutSessionMutation = useMutation({
100
+ mutationFn: async ({ offerId, returnUrl }) => {
101
+ return await offersResource.createCheckoutSession(offerId, returnUrl);
102
+ },
103
+ onError: (error) => {
104
+ console.error('Failed to create checkout session:', error);
105
+ },
106
+ });
107
+ const payOfferMutation = useMutation({
108
+ mutationFn: async ({ offerId, orderId }) => {
109
+ return await offersResource.payOffer(offerId, orderId);
110
+ },
111
+ onError: (error) => {
112
+ console.error('Failed to pay offer:', error);
113
+ },
114
+ });
115
+ const transformToCheckoutMutation = useMutation({
116
+ mutationFn: async ({ offerId, returnUrl }) => {
117
+ return await offersResource.transformToCheckout(offerId, returnUrl);
118
+ },
119
+ onError: (error) => {
120
+ console.error('Failed to transform offer to checkout:', error);
121
+ },
122
+ });
123
+ const payWithCheckoutSessionMutation = useMutation({
124
+ mutationFn: async ({ checkoutSessionId, orderId }) => {
125
+ return await offersResource.payWithCheckoutSession(checkoutSessionId, orderId);
126
+ },
127
+ onError: (error) => {
128
+ console.error('Failed to pay with checkout session:', error);
129
+ },
130
+ });
131
+ const initCheckoutSessionMutation = useMutation({
132
+ mutationFn: async ({ offerId, orderId, customerId }) => {
133
+ return await offersResource.initCheckoutSession(offerId, orderId, customerId);
134
+ },
135
+ onError: (error) => {
136
+ console.error('Failed to initialize checkout session:', error);
137
+ },
138
+ });
139
+ // Helper functions (matching V1 useOffers exactly)
140
+ const getOffer = useCallback((offerId) => {
141
+ return offers.find((offer) => offer.id === offerId);
142
+ }, [offers]);
143
+ const getTotalValue = useCallback(() => {
144
+ return offers.reduce((total, offer) => {
145
+ const firstSummary = offer.summaries[0];
146
+ return total + (firstSummary?.totalAdjustedAmount || 0);
147
+ }, 0);
148
+ }, [offers]);
149
+ const getTotalSavings = useCallback(() => {
150
+ return offers.reduce((total, offer) => {
151
+ const firstSummary = offer.summaries[0];
152
+ return total + (firstSummary?.totalPromotionAmount || 0);
153
+ }, 0);
154
+ }, [offers]);
155
+ // Action functions
156
+ const createCheckoutSession = useCallback(async (offerId, options) => {
157
+ return await createCheckoutSessionMutation.mutateAsync({
158
+ offerId,
159
+ returnUrl: options?.returnUrl || returnUrl,
160
+ });
161
+ }, [createCheckoutSessionMutation, returnUrl]);
162
+ const payOffer = useCallback(async (offerId, orderId) => {
163
+ return await payOfferMutation.mutateAsync({ offerId, orderId });
164
+ }, [payOfferMutation]);
165
+ const transformToCheckout = useCallback(async (offerId, options) => {
166
+ return await transformToCheckoutMutation.mutateAsync({
167
+ offerId,
168
+ returnUrl: options?.returnUrl || returnUrl,
169
+ });
170
+ }, [transformToCheckoutMutation, returnUrl]);
171
+ const payWithCheckoutSession = useCallback(async (checkoutSessionId, orderId) => {
172
+ return await payWithCheckoutSessionMutation.mutateAsync({ checkoutSessionId, orderId });
173
+ }, [payWithCheckoutSessionMutation]);
174
+ const initCheckoutSession = useCallback(async (offerId, orderId, customerId) => {
175
+ // Use customerId from session context if not provided
176
+ const effectiveCustomerId = customerId || session?.customerId;
177
+ if (!effectiveCustomerId) {
178
+ throw new Error('Customer ID is required. Make sure the session is properly initialized.');
179
+ }
180
+ return await initCheckoutSessionMutation.mutateAsync({
181
+ offerId,
182
+ orderId,
183
+ customerId: effectiveCustomerId
184
+ });
185
+ }, [initCheckoutSessionMutation, session?.customerId]);
186
+ return {
187
+ // Query data
188
+ offers,
189
+ isLoading,
190
+ error,
191
+ // Actions
192
+ refetch: async () => {
193
+ await refetch();
194
+ },
195
+ // Offer management
196
+ createCheckoutSession,
197
+ payOffer,
198
+ transformToCheckout,
199
+ // Helper functions
200
+ getOffer,
201
+ getTotalValue,
202
+ getTotalSavings,
203
+ // Payment functions
204
+ payWithCheckoutSession,
205
+ initCheckoutSession,
206
+ // Checkout session management (similar to postPurchases)
207
+ getCheckoutSessionState: (offerId) => {
208
+ return checkoutSessions[offerId] || null;
209
+ },
210
+ initializeOfferCheckout: async (offerId) => {
211
+ try {
212
+ // Check if customer ID is available
213
+ if (!session?.customerId) {
214
+ throw new Error('Customer ID is required. Make sure the session is properly initialized.');
215
+ }
216
+ // Initialize checkout session using transformToCheckout
217
+ const initResult = await offersResource.transformToCheckout(offerId, returnUrl);
218
+ if (!initResult.checkoutUrl) {
219
+ throw new Error('Failed to initialize checkout session');
220
+ }
221
+ // Extract session ID from checkout URL or use a placeholder
222
+ const sessionId = 'session_' + offerId; // This would need to be extracted from the actual response
223
+ // Initialize session state
224
+ setCheckoutSessions(prev => ({
225
+ ...prev,
226
+ [offerId]: {
227
+ checkoutSessionId: sessionId,
228
+ orderSummary: null,
229
+ selectedVariants: {},
230
+ loadingVariants: {},
231
+ isUpdatingSummary: false,
232
+ }
233
+ }));
234
+ // Fetch order summary with variant options
235
+ await fetchOrderSummary(offerId, sessionId);
236
+ }
237
+ catch (error) {
238
+ console.error(`[SDK] Failed to initialize checkout for offer ${offerId}:`, error);
239
+ throw error;
240
+ }
241
+ },
242
+ getAvailableVariants: (offerId, productId) => {
243
+ const sessionState = checkoutSessions[offerId];
244
+ if (!sessionState?.orderSummary?.options?.[productId])
245
+ return [];
246
+ return sessionState.orderSummary.options[productId].map((variant) => ({
247
+ variantId: variant.id,
248
+ variantName: variant.name,
249
+ variantSku: variant.sku,
250
+ variantDefault: variant.default,
251
+ variantExternalId: variant.externalVariantId,
252
+ priceId: variant.prices[0]?.id,
253
+ currencyOptions: variant.prices[0]?.currencyOptions,
254
+ }));
255
+ },
256
+ selectVariant: async (offerId, productId, variantId) => {
257
+ const sessionState = checkoutSessions[offerId];
258
+ if (!sessionState?.checkoutSessionId || !sessionState.orderSummary) {
259
+ throw new Error('Checkout session not initialized for this offer');
260
+ }
261
+ const sessionId = sessionState.checkoutSessionId;
262
+ // Set loading state for this specific variant
263
+ setCheckoutSessions(prev => ({
264
+ ...prev,
265
+ [offerId]: {
266
+ ...prev[offerId],
267
+ loadingVariants: {
268
+ ...prev[offerId].loadingVariants,
269
+ [productId]: true,
270
+ }
271
+ }
272
+ }));
273
+ try {
274
+ const sessionState = checkoutSessions[offerId];
275
+ if (!sessionState?.orderSummary?.options?.[productId]) {
276
+ throw new Error('No variants available for this product');
277
+ }
278
+ const availableVariants = sessionState.orderSummary.options[productId].map((variant) => ({
279
+ variantId: variant.id,
280
+ variantName: variant.name,
281
+ variantSku: variant.sku,
282
+ variantDefault: variant.default,
283
+ variantExternalId: variant.externalVariantId,
284
+ priceId: variant.prices[0]?.id,
285
+ currencyOptions: variant.prices[0]?.currencyOptions,
286
+ }));
287
+ const selectedVariant = availableVariants.find(v => v.variantId === variantId);
288
+ if (!selectedVariant) {
289
+ throw new Error('Selected variant not found');
290
+ }
291
+ // Find the current item to get its quantity
292
+ const currentItem = sessionState.orderSummary.items.find(item => item.productId === productId);
293
+ if (!currentItem) {
294
+ throw new Error('Current item not found');
295
+ }
296
+ // Update selected variants state
297
+ setCheckoutSessions(prev => ({
298
+ ...prev,
299
+ [offerId]: {
300
+ ...prev[offerId],
301
+ selectedVariants: {
302
+ ...prev[offerId].selectedVariants,
303
+ [productId]: variantId,
304
+ }
305
+ }
306
+ }));
307
+ // Update line items on the server
308
+ await offersResource.updateLineItems(sessionId, [
309
+ {
310
+ variantId: selectedVariant.variantId,
311
+ quantity: currentItem.quantity,
312
+ },
313
+ ]);
314
+ // Refetch order summary after successful line item update
315
+ await fetchOrderSummary(offerId, sessionId);
316
+ }
317
+ catch (error) {
318
+ console.error(`[SDK] Failed to update variant for offer ${offerId}:`, error);
319
+ throw error;
320
+ }
321
+ finally {
322
+ // Clear loading state for this specific variant
323
+ setCheckoutSessions(prev => ({
324
+ ...prev,
325
+ [offerId]: {
326
+ ...prev[offerId],
327
+ loadingVariants: {
328
+ ...prev[offerId].loadingVariants,
329
+ [productId]: false,
330
+ }
331
+ }
332
+ }));
333
+ }
334
+ },
335
+ getOrderSummary: (offerId) => {
336
+ return checkoutSessions[offerId]?.orderSummary ?? null;
337
+ },
338
+ isLoadingVariants: (offerId, productId) => {
339
+ return checkoutSessions[offerId]?.loadingVariants?.[productId] ?? false;
340
+ },
341
+ isUpdatingOrderSummary: (offerId) => {
342
+ return checkoutSessions[offerId]?.isUpdatingSummary || false;
343
+ },
344
+ confirmPurchase: async (offerId, _options) => {
345
+ const sessionState = checkoutSessions[offerId];
346
+ if (!sessionState?.checkoutSessionId) {
347
+ throw new Error('Checkout session not initialized for this offer');
348
+ }
349
+ // Use the enhanced payWithCheckoutSession with proper metadata
350
+ await offersResource.payWithCheckoutSession(sessionState.checkoutSessionId);
351
+ },
352
+ };
353
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Order Bump Hook using TanStack Query
3
+ * Replaces coordinator pattern with automatic cache invalidation
4
+ */
5
+ export interface UseOrderBumpQueryOptions {
6
+ checkoutToken: string | null;
7
+ offerId: string;
8
+ productId?: string;
9
+ checkout?: any;
10
+ }
11
+ export interface UseOrderBumpQueryResult {
12
+ isSelected: boolean;
13
+ isToggling: boolean;
14
+ error: Error | null;
15
+ toggle: (selected?: boolean) => Promise<{
16
+ success: boolean;
17
+ error?: any;
18
+ }>;
19
+ }
20
+ export declare function useOrderBumpQuery(options: UseOrderBumpQueryOptions): UseOrderBumpQueryResult;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Order Bump Hook using TanStack Query
3
+ * Replaces coordinator pattern with automatic cache invalidation
4
+ */
5
+ import { useState, useCallback, useEffect } from 'react';
6
+ import { useApiMutation, useInvalidateQuery, getGlobalApiClient } from './useApiQuery';
7
+ import { useCheckoutQuery } from './useCheckoutQuery';
8
+ export function useOrderBumpQuery(options) {
9
+ const { checkoutToken, offerId, productId, checkout: providedCheckout } = options;
10
+ const { invalidateCheckout, invalidatePromotions } = useInvalidateQuery();
11
+ const client = getGlobalApiClient();
12
+ // Use checkout query only if no checkout is provided
13
+ const { checkout: loadedCheckout } = useCheckoutQuery({
14
+ checkoutToken: checkoutToken || undefined,
15
+ enabled: !providedCheckout && !!checkoutToken,
16
+ });
17
+ // Use provided checkout or loaded checkout
18
+ const checkout = providedCheckout || loadedCheckout;
19
+ // Function to check if order bump is selected
20
+ const checkOrderBumpSelection = useCallback(() => {
21
+ if (!checkout?.checkoutSession?.sessionLineItems) {
22
+ return false;
23
+ }
24
+ const targetProductId = productId || offerId;
25
+ return checkout.checkoutSession.sessionLineItems.some((item) => {
26
+ return item.isOrderBump === true && item.productId === targetProductId;
27
+ });
28
+ }, [checkout?.checkoutSession?.sessionLineItems, offerId, productId]);
29
+ // State management
30
+ const [isSelected, setIsSelected] = useState(() => checkOrderBumpSelection());
31
+ const [error, setError] = useState(null);
32
+ // Update state when checkout session changes
33
+ useEffect(() => {
34
+ const isSelectedFromSession = checkOrderBumpSelection();
35
+ setIsSelected(isSelectedFromSession);
36
+ }, [checkOrderBumpSelection]);
37
+ // Order bump toggle mutation
38
+ const toggleMutation = useApiMutation(async ({ selected }) => {
39
+ if (!checkout?.checkoutSession?.id) {
40
+ throw new Error('No checkout session available');
41
+ }
42
+ return client.post(`/api/v1/checkout-sessions/${checkout.checkoutSession.id}/toggle-order-bump`, {
43
+ orderBumpOfferId: offerId,
44
+ selected,
45
+ });
46
+ }, {
47
+ onMutate: async ({ selected }) => {
48
+ // Optimistic update
49
+ setIsSelected(selected);
50
+ setError(null);
51
+ },
52
+ onSuccess: () => {
53
+ // Invalidate related queries
54
+ if (checkoutToken) {
55
+ invalidateCheckout(checkoutToken);
56
+ }
57
+ if (checkout?.checkoutSession?.id) {
58
+ invalidatePromotions(checkout.checkoutSession.id);
59
+ }
60
+ },
61
+ onError: (err, { selected }) => {
62
+ // Revert optimistic update on error
63
+ setIsSelected(!selected);
64
+ setError(err instanceof Error ? err : new Error('Failed to toggle order bump'));
65
+ },
66
+ });
67
+ const toggle = useCallback(async (selected) => {
68
+ if (!checkout?.checkoutSession?.id) {
69
+ console.warn('useOrderBumpQuery: No checkout session available yet');
70
+ return { success: false, error: 'Checkout session not ready' };
71
+ }
72
+ const targetState = selected ?? !isSelected;
73
+ try {
74
+ const result = await toggleMutation.mutateAsync({ selected: targetState });
75
+ return { success: true };
76
+ }
77
+ catch (err) {
78
+ const error = err instanceof Error ? err : new Error('Failed to toggle order bump');
79
+ return { success: false, error: error.message };
80
+ }
81
+ }, [checkout?.checkoutSession?.id, isSelected, toggleMutation]);
82
+ return {
83
+ isSelected,
84
+ isToggling: toggleMutation.isPending,
85
+ error,
86
+ toggle,
87
+ };
88
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Order Hook using TanStack Query
3
+ * Handles order creation and management with automatic caching
4
+ */
5
+ import { Order, OrderLineItem } from '../../core/utils/order';
6
+ export interface UseOrderQueryOptions {
7
+ orderId?: string;
8
+ enabled?: boolean;
9
+ }
10
+ export interface UseOrderQueryResult {
11
+ order: Order | undefined;
12
+ isLoading: boolean;
13
+ error: Error | null;
14
+ createOrder: (checkoutSessionId: string) => Promise<{
15
+ success: boolean;
16
+ order?: Order;
17
+ error?: string;
18
+ }>;
19
+ updateOrderStatus: (status: string) => Promise<{
20
+ success: boolean;
21
+ error?: string;
22
+ }>;
23
+ addOrderItems: (items: Omit<OrderLineItem, 'id'>[]) => Promise<{
24
+ success: boolean;
25
+ error?: string;
26
+ }>;
27
+ refresh: () => void;
28
+ }
29
+ export declare function useOrderQuery(options?: UseOrderQueryOptions): UseOrderQueryResult;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Order Hook using TanStack Query
3
+ * Handles order creation and management with automatic caching
4
+ */
5
+ import { useMemo } from 'react';
6
+ import { useQuery, useMutation } from '@tanstack/react-query';
7
+ import { OrdersResource } from '../../core/resources/orders';
8
+ import { getGlobalApiClient } from './useApiQuery';
9
+ export function useOrderQuery(options = {}) {
10
+ const { orderId, enabled = true } = options;
11
+ // Create orders resource client
12
+ const ordersResource = useMemo(() => {
13
+ try {
14
+ return new OrdersResource(getGlobalApiClient());
15
+ }
16
+ catch (error) {
17
+ throw new Error('Failed to initialize orders resource: ' + (error instanceof Error ? error.message : 'Unknown error'));
18
+ }
19
+ }, []);
20
+ // Main order query
21
+ const { data: order, isLoading, error, refetch, } = useQuery({
22
+ queryKey: ['order', orderId],
23
+ queryFn: () => ordersResource.getOrder(orderId),
24
+ enabled: enabled && !!orderId,
25
+ staleTime: 60000, // 1 minute
26
+ refetchOnWindowFocus: false,
27
+ });
28
+ // Create order mutation
29
+ const createMutation = useMutation({
30
+ mutationFn: ({ checkoutSessionId }) => {
31
+ return ordersResource.createOrder(checkoutSessionId);
32
+ },
33
+ onSuccess: () => {
34
+ // Order created successfully - no specific cache invalidation needed
35
+ },
36
+ });
37
+ // Update order status mutation
38
+ const updateStatusMutation = useMutation({
39
+ mutationFn: ({ status }) => {
40
+ if (!orderId) {
41
+ throw new Error('No order ID available');
42
+ }
43
+ return ordersResource.updateOrderStatus(orderId, status);
44
+ },
45
+ onSuccess: () => {
46
+ // Refetch order data
47
+ void refetch();
48
+ },
49
+ });
50
+ // Add order items mutation
51
+ const addItemsMutation = useMutation({
52
+ mutationFn: ({ items }) => {
53
+ if (!orderId) {
54
+ throw new Error('No order ID available');
55
+ }
56
+ return ordersResource.addOrderItems(orderId, items);
57
+ },
58
+ onSuccess: () => {
59
+ // Refetch order data
60
+ void refetch();
61
+ },
62
+ });
63
+ return {
64
+ // Query data
65
+ order,
66
+ isLoading,
67
+ error,
68
+ // Actions
69
+ createOrder: async (checkoutSessionId) => {
70
+ try {
71
+ return await createMutation.mutateAsync({ checkoutSessionId });
72
+ }
73
+ catch (err) {
74
+ const error = err instanceof Error ? err : new Error('Failed to create order');
75
+ return { success: false, error: error.message };
76
+ }
77
+ },
78
+ updateOrderStatus: async (status) => {
79
+ try {
80
+ return await updateStatusMutation.mutateAsync({ status });
81
+ }
82
+ catch (err) {
83
+ const error = err instanceof Error ? err : new Error('Failed to update order status');
84
+ return { success: false, error: error.message };
85
+ }
86
+ },
87
+ addOrderItems: async (items) => {
88
+ try {
89
+ return await addItemsMutation.mutateAsync({ items });
90
+ }
91
+ catch (err) {
92
+ const error = err instanceof Error ? err : new Error('Failed to add order items');
93
+ return { success: false, error: error.message };
94
+ }
95
+ },
96
+ refresh: () => void refetch(),
97
+ };
98
+ }
@@ -0,0 +1,45 @@
1
+ export interface Payment {
2
+ id: string;
3
+ status: string;
4
+ subStatus: string;
5
+ requireAction: 'none' | 'redirect' | 'error';
6
+ requireActionData?: {
7
+ type: 'redirect' | 'threeds_auth' | 'processor_auth' | 'error';
8
+ url?: string;
9
+ processed: boolean;
10
+ processorId?: string;
11
+ metadata?: {
12
+ type: 'redirect';
13
+ redirect?: {
14
+ redirectUrl: string;
15
+ returnUrl: string;
16
+ };
17
+ threedsSession?: {
18
+ externalSessionId: string;
19
+ acsChallengeUrl: string;
20
+ acsTransID: string;
21
+ messageVersion: string;
22
+ };
23
+ };
24
+ redirectUrl?: string;
25
+ resumeToken?: string;
26
+ message?: string;
27
+ errorCode?: string;
28
+ };
29
+ }
30
+ export interface PollingOptions {
31
+ onRequireAction?: (payment: Payment, stop: () => void) => void;
32
+ onSuccess?: (payment: Payment) => void;
33
+ onFailure?: (error: string) => void;
34
+ maxAttempts?: number;
35
+ pollInterval?: number;
36
+ }
37
+ export interface PaymentPollingHook {
38
+ startPolling: (paymentId: string, options?: PollingOptions) => {
39
+ stop: () => void;
40
+ isPolling: () => boolean;
41
+ };
42
+ stopPolling: () => void;
43
+ isPolling: () => boolean;
44
+ }
45
+ export declare function usePaymentPolling(): PaymentPollingHook;