@tagadapay/plugin-sdk 2.7.0 → 2.7.3

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.
@@ -105,15 +105,16 @@ const loadLocalDevConfig = async (configVariant = 'default') => {
105
105
  }
106
106
  };
107
107
  /**
108
- * Load production config from headers and meta tags
108
+ * Load production config from meta tags
109
+ * Meta tags are injected by the plugin middleware during HTML serving
110
+ * This avoids making an additional HEAD request (~300ms savings)
109
111
  */
110
112
  const loadProductionConfig = async () => {
111
113
  try {
112
- // Get headers
113
- const response = await fetch(window.location.href, { method: 'HEAD' });
114
- const storeId = response.headers.get('X-Plugin-Store-Id') || undefined;
115
- const accountId = response.headers.get('X-Plugin-Account-Id') || undefined;
116
- const basePath = response.headers.get('X-Plugin-Base-Path') || '/';
114
+ // Read store/account info from meta tags (injected by middleware)
115
+ const storeId = document.querySelector('meta[name="x-plugin-store-id"]')?.getAttribute('content') || undefined;
116
+ const accountId = document.querySelector('meta[name="x-plugin-account-id"]')?.getAttribute('content') || undefined;
117
+ const basePath = document.querySelector('meta[name="x-plugin-base-path"]')?.getAttribute('content') || '/';
117
118
  // Get deployment config from meta tags
118
119
  let config = {};
119
120
  try {
@@ -127,9 +128,16 @@ const loadProductionConfig = async () => {
127
128
  catch {
128
129
  // Deployment config is optional
129
130
  }
131
+ console.log('🚀 Plugin config loaded from meta tags (no HEAD request needed):', {
132
+ storeId,
133
+ accountId,
134
+ basePath,
135
+ configKeys: Object.keys(config)
136
+ });
130
137
  return { storeId, accountId, basePath, config };
131
138
  }
132
- catch {
139
+ catch (error) {
140
+ console.warn('⚠️ Failed to load plugin config from meta tags:', error);
133
141
  return { basePath: '/', config: {} };
134
142
  }
135
143
  };
@@ -4,5 +4,6 @@
4
4
  */
5
5
  export * from './utils';
6
6
  export * from './resources';
7
+ export * from './pathRemapping';
7
8
  export * from './googleAutocomplete';
8
9
  export * from './isoData';
@@ -6,6 +6,8 @@
6
6
  export * from './utils';
7
7
  // Export resources (axios-based API clients)
8
8
  export * from './resources';
9
+ // Export path remapping helpers (framework-agnostic)
10
+ export * from './pathRemapping';
9
11
  // Export legacy files that are still needed
10
12
  export * from './googleAutocomplete';
11
13
  export * from './isoData';
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Path Remapping Helpers - Production Ready
3
+ *
4
+ * Framework-agnostic utility for handling path remapping from TagadaPay routing
5
+ * Includes error handling, SSR compatibility, performance optimizations, and edge case handling
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ /**
10
+ * Path information including both external and internal paths
11
+ */
12
+ export interface PathInfo {
13
+ /** The path shown in the browser URL bar (external/remapped path) */
14
+ externalPath: string;
15
+ /** The path that the plugin expects (internal/original path) */
16
+ internalPath: string | null;
17
+ /** Whether path remapping is active */
18
+ isRemapped: boolean;
19
+ /** The full URL including protocol, host, query, and hash */
20
+ fullUrl: string;
21
+ /** Query parameters from the URL */
22
+ query: URLSearchParams;
23
+ /** URL hash without the # symbol */
24
+ hash: string;
25
+ }
26
+ /**
27
+ * Get the internal path that the plugin expects
28
+ *
29
+ * Reads from the meta tag injected by TagadaPay middleware.
30
+ * Results are cached for performance.
31
+ *
32
+ * @returns The internal path from meta tag, or null if no remapping is active
33
+ * @throws {Error} If called in a non-browser environment (SSR)
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const internalPath = getInternalPath();
38
+ * if (internalPath) {
39
+ * console.log('Remapped from external to:', internalPath);
40
+ * }
41
+ * ```
42
+ */
43
+ export declare function getInternalPath(): string | null;
44
+ /**
45
+ * Check if path remapping is currently active
46
+ *
47
+ * @returns true if a remapped path is configured, false otherwise
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * if (isPathRemapped()) {
52
+ * console.log('User is viewing a remapped URL');
53
+ * }
54
+ * ```
55
+ */
56
+ export declare function isPathRemapped(): boolean;
57
+ /**
58
+ * Get comprehensive path mapping information
59
+ *
60
+ * Returns both the external (browser) path and internal (plugin) path,
61
+ * along with query parameters and hash.
62
+ *
63
+ * @returns {PathInfo} Complete path information object
64
+ * @throws {Error} If called in a non-browser environment (SSR)
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const pathInfo = getPathInfo();
69
+ * console.log('Browser shows:', pathInfo.externalPath);
70
+ * console.log('Plugin expects:', pathInfo.internalPath);
71
+ * console.log('Query params:', pathInfo.query.get('id'));
72
+ * ```
73
+ */
74
+ export declare function getPathInfo(): PathInfo;
75
+ export declare function shouldMatchRoute(internalPath: string): boolean;
76
+ /**
77
+ * Get the current matched path
78
+ *
79
+ * Returns the actual path in the browser if it matches the given internal path.
80
+ * Useful when you need the actual URL path for constructing links or API calls.
81
+ *
82
+ * @param internalPath - The plugin's defined path
83
+ * @returns The current browser path if it matches, null otherwise
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const matchedPath = getMatchedPath('/hello');
88
+ * if (matchedPath) {
89
+ * console.log('Matched at:', matchedPath); // Could be /hello or /hello2
90
+ * }
91
+ * ```
92
+ */
93
+ export declare function getMatchedPath(internalPath: string): string | null;
94
+ /**
95
+ * Manually clear the internal path cache
96
+ *
97
+ * Useful for testing or when you know the meta tag has changed.
98
+ * Normally you don't need to call this as it's handled automatically.
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * // After dynamically updating the meta tag
103
+ * document.querySelector('meta[name="x-plugin-internal-path"]')
104
+ * ?.setAttribute('content', '/new-path');
105
+ * clearInternalPathCache();
106
+ * ```
107
+ */
108
+ export declare function clearInternalPathCache(): void;
109
+ /**
110
+ * Check if running in a browser environment
111
+ *
112
+ * Useful for conditional logic in universal/isomorphic code.
113
+ *
114
+ * @returns true if in browser, false if in SSR/Node environment
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * if (isBrowserEnvironment()) {
119
+ * // Safe to use window/document
120
+ * const path = getInternalPath();
121
+ * }
122
+ * ```
123
+ */
124
+ export declare function isBrowserEnvironment(): boolean;
125
+ /**
126
+ * Get debug information about the current routing state
127
+ *
128
+ * Returns detailed information for debugging path remapping issues.
129
+ * Only available in development mode.
130
+ *
131
+ * @returns Debug information object
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * const debug = getDebugInfo();
136
+ * console.table(debug);
137
+ * ```
138
+ */
139
+ export declare function getDebugInfo(): Record<string, any>;
140
+ /**
141
+ * Cleanup function to remove event listeners
142
+ *
143
+ * Call this when unmounting the plugin or cleaning up resources.
144
+ * Important for preventing memory leaks in SPAs.
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * // In React
149
+ * useEffect(() => {
150
+ * return () => {
151
+ * cleanupPathRemapping();
152
+ * };
153
+ * }, []);
154
+ * ```
155
+ */
156
+ export declare function cleanupPathRemapping(): void;
@@ -0,0 +1,440 @@
1
+ /**
2
+ * Path Remapping Helpers - Production Ready
3
+ *
4
+ * Framework-agnostic utility for handling path remapping from TagadaPay routing
5
+ * Includes error handling, SSR compatibility, performance optimizations, and edge case handling
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ // ============================================================================
10
+ // Private Cache
11
+ // ============================================================================
12
+ /**
13
+ * Cache for internal path lookup to avoid repeated DOM queries
14
+ * Cleared on navigation events
15
+ */
16
+ let internalPathCache = undefined;
17
+ /**
18
+ * Flag to check if we're in a browser environment
19
+ * Prevents errors during SSR
20
+ */
21
+ const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
22
+ /**
23
+ * Store original history methods for cleanup
24
+ */
25
+ const originalPushState = isBrowser ? history.pushState.bind(history) : null;
26
+ const originalReplaceState = isBrowser ? history.replaceState.bind(history) : null;
27
+ /**
28
+ * Clear the internal path cache
29
+ * Called automatically on navigation events
30
+ */
31
+ function clearCache() {
32
+ internalPathCache = undefined;
33
+ }
34
+ // Setup cache invalidation on navigation (only in browser)
35
+ if (isBrowser) {
36
+ // Clear cache on history navigation
37
+ window.addEventListener('popstate', clearCache);
38
+ // Clear cache on hash change
39
+ window.addEventListener('hashchange', clearCache);
40
+ // Intercept pushState and replaceState to clear cache
41
+ history.pushState = function (...args) {
42
+ clearCache();
43
+ return originalPushState.apply(this, args);
44
+ };
45
+ history.replaceState = function (...args) {
46
+ clearCache();
47
+ return originalReplaceState.apply(this, args);
48
+ };
49
+ }
50
+ // ============================================================================
51
+ // Core Functions
52
+ // ============================================================================
53
+ /**
54
+ * Get the internal path that the plugin expects
55
+ *
56
+ * Reads from the meta tag injected by TagadaPay middleware.
57
+ * Results are cached for performance.
58
+ *
59
+ * @returns The internal path from meta tag, or null if no remapping is active
60
+ * @throws {Error} If called in a non-browser environment (SSR)
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const internalPath = getInternalPath();
65
+ * if (internalPath) {
66
+ * console.log('Remapped from external to:', internalPath);
67
+ * }
68
+ * ```
69
+ */
70
+ export function getInternalPath() {
71
+ // SSR check
72
+ if (!isBrowser) {
73
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
74
+ console.warn('[TagadaPay SDK] getInternalPath() called in SSR context');
75
+ }
76
+ return null;
77
+ }
78
+ // Return cached value if available
79
+ if (internalPathCache !== undefined) {
80
+ return internalPathCache;
81
+ }
82
+ try {
83
+ // Query the meta tag
84
+ const metaTag = document.querySelector('meta[name="x-plugin-internal-path"]');
85
+ if (!metaTag) {
86
+ internalPathCache = null;
87
+ return null;
88
+ }
89
+ // Get and validate the content
90
+ const content = metaTag.getAttribute('content');
91
+ if (!content || content.trim() === '') {
92
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
93
+ console.warn('[TagadaPay SDK] Meta tag found but content is empty');
94
+ }
95
+ internalPathCache = null;
96
+ return null;
97
+ }
98
+ // Validate path format
99
+ const trimmedContent = content.trim();
100
+ if (!trimmedContent.startsWith('/')) {
101
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
102
+ console.warn(`[TagadaPay SDK] Invalid internal path format: "${trimmedContent}" (must start with /)`);
103
+ }
104
+ internalPathCache = null;
105
+ return null;
106
+ }
107
+ // Cache and return
108
+ internalPathCache = trimmedContent;
109
+ return trimmedContent;
110
+ }
111
+ catch (error) {
112
+ // Log error in development
113
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
114
+ console.error('[TagadaPay SDK] Error reading internal path:', error);
115
+ }
116
+ internalPathCache = null;
117
+ return null;
118
+ }
119
+ }
120
+ /**
121
+ * Check if path remapping is currently active
122
+ *
123
+ * @returns true if a remapped path is configured, false otherwise
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * if (isPathRemapped()) {
128
+ * console.log('User is viewing a remapped URL');
129
+ * }
130
+ * ```
131
+ */
132
+ export function isPathRemapped() {
133
+ return getInternalPath() !== null;
134
+ }
135
+ /**
136
+ * Get comprehensive path mapping information
137
+ *
138
+ * Returns both the external (browser) path and internal (plugin) path,
139
+ * along with query parameters and hash.
140
+ *
141
+ * @returns {PathInfo} Complete path information object
142
+ * @throws {Error} If called in a non-browser environment (SSR)
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * const pathInfo = getPathInfo();
147
+ * console.log('Browser shows:', pathInfo.externalPath);
148
+ * console.log('Plugin expects:', pathInfo.internalPath);
149
+ * console.log('Query params:', pathInfo.query.get('id'));
150
+ * ```
151
+ */
152
+ export function getPathInfo() {
153
+ // SSR check
154
+ if (!isBrowser) {
155
+ throw new Error('[TagadaPay SDK] getPathInfo() cannot be called in SSR context');
156
+ }
157
+ try {
158
+ const externalPath = window.location.pathname;
159
+ const internalPath = getInternalPath();
160
+ const query = new URLSearchParams(window.location.search);
161
+ const hash = window.location.hash.replace(/^#/, '');
162
+ return {
163
+ externalPath,
164
+ internalPath,
165
+ isRemapped: internalPath !== null,
166
+ fullUrl: window.location.href,
167
+ query,
168
+ hash,
169
+ };
170
+ }
171
+ catch (error) {
172
+ // Fallback to safe defaults
173
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
174
+ console.error('[TagadaPay SDK] Error getting path info:', error);
175
+ }
176
+ return {
177
+ externalPath: '/',
178
+ internalPath: null,
179
+ isRemapped: false,
180
+ fullUrl: '',
181
+ query: new URLSearchParams(),
182
+ hash: '',
183
+ };
184
+ }
185
+ }
186
+ /**
187
+ * Check if the current URL should match a given internal path
188
+ *
189
+ * Handles path remapping automatically. Use this for catch-all routing
190
+ * implementations where you need to check multiple paths.
191
+ *
192
+ * @param internalPath - The plugin's defined path (must start with /)
193
+ * @returns true if current URL should render this path's component
194
+ * @throws {Error} If internalPath is invalid
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * // In a catch-all route handler
199
+ * if (shouldMatchRoute('/hello')) {
200
+ * return <HelloPage />;
201
+ * }
202
+ * if (shouldMatchRoute('/about')) {
203
+ * return <AboutPage />;
204
+ * }
205
+ * ```
206
+ */
207
+ /**
208
+ * Helper function to match a path against a pattern using path-to-regexp
209
+ * @param pathname - The actual path to test
210
+ * @param pattern - The pattern to match against (can include :params)
211
+ * @returns true if the path matches the pattern
212
+ */
213
+ function matchesPathPattern(pathname, pattern) {
214
+ try {
215
+ // Exact match (no pattern)
216
+ if (pathname === pattern) {
217
+ return true;
218
+ }
219
+ // Check if pattern contains parameters (e.g., :id, :name)
220
+ if (!pattern.includes(':')) {
221
+ // No parameters - use simple prefix matching
222
+ return pathname.startsWith(pattern);
223
+ }
224
+ // Use path-to-regexp for parameterized patterns
225
+ // Import dynamically to avoid bundle size issues
226
+ const { match } = require('path-to-regexp');
227
+ const matchFn = match(pattern, { decode: decodeURIComponent, end: false });
228
+ const result = matchFn(pathname);
229
+ return result !== false;
230
+ }
231
+ catch (error) {
232
+ // Fallback to exact match if path-to-regexp fails
233
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
234
+ console.warn(`[TagadaPay SDK] Pattern matching failed for "${pattern}":`, error);
235
+ }
236
+ return pathname === pattern || pathname.startsWith(pattern);
237
+ }
238
+ }
239
+ export function shouldMatchRoute(internalPath) {
240
+ // Validate input
241
+ if (typeof internalPath !== 'string') {
242
+ throw new Error(`[TagadaPay SDK] internalPath must be a string, got ${typeof internalPath}`);
243
+ }
244
+ if (internalPath.trim() === '') {
245
+ throw new Error('[TagadaPay SDK] internalPath cannot be empty');
246
+ }
247
+ if (!internalPath.startsWith('/')) {
248
+ throw new Error(`[TagadaPay SDK] internalPath must start with /, got "${internalPath}"`);
249
+ }
250
+ // SSR check
251
+ if (!isBrowser) {
252
+ return false;
253
+ }
254
+ try {
255
+ const currentPath = window.location.pathname;
256
+ const remappedInternalPath = getInternalPath();
257
+ // If remapped, check if the remapped internal path matches the pattern
258
+ if (remappedInternalPath) {
259
+ return matchesPathPattern(remappedInternalPath, internalPath);
260
+ }
261
+ // No remapping - check if current path matches the pattern
262
+ return matchesPathPattern(currentPath, internalPath);
263
+ }
264
+ catch (error) {
265
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
266
+ console.error('[TagadaPay SDK] Error in shouldMatchRoute:', error);
267
+ }
268
+ return false;
269
+ }
270
+ }
271
+ /**
272
+ * Get the current matched path
273
+ *
274
+ * Returns the actual path in the browser if it matches the given internal path.
275
+ * Useful when you need the actual URL path for constructing links or API calls.
276
+ *
277
+ * @param internalPath - The plugin's defined path
278
+ * @returns The current browser path if it matches, null otherwise
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * const matchedPath = getMatchedPath('/hello');
283
+ * if (matchedPath) {
284
+ * console.log('Matched at:', matchedPath); // Could be /hello or /hello2
285
+ * }
286
+ * ```
287
+ */
288
+ export function getMatchedPath(internalPath) {
289
+ // Validate input
290
+ if (typeof internalPath !== 'string' || !internalPath.startsWith('/')) {
291
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
292
+ console.warn(`[TagadaPay SDK] Invalid internalPath: "${internalPath}"`);
293
+ }
294
+ return null;
295
+ }
296
+ // SSR check
297
+ if (!isBrowser) {
298
+ return null;
299
+ }
300
+ try {
301
+ const currentPath = window.location.pathname;
302
+ const remappedInternalPath = getInternalPath();
303
+ // If current path matches internal path
304
+ if (currentPath === internalPath ||
305
+ (remappedInternalPath === internalPath && currentPath !== internalPath)) {
306
+ return currentPath;
307
+ }
308
+ return null;
309
+ }
310
+ catch (error) {
311
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
312
+ console.error('[TagadaPay SDK] Error in getMatchedPath:', error);
313
+ }
314
+ return null;
315
+ }
316
+ }
317
+ // ============================================================================
318
+ // Utility Functions
319
+ // ============================================================================
320
+ /**
321
+ * Manually clear the internal path cache
322
+ *
323
+ * Useful for testing or when you know the meta tag has changed.
324
+ * Normally you don't need to call this as it's handled automatically.
325
+ *
326
+ * @example
327
+ * ```typescript
328
+ * // After dynamically updating the meta tag
329
+ * document.querySelector('meta[name="x-plugin-internal-path"]')
330
+ * ?.setAttribute('content', '/new-path');
331
+ * clearInternalPathCache();
332
+ * ```
333
+ */
334
+ export function clearInternalPathCache() {
335
+ clearCache();
336
+ }
337
+ /**
338
+ * Check if running in a browser environment
339
+ *
340
+ * Useful for conditional logic in universal/isomorphic code.
341
+ *
342
+ * @returns true if in browser, false if in SSR/Node environment
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * if (isBrowserEnvironment()) {
347
+ * // Safe to use window/document
348
+ * const path = getInternalPath();
349
+ * }
350
+ * ```
351
+ */
352
+ export function isBrowserEnvironment() {
353
+ return isBrowser;
354
+ }
355
+ /**
356
+ * Get debug information about the current routing state
357
+ *
358
+ * Returns detailed information for debugging path remapping issues.
359
+ * Only available in development mode.
360
+ *
361
+ * @returns Debug information object
362
+ *
363
+ * @example
364
+ * ```typescript
365
+ * const debug = getDebugInfo();
366
+ * console.table(debug);
367
+ * ```
368
+ */
369
+ export function getDebugInfo() {
370
+ if (!isBrowser) {
371
+ return {
372
+ environment: 'SSR/Node',
373
+ error: 'Not in browser environment',
374
+ };
375
+ }
376
+ try {
377
+ const pathInfo = getPathInfo();
378
+ const metaTag = document.querySelector('meta[name="x-plugin-internal-path"]');
379
+ return {
380
+ environment: 'Browser',
381
+ externalPath: pathInfo.externalPath,
382
+ internalPath: pathInfo.internalPath,
383
+ isRemapped: pathInfo.isRemapped,
384
+ fullUrl: pathInfo.fullUrl,
385
+ query: Object.fromEntries(pathInfo.query.entries()),
386
+ hash: pathInfo.hash,
387
+ metaTagExists: !!metaTag,
388
+ metaTagContent: metaTag?.getAttribute('content') || null,
389
+ cacheHit: internalPathCache !== undefined,
390
+ cachedValue: internalPathCache,
391
+ timestamp: new Date().toISOString(),
392
+ };
393
+ }
394
+ catch (error) {
395
+ return {
396
+ environment: 'Browser',
397
+ error: String(error),
398
+ };
399
+ }
400
+ }
401
+ // ============================================================================
402
+ // Cleanup
403
+ // ============================================================================
404
+ /**
405
+ * Cleanup function to remove event listeners
406
+ *
407
+ * Call this when unmounting the plugin or cleaning up resources.
408
+ * Important for preventing memory leaks in SPAs.
409
+ *
410
+ * @example
411
+ * ```typescript
412
+ * // In React
413
+ * useEffect(() => {
414
+ * return () => {
415
+ * cleanupPathRemapping();
416
+ * };
417
+ * }, []);
418
+ * ```
419
+ */
420
+ export function cleanupPathRemapping() {
421
+ if (!isBrowser)
422
+ return;
423
+ try {
424
+ window.removeEventListener('popstate', clearCache);
425
+ window.removeEventListener('hashchange', clearCache);
426
+ // Restore original history methods
427
+ if (originalPushState) {
428
+ history.pushState = originalPushState;
429
+ }
430
+ if (originalReplaceState) {
431
+ history.replaceState = originalReplaceState;
432
+ }
433
+ clearCache();
434
+ }
435
+ catch (error) {
436
+ if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') {
437
+ console.error('[TagadaPay SDK] Error during cleanup:', error);
438
+ }
439
+ }
440
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Credits Resource Client
3
+ * Handles credits-related API operations
4
+ */
5
+ import { ApiClient } from './apiClient';
6
+ export interface CreditTransaction {
7
+ id: string;
8
+ amount: number;
9
+ type: 'earn' | 'spend' | 'adjustment';
10
+ description: string;
11
+ createdAt: string;
12
+ sourceId: string | null;
13
+ sourceType: string | null;
14
+ }
15
+ export interface CustomerCredits {
16
+ balance: number;
17
+ lifetimeEarned: number;
18
+ lifetimeSpent: number;
19
+ transactions: CreditTransaction[];
20
+ }
21
+ export interface RedeemProductResponse {
22
+ success: boolean;
23
+ orderId: string;
24
+ creditsSpent: number;
25
+ remainingCredits: number;
26
+ }
27
+ export declare class CreditsResource {
28
+ private apiClient;
29
+ constructor(apiClient: ApiClient);
30
+ /**
31
+ * Get customer credit balance and transaction history
32
+ */
33
+ getCustomerCredits(customerId: string, storeId: string): Promise<CustomerCredits>;
34
+ /**
35
+ * Redeem a product using credits
36
+ */
37
+ redeemProduct(data: {
38
+ productId?: string;
39
+ variantId: string;
40
+ quantity?: number;
41
+ }): Promise<RedeemProductResponse>;
42
+ }