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