@thorprovider/types 2.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.
@@ -0,0 +1,1139 @@
1
+ /**
2
+ * @thorprovider/types v1.0
3
+ * Commerce Provider Interfaces
4
+ *
5
+ * Defines the unified interface that all commerce providers must implement.
6
+ * This ensures @thorprovider/components (L3) can depend on abstractions, not implementations.
7
+ *
8
+ * @see SOLID Dependency Inversion Principle
9
+ */
10
+
11
+ import type {
12
+ Product,
13
+ GetProductsOptions,
14
+ AdvancedSearchProductsOptions,
15
+ SearchResultMeta,
16
+ FilterConfig,
17
+ AdminProduct,
18
+ } from './product';
19
+ import type {
20
+ Cart,
21
+ CartLineInput,
22
+ CartLineUpdate,
23
+ ShippingMethod
24
+ } from './cart';
25
+ import type {
26
+ Collection,
27
+ CollectionProductsOptions,
28
+ GetCollectionsOptions
29
+ } from './collection';
30
+ import type {
31
+ ProductCategory,
32
+ GetCategoriesOptions
33
+ } from './category';
34
+ import type { Customer, Address, GetCustomersOptions } from './customer';
35
+ import type { Order, GetOrdersOptions } from './order';
36
+ import type { Region } from './region';
37
+ import type { PaymentMethod, PaymentMethodsOptions } from './payment';
38
+ import type { AuthProvider } from './auth';
39
+ import type { StorefrontContext } from './storefront';
40
+ import type { StockLocation, InventoryLevel, StockValidation, FulfillmentOption } from './stock-location';
41
+ import type { StorefrontConfig } from './storefront-config';
42
+ import type { DesignerConfig, DesignerHistoryEntry } from './designer-config';
43
+
44
+ /**
45
+ * Backend capabilities interface
46
+ *
47
+ * Defines what operations a backend commerce platform supports.
48
+ * Each capability is a boolean flag indicating support.
49
+ *
50
+ * This enables adaptive UI that adjusts based on backend capabilities.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const { capabilities } = useCommerce();
55
+ *
56
+ * // Adaptive UI based on backend capabilities
57
+ * if (capabilities.updateEmail) {
58
+ * return <EmailField />;
59
+ * } else {
60
+ * return <EmailField disabled />;
61
+ * }
62
+ * ```
63
+ */
64
+ export interface BackendCapabilities {
65
+ /**
66
+ * Profile Management Capabilities
67
+ */
68
+
69
+ /** Can update customer profile (name, phone) */
70
+ updateProfile: boolean;
71
+
72
+ /** Can update customer email address */
73
+ updateEmail: boolean;
74
+
75
+ /** Can change password directly (current + new password) */
76
+ changePasswordDirect: boolean;
77
+
78
+ /** Can request password reset via email */
79
+ changePasswordViaEmail: boolean;
80
+
81
+ /** Can delete customer account (self-service) */
82
+ deleteAccount: boolean;
83
+
84
+ /**
85
+ * Avatar/Profile Picture Capabilities
86
+ */
87
+
88
+ /** Has native avatar/profile picture upload API */
89
+ uploadAvatar: boolean;
90
+
91
+ /** Can store avatar URL in customer metadata/attributes */
92
+ avatarViaMetadata: boolean;
93
+
94
+ /**
95
+ * Custom Attributes/Metadata Capabilities
96
+ */
97
+
98
+ /** Supports custom customer attributes/metadata */
99
+ customAttributes: boolean;
100
+
101
+ /**
102
+ * Order Management Capabilities
103
+ */
104
+
105
+ /** Can fetch customer order history */
106
+ fetchOrders: boolean;
107
+
108
+ /** Can cancel orders (before fulfillment) */
109
+ cancelOrder: boolean;
110
+
111
+ /**
112
+ * Address Management Capabilities
113
+ */
114
+
115
+ /** Can add/update/delete shipping addresses */
116
+ manageAddresses: boolean;
117
+
118
+ /** Can set default shipping address */
119
+ defaultAddress: boolean;
120
+
121
+ /**
122
+ * Cart/Checkout Capabilities
123
+ */
124
+
125
+ /** Can apply discount codes to cart */
126
+ applyDiscounts: boolean;
127
+
128
+ /** Can calculate shipping rates before checkout */
129
+ shippingCalculation: boolean;
130
+
131
+ /**
132
+ * Payment/Checkout Capabilities
133
+ */
134
+
135
+ /** Supports guest checkout (no account required) */
136
+ guestCheckout: boolean;
137
+
138
+ /** Can save payment methods for future use */
139
+ savedPaymentMethods: boolean;
140
+
141
+ /** Can list available payment providers dynamically */
142
+ listPaymentProviders: boolean;
143
+
144
+ /** Payment providers are region-specific */
145
+ regionSpecificPayments: boolean;
146
+
147
+ /**
148
+ * Wishlist/Favorites Capabilities
149
+ */
150
+
151
+ /** Has native wishlist/favorites feature */
152
+ wishlist: boolean;
153
+
154
+ /**
155
+ * Search/Filter Capabilities
156
+ */
157
+
158
+ /** Supports advanced product search (full-text, filters) */
159
+ advancedSearch: boolean;
160
+
161
+ /** Supports faceted filtering (by attributes) */
162
+ facetedFilters: boolean;
163
+
164
+ /**
165
+ * Review/Rating Capabilities
166
+ */
167
+
168
+ /** Has native product reviews/ratings system */
169
+ productReviews: boolean;
170
+
171
+ /**
172
+ * Loyalty/Rewards Capabilities
173
+ */
174
+
175
+ /** Has native loyalty/rewards program */
176
+ loyaltyProgram: boolean;
177
+ }
178
+
179
+ /**
180
+ * Main provider interface that all commerce providers must implement
181
+ *
182
+ * This interface defines a unified API for interacting with different
183
+ * e-commerce platforms (Medusa, Shopify, WooCommerce, etc.)
184
+ *
185
+ * @thorprovider/adapters (L2) implements this interface
186
+ * @thorprovider/components (L3) depends on this abstraction
187
+ */
188
+ export interface CommerceProvider {
189
+ /**
190
+ * Provider metadata
191
+ */
192
+ readonly name: string;
193
+ readonly version: string;
194
+
195
+ /**
196
+ * Backend capabilities
197
+ *
198
+ * Defines what operations this backend supports.
199
+ * Used for adaptive UI that adjusts based on platform features.
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const { capabilities } = useCommerce();
204
+ *
205
+ * if (capabilities.updateEmail) {
206
+ * return <EmailField />;
207
+ * } else {
208
+ * return <EmailField disabled />;
209
+ * }
210
+ * ```
211
+ */
212
+ readonly capabilities: BackendCapabilities;
213
+
214
+ /**
215
+ * Authentication provider
216
+ *
217
+ * Available if the backend supports authentication and is configured.
218
+ */
219
+ readonly auth?: AuthProvider;
220
+
221
+ // ============================================
222
+ // Product Operations (Required)
223
+ // ============================================
224
+
225
+ /**
226
+ * Get a single product by handle/slug
227
+ * @param handle - Product handle (slug)
228
+ * @param options - Query options (e.g., regionId)
229
+ * @returns Product or undefined if not found
230
+ */
231
+ getProduct(handle: string, options?: { regionId?: string }): Promise<Product | undefined>;
232
+
233
+ /**
234
+ * Get multiple products with optional filtering and sorting
235
+ * @param options - Query options (search, sort, pagination)
236
+ * @returns Array of products
237
+ */
238
+ getProducts(options?: GetProductsOptions): Promise<Product[]>;
239
+
240
+ /**
241
+ * Get product recommendations for a given product
242
+ * @param productId - Product ID
243
+ * @returns Array of recommended products
244
+ */
245
+ getProductRecommendations(productId: string): Promise<Product[]>;
246
+
247
+ // ============================================
248
+ // Advanced Search Operations (Optional)
249
+ // ============================================
250
+
251
+ /**
252
+ * Advanced product search with filtering (OPTIONAL)
253
+ *
254
+ * Performs enhanced product search using backend-specific search engines
255
+ * (e.g., MeiliSearch, Algolia, Elasticsearch) with advanced filtering:
256
+ * - Price range filtering
257
+ * - Stock availability
258
+ * - Custom product attributes (category-specific)
259
+ * - Faceted search with metadata
260
+ *
261
+ * **Check `capabilities.advancedSearch` before calling.**
262
+ * Falls back to getProducts() if not supported.
263
+ *
264
+ * Only available if backend supports advanced search endpoint.
265
+ * Backends without this capability should NOT implement this method.
266
+ *
267
+ * @param options - Advanced search options with filters
268
+ * @returns Products matching criteria with search metadata
269
+ *
270
+ * @example
271
+ * ```typescript
272
+ * // Check capability first
273
+ * if (!commerce.capabilities.advancedSearch || !commerce.searchProductsAdvanced) {
274
+ * // Fallback to basic search
275
+ * const products = await commerce.getProducts({ query: 'laptop' });
276
+ * return { products, meta: { limit: 20, offset: 0 } };
277
+ * }
278
+ *
279
+ * // Use advanced search
280
+ * const result = await commerce.searchProductsAdvanced({
281
+ * query: 'gaming laptop',
282
+ * categoryIds: ['cat_electronics'],
283
+ * priceMin: 500,
284
+ * priceMax: 1500,
285
+ * currencyCode: 'EUR',
286
+ * inStockOnly: true,
287
+ * attributes: [
288
+ * { id: 'att_ram', values: ['att_val_16gb', 'att_val_32gb'] },
289
+ * { id: 'att_brand', values: ['att_val_dell'] }
290
+ * ],
291
+ * includeVariants: true,
292
+ * regionId: 'reg_europe',
293
+ * first: 20,
294
+ * after: 0
295
+ * });
296
+ *
297
+ * console.log(`Found ${result.meta?.total} products`);
298
+ * console.log(`Search took ${result.meta?.processingTimeMs}ms`);
299
+ * console.log(`Hybrid search: ${result.meta?.hybridSearchUsed}`);
300
+ *
301
+ * result.products.forEach(product => {
302
+ * console.log(product.title, product.priceRange);
303
+ * if (product.variants) {
304
+ * product.variants.forEach(v => console.log(' -', v.title, v.price));
305
+ * }
306
+ * });
307
+ * ```
308
+ *
309
+ * @throws {ProviderAPIError} If backend search service fails
310
+ * @throws {Error} If required parameters missing (e.g., regionId for price filtering)
311
+ *
312
+ * @see AdvancedSearchProductsOptions for all available filter options
313
+ * @see SearchResultMeta for metadata structure
314
+ * @see BackendCapabilities.advancedSearch to check if supported
315
+ */
316
+ searchProductsAdvanced?(
317
+ options?: AdvancedSearchProductsOptions
318
+ ): Promise<{
319
+ products: Product[];
320
+ meta?: SearchResultMeta;
321
+ }>;
322
+
323
+ /**
324
+ * Get available filter options for product filtering (OPTIONAL)
325
+ *
326
+ * Returns dynamic filter configuration based on:
327
+ * - Backend capabilities (basic vs advanced filters)
328
+ * - Category context (category-specific custom attributes)
329
+ * - Current product set (facet counts if supported)
330
+ *
331
+ * Enables building adaptive filter UI that shows only relevant filters
332
+ * for the current context. For example:
333
+ * - Electronics category: RAM, Storage, Brand filters
334
+ * - Clothing category: Size, Color, Material filters
335
+ * - No category: Only basic filters (price, availability)
336
+ *
337
+ * **Check `capabilities.facetedFilters` before calling.**
338
+ * Returns empty array if not supported.
339
+ *
340
+ * @param options - Filter query options
341
+ * @param options.categoryId - Optional category ID for category-specific attributes
342
+ * @param options.useAdvancedSearch - Enable advanced filters (price, stock, attributes)
343
+ * @param options.includeBasicFilters - Include basic filters (categories, collections)
344
+ * @param options.includeFacetCounts - Include product counts per filter value (e.g., "Red (23)")
345
+ * @returns Array of filter configurations sorted by ranking
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * // Get filters for electronics category
350
+ * const filters = await commerce.getAvailableFilters({
351
+ * categoryId: 'cat_electronics',
352
+ * useAdvancedSearch: true,
353
+ * includeBasicFilters: true,
354
+ * includeFacetCounts: false
355
+ * });
356
+ *
357
+ * filters.forEach(filter => {
358
+ * console.log(`${filter.label} (${filter.type})`);
359
+ *
360
+ * if (filter.type === 'multi' && filter.options) {
361
+ * filter.options.forEach(opt => {
362
+ * console.log(` - ${opt.label}${opt.count ? ` (${opt.count})` : ''}`);
363
+ * });
364
+ * } else if (filter.type === 'range') {
365
+ * console.log(` Range: ${filter.min} - ${filter.max}`);
366
+ * }
367
+ * });
368
+ *
369
+ * // Output:
370
+ * // Price (range)
371
+ * // Range: 0 - 5000
372
+ * // RAM (multi)
373
+ * // - 8GB (15)
374
+ * // - 16GB (23)
375
+ * // - 32GB (8)
376
+ * // Brand (multi)
377
+ * // - Dell (12)
378
+ * // - HP (8)
379
+ * // In Stock (boolean)
380
+ * ```
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * // Use in React component
385
+ * function FilterSidebar({ categoryId }: { categoryId?: string }) {
386
+ * const [filters, setFilters] = useState<FilterConfig[]>([]);
387
+ *
388
+ * useEffect(() => {
389
+ * if (!commerce.getAvailableFilters) return;
390
+ *
391
+ * commerce.getAvailableFilters({
392
+ * categoryId,
393
+ * useAdvancedSearch: true,
394
+ * includeBasicFilters: true
395
+ * }).then(setFilters);
396
+ * }, [categoryId]);
397
+ *
398
+ * return (
399
+ * <div>
400
+ * {filters.map(filter => (
401
+ * <FilterComponent key={filter.id} filter={filter} />
402
+ * ))}
403
+ * </div>
404
+ * );
405
+ * }
406
+ * ```
407
+ *
408
+ * @throws {ProviderAPIError} If backend fails to fetch attributes
409
+ * @throws {Error} If category ID invalid
410
+ *
411
+ * @see FilterConfig for filter structure
412
+ * @see BackendCapabilities.facetedFilters to check if supported
413
+ */
414
+ getAvailableFilters?(options: {
415
+ categoryId?: string;
416
+ useAdvancedSearch?: boolean;
417
+ includeBasicFilters?: boolean;
418
+ includeFacetCounts?: boolean;
419
+ }): Promise<FilterConfig[]>;
420
+
421
+ // ============================================
422
+ // Collection Operations (Required)
423
+ // ============================================
424
+
425
+ /**
426
+ * Get a single collection by handle/slug
427
+ * @param handle - Collection handle (slug)
428
+ * @returns Collection or undefined if not found
429
+ */
430
+ getCollection(handle: string): Promise<Collection | undefined>;
431
+
432
+ /**
433
+ * Get all collections
434
+ * @param options - Query options (limit, search)
435
+ * @returns Array of collections
436
+ */
437
+ getCollections(options?: GetCollectionsOptions): Promise<Collection[]>;
438
+
439
+ /**
440
+ * Get products in a collection
441
+ * @param handle - Collection handle
442
+ * @param options - Query options (sort, pagination)
443
+ * @returns Array of products in the collection
444
+ */
445
+ getCollectionProducts(
446
+ handle: string,
447
+ options?: CollectionProductsOptions
448
+ ): Promise<Product[]>;
449
+
450
+ // ============================================
451
+ // Category Operations (Required for taxonomy-based navigation)
452
+ // ============================================
453
+
454
+ /**
455
+ * Get a single product category by ID or handle
456
+ * @param idOrHandle - Category ID or handle (slug)
457
+ * @returns Category or undefined if not found
458
+ */
459
+ getCategory(idOrHandle: string): Promise<ProductCategory | undefined>;
460
+
461
+ /**
462
+ * Get all product categories
463
+ * @param options - Query options (parent filter, descendants)
464
+ * @returns Array of categories
465
+ */
466
+ getCategories(options?: GetCategoriesOptions): Promise<ProductCategory[]>;
467
+
468
+ /**
469
+ * Get products in a category
470
+ * @param handle - Category handle (slug)
471
+ * @param options - Query options (sort, pagination)
472
+ * @returns Array of products in the category
473
+ */
474
+ getCategoryProducts(
475
+ handle: string,
476
+ options?: CollectionProductsOptions
477
+ ): Promise<Product[]>;
478
+
479
+ // ============================================
480
+ // Cart Operations (Required)
481
+ // ============================================
482
+
483
+ /**
484
+ * Create a new empty cart
485
+ * @param options - Optional cart creation options
486
+ * @param options.regionId - Region ID for the cart (required for some providers like Medusa)
487
+ * @returns New cart instance
488
+ */
489
+ createCart(options?: { regionId?: string }): Promise<Cart>;
490
+
491
+ /**
492
+ * Get an existing cart by ID
493
+ * @param cartId - Cart ID
494
+ * @returns Cart or undefined if not found
495
+ */
496
+ getCart(cartId: string): Promise<Cart | undefined>;
497
+
498
+ /**
499
+ * Add items to cart
500
+ * @param cartId - Cart ID
501
+ * @param lines - Items to add
502
+ * @returns Updated cart
503
+ */
504
+ addToCart(cartId: string, lines: CartLineInput[]): Promise<Cart>;
505
+
506
+ /**
507
+ * Remove items from cart
508
+ * @param cartId - Cart ID
509
+ * @param lineIds - Line item IDs to remove
510
+ * @returns Updated cart
511
+ */
512
+ removeFromCart(cartId: string, lineIds: string[]): Promise<Cart>;
513
+
514
+ /**
515
+ * Update cart items
516
+ * @param cartId - Cart ID
517
+ * @param lines - Items to update
518
+ * @returns Updated cart
519
+ */
520
+ updateCart(cartId: string, lines: CartLineUpdate[]): Promise<Cart>;
521
+
522
+ /**
523
+ * Get available regions from the backend
524
+ * @returns Array of regions with id, name, and currency information
525
+ */
526
+ getRegions?(): Promise<Array<{
527
+ id: string;
528
+ name: string;
529
+ currency_code: string;
530
+ countries?: Array<{
531
+ id: string;
532
+ iso_2: string;
533
+ name: string;
534
+ display_name?: string
535
+ }>
536
+ }>>;
537
+
538
+ // ============================================ // Checkout Operations (Required)
539
+ // ============================================
540
+
541
+ /**
542
+ * Complete checkout and create order from cart
543
+ *
544
+ * This is the final step in the checkout process that converts
545
+ * a cart into an order. The cart must have:
546
+ * - Valid shipping address
547
+ * - Selected shipping method (via addShippingMethod)
548
+ * - Initiated payment session (via initiatePaymentSession)
549
+ *
550
+ * @param cartId - Cart ID to checkout
551
+ * @returns Created order
552
+ * @throws {ProviderAPIError} If checkout fails (invalid cart, payment failure, etc.)
553
+ *
554
+ * @example
555
+ * ```typescript
556
+ * // Complete checkout flow:
557
+ * // 1. Add shipping address (via cart update)
558
+ * // 2. Select shipping method
559
+ * await commerce.addShippingMethod(cartId, 'shipping_standard');
560
+ * // 3. Initiate payment
561
+ * await commerce.initiatePaymentSession(cartId, 'stripe');
562
+ * // 4. Complete checkout
563
+ * const order = await commerce.completeCheckout(cartId);
564
+ * ```
565
+ */
566
+ completeCheckout(cartId: string): Promise<Order>;
567
+
568
+ /**
569
+ * Get available shipping methods for cart
570
+ *
571
+ * Fetches shipping options based on cart contents, shipping address,
572
+ * and region. Returns empty array if no methods available.
573
+ *
574
+ * @param cartId - Cart ID
575
+ * @returns List of shipping methods with prices
576
+ * @throws {ProviderAPIError} If cart not found or region not configured
577
+ *
578
+ * @example
579
+ * ```typescript
580
+ * const methods = await commerce.getShippingMethods(cartId);
581
+ *
582
+ * methods.forEach(method => {
583
+ * console.log(`${method.name}: ${method.price.amount} ${method.price.currencyCode}`);
584
+ * });
585
+ * ```
586
+ */
587
+ getShippingMethods(cartId: string): Promise<ShippingMethod[]>;
588
+
589
+ /**
590
+ * Get available countries for a region
591
+ *
592
+ * Returns list of countries that are available for shipping
593
+ * in the specified region. Used for checkout address validation.
594
+ *
595
+ * @param regionId - Region ID
596
+ * @returns Array of countries with codes and names
597
+ * @throws {ProviderAPIError} If region not found
598
+ *
599
+ * @example
600
+ * ```typescript
601
+ * const countries = await commerce.getRegionCountries('reg_europe');
602
+ *
603
+ * countries.forEach(country => {
604
+ * console.log(`${country.code}: ${country.name}`);
605
+ * });
606
+ * // Output:
607
+ * // ES: Spain
608
+ * // FR: France
609
+ * // DE: Germany
610
+ * ```
611
+ */
612
+ getRegionCountries(regionId: string): Promise<import('./common').Country[]>;
613
+
614
+ /**
615
+ * Add shipping method to cart
616
+ *
617
+ * Selects a shipping method from getShippingMethods() and applies it
618
+ * to the cart. This updates cart totals to include shipping cost.
619
+ *
620
+ * @param cartId - Cart ID
621
+ * @param methodId - Shipping method ID (from getShippingMethods)
622
+ * @returns Updated cart with shipping method and updated totals
623
+ * @throws {ProviderAPIError} If method ID invalid or cart not ready
624
+ *
625
+ * @example
626
+ * ```typescript
627
+ * const methods = await commerce.getShippingMethods(cartId);
628
+ * const standardShipping = methods.find(m => m.name === 'Standard');
629
+ *
630
+ * const updatedCart = await commerce.addShippingMethod(cartId, standardShipping.id);
631
+ * console.log('New total:', updatedCart.cost.totalAmount);
632
+ * ```
633
+ */
634
+ addShippingMethod(cartId: string, methodId: string): Promise<Cart>;
635
+
636
+ /**
637
+ * Initiate payment session with provider
638
+ *
639
+ * Creates a payment session with the selected payment provider.
640
+ * This is required before completeCheckout() can be called.
641
+ *
642
+ * Some providers (like Stripe) may return client secret for
643
+ * client-side payment confirmation.
644
+ *
645
+ * @param cartId - Cart ID
646
+ * @param providerId - Payment provider ID (from getPaymentMethods)
647
+ * @throws {ProviderAPIError} If provider invalid or cart not ready
648
+ *
649
+ * @example
650
+ * ```typescript
651
+ * const paymentMethods = await commerce.getPaymentMethods({ regionId });
652
+ * const stripeMethod = paymentMethods.find(m => m.id === 'stripe');
653
+ *
654
+ * await commerce.initiatePaymentSession(cartId, stripeMethod.id);
655
+ * // Payment session ready, can now complete checkout
656
+ * ```
657
+ */
658
+ initiatePaymentSession(cartId: string, providerId: string): Promise<void>;
659
+
660
+ /**
661
+ * Apply discount code to cart
662
+ *
663
+ * Validates and applies a discount/promo code to the cart.
664
+ * Updates cart totals to reflect discount.
665
+ *
666
+ * @param cartId - Cart ID
667
+ * @param code - Discount code (case-insensitive)
668
+ * @returns Updated cart with discount applied
669
+ * @throws {ProviderAPIError} If code invalid, expired, or not applicable
670
+ *
671
+ * @example
672
+ * ```typescript
673
+ * try {
674
+ * const updatedCart = await commerce.applyDiscountCode(cartId, 'SUMMER2026');
675
+ * console.log('Discount applied! New total:', updatedCart.cost.totalAmount);
676
+ * } catch (error) {
677
+ * console.error('Invalid code:', error.message);
678
+ * }
679
+ * ```
680
+ */
681
+ applyDiscountCode(cartId: string, code: string): Promise<Cart>;
682
+
683
+ /**
684
+ * Remove discount code from cart
685
+ *
686
+ * Removes a previously applied discount code.
687
+ * Updates cart totals to remove discount.
688
+ *
689
+ * @param cartId - Cart ID
690
+ * @param code - Discount code to remove
691
+ * @returns Updated cart without discount
692
+ * @throws {ProviderAPIError} If cart not found
693
+ *
694
+ * @example
695
+ * ```typescript
696
+ * const updatedCart = await commerce.removeDiscountCode(cartId, 'SUMMER2026');
697
+ * console.log('Discount removed. New total:', updatedCart.cost.totalAmount);
698
+ * ```
699
+ */
700
+ removeDiscountCode(cartId: string, code: string): Promise<Cart>;
701
+
702
+ /**
703
+ * Update billing address for cart (optional)
704
+ *
705
+ * Sets or updates the billing address on the cart.
706
+ * Use when billing address differs from shipping address.
707
+ *
708
+ * Recommended to call BEFORE initiatePaymentSession() as some
709
+ * payment providers may require billing address.
710
+ *
711
+ * @param cartId - Cart ID
712
+ * @param address - Billing address to set
713
+ * @returns Updated cart with billing address
714
+ * @throws {ProviderAPIError} If cart not found or address invalid
715
+ *
716
+ * @example
717
+ * ```typescript
718
+ * await commerce.updateBillingAddress(cartId, {
719
+ * firstName: 'John',
720
+ * lastName: 'Doe',
721
+ * address1: '123 Billing St',
722
+ * city: 'Madrid',
723
+ * province: 'Madrid',
724
+ * postalCode: '28001',
725
+ * countryCode: 'ES',
726
+ * phone: '+34 600 000 000'
727
+ * });
728
+ * ```
729
+ */
730
+ updateBillingAddress?(cartId: string, address: Address): Promise<Cart>;
731
+
732
+ // ============================================ // Customer Operations (Optional)
733
+ // ============================================
734
+
735
+ /**
736
+ * Get customer by ID (optional)
737
+ * @param customerId - Customer ID
738
+ * @returns Customer or undefined if not found
739
+ */
740
+ getCustomer?(customerId: string): Promise<Customer | undefined>;
741
+
742
+ /**
743
+ * List customers with optional filtering and pagination (optional)
744
+ * @param options - Query options (search, sort, pagination)
745
+ * @returns Array of customers
746
+ */
747
+ getCustomers?(options?: GetCustomersOptions): Promise<Customer[]>;
748
+
749
+ /**
750
+ * Update customer information (optional)
751
+ * @param customerId - Customer ID
752
+ * @param data - Fields to update
753
+ * @returns Updated customer
754
+ */
755
+ updateCustomer?(customerId: string, data: Partial<Omit<Customer, 'id' | 'createdAt'>>): Promise<Customer>;
756
+
757
+ // ============================================
758
+ // Order Operations (Optional)
759
+ // ============================================
760
+
761
+ /**
762
+ * Get order by ID (optional)
763
+ * @param orderId - Order ID
764
+ * @returns Order or undefined if not found
765
+ */
766
+ getOrder?(orderId: string): Promise<Order | undefined>;
767
+
768
+ /**
769
+ * Get all orders for a customer (optional)
770
+ *
771
+ * @param options - Order query options (supports callbacks for custom backends)
772
+ * @returns Array of orders
773
+ */
774
+ getOrders?(options: GetOrdersOptions): Promise<Order[]>;
775
+
776
+ // ============================================
777
+ // Payment Operations
778
+ // ============================================
779
+
780
+ /**
781
+ * Get available payment methods
782
+ *
783
+ * This method queries the backend for available payment providers
784
+ * based on region, cart, or other contextual factors.
785
+ *
786
+ * Backends that don't support dynamic payment method listing
787
+ * (capabilities.listPaymentProviders === false) should throw
788
+ * an error or return empty array.
789
+ *
790
+ * @param options - Payment methods query options
791
+ * @returns Array of available payment methods
792
+ *
793
+ * @example
794
+ * ```typescript
795
+ * // Get payment methods for a specific region
796
+ * const methods = await provider.getPaymentMethods({
797
+ * regionId: 'reg_123',
798
+ * enabledOnly: true
799
+ * });
800
+ *
801
+ * // Display to user
802
+ * methods.forEach(method => {
803
+ * console.log(method.name, method.type);
804
+ * });
805
+ * ```
806
+ *
807
+ * @throws {Error} If backend request fails
808
+ * @throws {Error} If required options are missing (e.g., regionId for Medusa)
809
+ */
810
+ getPaymentMethods(options: PaymentMethodsOptions): Promise<PaymentMethod[]>;
811
+
812
+ // ============================================
813
+ // Inventory Operations (Admin API — Optional)
814
+ // ============================================
815
+
816
+ /**
817
+ * Fetch stock locations, optionally filtered by sales channel.
818
+ * Returns empty array when admin config is not provided (graceful degradation).
819
+ *
820
+ * @param params - Optional filter parameters
821
+ * @returns Array of stock locations
822
+ */
823
+ getStockLocations(params?: { salesChannelId?: string }): Promise<StockLocation[]>;
824
+
825
+ /**
826
+ * Check inventory levels for a variant across locations.
827
+ * Returns empty array when admin config is not provided (graceful degradation).
828
+ *
829
+ * @param variantId - Variant ID to check
830
+ * @param salesChannelId - Optional sales channel filter
831
+ * @returns Array of inventory levels per location
832
+ */
833
+ checkInventoryLevels(variantId: string, salesChannelId?: string): Promise<InventoryLevel[]>;
834
+
835
+ /**
836
+ * Validate cart stock availability across all items.
837
+ * Returns all-available when admin config is not provided (graceful degradation).
838
+ *
839
+ * @param cartId - Cart ID to validate
840
+ * @returns Stock validation result with unavailable items (if any)
841
+ */
842
+ validateCartStock(cartId: string): Promise<StockValidation>;
843
+
844
+ /**
845
+ * Get fulfillment options enriched with stock location data.
846
+ *
847
+ * @param cartId - Cart ID
848
+ * @param salesChannelId - Sales channel ID for filtering
849
+ * @returns Array of fulfillment options
850
+ */
851
+ getFulfillmentOptions(cartId: string, salesChannelId: string): Promise<FulfillmentOption[]>;
852
+
853
+ /**
854
+ * Get a map of product IDs to their admin-level status.
855
+ * Only available when admin credentials are configured.
856
+ * Returns empty object when admin config is not provided (graceful degradation).
857
+ *
858
+ * Medusa statuses: 'published' | 'draft' | 'proposed' | 'rejected'
859
+ *
860
+ * @param options - Optional pagination (default limit: 1000)
861
+ * @returns Record mapping productId → status string
862
+ */
863
+ getAdminProductStatuses?(options?: { limit?: number }): Promise<Record<string, string>>;
864
+
865
+ /**
866
+ * List ALL products from the admin API, including non-published ones.
867
+ * Returns real backend status values (provider-specific strings).
868
+ * Gracefully degrades to empty result when admin credentials are not configured.
869
+ *
870
+ * Unlike `getProducts()` (storefront API, published only), this method
871
+ * uses admin credentials to list products of any status.
872
+ *
873
+ * @param options.query - Optional full-text search
874
+ * @param options.limit - Max products to fetch (default: 1000)
875
+ * @param options.offset - Pagination offset (default: 0)
876
+ * @returns { products: AdminProduct[]; count: number }
877
+ */
878
+ getAdminProducts?(options?: {
879
+ query?: string;
880
+ limit?: number;
881
+ offset?: number;
882
+ }): Promise<{ products: AdminProduct[]; count: number }>;
883
+
884
+ // ============================================
885
+ // Admin Order Operations (Admin API — Optional)
886
+ // ============================================
887
+
888
+ /**
889
+ * List all orders for admin dashboard.
890
+ * Gracefully degrades to empty result when adminConfig is not provided.
891
+ */
892
+ getAdminOrders(options?: {
893
+ search?: string;
894
+ status?: string | string[];
895
+ limit?: number;
896
+ offset?: number;
897
+ order?: string;
898
+ direction?: 'asc' | 'desc';
899
+ salesChannelId?: string;
900
+ /**
901
+ * When true, also fetches draft orders from /admin/draft-orders and merges
902
+ * them into the result sorted by created_at desc. Draft orders are stored
903
+ * as a separate entity from regular orders in Medusa v2.
904
+ */
905
+ includeDrafts?: boolean;
906
+ }): Promise<{ orders: Order[]; count: number }>;
907
+
908
+ /**
909
+ * Get a single order by ID for admin detail view.
910
+ */
911
+ getAdminOrder?(orderId: string): Promise<Order | undefined>;
912
+
913
+ /**
914
+ * Get a single product by ID for admin detail view.
915
+ */
916
+ getAdminProduct?(productId: string): Promise<AdminProduct | undefined>;
917
+
918
+ /**
919
+ * Create a draft order (admin operation).
920
+ */
921
+ createDraftOrder?(params: {
922
+ email: string;
923
+ regionId: string;
924
+ salesChannelId?: string;
925
+ items: Array<{ variantId: string; quantity: number }>;
926
+ shippingAddress?: {
927
+ firstName?: string;
928
+ lastName?: string;
929
+ address1?: string;
930
+ city?: string;
931
+ countryCode?: string;
932
+ postalCode?: string;
933
+ };
934
+ }): Promise<Order>;
935
+
936
+ /**
937
+ * Cancel an order (admin operation).
938
+ * Throws ProviderAPIError when adminConfig is not provided.
939
+ */
940
+ cancelOrder(orderId: string): Promise<Order>;
941
+
942
+ /**
943
+ * Create a fulfillment for an order (admin operation).
944
+ * Throws ProviderAPIError when adminConfig is not provided.
945
+ */
946
+ createFulfillment(
947
+ orderId: string,
948
+ items?: Array<{ id: string; quantity: number }>,
949
+ ): Promise<void>;
950
+
951
+ /**
952
+ * Convert a draft order into a regular pending order (admin operation).
953
+ * Throws ProviderAPIError when adminConfig is not provided.
954
+ */
955
+ undraftOrder?(draftOrderId: string): Promise<Order>;
956
+
957
+ /**
958
+ * Create a refund for an order (admin operation).
959
+ * Throws ProviderAPIError when adminConfig is not provided.
960
+ */
961
+ createRefund(orderId: string, amount?: number, reason?: string): Promise<void>;
962
+
963
+ // ============================================
964
+ // Admin Customer Operations (Admin API — Optional)
965
+ // ============================================
966
+
967
+ /**
968
+ * List customers via admin API with search and pagination.
969
+ * Requires admin credentials. Gracefully degrades to empty result when
970
+ * admin config is not provided.
971
+ *
972
+ * @param options - Query options (search, limit, salesChannelId)
973
+ * @returns Paginated customer list
974
+ */
975
+ getAdminCustomers?(options?: GetCustomersOptions): Promise<{
976
+ customers: Customer[];
977
+ count: number;
978
+ }>;
979
+
980
+ /**
981
+ * Get a single customer by ID via admin API.
982
+ * Requires admin credentials.
983
+ *
984
+ * @param customerId - Customer ID
985
+ * @returns Customer or undefined if not found
986
+ */
987
+ getAdminCustomer?(customerId: string): Promise<Customer | undefined>;
988
+
989
+ /**
990
+ * Get addresses for a specific customer via admin API.
991
+ * Requires admin credentials.
992
+ *
993
+ * @param customerId - Customer ID
994
+ * @returns Array of customer addresses
995
+ */
996
+ getAdminCustomerAddresses?(customerId: string): Promise<Address[]>;
997
+
998
+ /**
999
+ * Get orders belonging to a specific customer.
1000
+ * Uses admin API to list orders filtered by customer ID.
1001
+ *
1002
+ * @param customerId - Customer ID
1003
+ * @param limit - Max orders to return (default: 10)
1004
+ * @returns Array of orders
1005
+ */
1006
+ getCustomerOrders?(customerId: string, limit?: number): Promise<Order[]>;
1007
+
1008
+ // ============================================
1009
+ // Admin Region Operations (Admin API — Optional)
1010
+ // ============================================
1011
+
1012
+ /**
1013
+ * List all regions configured in the backend.
1014
+ * Used by admin dashboard for order creation (region selection).
1015
+ *
1016
+ * @returns Array of regions
1017
+ */
1018
+ getAdminRegions?(): Promise<Region[]>;
1019
+
1020
+ // ============================================
1021
+ // Storefront / Multi-Tenant Operations
1022
+ // ============================================
1023
+
1024
+ /**
1025
+ * Get the current storefront/sales channel context.
1026
+ *
1027
+ * @returns StorefrontContext for the current storefront
1028
+ * @throws StorefrontConfigError if storefront is required but not configured
1029
+ * @throws ProviderAPIError if storefront lookup fails
1030
+ *
1031
+ * @remarks
1032
+ * - Called on every request that accesses scoped data
1033
+ * - Should throw EARLY if configuration is invalid
1034
+ * - NOT optional — must always return a valid context or throw
1035
+ *
1036
+ * @example
1037
+ * ```typescript
1038
+ * const storefront = await commerce.getStorefrontContext();
1039
+ * console.log(storefront.name, storefront.platformType);
1040
+ * ```
1041
+ */
1042
+ getStorefrontContext(): Promise<StorefrontContext>;
1043
+
1044
+ /**
1045
+ * Check if this provider requires storefront configuration.
1046
+ *
1047
+ * @returns true if storefront is mandatory for correct operation
1048
+ *
1049
+ * @remarks
1050
+ * - Medusa: true
1051
+ * - Shopify: false (optional for single storefront)
1052
+ * - WooCommerce: false (not supported natively)
1053
+ * - BigCommerce: false (optional)
1054
+ * - Spree: false (optional for single store)
1055
+ * - Magento: true (always required)
1056
+ */
1057
+ isStorefrontRequired(): boolean;
1058
+
1059
+ // ============================================
1060
+ // Thor Plugin — Storefront Configuration (Optional)
1061
+ // ============================================
1062
+
1063
+ /**
1064
+ * Get per-channel storefront configuration (logo, accent color, currency, SEO).
1065
+ *
1066
+ * Calls `GET /store/thor/config` (resolved via publishable API key).
1067
+ * Returns `null` if the Thor Commerce plugin is not installed or
1068
+ * no config exists for the current channel.
1069
+ *
1070
+ * @returns StorefrontConfig or null
1071
+ */
1072
+ getStorefrontConfig?(): Promise<StorefrontConfig | null>;
1073
+
1074
+ /**
1075
+ * Get the active Site Designer configuration for the current channel.
1076
+ *
1077
+ * Calls `GET /store/thor/site-config` (resolved via publishable API key).
1078
+ * Returns `null` if the Thor Commerce plugin is not installed or
1079
+ * no config exists for the current channel.
1080
+ *
1081
+ * @returns Versioned DesignerConfig or null
1082
+ */
1083
+ getDesignerConfig?(): Promise<{ version: string; config: DesignerConfig } | null>;
1084
+
1085
+ // ============================================
1086
+ // Thor Plugin — Admin Site Designer (Optional)
1087
+ // ============================================
1088
+
1089
+ /**
1090
+ * Get admin Site Designer config + history for a sales channel.
1091
+ *
1092
+ * @param salesChannelId - Target sales channel ID
1093
+ * @returns Config with version history, or null if not found
1094
+ */
1095
+ getAdminDesignerConfig?(salesChannelId: string): Promise<{
1096
+ version: string;
1097
+ config: DesignerConfig;
1098
+ history?: DesignerHistoryEntry[];
1099
+ } | null>;
1100
+
1101
+ /**
1102
+ * Update the Site Designer config for a sales channel.
1103
+ *
1104
+ * @param salesChannelId - Target sales channel ID
1105
+ * @param config - New designer configuration
1106
+ * @returns Updated config with new version
1107
+ */
1108
+ updateAdminDesignerConfig?(salesChannelId: string, config: DesignerConfig): Promise<{
1109
+ version: string;
1110
+ config: DesignerConfig;
1111
+ }>;
1112
+
1113
+ /**
1114
+ * Restore a previous Site Designer config version.
1115
+ *
1116
+ * @param salesChannelId - Target sales channel ID
1117
+ * @param version - Version string to restore
1118
+ * @returns Restored config with new version
1119
+ */
1120
+ restoreAdminDesignerConfig?(salesChannelId: string, version: string): Promise<{
1121
+ version: string;
1122
+ config: DesignerConfig;
1123
+ }>;
1124
+ }
1125
+
1126
+ /**
1127
+ * Provider configuration base type
1128
+ */
1129
+ export interface ProviderConfig {
1130
+ /**
1131
+ * Enable debug mode (verbose logging)
1132
+ */
1133
+ debug?: boolean;
1134
+
1135
+ /**
1136
+ * Custom fetch implementation (for server-side use)
1137
+ */
1138
+ customFetch?: typeof fetch;
1139
+ }