@putiikkipalvelu/storefront-sdk 0.3.0 → 0.4.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.
package/dist/index.js CHANGED
@@ -116,19 +116,24 @@ async function handleErrorResponse(response) {
116
116
  if (errorData.requiresVerification && errorData.customerId) {
117
117
  throw new VerificationRequiredError(message, errorData.customerId);
118
118
  }
119
+ const code = errorData.code;
119
120
  switch (response.status) {
120
121
  case 401:
121
- throw new AuthError(message);
122
+ throw new StorefrontError(message, 401, code || "UNAUTHORIZED");
122
123
  case 404:
123
- throw new NotFoundError(message);
124
+ throw new StorefrontError(message, 404, code || "NOT_FOUND");
124
125
  case 429: {
125
126
  const retryAfter = response.headers.get("Retry-After");
126
- throw new RateLimitError(message, retryAfter ? parseInt(retryAfter, 10) : null);
127
+ const error = new RateLimitError(message, retryAfter ? parseInt(retryAfter, 10) : null);
128
+ if (code) {
129
+ error.code = code;
130
+ }
131
+ throw error;
127
132
  }
128
133
  case 400:
129
- throw new ValidationError(message);
134
+ throw new StorefrontError(message, 400, code || "VALIDATION_ERROR");
130
135
  default:
131
- throw new StorefrontError(message, response.status, "API_ERROR");
136
+ throw new StorefrontError(message, response.status, code || "API_ERROR");
132
137
  }
133
138
  }
134
139
 
@@ -414,6 +419,138 @@ function createCategoriesResource(fetcher) {
414
419
  };
415
420
  }
416
421
 
