@tagadapay/plugin-sdk 2.8.10 → 3.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.
- package/README.md +14 -14
- package/dist/index.js +1 -1
- package/dist/react/hooks/usePluginConfig.d.ts +1 -0
- package/dist/react/hooks/usePluginConfig.js +69 -18
- package/dist/react/providers/TagadaProvider.js +1 -4
- package/dist/v2/core/client.d.ts +18 -0
- package/dist/v2/core/client.js +45 -0
- package/dist/v2/core/config/environment.d.ts +8 -0
- package/dist/v2/core/config/environment.js +18 -0
- package/dist/v2/core/funnelClient.d.ts +84 -0
- package/dist/v2/core/funnelClient.js +252 -0
- package/dist/v2/core/index.d.ts +2 -0
- package/dist/v2/core/index.js +3 -0
- package/dist/v2/core/resources/apiClient.js +1 -1
- package/dist/v2/core/resources/funnel.d.ts +1 -0
- package/dist/v2/core/resources/offers.d.ts +182 -8
- package/dist/v2/core/resources/offers.js +25 -0
- package/dist/v2/core/resources/products.d.ts +5 -0
- package/dist/v2/core/resources/products.js +15 -1
- package/dist/v2/core/types.d.ts +1 -0
- package/dist/v2/core/utils/funnelQueryKeys.d.ts +23 -0
- package/dist/v2/core/utils/funnelQueryKeys.js +23 -0
- package/dist/v2/core/utils/index.d.ts +2 -0
- package/dist/v2/core/utils/index.js +2 -0
- package/dist/v2/core/utils/pluginConfig.js +44 -32
- package/dist/v2/core/utils/sessionStorage.d.ts +20 -0
- package/dist/v2/core/utils/sessionStorage.js +39 -0
- package/dist/v2/index.d.ts +4 -2
- package/dist/v2/index.js +1 -1
- package/dist/v2/react/components/DebugDrawer.js +99 -2
- package/dist/v2/react/hooks/__examples__/FunnelContextExample.d.ts +3 -0
- package/dist/v2/react/hooks/__examples__/FunnelContextExample.js +4 -3
- package/dist/v2/react/hooks/useClubOffers.d.ts +2 -2
- package/dist/v2/react/hooks/useFunnel.d.ts +27 -39
- package/dist/v2/react/hooks/useFunnel.js +22 -659
- package/dist/v2/react/hooks/useFunnelLegacy.d.ts +52 -0
- package/dist/v2/react/hooks/useFunnelLegacy.js +733 -0
- package/dist/v2/react/hooks/useOfferQuery.d.ts +109 -0
- package/dist/v2/react/hooks/useOfferQuery.js +483 -0
- package/dist/v2/react/hooks/useOffersQuery.d.ts +9 -75
- package/dist/v2/react/hooks/useProductsQuery.d.ts +1 -0
- package/dist/v2/react/hooks/useProductsQuery.js +10 -6
- package/dist/v2/react/index.d.ts +8 -4
- package/dist/v2/react/index.js +4 -2
- package/dist/v2/react/providers/TagadaProvider.d.ts +66 -5
- package/dist/v2/react/providers/TagadaProvider.js +120 -6
- package/dist/v2/standalone/index.d.ts +20 -0
- package/dist/v2/standalone/index.js +22 -0
- package/dist/v2/vue/index.d.ts +6 -0
- package/dist/v2/vue/index.js +10 -0
- package/package.json +6 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { FunnelResource } from './resources/funnel';
|
|
2
|
+
import { EventDispatcher } from './utils/eventDispatcher';
|
|
3
|
+
import { getFunnelSessionCookie, setFunnelSessionCookie } from './utils/sessionStorage';
|
|
4
|
+
import { detectEnvironment } from './config/environment';
|
|
5
|
+
export class FunnelClient {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.eventDispatcher = new EventDispatcher();
|
|
8
|
+
// Guards
|
|
9
|
+
this.isInitializing = false;
|
|
10
|
+
this.initializationAttempted = false;
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.resource = new FunnelResource(config.apiClient);
|
|
13
|
+
this.state = {
|
|
14
|
+
context: null,
|
|
15
|
+
isLoading: false,
|
|
16
|
+
isInitialized: false,
|
|
17
|
+
isNavigating: false,
|
|
18
|
+
error: null,
|
|
19
|
+
sessionError: null,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Update configuration (e.g. when plugin config loads)
|
|
24
|
+
*/
|
|
25
|
+
setConfig(config) {
|
|
26
|
+
this.config = { ...this.config, ...config };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Subscribe to state changes
|
|
30
|
+
*/
|
|
31
|
+
subscribe(listener) {
|
|
32
|
+
return this.eventDispatcher.subscribe(listener);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get current state
|
|
36
|
+
*/
|
|
37
|
+
getState() {
|
|
38
|
+
return this.state;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Initialize session with automatic detection (cookies, URL, etc.)
|
|
42
|
+
*/
|
|
43
|
+
async autoInitialize(authSession, store, funnelId) {
|
|
44
|
+
if (this.state.context)
|
|
45
|
+
return this.state.context;
|
|
46
|
+
if (this.isInitializing)
|
|
47
|
+
return null;
|
|
48
|
+
if (this.initializationAttempted)
|
|
49
|
+
return null;
|
|
50
|
+
this.initializationAttempted = true;
|
|
51
|
+
this.isInitializing = true;
|
|
52
|
+
this.updateState({ isLoading: true, error: null });
|
|
53
|
+
try {
|
|
54
|
+
// URL params
|
|
55
|
+
const params = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : '');
|
|
56
|
+
const urlFunnelId = params.get('funnelId');
|
|
57
|
+
const effectiveFunnelId = urlFunnelId || funnelId;
|
|
58
|
+
let existingSessionId = params.get('funnelSessionId');
|
|
59
|
+
// Cookie fallback
|
|
60
|
+
if (!existingSessionId) {
|
|
61
|
+
existingSessionId = getFunnelSessionCookie() || null;
|
|
62
|
+
}
|
|
63
|
+
if (this.config.debugMode) {
|
|
64
|
+
console.log('🚀 [FunnelClient] Auto-initializing...', { existingSessionId, effectiveFunnelId });
|
|
65
|
+
}
|
|
66
|
+
// Note: We proceed even without funnelId/sessionId - the backend will create a new anonymous session if needed
|
|
67
|
+
const response = await this.resource.initialize({
|
|
68
|
+
cmsSession: {
|
|
69
|
+
customerId: authSession.customerId,
|
|
70
|
+
sessionId: authSession.sessionId,
|
|
71
|
+
storeId: store.id,
|
|
72
|
+
accountId: store.accountId,
|
|
73
|
+
},
|
|
74
|
+
funnelId: effectiveFunnelId,
|
|
75
|
+
existingSessionId: existingSessionId || undefined,
|
|
76
|
+
currentUrl: typeof window !== 'undefined' ? window.location.href : undefined,
|
|
77
|
+
});
|
|
78
|
+
if (response.success && response.context) {
|
|
79
|
+
const enriched = this.enrichContext(response.context);
|
|
80
|
+
this.handleSessionSuccess(enriched);
|
|
81
|
+
return enriched;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error(response.error || 'Failed to initialize funnel session');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
89
|
+
this.updateState({ error: err, isLoading: false });
|
|
90
|
+
if (this.config.debugMode) {
|
|
91
|
+
console.error('❌ [FunnelClient] Init failed:', err);
|
|
92
|
+
}
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
this.isInitializing = false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Manual initialization
|
|
101
|
+
*/
|
|
102
|
+
async initialize(authSession, store, funnelId, entryStepId) {
|
|
103
|
+
this.updateState({ isLoading: true, error: null });
|
|
104
|
+
try {
|
|
105
|
+
const response = await this.resource.initialize({
|
|
106
|
+
cmsSession: {
|
|
107
|
+
customerId: authSession.customerId,
|
|
108
|
+
sessionId: authSession.sessionId,
|
|
109
|
+
storeId: store.id,
|
|
110
|
+
accountId: store.accountId,
|
|
111
|
+
},
|
|
112
|
+
funnelId: funnelId,
|
|
113
|
+
entryStepId,
|
|
114
|
+
currentUrl: typeof window !== 'undefined' ? window.location.href : undefined,
|
|
115
|
+
});
|
|
116
|
+
if (response.success && response.context) {
|
|
117
|
+
const enriched = this.enrichContext(response.context);
|
|
118
|
+
this.handleSessionSuccess(enriched);
|
|
119
|
+
return enriched;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
throw new Error(response.error || 'Failed to initialize');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
127
|
+
this.updateState({ error: err, isLoading: false });
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Navigate
|
|
133
|
+
*/
|
|
134
|
+
async navigate(event) {
|
|
135
|
+
if (!this.state.context?.sessionId)
|
|
136
|
+
throw new Error('No active session');
|
|
137
|
+
this.updateState({ isNavigating: true, isLoading: true });
|
|
138
|
+
try {
|
|
139
|
+
const response = await this.resource.navigate({
|
|
140
|
+
sessionId: this.state.context.sessionId,
|
|
141
|
+
event
|
|
142
|
+
});
|
|
143
|
+
if (response.success && response.result) {
|
|
144
|
+
// Refresh session to get updated context
|
|
145
|
+
await this.refreshSession();
|
|
146
|
+
this.updateState({ isNavigating: false, isLoading: false });
|
|
147
|
+
const result = response.result;
|
|
148
|
+
// Auto-redirect if enabled (default: true) and result has a URL
|
|
149
|
+
const shouldAutoRedirect = this.config.autoRedirect !== false; // Default to true
|
|
150
|
+
if (shouldAutoRedirect && result?.url && typeof window !== 'undefined') {
|
|
151
|
+
if (this.config.debugMode) {
|
|
152
|
+
console.log('🚀 [FunnelClient] Auto-redirecting to:', result.url);
|
|
153
|
+
}
|
|
154
|
+
window.location.href = result.url;
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
throw new Error(response.error || 'Navigation failed');
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
162
|
+
this.updateState({ error: err, isNavigating: false, isLoading: false });
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Refresh session data
|
|
168
|
+
*/
|
|
169
|
+
async refreshSession() {
|
|
170
|
+
if (!this.state.context?.sessionId)
|
|
171
|
+
return;
|
|
172
|
+
try {
|
|
173
|
+
const response = await this.resource.getSession(this.state.context.sessionId);
|
|
174
|
+
if (response.success && response.context) {
|
|
175
|
+
const enriched = this.enrichContext(response.context);
|
|
176
|
+
this.updateState({ context: enriched, sessionError: null });
|
|
177
|
+
return enriched;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
this.updateState({ sessionError: error instanceof Error ? error : new Error(String(error)) });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Update context data
|
|
186
|
+
*/
|
|
187
|
+
async updateContext(updates) {
|
|
188
|
+
if (!this.state.context?.sessionId)
|
|
189
|
+
throw new Error('No active session');
|
|
190
|
+
this.updateState({ isLoading: true });
|
|
191
|
+
try {
|
|
192
|
+
const response = await this.resource.updateContext(this.state.context.sessionId, { contextUpdates: updates });
|
|
193
|
+
if (response.success) {
|
|
194
|
+
await this.refreshSession();
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
throw new Error(response.error || 'Failed to update context');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
finally {
|
|
201
|
+
this.updateState({ isLoading: false });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* End session
|
|
206
|
+
*/
|
|
207
|
+
async endSession() {
|
|
208
|
+
if (!this.state.context?.sessionId)
|
|
209
|
+
return;
|
|
210
|
+
try {
|
|
211
|
+
await this.resource.endSession(this.state.context.sessionId);
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
this.state.context = null;
|
|
215
|
+
this.updateState({ context: null, isInitialized: false });
|
|
216
|
+
if (typeof document !== 'undefined') {
|
|
217
|
+
// Clear cookie via import or manually if needed, but we have utility for that
|
|
218
|
+
// We should probably import clearFunnelSessionCookie
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Private helpers
|
|
223
|
+
updateState(updates) {
|
|
224
|
+
this.state = { ...this.state, ...updates };
|
|
225
|
+
this.eventDispatcher.notify(this.state);
|
|
226
|
+
}
|
|
227
|
+
handleSessionSuccess(context) {
|
|
228
|
+
setFunnelSessionCookie(context.sessionId);
|
|
229
|
+
this.updateState({
|
|
230
|
+
context,
|
|
231
|
+
isLoading: false,
|
|
232
|
+
isInitialized: true,
|
|
233
|
+
error: null,
|
|
234
|
+
sessionError: null
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
enrichContext(ctx) {
|
|
238
|
+
const env = this.config.environment?.environment || detectEnvironment();
|
|
239
|
+
if (env !== 'local')
|
|
240
|
+
return ctx;
|
|
241
|
+
const localResources = this.config.pluginConfig?.staticResources || {};
|
|
242
|
+
if (Object.keys(localResources).length === 0)
|
|
243
|
+
return ctx;
|
|
244
|
+
return {
|
|
245
|
+
...ctx,
|
|
246
|
+
static: {
|
|
247
|
+
...localResources,
|
|
248
|
+
...(ctx.static || {})
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
package/dist/v2/core/index.d.ts
CHANGED
package/dist/v2/core/index.js
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
export * from './utils';
|
|
7
7
|
// Export resources (axios-based API clients)
|
|
8
8
|
export * from './resources';
|
|
9
|
+
// Export clients (stateful logic)
|
|
10
|
+
export * from './client';
|
|
11
|
+
export * from './funnelClient';
|
|
9
12
|
// Export path remapping helpers (framework-agnostic)
|
|
10
13
|
export * from './pathRemapping';
|
|
11
14
|
// Export legacy files that are still needed
|
|
@@ -10,7 +10,7 @@ export class ApiClient {
|
|
|
10
10
|
// Circuit breaker state
|
|
11
11
|
this.requestHistory = new Map();
|
|
12
12
|
this.WINDOW_MS = 5000; // 5 seconds window
|
|
13
|
-
this.MAX_REQUESTS =
|
|
13
|
+
this.MAX_REQUESTS = 30; // Max 30 requests per endpoint in window
|
|
14
14
|
this.axios = axios.create({
|
|
15
15
|
baseURL: config.baseURL,
|
|
16
16
|
timeout: config.timeout || 30000,
|
|
@@ -3,44 +3,205 @@
|
|
|
3
3
|
* Axios-based API client for offers endpoints
|
|
4
4
|
*/
|
|
5
5
|
import { ApiClient } from './apiClient';
|
|
6
|
-
|
|
6
|
+
import type { CheckoutSessionState, OrderSummary, OrderSummaryItem, VariantOption } from './postPurchases';
|
|
7
|
+
export type { CheckoutSessionState, OrderSummary, OrderSummaryItem, VariantOption };
|
|
8
|
+
/**
|
|
9
|
+
* Currency option with rate, amount, and lock info
|
|
10
|
+
*/
|
|
11
|
+
export interface OfferCurrencyOption {
|
|
12
|
+
rate: number;
|
|
13
|
+
amount: number;
|
|
14
|
+
lock?: boolean;
|
|
15
|
+
date?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Product info within an offer
|
|
19
|
+
*/
|
|
20
|
+
export interface OfferProduct {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
description: string | null;
|
|
24
|
+
storeId?: string;
|
|
25
|
+
externalProductId?: string | null;
|
|
26
|
+
externalCollectionIds?: string[] | null;
|
|
27
|
+
active?: boolean;
|
|
28
|
+
isShippable?: boolean;
|
|
29
|
+
isTaxable?: boolean;
|
|
30
|
+
taxCategory?: string;
|
|
31
|
+
unitLabel?: string;
|
|
32
|
+
accountingCode?: string;
|
|
33
|
+
accountId?: string;
|
|
34
|
+
autoSync?: boolean;
|
|
35
|
+
creditPrice?: number | null;
|
|
36
|
+
createdAt?: string;
|
|
37
|
+
updatedAt?: string;
|
|
38
|
+
storefrontRawData?: any;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Variant info within an offer
|
|
42
|
+
*/
|
|
43
|
+
export interface OfferVariant {
|
|
44
|
+
id: string;
|
|
45
|
+
productId?: string;
|
|
46
|
+
default?: boolean;
|
|
47
|
+
name: string;
|
|
48
|
+
description?: string | null;
|
|
49
|
+
sku?: string;
|
|
50
|
+
externalVariantId?: string | null;
|
|
51
|
+
price?: number | null;
|
|
52
|
+
currency?: string | null;
|
|
53
|
+
compareAtPrice?: number | null;
|
|
54
|
+
grams?: number | null;
|
|
55
|
+
imageUrl: string | null;
|
|
56
|
+
active?: boolean;
|
|
57
|
+
accountId?: string | null;
|
|
58
|
+
creditPrice?: number | null;
|
|
59
|
+
createdAt?: string;
|
|
60
|
+
updatedAt?: string;
|
|
61
|
+
product?: OfferProduct | null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Price info within an offer line item
|
|
65
|
+
*/
|
|
66
|
+
export interface OfferLineItemPrice {
|
|
7
67
|
id: string;
|
|
68
|
+
createdAt?: string;
|
|
69
|
+
updatedAt?: string;
|
|
70
|
+
accountId?: string;
|
|
71
|
+
productId?: string;
|
|
72
|
+
variantId: string;
|
|
73
|
+
default?: boolean;
|
|
74
|
+
currencyOptions: Record<string, OfferCurrencyOption>;
|
|
75
|
+
recurring?: boolean;
|
|
76
|
+
billingTiming?: string;
|
|
77
|
+
interval?: string;
|
|
78
|
+
intervalCount?: number;
|
|
79
|
+
rebillWithOrder?: boolean;
|
|
80
|
+
rebillMode?: string | null;
|
|
81
|
+
rebillStepIntervalMs?: number | null;
|
|
82
|
+
isExternalSellingPlan?: boolean;
|
|
83
|
+
isInternalSellingPlan?: boolean;
|
|
84
|
+
shopifySellingPlanId?: string | null;
|
|
85
|
+
creditsPerRebill?: number | null;
|
|
86
|
+
deliverySettings?: Record<string, any>;
|
|
87
|
+
priceSettings?: Record<string, any>;
|
|
88
|
+
variant?: OfferVariant | null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Line item within an offer
|
|
92
|
+
*/
|
|
93
|
+
export interface OfferLineItem {
|
|
94
|
+
id: string;
|
|
95
|
+
offerId?: string;
|
|
96
|
+
quantity: number;
|
|
97
|
+
priceId?: string;
|
|
98
|
+
variantId?: string;
|
|
99
|
+
productId?: string;
|
|
100
|
+
price: OfferLineItemPrice | null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Summary item within an offer summary
|
|
104
|
+
*/
|
|
105
|
+
export interface OfferSummaryItem {
|
|
106
|
+
id: string;
|
|
107
|
+
productId: string;
|
|
108
|
+
variantId: string;
|
|
109
|
+
priceId?: string;
|
|
8
110
|
product: {
|
|
9
111
|
name: string;
|
|
10
112
|
description: string;
|
|
11
113
|
};
|
|
12
114
|
variant: {
|
|
13
115
|
name: string;
|
|
14
|
-
|
|
116
|
+
description?: string;
|
|
117
|
+
imageUrl: string | null;
|
|
118
|
+
grams?: number | null;
|
|
15
119
|
};
|
|
16
|
-
|
|
120
|
+
sku?: string;
|
|
17
121
|
unitAmount: number;
|
|
122
|
+
quantity: number;
|
|
18
123
|
amount: number;
|
|
19
124
|
adjustedAmount: number;
|
|
20
125
|
currency: string;
|
|
126
|
+
adjustments?: any[];
|
|
127
|
+
recurring?: boolean;
|
|
128
|
+
rebillMode?: string | null;
|
|
129
|
+
interval?: string;
|
|
130
|
+
intervalCount?: number;
|
|
131
|
+
totalBillingCycles?: number;
|
|
132
|
+
unitAmountAfterFirstCycle?: number;
|
|
133
|
+
isExternalSellingPlan?: boolean;
|
|
134
|
+
isInternalSellingPlan?: boolean;
|
|
135
|
+
deliverySettings?: Record<string, any>;
|
|
136
|
+
priceSettings?: Record<string, any>;
|
|
137
|
+
orderLineItemProduct?: {
|
|
138
|
+
name: string;
|
|
139
|
+
};
|
|
140
|
+
orderLineItemVariant?: {
|
|
141
|
+
name: string;
|
|
142
|
+
imageUrl: string | null;
|
|
143
|
+
};
|
|
144
|
+
metadata?: Record<string, any>;
|
|
21
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Offer summary containing items and totals
|
|
148
|
+
*/
|
|
22
149
|
export interface OfferSummary {
|
|
23
|
-
items:
|
|
150
|
+
items: OfferSummaryItem[];
|
|
151
|
+
totalWeight?: number;
|
|
152
|
+
subtotalAmount?: number;
|
|
153
|
+
lineItemsPromotionAmount?: number;
|
|
154
|
+
subtotalAdjustedAmount?: number;
|
|
155
|
+
totalPromotionAmount: number;
|
|
156
|
+
lineItemsTaxAmount?: number;
|
|
157
|
+
totalTaxAmount?: number;
|
|
158
|
+
shippingCost?: number;
|
|
159
|
+
shippingCostIsFree?: boolean;
|
|
24
160
|
totalAmount: number;
|
|
25
161
|
totalAdjustedAmount: number;
|
|
26
|
-
totalPromotionAmount: number;
|
|
27
|
-
currency: string;
|
|
28
162
|
adjustments: {
|
|
29
163
|
type: string;
|
|
30
164
|
description: string;
|
|
31
165
|
amount: number;
|
|
32
166
|
}[];
|
|
167
|
+
currency: string;
|
|
168
|
+
_consolidated?: boolean;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Offer promotion association
|
|
172
|
+
*/
|
|
173
|
+
export interface OfferPromotion {
|
|
174
|
+
id: string;
|
|
175
|
+
promotionId: string;
|
|
176
|
+
promotion?: {
|
|
177
|
+
id: string;
|
|
178
|
+
name: string;
|
|
179
|
+
code?: string | null;
|
|
180
|
+
automatic?: boolean;
|
|
181
|
+
};
|
|
33
182
|
}
|
|
34
|
-
|
|
35
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Main Offer type
|
|
185
|
+
*/
|
|
36
186
|
export interface Offer {
|
|
37
187
|
id: string;
|
|
188
|
+
createdAt?: string;
|
|
189
|
+
updatedAt?: string;
|
|
190
|
+
storeId?: string;
|
|
191
|
+
type?: string;
|
|
38
192
|
titleTrans: Record<string, string>;
|
|
193
|
+
expiryDate?: string | null;
|
|
39
194
|
summaries: OfferSummary[];
|
|
195
|
+
offerLineItems?: OfferLineItem[];
|
|
196
|
+
promotions?: OfferPromotion[];
|
|
40
197
|
}
|
|
41
198
|
export declare class OffersResource {
|
|
42
199
|
private apiClient;
|
|
43
200
|
constructor(apiClient: ApiClient);
|
|
201
|
+
/**
|
|
202
|
+
* Get a single offer by ID
|
|
203
|
+
*/
|
|
204
|
+
getOfferById(offerId: string): Promise<Offer>;
|
|
44
205
|
/**
|
|
45
206
|
* Get offers for a store
|
|
46
207
|
*/
|
|
@@ -70,6 +231,19 @@ export declare class OffersResource {
|
|
|
70
231
|
*/
|
|
71
232
|
payOffer(offerId: string, orderId?: string): Promise<any>;
|
|
72
233
|
/**
|
|
234
|
+
* Transform offer to checkout session with dynamic variant selection
|
|
235
|
+
* Uses lineItems from the offer to create a new checkout session
|
|
236
|
+
*/
|
|
237
|
+
transformToCheckoutSession(offerId: string, lineItems: {
|
|
238
|
+
variantId: string;
|
|
239
|
+
quantity: number;
|
|
240
|
+
}[], mainOrderId: string, returnUrl?: string): Promise<{
|
|
241
|
+
checkoutUrl: string;
|
|
242
|
+
checkoutSessionId?: string;
|
|
243
|
+
customerId?: string;
|
|
244
|
+
}>;
|
|
245
|
+
/**
|
|
246
|
+
* @deprecated Use transformToCheckoutSession instead
|
|
73
247
|
* Transform offer to checkout session with dynamic variant selection
|
|
74
248
|
*/
|
|
75
249
|
transformToCheckout(offerId: string, returnUrl?: string): Promise<{
|
|
@@ -6,6 +6,13 @@ export class OffersResource {
|
|
|
6
6
|
constructor(apiClient) {
|
|
7
7
|
this.apiClient = apiClient;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Get a single offer by ID
|
|
11
|
+
*/
|
|
12
|
+
async getOfferById(offerId) {
|
|
13
|
+
const response = await this.apiClient.get(`/api/v1/offers/${offerId}`);
|
|
14
|
+
return response;
|
|
15
|
+
}
|
|
9
16
|
/**
|
|
10
17
|
* Get offers for a store
|
|
11
18
|
*/
|
|
@@ -77,6 +84,24 @@ export class OffersResource {
|
|
|
77
84
|
});
|
|
78
85
|
}
|
|
79
86
|
/**
|
|
87
|
+
* Transform offer to checkout session with dynamic variant selection
|
|
88
|
+
* Uses lineItems from the offer to create a new checkout session
|
|
89
|
+
*/
|
|
90
|
+
async transformToCheckoutSession(offerId, lineItems, mainOrderId, returnUrl) {
|
|
91
|
+
const response = await this.apiClient.post(`/api/v1/offers/${offerId}/transform-to-checkout`, {
|
|
92
|
+
offerId,
|
|
93
|
+
lineItems,
|
|
94
|
+
mainOrderId,
|
|
95
|
+
returnUrl: returnUrl || (typeof window !== 'undefined' ? window.location.origin : ''),
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
checkoutUrl: response.checkoutUrl,
|
|
99
|
+
checkoutSessionId: response.checkoutSessionId,
|
|
100
|
+
customerId: response.customerId,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @deprecated Use transformToCheckoutSession instead
|
|
80
105
|
* Transform offer to checkout session with dynamic variant selection
|
|
81
106
|
*/
|
|
82
107
|
async transformToCheckout(offerId, returnUrl) {
|
|
@@ -7,6 +7,7 @@ import { ApiClient } from './apiClient';
|
|
|
7
7
|
export interface GetProductsOptions {
|
|
8
8
|
storeId: string;
|
|
9
9
|
productIds?: string[];
|
|
10
|
+
variantIds?: string[];
|
|
10
11
|
includeVariants?: boolean;
|
|
11
12
|
includePrices?: boolean;
|
|
12
13
|
}
|
|
@@ -26,4 +27,8 @@ export declare class ProductsResource {
|
|
|
26
27
|
* Get multiple products by IDs
|
|
27
28
|
*/
|
|
28
29
|
getProductsByIds(productIds: string[], options: GetProductsOptions): Promise<Product[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Get products by variant IDs (efficient filtering on the backend)
|
|
32
|
+
*/
|
|
33
|
+
getProductsByVariantIds(variantIds: string[], options: GetProductsOptions): Promise<Product[]>;
|
|
29
34
|
}
|
|
@@ -10,9 +10,10 @@ export class ProductsResource {
|
|
|
10
10
|
* Get all products for a store
|
|
11
11
|
*/
|
|
12
12
|
async getProducts(options) {
|
|
13
|
-
const { storeId, includeVariants = true, includePrices = true } = options;
|
|
13
|
+
const { storeId, variantIds, includeVariants = true, includePrices = true } = options;
|
|
14
14
|
const response = await this.apiClient.post('/api/v1/products', {
|
|
15
15
|
storeId,
|
|
16
|
+
variantIds,
|
|
16
17
|
includeVariants,
|
|
17
18
|
includePrices,
|
|
18
19
|
});
|
|
@@ -46,4 +47,17 @@ export class ProductsResource {
|
|
|
46
47
|
const results = await Promise.all(fetchPromises);
|
|
47
48
|
return results.filter((product) => product !== null);
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Get products by variant IDs (efficient filtering on the backend)
|
|
52
|
+
*/
|
|
53
|
+
async getProductsByVariantIds(variantIds, options) {
|
|
54
|
+
const { storeId, includeVariants = true, includePrices = true } = options;
|
|
55
|
+
const response = await this.apiClient.post('/api/v1/products', {
|
|
56
|
+
storeId,
|
|
57
|
+
variantIds, // Backend will filter products that have these variants
|
|
58
|
+
includeVariants,
|
|
59
|
+
includePrices,
|
|
60
|
+
});
|
|
61
|
+
return Array.isArray(response) ? response : response.items ?? [];
|
|
62
|
+
}
|
|
49
63
|
}
|
package/dist/v2/core/types.d.ts
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Funnel Query Keys - Core SDK (Framework-agnostic)
|
|
3
|
+
* Standardized query keys for caching funnel data
|
|
4
|
+
* Can be used with any query library (TanStack Query, SWR, etc.)
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Query keys for funnel operations
|
|
8
|
+
* These keys are used for caching and invalidation strategies
|
|
9
|
+
*/
|
|
10
|
+
export declare const funnelQueryKeys: {
|
|
11
|
+
/**
|
|
12
|
+
* Key for a specific funnel session
|
|
13
|
+
*/
|
|
14
|
+
session: (sessionId: string) => readonly ["funnel", "session", string];
|
|
15
|
+
/**
|
|
16
|
+
* Key for all funnel sessions
|
|
17
|
+
*/
|
|
18
|
+
allSessions: () => readonly ["funnel", "sessions"];
|
|
19
|
+
/**
|
|
20
|
+
* Key for funnel metadata/configuration
|
|
21
|
+
*/
|
|
22
|
+
funnelMeta: (funnelId: string) => readonly ["funnel", "meta", string];
|
|
23
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Funnel Query Keys - Core SDK (Framework-agnostic)
|
|
3
|
+
* Standardized query keys for caching funnel data
|
|
4
|
+
* Can be used with any query library (TanStack Query, SWR, etc.)
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Query keys for funnel operations
|
|
8
|
+
* These keys are used for caching and invalidation strategies
|
|
9
|
+
*/
|
|
10
|
+
export const funnelQueryKeys = {
|
|
11
|
+
/**
|
|
12
|
+
* Key for a specific funnel session
|
|
13
|
+
*/
|
|
14
|
+
session: (sessionId) => ['funnel', 'session', sessionId],
|
|
15
|
+
/**
|
|
16
|
+
* Key for all funnel sessions
|
|
17
|
+
*/
|
|
18
|
+
allSessions: () => ['funnel', 'sessions'],
|
|
19
|
+
/**
|
|
20
|
+
* Key for funnel metadata/configuration
|
|
21
|
+
*/
|
|
22
|
+
funnelMeta: (funnelId) => ['funnel', 'meta', funnelId],
|
|
23
|
+
};
|