@putiikkipalvelu/storefront-sdk 0.2.5 → 0.4.0
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.cjs +400 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +432 -95
- package/dist/index.d.ts +432 -95
- package/dist/index.js +390 -161
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -414,13 +414,145 @@ function createCategoriesResource(fetcher) {
|
|
|
414
414
|
};
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
+
// src/utils/pricing.ts
|
|
418
|
+
function isSaleActive(startDate, endDate) {
|
|
419
|
+
if (!startDate && !endDate) {
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
const now = /* @__PURE__ */ new Date();
|
|
423
|
+
const start = startDate ? new Date(startDate) : null;
|
|
424
|
+
const end = endDate ? new Date(endDate) : null;
|
|
425
|
+
if (start && !end) {
|
|
426
|
+
return now >= start;
|
|
427
|
+
}
|
|
428
|
+
if (!start && end) {
|
|
429
|
+
return now <= end;
|
|
430
|
+
}
|
|
431
|
+
if (start && end) {
|
|
432
|
+
return now >= start && now <= end;
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
function getPriceInfo(product, variation) {
|
|
437
|
+
if (variation) {
|
|
438
|
+
const isOnSale2 = isSaleActive(variation.saleStartDate, variation.saleEndDate) && variation.salePrice !== null;
|
|
439
|
+
const originalPrice2 = _nullishCoalesce(variation.price, () => ( product.price));
|
|
440
|
+
const effectivePrice2 = isOnSale2 ? _nullishCoalesce(variation.salePrice, () => ( originalPrice2)) : originalPrice2;
|
|
441
|
+
return {
|
|
442
|
+
effectivePrice: effectivePrice2,
|
|
443
|
+
originalPrice: originalPrice2,
|
|
444
|
+
isOnSale: isOnSale2,
|
|
445
|
+
salePercent: isOnSale2 ? _nullishCoalesce(variation.salePercent, () => ( null)) : null
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
const isOnSale = isSaleActive(product.saleStartDate, product.saleEndDate) && product.salePrice !== null;
|
|
449
|
+
const originalPrice = product.price;
|
|
450
|
+
const effectivePrice = isOnSale ? _nullishCoalesce(product.salePrice, () => ( originalPrice)) : originalPrice;
|
|
451
|
+
return {
|
|
452
|
+
effectivePrice,
|
|
453
|
+
originalPrice,
|
|
454
|
+
isOnSale,
|
|
455
|
+
salePercent: isOnSale ? _nullishCoalesce(product.salePercent, () => ( null)) : null
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// src/utils/cart-calculations.ts
|
|
460
|
+
function calculateCartWithCampaigns(items, campaigns) {
|
|
461
|
+
const buyXPayYCampaign = campaigns.find(
|
|
462
|
+
(c) => c.type === "BUY_X_PAY_Y" && c.isActive
|
|
463
|
+
);
|
|
464
|
+
const originalTotal = items.reduce((total, { product, variation, cartQuantity }) => {
|
|
465
|
+
const priceInfo = getPriceInfo(product, variation);
|
|
466
|
+
return total + priceInfo.effectivePrice * cartQuantity;
|
|
467
|
+
}, 0);
|
|
468
|
+
if (!_optionalChain([buyXPayYCampaign, 'optionalAccess', _6 => _6.BuyXPayYCampaign])) {
|
|
469
|
+
const calculatedItems2 = items.map((item) => ({
|
|
470
|
+
item,
|
|
471
|
+
paidQuantity: item.cartQuantity,
|
|
472
|
+
freeQuantity: 0,
|
|
473
|
+
totalQuantity: item.cartQuantity
|
|
474
|
+
}));
|
|
475
|
+
return {
|
|
476
|
+
calculatedItems: calculatedItems2,
|
|
477
|
+
cartTotal: originalTotal,
|
|
478
|
+
originalTotal,
|
|
479
|
+
totalSavings: 0
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
const { buyQuantity, payQuantity, applicableCategories } = buyXPayYCampaign.BuyXPayYCampaign;
|
|
483
|
+
const applicableCategoryIds = new Set(
|
|
484
|
+
applicableCategories.map((c) => c.id)
|
|
485
|
+
);
|
|
486
|
+
const eligibleUnits = items.flatMap((item) => {
|
|
487
|
+
const { product, variation } = item;
|
|
488
|
+
const itemCategories = _optionalChain([product, 'access', _7 => _7.categories, 'optionalAccess', _8 => _8.map, 'call', _9 => _9((cat) => cat.id)]) || [];
|
|
489
|
+
const isEligible = itemCategories.some(
|
|
490
|
+
(id) => applicableCategoryIds.has(id)
|
|
491
|
+
);
|
|
492
|
+
if (isEligible) {
|
|
493
|
+
const priceInfo = getPriceInfo(product, variation);
|
|
494
|
+
return Array.from({ length: item.cartQuantity }, () => ({
|
|
495
|
+
price: priceInfo.effectivePrice,
|
|
496
|
+
productId: product.id,
|
|
497
|
+
variationId: _optionalChain([variation, 'optionalAccess', _10 => _10.id]),
|
|
498
|
+
originalItem: item
|
|
499
|
+
}));
|
|
500
|
+
}
|
|
501
|
+
return [];
|
|
502
|
+
});
|
|
503
|
+
if (eligibleUnits.length < buyQuantity) {
|
|
504
|
+
const calculatedItems2 = items.map((item) => ({
|
|
505
|
+
item,
|
|
506
|
+
paidQuantity: item.cartQuantity,
|
|
507
|
+
freeQuantity: 0,
|
|
508
|
+
totalQuantity: item.cartQuantity
|
|
509
|
+
}));
|
|
510
|
+
return {
|
|
511
|
+
calculatedItems: calculatedItems2,
|
|
512
|
+
cartTotal: originalTotal,
|
|
513
|
+
originalTotal,
|
|
514
|
+
totalSavings: 0
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
eligibleUnits.sort((a, b) => a.price - b.price);
|
|
518
|
+
const numToMakeFree = buyQuantity - payQuantity;
|
|
519
|
+
const itemsToMakeFree = eligibleUnits.slice(0, numToMakeFree);
|
|
520
|
+
const totalSavings = itemsToMakeFree.reduce(
|
|
521
|
+
(sum, item) => sum + item.price,
|
|
522
|
+
0
|
|
523
|
+
);
|
|
524
|
+
const freeCountMap = /* @__PURE__ */ new Map();
|
|
525
|
+
for (const freebie of itemsToMakeFree) {
|
|
526
|
+
const key = `${freebie.productId}${freebie.variationId ? `_${freebie.variationId}` : ""}`;
|
|
527
|
+
freeCountMap.set(key, (freeCountMap.get(key) || 0) + 1);
|
|
528
|
+
}
|
|
529
|
+
const calculatedItems = items.map((item) => {
|
|
530
|
+
const key = `${item.product.id}${_optionalChain([item, 'access', _11 => _11.variation, 'optionalAccess', _12 => _12.id]) ? `_${item.variation.id}` : ""}`;
|
|
531
|
+
const freeQuantity = freeCountMap.get(key) || 0;
|
|
532
|
+
const paidQuantity = item.cartQuantity - freeQuantity;
|
|
533
|
+
return {
|
|
534
|
+
item,
|
|
535
|
+
paidQuantity: Math.max(0, paidQuantity),
|
|
536
|
+
freeQuantity,
|
|
537
|
+
totalQuantity: item.cartQuantity
|
|
538
|
+
};
|
|
539
|
+
});
|
|
540
|
+
const cartTotal = originalTotal - totalSavings;
|
|
541
|
+
return {
|
|
542
|
+
calculatedItems,
|
|
543
|
+
cartTotal,
|
|
544
|
+
originalTotal,
|
|
545
|
+
totalSavings
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
417
549
|
// src/resources/cart.ts
|
|
418
550
|
function buildCartHeaders(options) {
|
|
419
551
|
const headers = {};
|
|
420
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
552
|
+
if (_optionalChain([options, 'optionalAccess', _13 => _13.cartId])) {
|
|
421
553
|
headers["x-cart-id"] = options.cartId;
|
|
422
554
|
}
|
|
423
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
555
|
+
if (_optionalChain([options, 'optionalAccess', _14 => _14.sessionId])) {
|
|
424
556
|
headers["x-session-id"] = options.sessionId;
|
|
425
557
|
}
|
|
426
558
|
return headers;
|
|
@@ -576,33 +708,49 @@ function createCartResource(fetcher) {
|
|
|
576
708
|
* Checks product availability, stock levels, and prices.
|
|
577
709
|
* Auto-fixes issues (removes unavailable items, adjusts quantities).
|
|
578
710
|
*
|
|
711
|
+
* Campaign conflict detection:
|
|
712
|
+
* - SDK calculates if BuyXPayY campaigns apply using calculateCartWithCampaigns()
|
|
713
|
+
* - Sends x-campaigns-apply header to backend
|
|
714
|
+
* - If campaigns apply AND discount code exists, backend removes it
|
|
715
|
+
* - Returns changes.discountCouponRemoved = true
|
|
716
|
+
*
|
|
579
717
|
* @param options - Cart session options
|
|
718
|
+
* @param cartItems - Current cart items for campaign calculation
|
|
719
|
+
* @param campaigns - Active campaigns for conflict check
|
|
580
720
|
* @param fetchOptions - Fetch options
|
|
581
721
|
* @returns Validated cart with change metadata
|
|
582
722
|
*
|
|
583
723
|
* @example Validate before checkout
|
|
584
724
|
* ```typescript
|
|
585
|
-
* const
|
|
725
|
+
* const result = await client.cart.validate(
|
|
726
|
+
* { cartId },
|
|
727
|
+
* cartItems,
|
|
728
|
+
* storeConfig.campaigns
|
|
729
|
+
* );
|
|
586
730
|
*
|
|
587
|
-
* if (hasChanges) {
|
|
588
|
-
* if (changes.
|
|
589
|
-
* notify(
|
|
590
|
-
* }
|
|
591
|
-
* if (changes.quantityAdjusted > 0) {
|
|
592
|
-
* notify('Some quantities were adjusted');
|
|
593
|
-
* }
|
|
594
|
-
* if (changes.priceChanged > 0) {
|
|
595
|
-
* notify('Some prices have changed');
|
|
731
|
+
* if (result.hasChanges) {
|
|
732
|
+
* if (result.changes.discountCouponRemoved) {
|
|
733
|
+
* notify("Alennuskoodi poistettu - kampanja-alennus aktivoitui");
|
|
596
734
|
* }
|
|
735
|
+
* // Handle other changes...
|
|
597
736
|
* }
|
|
598
737
|
* ```
|
|
599
738
|
*/
|
|
600
|
-
async validate(options, fetchOptions) {
|
|
739
|
+
async validate(options, cartItems, campaigns, fetchOptions) {
|
|
740
|
+
let campaignsApply = false;
|
|
741
|
+
if (cartItems && campaigns && cartItems.length > 0) {
|
|
742
|
+
const campaignResult = calculateCartWithCampaigns(cartItems, campaigns);
|
|
743
|
+
campaignsApply = campaignResult.totalSavings > 0;
|
|
744
|
+
}
|
|
745
|
+
const headers = {
|
|
746
|
+
...buildCartHeaders(options),
|
|
747
|
+
"x-campaigns-apply": campaignsApply ? "true" : "false"
|
|
748
|
+
};
|
|
601
749
|
return fetcher.request(
|
|
602
750
|
"/api/storefront/v1/cart/validate",
|
|
603
751
|
{
|
|
604
752
|
method: "GET",
|
|
605
|
-
headers
|
|
753
|
+
headers,
|
|
606
754
|
...fetchOptions
|
|
607
755
|
}
|
|
608
756
|
);
|
|
@@ -613,52 +761,75 @@ function createCartResource(fetcher) {
|
|
|
613
761
|
// src/resources/shipping.ts
|
|
614
762
|
function calculateCartWeight(items) {
|
|
615
763
|
return items.reduce((total, item) => {
|
|
616
|
-
const itemWeight = _nullishCoalesce(_nullishCoalesce(_optionalChain([item, 'access',
|
|
764
|
+
const itemWeight = _nullishCoalesce(_nullishCoalesce(_optionalChain([item, 'access', _15 => _15.variation, 'optionalAccess', _16 => _16.weight]), () => ( item.product.weight)), () => ( 0.5));
|
|
617
765
|
return total + itemWeight * item.cartQuantity;
|
|
618
766
|
}, 0);
|
|
619
767
|
}
|
|
768
|
+
function calculateCartTotal(items) {
|
|
769
|
+
return items.reduce((total, item) => {
|
|
770
|
+
const itemPrice = item.variation ? _nullishCoalesce(item.variation.salePrice, () => ( item.variation.price)) : _nullishCoalesce(item.product.salePrice, () => ( item.product.price));
|
|
771
|
+
return total + itemPrice * item.cartQuantity;
|
|
772
|
+
}, 0);
|
|
773
|
+
}
|
|
620
774
|
function createShippingResource(fetcher) {
|
|
621
775
|
return {
|
|
622
776
|
/**
|
|
623
777
|
* Get shipping options for a specific postal code.
|
|
624
|
-
* Returns home delivery
|
|
778
|
+
* Returns pickup points and home delivery options in a unified format.
|
|
779
|
+
*
|
|
780
|
+
* **Pickup points are returned first** as they are more popular in Finland.
|
|
625
781
|
*
|
|
626
782
|
* @param postalCode - Customer's postal code (e.g., "00100")
|
|
627
783
|
* @param options - Fetch options including optional cartItems for weight-based filtering
|
|
628
|
-
* @returns
|
|
784
|
+
* @returns Unified shipping options (pickupPoints sorted by distance, homeDelivery sorted by price)
|
|
629
785
|
*
|
|
630
786
|
* @example
|
|
631
787
|
* ```typescript
|
|
632
|
-
* const {
|
|
633
|
-
*
|
|
634
|
-
* // Show
|
|
635
|
-
*
|
|
636
|
-
* console.log(`${
|
|
788
|
+
* const { pickupPoints, homeDelivery } = await client.shipping.getOptions("00100");
|
|
789
|
+
*
|
|
790
|
+
* // Show pickup points (more popular in Finland)
|
|
791
|
+
* pickupPoints.forEach(point => {
|
|
792
|
+
* console.log(`${point.name} - ${point.carrier}`);
|
|
793
|
+
* console.log(` ${point.address}, ${point.city}`);
|
|
794
|
+
* console.log(` ${(point.distance! / 1000).toFixed(1)} km away`);
|
|
795
|
+
* console.log(` Price: ${point.price / 100}€`);
|
|
637
796
|
* });
|
|
638
797
|
*
|
|
639
|
-
* // Show
|
|
640
|
-
*
|
|
641
|
-
* console.log(`${
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
798
|
+
* // Show home delivery options
|
|
799
|
+
* homeDelivery.forEach(option => {
|
|
800
|
+
* console.log(`${option.name}: ${option.price / 100}€`);
|
|
801
|
+
* if (option.estimatedDelivery) {
|
|
802
|
+
* console.log(` Delivery: ${option.estimatedDelivery} days`);
|
|
803
|
+
* }
|
|
645
804
|
* });
|
|
646
805
|
* ```
|
|
647
806
|
*
|
|
648
807
|
* @example Weight-based filtering
|
|
649
808
|
* ```typescript
|
|
650
|
-
* const
|
|
651
|
-
*
|
|
652
|
-
*
|
|
653
|
-
* );
|
|
809
|
+
* const options = await client.shipping.getOptions("00100", {
|
|
810
|
+
* cartItems: cartItems
|
|
811
|
+
* });
|
|
654
812
|
* // Only shows methods that support the cart's total weight
|
|
655
813
|
* ```
|
|
814
|
+
*
|
|
815
|
+
* @example International shipping
|
|
816
|
+
* ```typescript
|
|
817
|
+
* const options = await client.shipping.getOptions("112 22", {
|
|
818
|
+
* country: "SE"
|
|
819
|
+
* });
|
|
820
|
+
* ```
|
|
656
821
|
*/
|
|
657
|
-
async
|
|
822
|
+
async getOptions(postalCode, options) {
|
|
658
823
|
const params = new URLSearchParams();
|
|
659
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
824
|
+
if (_optionalChain([options, 'optionalAccess', _17 => _17.cartItems, 'optionalAccess', _18 => _18.length])) {
|
|
660
825
|
const cartWeight = calculateCartWeight(options.cartItems);
|
|
661
826
|
params.set("cartWeight", cartWeight.toString());
|
|
827
|
+
const cartTotal = options.campaigns ? calculateCartWithCampaigns(options.cartItems, options.campaigns).cartTotal : calculateCartTotal(options.cartItems);
|
|
828
|
+
const finalCartTotal = options.discountAmount ? Math.max(0, cartTotal - options.discountAmount) : cartTotal;
|
|
829
|
+
params.set("cartTotal", finalCartTotal.toString());
|
|
830
|
+
}
|
|
831
|
+
if (_optionalChain([options, 'optionalAccess', _19 => _19.country])) {
|
|
832
|
+
params.set("country", options.country);
|
|
662
833
|
}
|
|
663
834
|
const queryString = params.toString();
|
|
664
835
|
const url = `/api/storefront/v1/shipment-methods/${encodeURIComponent(postalCode)}${queryString ? `?${queryString}` : ""}`;
|
|
@@ -666,6 +837,12 @@ function createShippingResource(fetcher) {
|
|
|
666
837
|
method: "GET",
|
|
667
838
|
...options
|
|
668
839
|
});
|
|
840
|
+
},
|
|
841
|
+
/**
|
|
842
|
+
* @deprecated Use getOptions() instead. This method is kept for backwards compatibility.
|
|
843
|
+
*/
|
|
844
|
+
async getWithLocations(postalCode, options) {
|
|
845
|
+
return this.getOptions(postalCode, options);
|
|
669
846
|
}
|
|
670
847
|
};
|
|
671
848
|
}
|
|
@@ -743,7 +920,7 @@ function createCustomerResource(fetcher) {
|
|
|
743
920
|
*/
|
|
744
921
|
async login(email, password, options, fetchOptions) {
|
|
745
922
|
const headers = {};
|
|
746
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
923
|
+
if (_optionalChain([options, 'optionalAccess', _20 => _20.cartId])) {
|
|
747
924
|
headers["x-cart-id"] = options.cartId;
|
|
748
925
|
}
|
|
749
926
|
return fetcher.request(
|
|
@@ -1212,10 +1389,10 @@ function createOrderResource(fetcher) {
|
|
|
1212
1389
|
function createCheckoutResource(fetcher) {
|
|
1213
1390
|
function buildCheckoutHeaders(options) {
|
|
1214
1391
|
const headers = {};
|
|
1215
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
1392
|
+
if (_optionalChain([options, 'optionalAccess', _21 => _21.cartId])) {
|
|
1216
1393
|
headers["x-cart-id"] = options.cartId;
|
|
1217
1394
|
}
|
|
1218
|
-
if (_optionalChain([options, 'optionalAccess',
|
|
1395
|
+
if (_optionalChain([options, 'optionalAccess', _22 => _22.sessionId])) {
|
|
1219
1396
|
headers["x-session-id"] = options.sessionId;
|
|
1220
1397
|
}
|
|
1221
1398
|
return headers;
|
|
@@ -1279,7 +1456,7 @@ function createCheckoutResource(fetcher) {
|
|
|
1279
1456
|
method: "POST",
|
|
1280
1457
|
body,
|
|
1281
1458
|
headers: {
|
|
1282
|
-
..._optionalChain([options, 'optionalAccess',
|
|
1459
|
+
..._optionalChain([options, 'optionalAccess', _23 => _23.headers]),
|
|
1283
1460
|
...headers
|
|
1284
1461
|
},
|
|
1285
1462
|
...options
|
|
@@ -1357,7 +1534,7 @@ function createCheckoutResource(fetcher) {
|
|
|
1357
1534
|
method: "POST",
|
|
1358
1535
|
body,
|
|
1359
1536
|
headers: {
|
|
1360
|
-
..._optionalChain([options, 'optionalAccess',
|
|
1537
|
+
..._optionalChain([options, 'optionalAccess', _24 => _24.headers]),
|
|
1361
1538
|
...headers
|
|
1362
1539
|
},
|
|
1363
1540
|
...options
|
|
@@ -1367,6 +1544,102 @@ function createCheckoutResource(fetcher) {
|
|
|
1367
1544
|
};
|
|
1368
1545
|
}
|
|
1369
1546
|
|
|
1547
|
+
// src/resources/discount-code.ts
|
|
1548
|
+
function createDiscountCodeResource(fetcher) {
|
|
1549
|
+
return {
|
|
1550
|
+
/**
|
|
1551
|
+
* Apply a discount code to the cart
|
|
1552
|
+
*
|
|
1553
|
+
* Checks for BuyXPayY campaign conflict before calling API.
|
|
1554
|
+
* Discount codes cannot be used when a campaign discount is active.
|
|
1555
|
+
*
|
|
1556
|
+
* @param params - Parameters including code, session info, and cart/campaign data for conflict check
|
|
1557
|
+
* @returns Applied discount details
|
|
1558
|
+
* @throws {StorefrontError} With code "CAMPAIGN_ACTIVE" if a BuyXPayY campaign is active
|
|
1559
|
+
*
|
|
1560
|
+
* @example
|
|
1561
|
+
* ```typescript
|
|
1562
|
+
* const result = await client.discountCode.apply({
|
|
1563
|
+
* code: "SUMMER20",
|
|
1564
|
+
* cartId: cartId,
|
|
1565
|
+
* cartItems: cart.items,
|
|
1566
|
+
* campaigns: storeConfig.campaigns,
|
|
1567
|
+
* });
|
|
1568
|
+
* ```
|
|
1569
|
+
*/
|
|
1570
|
+
async apply(params) {
|
|
1571
|
+
const { code, cartId, sessionId, cartItems, campaigns } = params;
|
|
1572
|
+
if (cartItems && campaigns && cartItems.length > 0) {
|
|
1573
|
+
const campaignResult = calculateCartWithCampaigns(cartItems, campaigns);
|
|
1574
|
+
if (campaignResult.totalSavings > 0) {
|
|
1575
|
+
throw new StorefrontError(
|
|
1576
|
+
"Alennuskoodia ei voi k\xE4ytt\xE4\xE4 kun kampanja-alennus on voimassa",
|
|
1577
|
+
400,
|
|
1578
|
+
"CAMPAIGN_ACTIVE"
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
const cartTotal = cartItems ? cartItems.reduce((sum, item) => {
|
|
1583
|
+
const priceInfo = getPriceInfo(item.product, item.variation);
|
|
1584
|
+
return sum + priceInfo.effectivePrice * item.cartQuantity;
|
|
1585
|
+
}, 0) : 0;
|
|
1586
|
+
return fetcher.request("/api/storefront/v1/discount-code/apply", {
|
|
1587
|
+
method: "POST",
|
|
1588
|
+
body: { code, cartTotal },
|
|
1589
|
+
headers: {
|
|
1590
|
+
...cartId && { "x-cart-id": cartId },
|
|
1591
|
+
...sessionId && { "x-session-id": sessionId }
|
|
1592
|
+
}
|
|
1593
|
+
});
|
|
1594
|
+
},
|
|
1595
|
+
/**
|
|
1596
|
+
* Get the currently applied discount code
|
|
1597
|
+
*
|
|
1598
|
+
* @param params - Session info (cartId or sessionId)
|
|
1599
|
+
* @returns Current discount or null if none applied
|
|
1600
|
+
*
|
|
1601
|
+
* @example
|
|
1602
|
+
* ```typescript
|
|
1603
|
+
* const { discount } = await client.discountCode.get({ cartId });
|
|
1604
|
+
* if (discount) {
|
|
1605
|
+
* console.log(`Code ${discount.code}: ${discount.discountValue}${discount.discountType === 'PERCENTAGE' ? '%' : '¢'} off`);
|
|
1606
|
+
* }
|
|
1607
|
+
* ```
|
|
1608
|
+
*/
|
|
1609
|
+
async get(params = {}) {
|
|
1610
|
+
const { cartId, sessionId } = params;
|
|
1611
|
+
return fetcher.request("/api/storefront/v1/discount-code/apply", {
|
|
1612
|
+
method: "GET",
|
|
1613
|
+
headers: {
|
|
1614
|
+
...cartId && { "x-cart-id": cartId },
|
|
1615
|
+
...sessionId && { "x-session-id": sessionId }
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
},
|
|
1619
|
+
/**
|
|
1620
|
+
* Remove the currently applied discount code
|
|
1621
|
+
*
|
|
1622
|
+
* @param params - Session info (cartId or sessionId)
|
|
1623
|
+
* @returns Success status
|
|
1624
|
+
*
|
|
1625
|
+
* @example
|
|
1626
|
+
* ```typescript
|
|
1627
|
+
* await client.discountCode.remove({ cartId });
|
|
1628
|
+
* ```
|
|
1629
|
+
*/
|
|
1630
|
+
async remove(params = {}) {
|
|
1631
|
+
const { cartId, sessionId } = params;
|
|
1632
|
+
return fetcher.request("/api/storefront/v1/discount-code/apply", {
|
|
1633
|
+
method: "DELETE",
|
|
1634
|
+
headers: {
|
|
1635
|
+
...cartId && { "x-cart-id": cartId },
|
|
1636
|
+
...sessionId && { "x-session-id": sessionId }
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1370
1643
|
// src/client.ts
|
|
1371
1644
|
function createStorefrontClient(config) {
|
|
1372
1645
|
if (!config.apiKey) {
|
|
@@ -1390,148 +1663,100 @@ function createStorefrontClient(config) {
|
|
|
1390
1663
|
shipping: createShippingResource(fetcher),
|
|
1391
1664
|
customer: createCustomerResource(fetcher),
|
|
1392
1665
|
order: createOrderResource(fetcher),
|
|
1393
|
-
checkout: createCheckoutResource(fetcher)
|
|
1666
|
+
checkout: createCheckoutResource(fetcher),
|
|
1667
|
+
discountCode: createDiscountCodeResource(fetcher)
|
|
1394
1668
|
};
|
|
1395
1669
|
}
|
|
1396
1670
|
|
|
1397
|
-
// src/utils/
|
|
1398
|
-
function
|
|
1399
|
-
|
|
1400
|
-
|
|
1671
|
+
// src/utils/discount.ts
|
|
1672
|
+
function formatDiscountValue(discount, options = {}) {
|
|
1673
|
+
const {
|
|
1674
|
+
currencySymbol = "\u20AC",
|
|
1675
|
+
currencyPosition = "after",
|
|
1676
|
+
decimals = 2,
|
|
1677
|
+
showMinus = true
|
|
1678
|
+
} = options;
|
|
1679
|
+
const prefix = showMinus ? "-" : "";
|
|
1680
|
+
if (discount.discountType === "PERCENTAGE") {
|
|
1681
|
+
return `${prefix}${discount.discountValue}%`;
|
|
1401
1682
|
}
|
|
1402
|
-
const
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
return now >= start;
|
|
1407
|
-
}
|
|
1408
|
-
if (!start && end) {
|
|
1409
|
-
return now <= end;
|
|
1683
|
+
const amount = (discount.discountValue / 100).toFixed(decimals);
|
|
1684
|
+
const formattedAmount = amount.replace(".", ",");
|
|
1685
|
+
if (currencyPosition === "before") {
|
|
1686
|
+
return `${prefix}${currencySymbol}${formattedAmount}`;
|
|
1410
1687
|
}
|
|
1411
|
-
|
|
1412
|
-
return now >= start && now <= end;
|
|
1413
|
-
}
|
|
1414
|
-
return true;
|
|
1688
|
+
return `${prefix}${formattedAmount} ${currencySymbol}`;
|
|
1415
1689
|
}
|
|
1416
|
-
function
|
|
1417
|
-
if (
|
|
1418
|
-
|
|
1419
|
-
const originalPrice2 = _nullishCoalesce(variation.price, () => ( product.price));
|
|
1420
|
-
const effectivePrice2 = isOnSale2 ? _nullishCoalesce(variation.salePrice, () => ( originalPrice2)) : originalPrice2;
|
|
1421
|
-
return {
|
|
1422
|
-
effectivePrice: effectivePrice2,
|
|
1423
|
-
originalPrice: originalPrice2,
|
|
1424
|
-
isOnSale: isOnSale2,
|
|
1425
|
-
salePercent: isOnSale2 ? _nullishCoalesce(variation.salePercent, () => ( null)) : null
|
|
1426
|
-
};
|
|
1690
|
+
function calculateDiscountAmount(subtotal, discount) {
|
|
1691
|
+
if (discount.discountType === "PERCENTAGE") {
|
|
1692
|
+
return Math.round(subtotal * discount.discountValue / 100);
|
|
1427
1693
|
}
|
|
1428
|
-
|
|
1429
|
-
const originalPrice = product.price;
|
|
1430
|
-
const effectivePrice = isOnSale ? _nullishCoalesce(product.salePrice, () => ( originalPrice)) : originalPrice;
|
|
1431
|
-
return {
|
|
1432
|
-
effectivePrice,
|
|
1433
|
-
originalPrice,
|
|
1434
|
-
isOnSale,
|
|
1435
|
-
salePercent: isOnSale ? _nullishCoalesce(product.salePercent, () => ( null)) : null
|
|
1436
|
-
};
|
|
1694
|
+
return Math.min(discount.discountValue, subtotal);
|
|
1437
1695
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
minimumSpend: 0,
|
|
1451
|
-
remainingAmount: 0
|
|
1452
|
-
};
|
|
1453
|
-
if (!_optionalChain([buyXPayYCampaign, 'optionalAccess', _17 => _17.BuyXPayYCampaign])) {
|
|
1454
|
-
const calculatedItems2 = items.map((item) => ({
|
|
1455
|
-
item,
|
|
1456
|
-
paidQuantity: item.cartQuantity,
|
|
1457
|
-
freeQuantity: 0,
|
|
1458
|
-
totalQuantity: item.cartQuantity
|
|
1459
|
-
}));
|
|
1460
|
-
return {
|
|
1461
|
-
calculatedItems: calculatedItems2,
|
|
1462
|
-
cartTotal: originalTotal,
|
|
1463
|
-
originalTotal,
|
|
1464
|
-
totalSavings: 0,
|
|
1465
|
-
freeShipping
|
|
1466
|
-
};
|
|
1696
|
+
var REMOVAL_MESSAGES = {
|
|
1697
|
+
CAMPAIGN_ACTIVE: {
|
|
1698
|
+
fi: "Alennuskoodi poistettu - kampanja-alennus aktivoitui",
|
|
1699
|
+
en: "Discount code removed - campaign discount activated"
|
|
1700
|
+
},
|
|
1701
|
+
MIN_ORDER_NOT_MET: {
|
|
1702
|
+
fi: "Alennuskoodi poistettu - ostoskorin summa alittaa minimitilauksen",
|
|
1703
|
+
en: "Discount code removed - cart total below minimum order"
|
|
1704
|
+
},
|
|
1705
|
+
CODE_INVALID: {
|
|
1706
|
+
fi: "Alennuskoodi poistettu - koodi ei ole en\xE4\xE4 voimassa",
|
|
1707
|
+
en: "Discount code removed - code is no longer valid"
|
|
1467
1708
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
(id) => applicableCategoryIds.has(id)
|
|
1477
|
-
);
|
|
1478
|
-
if (isEligible) {
|
|
1479
|
-
const priceInfo = getPriceInfo(product, variation);
|
|
1480
|
-
return Array.from({ length: item.cartQuantity }, () => ({
|
|
1481
|
-
price: priceInfo.effectivePrice,
|
|
1482
|
-
productId: product.id,
|
|
1483
|
-
variationId: _optionalChain([variation, 'optionalAccess', _21 => _21.id]),
|
|
1484
|
-
originalItem: item
|
|
1485
|
-
}));
|
|
1486
|
-
}
|
|
1487
|
-
return [];
|
|
1488
|
-
});
|
|
1489
|
-
if (eligibleUnits.length < buyQuantity) {
|
|
1490
|
-
const calculatedItems2 = items.map((item) => ({
|
|
1491
|
-
item,
|
|
1492
|
-
paidQuantity: item.cartQuantity,
|
|
1493
|
-
freeQuantity: 0,
|
|
1494
|
-
totalQuantity: item.cartQuantity
|
|
1495
|
-
}));
|
|
1496
|
-
return {
|
|
1497
|
-
calculatedItems: calculatedItems2,
|
|
1498
|
-
cartTotal: originalTotal,
|
|
1499
|
-
originalTotal,
|
|
1500
|
-
totalSavings: 0,
|
|
1501
|
-
freeShipping
|
|
1502
|
-
};
|
|
1709
|
+
};
|
|
1710
|
+
var DEFAULT_REMOVAL_MESSAGE = {
|
|
1711
|
+
fi: "Alennuskoodi poistettu",
|
|
1712
|
+
en: "Discount code removed"
|
|
1713
|
+
};
|
|
1714
|
+
function getDiscountRemovalMessage(reason, locale = "fi") {
|
|
1715
|
+
if (!reason) {
|
|
1716
|
+
return DEFAULT_REMOVAL_MESSAGE[locale];
|
|
1503
1717
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1718
|
+
return _nullishCoalesce(_optionalChain([REMOVAL_MESSAGES, 'access', _25 => _25[reason], 'optionalAccess', _26 => _26[locale]]), () => ( DEFAULT_REMOVAL_MESSAGE[locale]));
|
|
1719
|
+
}
|
|
1720
|
+
var APPLY_ERROR_MESSAGES = {
|
|
1721
|
+
NOT_FOUND: {
|
|
1722
|
+
fi: "Alennuskoodia ei l\xF6ydy",
|
|
1723
|
+
en: "Discount code not found"
|
|
1724
|
+
},
|
|
1725
|
+
INACTIVE: {
|
|
1726
|
+
fi: "Alennuskoodi ei ole k\xE4yt\xF6ss\xE4",
|
|
1727
|
+
en: "Discount code is not active"
|
|
1728
|
+
},
|
|
1729
|
+
NOT_STARTED: {
|
|
1730
|
+
fi: "Alennuskoodi ei ole viel\xE4 voimassa",
|
|
1731
|
+
en: "Discount code is not yet valid"
|
|
1732
|
+
},
|
|
1733
|
+
EXPIRED: {
|
|
1734
|
+
fi: "Alennuskoodi on vanhentunut",
|
|
1735
|
+
en: "Discount code has expired"
|
|
1736
|
+
},
|
|
1737
|
+
MAX_USES_REACHED: {
|
|
1738
|
+
fi: "Alennuskoodi on k\xE4ytetty loppuun",
|
|
1739
|
+
en: "Discount code usage limit reached"
|
|
1740
|
+
},
|
|
1741
|
+
MIN_ORDER_NOT_MET: {
|
|
1742
|
+
fi: "Ostoskorin summa alittaa alennuskoodin minimitilauksen",
|
|
1743
|
+
en: "Cart total is below the minimum order for this code"
|
|
1744
|
+
},
|
|
1745
|
+
CAMPAIGN_ACTIVE: {
|
|
1746
|
+
fi: "Alennuskoodia ei voi k\xE4ytt\xE4\xE4 kun kampanja-alennus on voimassa",
|
|
1747
|
+
en: "Discount code cannot be used when a campaign discount is active"
|
|
1515
1748
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
const cartTotal = originalTotal - totalSavings;
|
|
1528
|
-
return {
|
|
1529
|
-
calculatedItems,
|
|
1530
|
-
cartTotal,
|
|
1531
|
-
originalTotal,
|
|
1532
|
-
totalSavings,
|
|
1533
|
-
freeShipping
|
|
1534
|
-
};
|
|
1749
|
+
};
|
|
1750
|
+
var DEFAULT_APPLY_ERROR = {
|
|
1751
|
+
fi: "Alennuskoodin k\xE4ytt\xF6 ep\xE4onnistui",
|
|
1752
|
+
en: "Failed to apply discount code"
|
|
1753
|
+
};
|
|
1754
|
+
function getDiscountApplyErrorMessage(errorCode, locale = "fi") {
|
|
1755
|
+
if (!errorCode) {
|
|
1756
|
+
return DEFAULT_APPLY_ERROR[locale];
|
|
1757
|
+
}
|
|
1758
|
+
const messages = APPLY_ERROR_MESSAGES[errorCode];
|
|
1759
|
+
return _nullishCoalesce(_optionalChain([messages, 'optionalAccess', _27 => _27[locale]]), () => ( DEFAULT_APPLY_ERROR[locale]));
|
|
1535
1760
|
}
|
|
1536
1761
|
|
|
1537
1762
|
|
|
@@ -1544,5 +1769,9 @@ function calculateCartWithCampaigns(items, campaigns) {
|
|
|
1544
1769
|
|
|
1545
1770
|
|
|
1546
1771
|
|
|
1547
|
-
|
|
1772
|
+
|
|
1773
|
+
|
|
1774
|
+
|
|
1775
|
+
|
|
1776
|
+
exports.AuthError = AuthError; exports.NotFoundError = NotFoundError; exports.RateLimitError = RateLimitError; exports.StorefrontError = StorefrontError; exports.ValidationError = ValidationError; exports.VerificationRequiredError = VerificationRequiredError; exports.calculateCartWithCampaigns = calculateCartWithCampaigns; exports.calculateDiscountAmount = calculateDiscountAmount; exports.createStorefrontClient = createStorefrontClient; exports.formatDiscountValue = formatDiscountValue; exports.getDiscountApplyErrorMessage = getDiscountApplyErrorMessage; exports.getDiscountRemovalMessage = getDiscountRemovalMessage; exports.getPriceInfo = getPriceInfo; exports.isSaleActive = isSaleActive;
|
|
1548
1777
|
//# sourceMappingURL=index.cjs.map
|