422
+ // src/utils/pricing.ts
423
+ function isSaleActive(startDate, endDate) {
424
+ if (!startDate && !endDate) {
425
+ return true;
426
+ }
427
+ const now = /* @__PURE__ */ new Date();
428
+ const start = startDate ? new Date(startDate) : null;
429
+ const end = endDate ? new Date(endDate) : null;
430
+ if (start && !end) {
431
+ return now >= start;
432
+ }
433
+ if (!start && end) {
434
+ return now <= end;
435
+ }
436
+ if (start && end) {
437
+ return now >= start && now <= end;
438
+ }
439
+ return true;
440
+ }
441
+ function getPriceInfo(product, variation) {
442
+ if (variation) {
443
+ const isOnSale2 = isSaleActive(variation.saleStartDate, variation.saleEndDate) && variation.salePrice !== null;
444
+ const originalPrice2 = variation.price ?? product.price;
445
+ const effectivePrice2 = isOnSale2 ? variation.salePrice ?? originalPrice2 : originalPrice2;
446
+ return {
447
+ effectivePrice: effectivePrice2,
448
+ originalPrice: originalPrice2,
449
+ isOnSale: isOnSale2,
450
+ salePercent: isOnSale2 ? variation.salePercent ?? null : null
451
+ };
452
+ }
453
+ const isOnSale = isSaleActive(product.saleStartDate, product.saleEndDate) && product.salePrice !== null;
454
+ const originalPrice = product.price;
455
+ const effectivePrice = isOnSale ? product.salePrice ?? originalPrice : originalPrice;
456
+ return {
457
+ effectivePrice,
458
+ originalPrice,
459
+ isOnSale,
460
+ salePercent: isOnSale ? product.salePercent ?? null : null
461
+ };
462
+ }
463
+
464
+ // src/utils/cart-calculations.ts
465
+ function calculateCartWithCampaigns(items, campaigns) {
466
+ const buyXPayYCampaign = campaigns.find(
467
+ (c) => c.type === "BUY_X_PAY_Y" && c.isActive
468
+ );
469
+ const originalTotal = items.reduce((total, { product, variation, cartQuantity }) => {
470
+ const priceInfo = getPriceInfo(product, variation);
471
+ return total + priceInfo.effectivePrice * cartQuantity;
472
+ }, 0);
473
+ if (!buyXPayYCampaign?.BuyXPayYCampaign) {
474
+ const calculatedItems2 = items.map((item) => ({
475
+ item,
476
+ paidQuantity: item.cartQuantity,
477
+ freeQuantity: 0,
478
+ totalQuantity: item.cartQuantity
479
+ }));
480
+ return {
481
+ calculatedItems: calculatedItems2,
482
+ cartTotal: originalTotal,
483
+ originalTotal,
484
+ totalSavings: 0
485
+ };
486
+ }
487
+ const { buyQuantity, payQuantity, applicableCategories } = buyXPayYCampaign.BuyXPayYCampaign;
488
+ const applicableCategoryIds = new Set(
489
+ applicableCategories.map((c) => c.id)
490
+ );
491
+ const eligibleUnits = items.flatMap((item) => {
492
+ const { product, variation } = item;
493
+ const itemCategories = product.categories?.map((cat) => cat.id) || [];
494
+ const isEligible = itemCategories.some(
495
+ (id) => applicableCategoryIds.has(id)
496
+ );
497
+ if (isEligible) {
498
+ const priceInfo = getPriceInfo(product, variation);
499
+ return Array.from({ length: item.cartQuantity }, () => ({
500
+ price: priceInfo.effectivePrice,
501
+ productId: product.id,
502
+ variationId: variation?.id,
503
+ originalItem: item
504
+ }));
505
+ }
506
+ return [];
507
+ });
508
+ if (eligibleUnits.length < buyQuantity) {
509
+ const calculatedItems2 = items.map((item) => ({
510
+ item,
511
+ paidQuantity: item.cartQuantity,
512
+ freeQuantity: 0,
513
+ totalQuantity: item.cartQuantity
514
+ }));
515
+ return {
516
+ calculatedItems: calculatedItems2,
517
+ cartTotal: originalTotal,
518
+ originalTotal,
519
+ totalSavings: 0
520
+ };
521
+ }
522
+ eligibleUnits.sort((a, b) => a.price - b.price);
523
+ const numToMakeFree = buyQuantity - payQuantity;
524
+ const itemsToMakeFree = eligibleUnits.slice(0, numToMakeFree);
525
+ const totalSavings = itemsToMakeFree.reduce(
526
+ (sum, item) => sum + item.price,
527
+ 0
528
+ );
529
+ const freeCountMap = /* @__PURE__ */ new Map();
530
+ for (const freebie of itemsToMakeFree) {
531
+ const key = `${freebie.productId}${freebie.variationId ? `_${freebie.variationId}` : ""}`;
532
+ freeCountMap.set(key, (freeCountMap.get(key) || 0) + 1);
533
+ }
534
+ const calculatedItems = items.map((item) => {
535
+ const key = `${item.product.id}${item.variation?.id ? `_${item.variation.id}` : ""}`;
536
+ const freeQuantity = freeCountMap.get(key) || 0;
537
+ const paidQuantity = item.cartQuantity - freeQuantity;
538
+ return {
539
+ item,
540
+ paidQuantity: Math.max(0, paidQuantity),
541
+ freeQuantity,
542
+ totalQuantity: item.cartQuantity
543
+ };
544
+ });
545
+ const cartTotal = originalTotal - totalSavings;
546
+ return {
547
+ calculatedItems,
548
+ cartTotal,
549
+ originalTotal,
550
+ totalSavings
551
+ };
552
+ }
553
+
417
554
  // src/resources/cart.ts
