@tagadapay/plugin-sdk 2.8.10 → 3.0.1

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 (50) hide show
  1. package/README.md +14 -14
  2. package/dist/index.js +1 -1
  3. package/dist/react/hooks/usePluginConfig.d.ts +1 -0
  4. package/dist/react/hooks/usePluginConfig.js +69 -18
  5. package/dist/react/providers/TagadaProvider.js +1 -4
  6. package/dist/v2/core/client.d.ts +18 -0
  7. package/dist/v2/core/client.js +45 -0
  8. package/dist/v2/core/config/environment.d.ts +8 -0
  9. package/dist/v2/core/config/environment.js +18 -0
  10. package/dist/v2/core/funnelClient.d.ts +84 -0
  11. package/dist/v2/core/funnelClient.js +252 -0
  12. package/dist/v2/core/index.d.ts +2 -0
  13. package/dist/v2/core/index.js +3 -0
  14. package/dist/v2/core/resources/apiClient.js +1 -1
  15. package/dist/v2/core/resources/funnel.d.ts +1 -0
  16. package/dist/v2/core/resources/offers.d.ts +182 -8
  17. package/dist/v2/core/resources/offers.js +25 -0
  18. package/dist/v2/core/resources/products.d.ts +5 -0
  19. package/dist/v2/core/resources/products.js +15 -1
  20. package/dist/v2/core/types.d.ts +1 -0
  21. package/dist/v2/core/utils/funnelQueryKeys.d.ts +23 -0
  22. package/dist/v2/core/utils/funnelQueryKeys.js +23 -0
  23. package/dist/v2/core/utils/index.d.ts +2 -0
  24. package/dist/v2/core/utils/index.js +2 -0
  25. package/dist/v2/core/utils/pluginConfig.js +44 -32
  26. package/dist/v2/core/utils/sessionStorage.d.ts +20 -0
  27. package/dist/v2/core/utils/sessionStorage.js +39 -0
  28. package/dist/v2/index.d.ts +3 -2
  29. package/dist/v2/index.js +1 -1
  30. package/dist/v2/react/hooks/__examples__/FunnelContextExample.d.ts +3 -0
  31. package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +4 -3
  32. package/dist/v2/react/hooks/useClubOffers.d.ts +2 -2
  33. package/dist/v2/react/hooks/useFunnel.d.ts +27 -39
  34. package/dist/v2/react/hooks/useFunnel.js +22 -659
  35. package/dist/v2/react/hooks/useFunnelLegacy.d.ts +52 -0
  36. package/dist/v2/react/hooks/useFunnelLegacy.js +733 -0
  37. package/dist/v2/react/hooks/useOfferQuery.d.ts +109 -0
  38. package/dist/v2/react/hooks/useOfferQuery.js +483 -0
  39. package/dist/v2/react/hooks/useOffersQuery.d.ts +9 -75
  40. package/dist/v2/react/hooks/useProductsQuery.d.ts +1 -0
  41. package/dist/v2/react/hooks/useProductsQuery.js +10 -6
  42. package/dist/v2/react/index.d.ts +7 -4
  43. package/dist/v2/react/index.js +4 -2
  44. package/dist/v2/react/providers/TagadaProvider.d.ts +40 -2
  45. package/dist/v2/react/providers/TagadaProvider.js +116 -3
  46. package/dist/v2/standalone/index.d.ts +20 -0
  47. package/dist/v2/standalone/index.js +22 -0
  48. package/dist/v2/vue/index.d.ts +6 -0
  49. package/dist/v2/vue/index.js +10 -0
  50. package/package.json +6 -1