418
555
  function buildCartHeaders(options) {
419
556
  const headers = {};
@@ -576,33 +713,49 @@ function createCartResource(fetcher) {
576
713
  * Checks product availability, stock levels, and prices.
577
714
  * Auto-fixes issues (removes unavailable items, adjusts quantities).
578
715
  *
716
+ * Campaign conflict detection:
717
+ * - SDK calculates if BuyXPayY campaigns apply using calculateCartWithCampaigns()
718
+ * - Sends x-campaigns-apply header to backend
719
+ * - If campaigns apply AND discount code exists, backend removes it
720
+ * - Returns changes.discountCouponRemoved = true
721
+ *
579
722
  * @param options - Cart session options
723
+ * @param cartItems - Current cart items for campaign calculation
724
+ * @param campaigns - Active campaigns for conflict check
580
725
  * @param fetchOptions - Fetch options
581
726
  * @returns Validated cart with change metadata
582
727
  *
583
728
  * @example Validate before checkout
584
729
  * ```typescript
585
- * const { items, hasChanges, changes } = await client.cart.validate({ cartId });
730
+ * const result = await client.cart.validate(
731
+ * { cartId },
732
+ * cartItems,
733
+ * storeConfig.campaigns
734
+ * );
586
735
  *
587
- * if (hasChanges) {
588
- * if (changes.removedItems > 0) {
589
- * notify('Some items were removed (out of stock)');
590
- * }
591
- * if (changes.quantityAdjusted > 0) {
592
- * notify('Some quantities were adjusted');
593
- * }
594
- * if (changes.priceChanged > 0) {
595
- * notify('Some prices have changed');
736
+ * if (result.hasChanges) {
737
+ * if (result.changes.discountCouponRemoved) {
738
+ * notify("Alennuskoodi poistettu - kampanja-alennus aktivoitui");
596
739
  * }
740
+ * // Handle other changes...
597
741
  * }
598
742
  * ```
599
743
  */
600
- async validate(options, fetchOptions) {
744
+ async validate(options, cartItems, campaigns, fetchOptions) {
745
+ let campaignsApply = false;
746
+ if (cartItems && campaigns && cartItems.length > 0) {
747
+ const campaignResult = calculateCartWithCampaigns(cartItems, campaigns);
748
+ campaignsApply = campaignResult.totalSavings > 0;
749
+ }
750
+ const headers = {
751
+ ...buildCartHeaders(options),
752
+ "x-campaigns-apply": campaignsApply ? "true" : "false"
753
+ };
601
754
  return fetcher.request(
602
755
  "/api/storefront/v1/cart/validate",
603
756
  {
604
757
  method: "GET",
605
- headers: buildCartHeaders(options),
758
+ headers,
606
759
  ...fetchOptions
607
760
  }
608
761
  );
@@ -610,138 +763,6 @@ function createCartResource(fetcher) {
610
763
  };
611
764
  }
612
765
 
613
- // src/utils/pricing.ts
614
- function isSaleActive(startDate, endDate) {
615
- if (!startDate && !endDate) {
616
- return true;
617
- }
618
- const now = /* @__PURE__ */ new Date();
619
- const start = startDate ? new Date(startDate) : null;
620
- const end = endDate ? new Date(endDate) : null;
621
- if (start && !end) {
622
- return now >= start;
623
- }
624
- if (!start && end) {
625
- return now <= end;
626
- }
627
- if (start && end) {
628
- return now >= start && now <= end;
629
- }
630
- return true;
631
- }
632
- function getPriceInfo(product, variation) {
633
- if (variation) {
634
- const isOnSale2 = isSaleActive(variation.saleStartDate, variation.saleEndDate) && variation.salePrice !== null;
635
- const originalPrice2 = variation.price ?? product.price;
636
- const effectivePrice2 = isOnSale2 ? variation.salePrice ?? originalPrice2 : originalPrice2;
637
- return {
638
- effectivePrice: effectivePrice2,
639
- originalPrice: originalPrice2,
640
- isOnSale: isOnSale2,
641
- salePercent: isOnSale2 ? variation.salePercent ?? null : null
642
- };
643
- }
644
- const isOnSale = isSaleActive(product.saleStartDate, product.saleEndDate) && product.salePrice !== null;
645
- const originalPrice = product.price;
646
- const effectivePrice = isOnSale ? product.salePrice ?? originalPrice : originalPrice;
647
- return {
648
- effectivePrice,
649
- originalPrice,
650
- isOnSale,
651
- salePercent: isOnSale ? product.salePercent ?? null : null
652
- };
653
- }
654
-
655
- // src/utils/cart-calculations.ts
656
- function calculateCartWithCampaigns(items, campaigns) {
657
- const buyXPayYCampaign = campaigns.find(
658
- (c) => c.type === "BUY_X_PAY_Y" && c.isActive
659
- );
660
- const originalTotal = items.reduce((total, { product, variation, cartQuantity }) => {
661
- const priceInfo = getPriceInfo(product, variation);
662
- return total + priceInfo.effectivePrice * cartQuantity;
663
- }, 0);
664
- if (!buyXPayYCampaign?.BuyXPayYCampaign) {
665
- const calculatedItems2 = items.map((item) => ({
666
- item,
667
- paidQuantity: item.cartQuantity,
668
- freeQuantity: 0,
669
- totalQuantity: item.cartQuantity
670
- }));
671
- return {
672
- calculatedItems: calculatedItems2,
673
- cartTotal: originalTotal,
674
- originalTotal,
675
- totalSavings: 0
676
- };
677
- }
678
- const { buyQuantity, payQuantity, applicableCategories } = buyXPayYCampaign.BuyXPayYCampaign;
679
- const applicableCategoryIds = new Set(
680
- applicableCategories.map((c) => c.id)
681
- );
682
- const eligibleUnits = items.flatMap((item) => {
683
- const { product, variation } = item;
684
- const itemCategories = product.categories?.map((cat) => cat.id) || [];
685
- const isEligible = itemCategories.some(
686
- (id) => applicableCategoryIds.has(id)
687
- );
688
- if (isEligible) {
689
- const priceInfo = getPriceInfo(product, variation);
690
- return Array.from({ length: item.cartQuantity }, () => ({
691
- price: priceInfo.effectivePrice,
692
- productId: product.id,
693
- variationId: variation?.id,
694
- originalItem: item
695
- }));
696
- }
697
- return [];
698
- });
699
- if (eligibleUnits.length < buyQuantity) {
700
- const calculatedItems2 = items.map((item) => ({
701
- item,
702
- paidQuantity: item.cartQuantity,
703
- freeQuantity: 0,
704
- totalQuantity: item.cartQuantity
705
- }));
706
- return {
707
- calculatedItems: calculatedItems2,
708
- cartTotal: originalTotal,
709
- originalTotal,
710
- totalSavings: 0
711
- };
712
- }
713
- eligibleUnits.sort((a, b) => a.price - b.price);
714
- const numToMakeFree = buyQuantity - payQuantity;
715
- const itemsToMakeFree = eligibleUnits.slice(0, numToMakeFree);
716
- const totalSavings = itemsToMakeFree.reduce(
717
- (sum, item) => sum + item.price,
718
- 0
719
- );
720
- const freeCountMap = /* @__PURE__ */ new Map();
721
- for (const freebie of itemsToMakeFree) {
722
- const key = `${freebie.productId}${freebie.variationId ? `_${freebie.variationId}` : ""}`;
723
- freeCountMap.set(key, (freeCountMap.get(key) || 0) + 1);
724
- }
725
- const calculatedItems = items.map((item) => {
726
- const key = `${item.product.id}${item.variation?.id ? `_${item.variation.id}` : ""}`;
727
- const freeQuantity = freeCountMap.get(key) || 0;
728
- const paidQuantity = item.cartQuantity - freeQuantity;
729
- return {
730
- item,
731
- paidQuantity: Math.max(0, paidQuantity),
732
- freeQuantity,
733
- totalQuantity: item.cartQuantity
734
- };
735
- });
736
- const cartTotal = originalTotal - totalSavings;
737
- return {
738
- calculatedItems,
739
- cartTotal,
740
- originalTotal,
741
- totalSavings
742
- };
743
- }
744
-
745
766
  // src/resources/shipping.ts
746
767
  function calculateCartWeight(items) {
747
768
  return items.reduce((total, item) => {
@@ -809,7 +830,8 @@ function createShippingResource(fetcher) {
809
830
  const cartWeight = calculateCartWeight(options.cartItems);
810
831
  params.set("cartWeight", cartWeight.toString());
811
832
  const cartTotal = options.campaigns ? calculateCartWithCampaigns(options.cartItems, options.campaigns).cartTotal : calculateCartTotal(options.cartItems);
812
- params.set("cartTotal", cartTotal.toString());
833
+ const finalCartTotal = options.discountAmount ? Math.max(0, cartTotal - options.discountAmount) : cartTotal;
834
+ params.set("cartTotal", finalCartTotal.toString());
813
835
  }
814
836
  if (options?.country) {
815
837
  params.set("country", options.country);
@@ -1527,6 +1549,102 @@ function createCheckoutResource(fetcher) {
1527
1549
  };
1528
1550
  }
1529
1551
 
1552
+ // src/resources/discount-code.ts
1553
+ function createDiscountCodeResource(fetcher) {
1554
+ return {
1555
+ /**
1556
+ * Apply a discount code to the cart
1557
+ *
1558
+ * Checks for BuyXPayY campaign conflict before calling API.
1559
+ * Discount codes cannot be used when a campaign discount is active.
1560
+ *
1561
+ * @param params - Parameters including code, session info, and cart/campaign data for conflict check
1562
+ * @returns Applied discount details
1563
+ * @throws {StorefrontError} With code "CAMPAIGN_ACTIVE" if a BuyXPayY campaign is active
1564
+ *
1565
+ * @example
1566
+ * ```typescript
1567
+ * const result = await client.discountCode.apply({
1568
+ * code: "SUMMER20",
1569
+ * cartId: cartId,
1570
+ * cartItems: cart.items,
1571
+ * campaigns: storeConfig.campaigns,
1572
+ * });
1573
+ * ```
1574
+ */
1575
+ async apply(params) {
1576
+ const { code, cartId, sessionId, cartItems, campaigns } = params;
1577
+ if (cartItems && campaigns && cartItems.length > 0) {
1578
+ const campaignResult = calculateCartWithCampaigns(cartItems, campaigns);
1579
+ if (campaignResult.totalSavings > 0) {
1580
+ throw new StorefrontError(
1581
+ "Alennuskoodia ei voi k\xE4ytt\xE4\xE4 kun kampanja-alennus on voimassa",
1582
+ 400,
1583
+ "CAMPAIGN_ACTIVE"
1584
+ );
1585
+ }
1586
+ }
1587
+ const cartTotal = cartItems ? cartItems.reduce((sum, item) => {
1588
+ const priceInfo = getPriceInfo(item.product, item.variation);
1589
+ return sum + priceInfo.effectivePrice * item.cartQuantity;
1590
+ }, 0) : 0;
1591
+ return fetcher.request("/api/storefront/v1/discount-code/apply", {
1592
+ method: "POST",
1593
+ body: { code, cartTotal },
1594
+ headers: {
1595
+ ...cartId && { "x-cart-id": cartId },
1596
+ ...sessionId && { "x-session-id": sessionId }
1597
+ }
1598
+ });
1599
+ },
1600
+ /**
1601
+ * Get the currently applied discount code
1602
+ *
1603
+ * @param params - Session info (cartId or sessionId)
1604
+ * @returns Current discount or null if none applied
1605
+ *
1606
+ * @example
1607
+ * ```typescript
1608
+ * const { discount } = await client.discountCode.get({ cartId });
1609
+ * if (discount) {
1610
+ * console.log(`Code ${discount.code}: ${discount.discountValue}${discount.discountType === 'PERCENTAGE' ? '%' : '¢'} off`);
1611
+ * }
1612
+ * ```
1613
+ */
1614
+ async get(params = {}) {
1615
+ const { cartId, sessionId } = params;
1616
+ return fetcher.request("/api/storefront/v1/discount-code/apply", {
1617
+ method: "GET",
1618
+ headers: {
1619
+ ...cartId && { "x-cart-id": cartId },
1620
+ ...sessionId && { "x-session-id": sessionId }
1621
+ }
1622
+ });
1623
+ },
1624
+ /**
1625
+ * Remove the currently applied discount code
1626
+ *
1627
+ * @param params - Session info (cartId or sessionId)
1628
+ * @returns Success status
1629
+ *
1630
+ * @example
1631
+ * ```typescript
1632
+ * await client.discountCode.remove({ cartId });
1633
+ * ```
1634
+ */
1635
+ async remove(params = {}) {
1636
+ const { cartId, sessionId } = params;
1637
+ return fetcher.request("/api/storefront/v1/discount-code/apply", {
1638
+ method: "DELETE",
1639
+ headers: {
1640
+ ...cartId && { "x-cart-id": cartId },
1641
+ ...sessionId && { "x-session-id": sessionId }
1642
+ }
1643
+ });
1644
+ }
1645
+ };
1646
+ }
1647
+
1530
1648
  // src/client.ts
1531
1649
  function createStorefrontClient(config) {
1532
1650
  if (!config.apiKey) {
@@ -1550,9 +1668,101 @@ function createStorefrontClient(config) {
1550
1668
  shipping: createShippingResource(fetcher),
1551
1669
  customer: createCustomerResource(fetcher),
1552
1670
  order: createOrderResource(fetcher),
1553
- checkout: createCheckoutResource(fetcher)
1671
+ checkout: createCheckoutResource(fetcher),
1672
+ discountCode: createDiscountCodeResource(fetcher)
1554
1673
  };
1555
1674
  }
1675
+
1676
+ // src/utils/discount.ts
1677
+ function formatDiscountValue(discount, options = {}) {
1678
+ const {
1679
+ currencySymbol = "\u20AC",
1680
+ currencyPosition = "after",
1681
+ decimals = 2,
1682
+ showMinus = true
1683
+ } = options;
1684
+ const prefix = showMinus ? "-" : "";
1685
+ if (discount.discountType === "PERCENTAGE") {
1686
+ return `${prefix}${discount.discountValue}%`;
1687
+ }
1688
+ const amount = (discount.discountValue / 100).toFixed(decimals);
1689
+ const formattedAmount = amount.replace(".", ",");
1690
+ if (currencyPosition === "before") {
1691
+ return `${prefix}${currencySymbol}${formattedAmount}`;
1692
+ }
1693
+ return `${prefix}${formattedAmount} ${currencySymbol}`;
1694
+ }
1695
+ function calculateDiscountAmount(subtotal, discount) {
1696
+ if (discount.discountType === "PERCENTAGE") {
1697
+ return Math.round(subtotal * discount.discountValue / 100);
1698
+ }
1699
+ return Math.min(discount.discountValue, subtotal);
1700
+ }
1701
+ var REMOVAL_MESSAGES = {
1702
+ CAMPAIGN_ACTIVE: {
1703
+ fi: "Alennuskoodi poistettu - kampanja-alennus aktivoitui",
1704
+ en: "Discount code removed - campaign discount activated"
1705
+ },
1706
+ MIN_ORDER_NOT_MET: {
1707
+ fi: "Alennuskoodi poistettu - ostoskorin summa alittaa minimitilauksen",
1708
+ en: "Discount code removed - cart total below minimum order"
1709
+ },
1710
+ CODE_INVALID: {
1711
+ fi: "Alennuskoodi poistettu - koodi ei ole en\xE4\xE4 voimassa",
1712
+ en: "Discount code removed - code is no longer valid"
1713
+ }
1714
+ };
1715
+ var DEFAULT_REMOVAL_MESSAGE = {
1716
+ fi: "Alennuskoodi poistettu",
1717
+ en: "Discount code removed"
1718
+ };
1719
+ function getDiscountRemovalMessage(reason, locale = "fi") {
1720
+ if (!reason) {
1721
+ return DEFAULT_REMOVAL_MESSAGE[locale];
1722
+ }
1723
+ return REMOVAL_MESSAGES[reason]?.[locale] ?? DEFAULT_REMOVAL_MESSAGE[locale];
1724
+ }
1725
+ var APPLY_ERROR_MESSAGES = {
1726
+ NOT_FOUND: {
1727
+ fi: "Alennuskoodia ei l\xF6ydy",
1728
+ en: "Discount code not found"
1729
+ },
1730
+ INACTIVE: {
1731
+ fi: "Alennuskoodi ei ole k\xE4yt\xF6ss\xE4",
1732
+ en: "Discount code is not active"
1733
+ },
1734
+ NOT_STARTED: {
1735
+ fi: "Alennuskoodi ei ole viel\xE4 voimassa",
1736
+ en: "Discount code is not yet valid"
1737
+ },
1738
+ EXPIRED: {
1739
+ fi: "Alennuskoodi on vanhentunut",
1740
+ en: "Discount code has expired"
1741
+ },
1742
+ MAX_USES_REACHED: {
1743
+ fi: "Alennuskoodi on k\xE4ytetty loppuun",
1744
+ en: "Discount code usage limit reached"
1745
+ },
1746
+ MIN_ORDER_NOT_MET: {
1747
+ fi: "Ostoskorin summa alittaa alennuskoodin minimitilauksen",
1748
+ en: "Cart total is below the minimum order for this code"
1749
+ },
1750
+ CAMPAIGN_ACTIVE: {
1751
+ fi: "Alennuskoodia ei voi k\xE4ytt\xE4\xE4 kun kampanja-alennus on voimassa",
1752
+ en: "Discount code cannot be used when a campaign discount is active"
1753
+ }
1754
+ };
1755
+ var DEFAULT_APPLY_ERROR = {
1756
+ fi: "Alennuskoodin k\xE4ytt\xF6 ep\xE4onnistui",
1757
+ en: "Failed to apply discount code"
1758
+ };
1759
+ function getDiscountApplyErrorMessage(errorCode, locale = "fi") {
1760
+ if (!errorCode) {
1761
+ return DEFAULT_APPLY_ERROR[locale];
1762
+ }
1763
+ const messages = APPLY_ERROR_MESSAGES[errorCode];
1764
+ return messages?.[locale] ?? DEFAULT_APPLY_ERROR[locale];
1765
+ }
1556
1766
  export {
1557
1767
  AuthError,
1558
1768
  NotFoundError,
@@ -1561,7 +1771,11 @@ export {
1561
1771
  ValidationError,
1562
1772
  VerificationRequiredError,
1563
1773
  calculateCartWithCampaigns,
1774
+ calculateDiscountAmount,
1564
1775
  createStorefrontClient,
1776
+ formatDiscountValue,
1777
+ getDiscountApplyErrorMessage,
1778
+ getDiscountRemovalMessage,
1565
1779
  getPriceInfo,
1566
1780
  isSaleActive
1567
1781
  };