@@ -0,0 +1,109 @@
1
+ /**
2
+ * useOffer Hook - Single offer workflow with checkout session management
3
+ *
4
+ * Behavior copied from useOffersQuery but simplified for a single offer:
5
+ * 1. Fetches offer data
6
+ * 2. Auto-initializes checkout session when mainOrderId is provided
7
+ * 3. Fetches order summary with variant options
8
+ * 4. Updates line items when variant/quantity changes
9
+ * 5. Pays with payOffer
10
+ */
11
+ import { Offer } from '../../core/resources/offers';
12
+ export type { Offer };
13
+ /**
14
+ * Line item displayed in the UI
15
+ */
16
+ export interface OfferLineItem {
17
+ id: string;
18
+ variantId: string;
19
+ variantName: string;
20
+ productId: string;
21
+ productName: string;
22
+ productDescription: string | null;
23
+ imageUrl: string | null;
24
+ quantity: number;
25
+ unitAmount: number;
26
+ currency: string;
27
+ }
28
+ /**
29
+ * Available variant option for a product
30
+ */
31
+ export interface AvailableVariant {
32
+ variantId: string;
33
+ variantName: string;
34
+ sku: string | null;
35
+ imageUrl: string | null;
36
+ unitAmount: number;
37
+ currency: string;
38
+ isDefault: boolean;
39
+ }
40
+ /**
41
+ * User selection for a line item
42
+ */
43
+ export interface LineItemSelection {
44
+ variantId: string;
45
+ quantity: number;
46
+ }
47
+ /**
48
+ * Summary derived from offer with user selections
49
+ */
50
+ export interface OfferPreviewSummary {
51
+ items: OfferLineItem[];
52
+ currency: string;
53
+ totalAmount: number;
54
+ totalAdjustedAmount: number;
55
+ }
56
+ export interface UseOfferQueryOptions {
57
+ /**
58
+ * The offer ID to fetch (required)
59
+ */
60
+ offerId: string;
61
+ /**
62
+ * Whether to fetch the offer automatically
63
+ * @default true
64
+ */
65
+ enabled?: boolean;
66
+ /**
67
+ * Main order ID - when provided, auto-inits checkout session
68
+ */
69
+ mainOrderId?: string;
70
+ }
71
+ export interface UseOfferQueryResult {
72
+ /** The fetched offer */
73
+ offer: Offer | null;
74
+ /** Loading state */
75
+ isLoading: boolean;
76
+ /** Whether order summary is being updated */
77
+ isUpdatingSummary: boolean;
78
+ /** Fetch error */
79
+ error: Error | null;
80
+ /** Preview summary with current selections */
81
+ summary: OfferPreviewSummary | null;
82
+ /** Line items with current selections */
83
+ lineItems: OfferLineItem[];
84
+ /** Get available variants for a product */
85
+ getAvailableVariants: (productId: string) => AvailableVariant[];
86
+ /** Select a variant for a product */
87
+ selectVariant: (productId: string, variantId: string) => void;
88
+ /** Update variant for a product (same as selectVariant) */
89
+ updateVariant: (productId: string, variantId: string) => void;
90
+ /** Update quantity for a product */
91
+ updateQuantity: (productId: string, quantity: number) => void;
92
+ /** Current selections per product */
93
+ selections: Record<string, LineItemSelection>;
94
+ /** Pay for the offer */
95
+ payOffer: (orderId?: string) => Promise<{
96
+ checkoutUrl: string;
97
+ }>;
98
+ /** Payment in progress */
99
+ isPaying: boolean;
100
+ /** Payment error */
101
+ paymentError: Error | null;
102
+ /** Checkout session ID (set after init) */
103
+ checkoutSessionId: string | null;
104
+ /** Whether checkout session is initializing */
105
+ isInitializing: boolean;
106
+ /** Whether variant is loading for a product */
107
+ isLoadingVariant: (productId: string) => boolean;
108
+ }
109
+ export declare function useOfferQuery(options: UseOfferQueryOptions): UseOfferQueryResult;
@@ -0,0 +1,483 @@
1
+ /**
2
+ * useOffer Hook - Single offer workflow with checkout session management
3
+ *
4
+ * Behavior copied from useOffersQuery but simplified for a single offer:
5
+ * 1. Fetches offer data
6
+ * 2. Auto-initializes checkout session when mainOrderId is provided
7
+ * 3. Fetches order summary with variant options
8
+ * 4. Updates line items when variant/quantity changes
9
+ * 5. Pays with payOffer
10
+ */
11
+ import { useMutation, useQuery } from '@tanstack/react-query';
12
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
13
+ import { OffersResource } from '../../core/resources/offers';
14
+ import { useTagadaContext } from '../providers/TagadaProvider';
15
+ import { getGlobalApiClient } from './useApiQuery';
16
+ import { usePluginConfig } from './usePluginConfig';
17
+ export function useOfferQuery(options) {
18
+ const { offerId, enabled = true, mainOrderId } = options;
19
+ const { storeId } = usePluginConfig();
20
+ const { isSessionInitialized, session } = useTagadaContext();
21
+ // ─────────────────────────────────────────────────────────────────────────
22
+ // State
23
+ // ─────────────────────────────────────────────────────────────────────────
24
+ const [checkoutSession, setCheckoutSession] = useState({
25
+ checkoutSessionId: null,
26
+ orderSummary: null,
27
+ selectedVariants: {},
28
+ loadingVariants: {},
29
+ isUpdatingSummary: false,
30
+ });
31
+ const [isInitializing, setIsInitializing] = useState(false);
32
+ const [isPaying, setIsPaying] = useState(false);
33
+ const [paymentError, setPaymentError] = useState(null);
34
+ // Use ref to break dependency cycles in callbacks
35
+ const checkoutSessionRef = useRef(checkoutSession);
36
+ checkoutSessionRef.current = checkoutSession;
37
+ // Track if we've already initialized to prevent duplicate calls
38
+ const hasInitializedRef = useRef(false);
39
+ // ─────────────────────────────────────────────────────────────────────────
40
+ // Initialize offers resource
41
+ // ─────────────────────────────────────────────────────────────────────────
42
+ const offersResource = useMemo(() => {
43
+ try {
44
+ return new OffersResource(getGlobalApiClient());
45
+ }
46
+ catch (error) {
47
+ throw new Error('Failed to initialize offers resource: ' +
48
+ (error instanceof Error ? error.message : 'Unknown error'));
49
+ }
50
+ }, []);
51
+ // ─────────────────────────────────────────────────────────────────────────
52
+ // 1. Fetch offer data
53
+ // ─────────────────────────────────────────────────────────────────────────
54
+ const { data: offer, isLoading: isOfferLoading, error, } = useQuery({
55
+ queryKey: ['offer', offerId, storeId],
56
+ queryFn: async () => {
57
+ if (!storeId) {
58
+ throw new Error('Store ID not found');
59
+ }
60
+ const offers = await offersResource.getOffersByIds(storeId, [offerId]);
61
+ return offers[0] || null;
62
+ },
63
+ enabled: enabled && !!offerId && !!storeId && isSessionInitialized,
64
+ staleTime: 5 * 60 * 1000,
65
+ refetchOnWindowFocus: false,
66
+ });
67
+ // ─────────────────────────────────────────────────────────────────────────
68
+ // Mutations
69
+ // ─────────────────────────────────────────────────────────────────────────
70
+ const { mutateAsync: initCheckoutSessionAsync } = useMutation({
71
+ mutationFn: async ({ offerId, orderId, customerId }) => {
72
+ return await offersResource.initCheckoutSession(offerId, orderId, customerId);
73
+ },
74
+ });
75
+ const { mutateAsync: payWithCheckoutSessionAsync } = useMutation({
76
+ mutationFn: async ({ checkoutSessionId, orderId }) => {
77
+ return await offersResource.payWithCheckoutSession(checkoutSessionId, orderId);
78
+ },
79
+ });
80
+ // ─────────────────────────────────────────────────────────────────────────
81
+ // Helper: Fetch order summary from checkout session
82
+ // ─────────────────────────────────────────────────────────────────────────
83
+ const fetchOrderSummary = useCallback(async (sessionId) => {
84
+ if (!isSessionInitialized)
85
+ return null;
86
+ try {
87
+ setCheckoutSession(prev => ({
88
+ ...prev,
89
+ isUpdatingSummary: true,
90
+ }));
91
+ const summaryResult = await offersResource.getOrderSummary(sessionId, true);
92
+ if (summaryResult) {
93
+ // Sort items by productId to ensure consistent order
94
+ const sortedItems = [...summaryResult.items].sort((a, b) => a.productId.localeCompare(b.productId));
95
+ const orderSummary = {
96
+ ...summaryResult,
97
+ items: sortedItems,
98
+ };
99
+ // Initialize selected variants based on the summary
100
+ const initialVariants = {};
101
+ sortedItems.forEach((item) => {
102
+ if (item.productId && item.variantId) {
103
+ initialVariants[item.productId] = item.variantId;
104
+ }
105
+ });
106
+ setCheckoutSession(prev => ({
107
+ ...prev,
108
+ orderSummary,
109
+ selectedVariants: initialVariants,
110
+ isUpdatingSummary: false,
111
+ }));
112
+ return orderSummary;
113
+ }
114
+ return null;
115
+ }
116
+ catch (error) {
117
+ console.error('Failed to fetch order summary:', error);
118
+ setCheckoutSession(prev => ({
119
+ ...prev,
120
+ isUpdatingSummary: false,
121
+ }));
122
+ return null;
123
+ }
124
+ }, [offersResource, isSessionInitialized]);
125
+ // ─────────────────────────────────────────────────────────────────────────
126
+ // Helper: Initialize checkout session
127
+ // ─────────────────────────────────────────────────────────────────────────
128
+ const initCheckoutSession = useCallback(async (offerId, orderId, customerId) => {
129
+ if (!isSessionInitialized) {
130
+ throw new Error('Cannot initialize checkout session: CMS session is not initialized');
131
+ }
132
+ const effectiveCustomerId = customerId || session?.customerId;
133
+ if (!effectiveCustomerId) {
134
+ throw new Error('Customer ID is required. Make sure the session is properly initialized.');
135
+ }
136
+ return await initCheckoutSessionAsync({
137
+ offerId,
138
+ orderId,
139
+ customerId: effectiveCustomerId,
140
+ });
141
+ }, [initCheckoutSessionAsync, session?.customerId, isSessionInitialized]);
142
+ // ─────────────────────────────────────────────────────────────────────────
143
+ // 2. Auto-initialize checkout session when mainOrderId is provided
144
+ // ─────────────────────────────────────────────────────────────────────────
145
+ useEffect(() => {
146
+ if (!offer || !mainOrderId || !isSessionInitialized)
147
+ return;
148
+ if (checkoutSession.checkoutSessionId || isInitializing || hasInitializedRef.current)
149
+ return;
150
+ const initSession = async () => {
151
+ hasInitializedRef.current = true;
152
+ setIsInitializing(true);
153
+ try {
154
+ const result = await initCheckoutSession(offerId, mainOrderId);
155
+ setCheckoutSession(prev => ({
156
+ ...prev,
157
+ checkoutSessionId: result.checkoutSessionId,
158
+ }));
159
+ // Fetch order summary after session is initialized
160
+ await fetchOrderSummary(result.checkoutSessionId);
161
+ }
162
+ catch (err) {
163
+ console.error('Failed to init checkout session:', err);
164
+ hasInitializedRef.current = false; // Allow retry on error
165
+ }
166
+ finally {
167
+ setIsInitializing(false);
168
+ }
169
+ };
170
+ initSession();
171
+ }, [offer, mainOrderId, offerId, isSessionInitialized, checkoutSession.checkoutSessionId, isInitializing, initCheckoutSession, fetchOrderSummary]);
172
+ // ─────────────────────────────────────────────────────────────────────────
173
+ // Derive line items from order summary or static offer data
174
+ // ─────────────────────────────────────────────────────────────────────────
175
+ const lineItems = useMemo(() => {
176
+ // Prefer dynamic data from order summary
177
+ if (checkoutSession.orderSummary?.items) {
178
+ return checkoutSession.orderSummary.items.map((item) => ({
179
+ id: item.id || `${item.productId}-${item.variantId}`,
180
+ variantId: item.variantId,
181
+ variantName: item.variant?.name || item.variantName || '',
182
+ productId: item.productId,
183
+ productName: item.product?.name || item.productName || '',
184
+ productDescription: item.product?.description || null,
185
+ imageUrl: item.variant?.imageUrl || item.imageUrl || null,
186
+ quantity: item.quantity,
187
+ unitAmount: item.unitAmount || 0,
188
+ currency: checkoutSession.orderSummary?.currency || 'USD',
189
+ }));
190
+ }
191
+ // Fallback to static offer data
192
+ if (!offer?.offerLineItems)
193
+ return [];
194
+ const items = [];
195
+ for (const item of offer.offerLineItems) {
196
+ const price = item.price;
197
+ const variant = price?.variant;
198
+ const product = variant?.product;
199
+ if (!variant || !product)
200
+ continue;
201
+ const currency = offer.summaries?.[0]?.currency || 'USD';
202
+ const currencyOption = price?.currencyOptions?.[currency];
203
+ const unitAmount = currencyOption?.amount ?? variant.price ?? 0;
204
+ items.push({
205
+ id: item.id,
206
+ variantId: variant.id,
207
+ variantName: variant.name,
208
+ productId: product.id,
209
+ productName: product.name,
210
+ productDescription: product.description,
211
+ imageUrl: variant.imageUrl,
212
+ quantity: item.quantity,
213
+ unitAmount,
214
+ currency,
215
+ });
216
+ }
217
+ return items;
218
+ }, [offer, checkoutSession.orderSummary]);
219
+ // ─────────────────────────────────────────────────────────────────────────
220
+ // Derive selections from order summary or line items
221
+ // ─────────────────────────────────────────────────────────────────────────
222
+ const selections = useMemo(() => {
223
+ if (Object.keys(checkoutSession.selectedVariants).length > 0) {
224
+ const result = {};
225
+ for (const item of lineItems) {
226
+ result[item.productId] = {
227
+ variantId: checkoutSession.selectedVariants[item.productId] || item.variantId,
228
+ quantity: item.quantity,
229
+ };
230
+ }
231
+ return result;
232
+ }
233
+ // Fallback to line items
234
+ const result = {};
235
+ for (const item of lineItems) {
236
+ if (!result[item.productId]) {
237
+ result[item.productId] = {
238
+ variantId: item.variantId,
239
+ quantity: item.quantity,
240
+ };
241
+ }
242
+ }
243
+ return result;
244
+ }, [lineItems, checkoutSession.selectedVariants]);
245
+ // ─────────────────────────────────────────────────────────────────────────
246
+ // Summary calculation
247
+ // ─────────────────────────────────────────────────────────────────────────
248
+ const summary = useMemo(() => {
249
+ // Prefer dynamic data from order summary
250
+ if (checkoutSession.orderSummary) {
251
+ return {
252
+ items: lineItems,
253
+ currency: checkoutSession.orderSummary.currency || 'USD',
254
+ totalAmount: checkoutSession.orderSummary.totalAmount || 0,
255
+ totalAdjustedAmount: checkoutSession.orderSummary.totalAdjustedAmount || 0,
256
+ };
257
+ }
258
+ // Fallback to static calculation
259
+ if (lineItems.length === 0)
260
+ return null;
261
+ const currency = lineItems[0]?.currency || 'USD';
262
+ let totalAmount = 0;
263
+ for (const item of lineItems) {
264
+ totalAmount += item.unitAmount * item.quantity;
265
+ }
266
+ const offerSummary = offer?.summaries?.[0];
267
+ const discountRatio = offerSummary && offerSummary.totalAmount > 0
268
+ ? offerSummary.totalAdjustedAmount / offerSummary.totalAmount
269
+ : 1;
270
+ return {
271
+ items: lineItems,
272
+ currency,
273
+ totalAmount,
274
+ totalAdjustedAmount: Math.round(totalAmount * discountRatio),
275
+ };
276
+ }, [lineItems, offer, checkoutSession.orderSummary]);
277
+ // ─────────────────────────────────────────────────────────────────────────
278
+ // Get available variants for a product
279
+ // ─────────────────────────────────────────────────────────────────────────
280
+ const getAvailableVariants = useCallback((productId) => {
281
+ // Get variants from order summary options (dynamic, from checkout session)
282
+ const orderSummary = checkoutSession.orderSummary;
283
+ if (orderSummary?.options?.[productId]) {
284
+ const currency = orderSummary.currency || 'USD';
285
+ return orderSummary.options[productId].map((variant) => ({
286
+ variantId: variant.id,
287
+ variantName: variant.name,
288
+ sku: variant.sku || null,
289
+ imageUrl: variant.imageUrl || null,
290
+ unitAmount: variant.prices?.[0]?.currencyOptions?.[currency]?.amount ?? 0,
291
+ currency,
292
+ isDefault: variant.default ?? false,
293
+ }));
294
+ }
295
+ // Fallback to static offer data
296
+ if (!offer?.offerLineItems)
297
+ return [];
298
+ const variants = [];
299
+ const currency = offer.summaries?.[0]?.currency || 'USD';
300
+ for (const item of offer.offerLineItems) {
301
+ const price = item.price;
302
+ const variant = price?.variant;
303
+ const product = variant?.product;
304
+ if (!variant || !product || product.id !== productId)
305
+ continue;
306
+ if (variants.some(v => v.variantId === variant.id))
307
+ continue;
308
+ const currencyOption = price?.currencyOptions?.[currency];
309
+ const unitAmount = currencyOption?.amount ?? variant.price ?? 0;
310
+ variants.push({
311
+ variantId: variant.id,
312
+ variantName: variant.name,
313
+ sku: variant.sku || null,
314
+ imageUrl: variant.imageUrl,
315
+ unitAmount,
316
+ currency,
317
+ isDefault: variant.default ?? false,
318
+ });
319
+ }
320
+ return variants;
321
+ }, [checkoutSession.orderSummary, offer]);
322
+ // ─────────────────────────────────────────────────────────────────────────
323
+ // Check if variant is loading
324
+ // ─────────────────────────────────────────────────────────────────────────
325
+ const isLoadingVariant = useCallback((productId) => {
326
+ return checkoutSession.loadingVariants[productId] ?? false;
327
+ }, [checkoutSession.loadingVariants]);
328
+ // ─────────────────────────────────────────────────────────────────────────
329
+ // Select/Update variant for a product
330
+ // ─────────────────────────────────────────────────────────────────────────
331
+ const selectVariant = useCallback(async (productId, variantId) => {
332
+ if (!isSessionInitialized) {
333
+ throw new Error('Cannot select variant: CMS session is not initialized');
334
+ }
335
+ const currentSession = checkoutSessionRef.current;
336
+ if (!currentSession.checkoutSessionId || !currentSession.orderSummary) {
337
+ throw new Error('Checkout session not initialized');
338
+ }
339
+ const sessionId = currentSession.checkoutSessionId;
340
+ // Set loading state
341
+ setCheckoutSession(prev => ({
342
+ ...prev,
343
+ loadingVariants: {
344
+ ...prev.loadingVariants,
345
+ [productId]: true,
346
+ },
347
+ }));
348
+ try {
349
+ // Find the current item to get its quantity
350
+ const currentItem = currentSession.orderSummary.items.find((item) => item.productId === productId);
351
+ if (!currentItem) {
352
+ throw new Error('Current item not found');
353
+ }
354
+ // Update selected variants state optimistically
355
+ setCheckoutSession(prev => ({
356
+ ...prev,
357
+ selectedVariants: {
358
+ ...prev.selectedVariants,
359
+ [productId]: variantId,
360
+ },
361
+ }));
362
+ // Update line items on the server
363
+ await offersResource.updateLineItems(sessionId, [
364
+ {
365
+ variantId,
366
+ quantity: currentItem.quantity,
367
+ },
368
+ ]);
369
+ // Refetch order summary
370
+ await fetchOrderSummary(sessionId);
371
+ }
372
+ finally {
373
+ setCheckoutSession(prev => ({
374
+ ...prev,
375
+ loadingVariants: {
376
+ ...prev.loadingVariants,
377
+ [productId]: false,
378
+ },
379
+ }));
380
+ }
381
+ }, [offersResource, fetchOrderSummary, isSessionInitialized]);
382
+ // Alias for selectVariant
383
+ const updateVariant = selectVariant;
384
+ // ─────────────────────────────────────────────────────────────────────────
385
+ // Update quantity for a product
386
+ // ─────────────────────────────────────────────────────────────────────────
387
+ const updateQuantity = useCallback(async (productId, quantity) => {
388
+ if (quantity < 1)
389
+ return;
390
+ if (!isSessionInitialized) {
391
+ throw new Error('Cannot update quantity: CMS session is not initialized');
392
+ }
393
+ const currentSession = checkoutSessionRef.current;
394
+ if (!currentSession.checkoutSessionId || !currentSession.orderSummary) {
395
+ throw new Error('Checkout session not initialized');
396
+ }
397
+ const sessionId = currentSession.checkoutSessionId;
398
+ // Find the current item
399
+ const currentItem = currentSession.orderSummary.items.find((item) => item.productId === productId);
400
+ if (!currentItem) {
401
+ throw new Error('Current item not found');
402
+ }
403
+ // Set loading state
404
+ setCheckoutSession(prev => ({
405
+ ...prev,
406
+ loadingVariants: {
407
+ ...prev.loadingVariants,
408
+ [productId]: true,
409
+ },
410
+ }));
411
+ try {
412
+ // Update line items on the server
413
+ await offersResource.updateLineItems(sessionId, [
414
+ {
415
+ variantId: currentItem.variantId,
416
+ quantity,
417
+ },
418
+ ]);
419
+ // Refetch order summary
420
+ await fetchOrderSummary(sessionId);
421
+ }
422
+ finally {
423
+ setCheckoutSession(prev => ({
424
+ ...prev,
425
+ loadingVariants: {
426
+ ...prev.loadingVariants,
427
+ [productId]: false,
428
+ },
429
+ }));
430
+ }
431
+ }, [offersResource, fetchOrderSummary, isSessionInitialized]);
432
+ // ─────────────────────────────────────────────────────────────────────────
433
+ // Pay for the offer
434
+ // ─────────────────────────────────────────────────────────────────────────
435
+ const payOffer = useCallback(async (orderId) => {
436
+ if (!isSessionInitialized) {
437
+ throw new Error('Cannot pay offer: CMS session is not initialized');
438
+ }
439
+ const currentSession = checkoutSessionRef.current;
440
+ if (!currentSession.checkoutSessionId) {
441
+ throw new Error('Checkout session not initialized');
442
+ }
443
+ setIsPaying(true);
444
+ setPaymentError(null);
445
+ try {
446
+ await payWithCheckoutSessionAsync({
447
+ checkoutSessionId: currentSession.checkoutSessionId,
448
+ orderId: orderId || mainOrderId,
449
+ });
450
+ return { checkoutUrl: '' };
451
+ }
452
+ catch (err) {
453
+ const error = err instanceof Error ? err : new Error('Payment failed');
454
+ setPaymentError(error);
455
+ throw error;
456
+ }
457
+ finally {
458
+ setIsPaying(false);
459
+ }
460
+ }, [mainOrderId, payWithCheckoutSessionAsync, isSessionInitialized]);
461
+ // ─────────────────────────────────────────────────────────────────────────
462
+ // Return
463
+ // ─────────────────────────────────────────────────────────────────────────
464
+ return {
465
+ offer: offer || null,
466
+ isLoading: isOfferLoading,
467
+ isUpdatingSummary: checkoutSession.isUpdatingSummary,
468
+ error: error,
469
+ summary,
470
+ lineItems,
471
+ getAvailableVariants,
472
+ selectVariant,
473
+ updateVariant,
474
+ updateQuantity,
475
+ selections,
476
+ payOffer,
477
+ isPaying,
478
+ paymentError,
479
+ checkoutSessionId: checkoutSession.checkoutSessionId,
480
+ isInitializing,
481
+ isLoadingVariant,
482
+ };
483
+ }
@@ -1,78 +1,12 @@
1
- /**
2
- * Offers Hook using TanStack Query
3
- * Handles offers with automatic caching
4
- */
5
- import { Offer, OfferSummary } from '../../core/resources/offers';
6
- import { CurrencyOptions, OrderSummary } from '../../core/resources/postPurchases';
7
- export interface UseOffersQueryOptions {
8
- /**
9
- * Array of offer IDs to fetch
10
- */
11
- offerIds?: string[];
12
- /**
13
- * Whether to fetch offers automatically on mount
14
- * @default true
15
- */
16
- enabled?: boolean;
17
- /**
18
- * Return URL for checkout sessions
19
- */
20
- returnUrl?: string;
21
- /**
22
- * Order ID to associate with the offers (required for payments)
23
- */
24
- orderId?: string;
25
- /**
26
- * The ID of the currently active offer to preview/fetch summary for.
27
- * If provided, the hook will automatically fetch and manage the summary for this offer.
28
- */
29
- activeOfferId?: string;
30
- /**
31
- * Whether to skip auto-preview fetching (e.g. during navigation or processing)
32
- */
33
- skipPreview?: boolean;
34
- }
35
- export interface UseOffersQueryResult {
36
- offers: Offer[];
1
+ export function useOffersQuery(options?: {}): {
2
+ offers: import("../../core/resources/offers").Offer[];
37
3
  isLoading: boolean;
38
4
  error: Error | null;
39
- /**
40
- * Summary for the active offer (if activeOfferId is provided)
41
- * Automatically falls back to static summary while loading dynamic data
42
- */
43
- activeSummary: OrderSummary | OfferSummary | null;
44
- /**
45
- * Whether the active offer summary is currently loading
46
- */
5
+ activeSummary: any;
47
6
  isActiveSummaryLoading: boolean;
48
- /**
49
- * Pay for an offer
50
- * Initializes a checkout session and pays it
51
- */
52
- payOffer: (offerId: string, orderId?: string) => Promise<void>;
53
- /**
54
- * Preview an offer's price/summary
55
- */
56
- preview: (offerId: string) => Promise<OrderSummary | OfferSummary | null>;
57
- /**
58
- * Get available variants for a product in an offer
59
- */
60
- getAvailableVariants: (offerId: string, productId: string) => {
61
- variantId: string;
62
- variantName: string;
63
- variantSku: string | null;
64
- variantDefault: boolean | null;
65
- variantExternalId: string | null;
66
- priceId: string;
67
- currencyOptions: CurrencyOptions;
68
- }[];
69
- /**
70
- * Select a variant for a product in an offer
71
- */
72
- selectVariant: (offerId: string, productId: string, variantId: string) => Promise<OrderSummary | null>;
73
- /**
74
- * Check if variants are loading for a product
75
- */
76
- isLoadingVariants: (offerId: string, productId: string) => boolean;
77
- }
78
- export declare function useOffersQuery(options?: UseOffersQueryOptions): UseOffersQueryResult;
7
+ payOffer: (offerId: any, orderId: any) => Promise<void>;
8
+ preview: (offerId: any) => Promise<any>;
9
+ getAvailableVariants: (offerId: any, productId: any) => any;
10
+ selectVariant: (offerId: any, productId: any, variantId: any) => Promise<any>;
11
+ isLoadingVariants: (offerId: any, productId: any) => any;
12
+ };