@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.d.ts CHANGED
@@ -419,6 +419,10 @@ interface CartResponse {
419
419
  /** Cart ID for guest users (undefined for logged-in users) */
420
420
  cartId?: string;
421
421
  }
422
+ /**
423
+ * Reason why a discount code was removed during validation
424
+ */
425
+ type DiscountRemovalReason = "CAMPAIGN_ACTIVE" | "MIN_ORDER_NOT_MET" | "CODE_INVALID";
422
426
  /**
423
427
  * Changes detected during cart validation
424
428
  */
@@ -429,9 +433,16 @@ interface CartValidationChanges {
429
433
  quantityAdjusted: number;
430
434
  /** Number of items with changed price */
431
435
  priceChanged: number;
436
+ /** Whether discount code was removed */
437
+ discountCouponRemoved: boolean;
438
+ /** Reason why discount was removed (only present if discountCouponRemoved is true) */
439
+ discountRemovalReason?: DiscountRemovalReason;
432
440
  }
433
441
  /**
434
442
  * Response from GET /cart/validate
443
+ *
444
+ * Returns validated cart items and change metadata.
445
+ * No totals or discount data - those are calculated client-side or fetched separately.
435
446
  */
436
447
  interface CartValidationResponse {
437
448
  /** Validated cart items (with auto-fixed quantities/prices) */
@@ -507,21 +518,6 @@ interface CalculatedCartItem {
507
518
  /** Total quantity in cart */
508
519
  totalQuantity: number;
509
520
  }
510
- /**
511
- * Free shipping eligibility status
512
- */
513
- interface FreeShippingStatus {
514
- /** Whether the cart qualifies for free shipping */
515
- isEligible: boolean;
516
- /** Minimum spend required for free shipping (in cents) */
517
- minimumSpend: number;
518
- /** Amount remaining to qualify for free shipping (in cents) */
519
- remainingAmount: number;
520
- /** Name of the free shipping campaign */
521
- campaignName?: string;
522
- /** IDs of shipment methods eligible for free shipping (when isEligible is true) */
523
- eligibleShipmentMethodIds?: string[];
524
- }
525
521
  /**
526
522
  * Result of cart calculation with campaigns applied
527
523
  */
@@ -534,21 +530,18 @@ interface CartCalculationResult {
534
530
  originalTotal: number;
535
531
  /** Total savings from campaigns (in cents) */
536
532
  totalSavings: number;
537
- /** Free shipping eligibility status */
538
- freeShipping: FreeShippingStatus;
539
533
  }
540
534
 
541
535
  /**
542
536
  * Shipping Types
543
537
  *
544
538
  * Types for shipment methods and pickup locations.
545
- * ShipmentMethod and ShipitShippingMethod are re-exported from storeconfig.
539
+ * Uses unified response format that works with any provider (Shipit, custom, future integrations).
546
540
  */
547
-
548
541
  /**
549
542
  * Opening hours for a pickup location
550
543
  */
551
- interface PickupLocationOpeningHours {
544
+ interface OpeningHours {
552
545
  monday: string[];
553
546
  tuesday: string[];
554
547
  wednesday: string[];
@@ -559,57 +552,82 @@ interface PickupLocationOpeningHours {
559
552
  exceptions: string[];
560
553
  }
561
554
  /**
562
- * A pickup location (parcel locker, pickup point, etc.)
563
- * Returned from Shipit API with shipmentMethodId and price attached
555
+ * A home delivery option (works for any provider)
564
556
  */
565
- interface PickupLocation {
566
- /** Unique location ID from Shipit */
557
+ interface HomeDeliveryOption {
558
+ /** ShipmentMethods.id - unique identifier for this method */
567
559
  id: string;
568
- /** Shipit service ID */
560
+ /** Display name (e.g., "Posti Kotipaketti") */
561
+ name: string;
562
+ /** Optional description */
563
+ description: string | null;
564
+ /** Price in cents (0 if free shipping applies) */
565
+ price: number;
566
+ /** Original price in cents (always the base price before free shipping) */
567
+ originalPrice: number;
568
+ /** Free shipping threshold in cents, null = no free shipping available for this method */
569
+ freeShippingThreshold: number | null;
570
+ /** Carrier logo URL */
571
+ logo: string | null;
572
+ /** Provider type */
573
+ provider: "shipit" | "custom";
574
+ /** Carrier name (e.g., "Posti", "Matkahuolto") - null for custom methods */
575
+ carrier: string | null;
576
+ /** Estimated delivery time (e.g., "1-3") - null if not available */
577
+ estimatedDelivery: string | null;
578
+ }
579
+ /**
580
+ * A pickup point option (parcel locker, service point, etc.)
581
+ * Works for any provider that supports pickup locations.
582
+ */
583
+ interface PickupPointOption {
584
+ /** Unique pickup point ID from carrier */
585
+ id: string;
586
+ /** ShipmentMethods.id - needed for checkout */
587
+ shipmentMethodId: string;
588
+ /** Shipit service ID - needed for checkout and shipment creation */
569
589
  serviceId: string;
570
- /** Location name */
590
+ /** Location name (e.g., "Lidl Graniittitalo") */
571
591
  name: string;
572
592
  /** Street address */
573
- address1: string;
593
+ address: string;
574
594
  /** City */
575
595
  city: string;
576
596
  /** Postal code */
577
- zipcode: string;
578
- /** Country code (e.g., "FI") */
579
- countryCode: string;
580
- /** Carrier name (e.g., "Posti", "Matkahuolto") */
581
- carrier: string;
597
+ postalCode: string;
598
+ /** Price in cents (0 if free shipping applies) */
599
+ price: number;
600
+ /** Original price in cents (always the base price before free shipping) */
601
+ originalPrice: number;
602
+ /** Free shipping threshold in cents, null = no free shipping available for this method */
603
+ freeShippingThreshold: number | null;
582
604
  /** Carrier logo URL */
583
- carrierLogo: string;
584
- /** GPS latitude */
585
- latitude?: number;
586
- /** GPS longitude */
587
- longitude?: number;
588
- /** Distance from postal code in meters */
589
- distanceInMeters: number;
590
- /** Distance from postal code in kilometers */
591
- distanceInKilometers: number;
592
- /** Location type (e.g., "parcel_locker", "service_point", "outdoor_parcel_locker") */
593
- type?: string;
605
+ logo: string | null;
606
+ /** Provider type */
607
+ provider: "shipit" | "custom";
608
+ /** Carrier name (e.g., "Posti", "Matkahuolto") */
609
+ carrier: string | null;
610
+ /** Distance from customer's postal code in meters */
611
+ distance: number | null;
594
612
  /** Structured opening hours */
595
- openingHours?: PickupLocationOpeningHours | null;
596
- /** Raw opening hours string from Shipit */
597
- openingHoursRaw?: string | null;
598
- /** Additional metadata */
599
- metadata?: unknown | null;
600
- /** The shipment method ID this location belongs to */
601
- shipmentMethodId: string;
602
- /** Price in cents (from store settings) */
603
- price: number;
613
+ openingHours: OpeningHours | null;
614
+ /** GPS coordinates */
615
+ coordinates: {
616
+ lat: number;
617
+ lng: number;
618
+ } | null;
604
619
  }
605
620
  /**
606
621
  * Response from GET /shipment-methods/[postalCode]
622
+ *
623
+ * Returns unified shipping options regardless of provider.
624
+ * Pickup points are sorted by distance, home delivery by price.
607
625
  */
608
- interface ShipmentMethodsWithLocationsResponse {
609
- /** Home delivery methods (custom methods + Shipit home delivery) */
610
- homeDeliveryMethods: ShipmentMethod[];
611
- /** Pickup locations with shipmentMethodId and price attached */
612
- pickupLocations: PickupLocation[];
626
+ interface ShipmentMethodsResponse {
627
+ /** Home delivery options (sorted by price) */
628
+ homeDelivery: HomeDeliveryOption[];
629
+ /** Pickup point options (sorted by distance) */
630
+ pickupPoints: PickupPointOption[];
613
631
  }
614
632
 
615
633
  /**
@@ -717,7 +735,7 @@ interface Order {
717
735
  storeId: string;
718
736
  /** Order creation timestamp */
719
737
  createdAt: string;
720
- /** Total order amount in cents */
738
+ /** Total order amount in cents (subtotal before discount) */
721
739
  totalAmount: number;
722
740
  /** Current order status */
723
741
  status: OrderStatus;
@@ -729,6 +747,12 @@ interface Order {
729
747
  orderCustomerData: ConfirmationOrderCustomerData | null;
730
748
  /** Shipment method with tracking info */
731
749
  orderShipmentMethod: ConfirmationOrderShipmentMethod | null;
750
+ /** Discount code string (e.g., "SUMMER20") - null if no discount applied */
751
+ discountCodeValue: string | null;
752
+ /** Discount amount in cents - null if no discount applied */
753
+ discountAmount: number | null;
754
+ /** VAT rate used for the discount calculation */
755
+ discountVatRate: number | null;
732
756
  }
733
757
 
734
758
  /**
@@ -1126,6 +1150,8 @@ interface CheckoutShipmentMethod {
1126
1150
  shipmentMethodId: string;
1127
1151
  /** ID of pickup point (for pickup delivery methods) */
1128
1152
  pickupId: string | null;
1153
+ /** Shipit service ID (for Shipit pickup points) */
1154
+ serviceId: string | null;
1129
1155
  }
1130
1156
  /**
1131
1157
  * Parameters for creating a checkout session
@@ -1223,6 +1249,104 @@ interface CheckoutErrorDetails {
1223
1249
  [key: string]: unknown;
1224
1250
  }
1225
1251
 
1252
+ /**
1253
+ * Discount Code Types
1254
+ *
1255
+ * Types for discount code API endpoints.
1256
+ */
1257
+ /**
1258
+ * Type of discount
1259
+ */
1260
+ type DiscountType = "PERCENTAGE" | "FIXED_AMOUNT";
1261
+ /**
1262
+ * Applied discount code information (stored in cart)
1263
+ */
1264
+ interface AppliedDiscount {
1265
+ /** The discount code string */
1266
+ code: string;
1267
+ /** Type of discount */
1268
+ discountType: DiscountType;
1269
+ /** Discount value (percentage 1-100 or cents for fixed amount) */
1270
+ discountValue: number;
1271
+ }
1272
+ /**
1273
+ * Parameters for applying a discount code
1274
+ */
1275
+ interface ApplyDiscountParams {
1276
+ /** The discount code to apply */
1277
+ code: string;
1278
+ /** Cart ID for guest users */
1279
+ cartId?: string;
1280
+ /** Session ID for logged-in users */
1281
+ sessionId?: string;
1282
+ /** Cart items for campaign conflict check (SDK uses this for instant validation) */
1283
+ cartItems?: CartItem[];
1284
+ /** Active campaigns for campaign conflict check (SDK uses this for instant validation) */
1285
+ campaigns?: Campaign[];
1286
+ }
1287
+ /**
1288
+ * Parameters for removing a discount code
1289
+ */
1290
+ interface RemoveDiscountParams {
1291
+ /** Cart ID for guest users */
1292
+ cartId?: string;
1293
+ /** Session ID for logged-in users */
1294
+ sessionId?: string;
1295
+ }
1296
+ /**
1297
+ * Parameters for getting current discount
1298
+ */
1299
+ interface GetDiscountParams {
1300
+ /** Cart ID for guest users */
1301
+ cartId?: string;
1302
+ /** Session ID for logged-in users */
1303
+ sessionId?: string;
1304
+ }
1305
+ /**
1306
+ * Response from POST /discount-code/apply
1307
+ *
1308
+ * Note: No discountAmount returned - cart can change before checkout.
1309
+ * Calculate locally or get from cart validate if needed.
1310
+ */
1311
+ interface ApplyDiscountResponse {
1312
+ /** Whether the discount was applied successfully */
1313
+ success: boolean;
1314
+ /** Applied discount details */
1315
+ discount: {
1316
+ code: string;
1317
+ discountType: DiscountType;
1318
+ discountValue: number;
1319
+ /** Minimum order amount for this code (in cents) - for UI display */
1320
+ minOrderAmount: number | null;
1321
+ };
1322
+ }
1323
+ /**
1324
+ * Response from GET /discount-code/apply (get current discount)
1325
+ *
1326
+ * Returns stored discount data without re-validation.
1327
+ * Use /cart/validate for full validation.
1328
+ */
1329
+ interface GetDiscountResponse {
1330
+ /** Current discount (null if none applied) */
1331
+ discount: AppliedDiscount | null;
1332
+ }
1333
+ /**
1334
+ * Response from DELETE /discount-code/apply
1335
+ */
1336
+ interface RemoveDiscountResponse {
1337
+ /** Whether the discount was removed successfully */
1338
+ success: boolean;
1339
+ }
1340
+ /**
1341
+ * Error response from discount code endpoints
1342
+ */
1343
+ interface DiscountCodeError {
1344
+ /** Error message (Finnish, user-friendly) */
1345
+ error: string;
1346
+ /** Error code for programmatic use */
1347
+ code: "MISSING_CODE" | "MISSING_CART_TOTAL" | "CART_MISSING" | "NOT_FOUND" | "INACTIVE" | "NOT_STARTED" | "EXPIRED" | "MAX_USES_REACHED" | "MIN_ORDER_NOT_MET" | "CAMPAIGN_ACTIVE" | "INTERNAL_ERROR";
1348
+ }
1349
+
1226
1350
  /**
1227
1351
  * Putiikkipalvelu Storefront SDK Types
1228
1352
  *
@@ -1649,28 +1773,35 @@ declare function createCartResource(fetcher: Fetcher): {
1649
1773
  * Checks product availability, stock levels, and prices.
1650
1774
  * Auto-fixes issues (removes unavailable items, adjusts quantities).
1651
1775
  *
1776
+ * Campaign conflict detection:
1777
+ * - SDK calculates if BuyXPayY campaigns apply using calculateCartWithCampaigns()
1778
+ * - Sends x-campaigns-apply header to backend
1779
+ * - If campaigns apply AND discount code exists, backend removes it
1780
+ * - Returns changes.discountCouponRemoved = true
1781
+ *
1652
1782
  * @param options - Cart session options
1783
+ * @param cartItems - Current cart items for campaign calculation
1784
+ * @param campaigns - Active campaigns for conflict check
1653
1785
  * @param fetchOptions - Fetch options
1654
1786
  * @returns Validated cart with change metadata
1655
1787
  *
1656
1788
  * @example Validate before checkout
1657
1789
  * ```typescript
1658
- * const { items, hasChanges, changes } = await client.cart.validate({ cartId });
1790
+ * const result = await client.cart.validate(
1791
+ * { cartId },
1792
+ * cartItems,
1793
+ * storeConfig.campaigns
1794
+ * );
1659
1795
  *
1660
- * if (hasChanges) {
1661
- * if (changes.removedItems > 0) {
1662
- * notify('Some items were removed (out of stock)');
1663
- * }
1664
- * if (changes.quantityAdjusted > 0) {
1665
- * notify('Some quantities were adjusted');
1666
- * }
1667
- * if (changes.priceChanged > 0) {
1668
- * notify('Some prices have changed');
1796
+ * if (result.hasChanges) {
1797
+ * if (result.changes.discountCouponRemoved) {
1798
+ * notify("Alennuskoodi poistettu - kampanja-alennus aktivoitui");
1669
1799
  * }
1800
+ * // Handle other changes...
1670
1801
  * }
1671
1802
  * ```
1672
1803
  */
1673
- validate(options?: CartSessionOptions, fetchOptions?: FetchOptions): Promise<CartValidationResponse>;
1804
+ validate(options?: CartSessionOptions, cartItems?: CartItem[], campaigns?: Campaign[], fetchOptions?: FetchOptions): Promise<CartValidationResponse>;
1674
1805
  };
1675
1806
  /**
1676
1807
  * Type for the cart resource
@@ -1686,9 +1817,15 @@ type CartResource = ReturnType<typeof createCartResource>;
1686
1817
  /**
1687
1818
  * Options for fetching shipment methods with weight-based filtering
1688
1819
  */
1689
- interface GetMethodsOptions extends FetchOptions {
1690
- /** Cart items - weight will be calculated automatically */
1820
+ interface GetShippingOptionsParams extends FetchOptions {
1821
+ /** Cart items - weight and total will be calculated automatically */
1691
1822
  cartItems?: CartItem[];
1823
+ /** Active campaigns - used to calculate cart total with discounts for free shipping */
1824
+ campaigns?: Campaign[];
1825
+ /** Discount amount in cents (from discount code) - subtracted from cart total for free shipping threshold */
1826
+ discountAmount?: number;
1827
+ /** Country code (default: "FI") */
1828
+ country?: string;
1692
1829
  }
1693
1830
  /**
1694
1831
  * Shipping resource for fetching shipment methods and pickup locations
@@ -1696,40 +1833,55 @@ interface GetMethodsOptions extends FetchOptions {
1696
1833
  declare function createShippingResource(fetcher: Fetcher): {
1697
1834
  /**
1698
1835
  * Get shipping options for a specific postal code.
1699
- * Returns home delivery methods and pickup locations.
1836
+ * Returns pickup points and home delivery options in a unified format.
1837
+ *
1838
+ * **Pickup points are returned first** as they are more popular in Finland.
1700
1839
  *
1701
1840
  * @param postalCode - Customer's postal code (e.g., "00100")
1702
1841
  * @param options - Fetch options including optional cartItems for weight-based filtering
1703
- * @returns Home delivery methods and pickup locations
1842
+ * @returns Unified shipping options (pickupPoints sorted by distance, homeDelivery sorted by price)
1704
1843
  *
1705
1844
  * @example
1706
1845
  * ```typescript
1707
- * const { homeDeliveryMethods, pickupLocations } = await client.shipping.getWithLocations("00100");
1708
- *
1709
- * // Show home delivery options
1710
- * homeDeliveryMethods.forEach(method => {
1711
- * console.log(`${method.name}: ${method.price / 100}€`);
1846
+ * const { pickupPoints, homeDelivery } = await client.shipping.getOptions("00100");
1847
+ *
1848
+ * // Show pickup points (more popular in Finland)
1849
+ * pickupPoints.forEach(point => {
1850
+ * console.log(`${point.name} - ${point.carrier}`);
1851
+ * console.log(` ${point.address}, ${point.city}`);
1852
+ * console.log(` ${(point.distance! / 1000).toFixed(1)} km away`);
1853
+ * console.log(` Price: ${point.price / 100}€`);
1712
1854
  * });
1713
1855
  *
1714
- * // Show pickup locations
1715
- * pickupLocations.forEach(location => {
1716
- * console.log(`${location.name} - ${location.carrier}`);
1717
- * console.log(` ${location.address1}, ${location.city}`);
1718
- * console.log(` ${location.distanceInKilometers.toFixed(1)} km away`);
1719
- * console.log(` Price: ${location.price / 100}€`);
1856
+ * // Show home delivery options
1857
+ * homeDelivery.forEach(option => {
1858
+ * console.log(`${option.name}: ${option.price / 100}€`);
1859
+ * if (option.estimatedDelivery) {
1860
+ * console.log(` Delivery: ${option.estimatedDelivery} days`);
1861
+ * }
1720
1862
  * });
1721
1863
  * ```
1722
1864
  *
1723
1865
  * @example Weight-based filtering
1724
1866
  * ```typescript
1725
- * const { homeDeliveryMethods, pickupLocations } = await client.shipping.getWithLocations(
1726
- * "00100",
1727
- * { cartItems: cartItems }
1728
- * );
1867
+ * const options = await client.shipping.getOptions("00100", {
1868
+ * cartItems: cartItems
1869
+ * });
1729
1870
  * // Only shows methods that support the cart's total weight
1730
1871
  * ```
1872
+ *
1873
+ * @example International shipping
1874
+ * ```typescript
1875
+ * const options = await client.shipping.getOptions("112 22", {
1876
+ * country: "SE"
1877
+ * });
1878
+ * ```
1731
1879
  */
1732
- getWithLocations(postalCode: string, options?: GetMethodsOptions): Promise<ShipmentMethodsWithLocationsResponse>;
1880
+ getOptions(postalCode: string, options?: GetShippingOptionsParams): Promise<ShipmentMethodsResponse>;
1881
+ /**
1882
+ * @deprecated Use getOptions() instead. This method is kept for backwards compatibility.
1883
+ */
1884
+ getWithLocations(postalCode: string, options?: GetShippingOptionsParams): Promise<ShipmentMethodsResponse>;
1733
1885
  };
1734
1886
  /**
1735
1887
  * Type for the shipping resource
@@ -2275,6 +2427,70 @@ declare function createCheckoutResource(fetcher: Fetcher): {
2275
2427
  */
2276
2428
  type CheckoutResource = ReturnType<typeof createCheckoutResource>;
2277
2429
 
2430
+ /**
2431
+ * Discount Code Resource
2432
+ *
2433
+ * Provides methods for managing discount codes in the cart.
2434
+ */
2435
+
2436
+ /**
2437
+ * Creates the discount code resource with methods for managing discount codes.
2438
+ */
2439
+ declare function createDiscountCodeResource(fetcher: Fetcher): {
2440
+ /**
2441
+ * Apply a discount code to the cart
2442
+ *
2443
+ * Checks for BuyXPayY campaign conflict before calling API.
2444
+ * Discount codes cannot be used when a campaign discount is active.
2445
+ *
2446
+ * @param params - Parameters including code, session info, and cart/campaign data for conflict check
2447
+ * @returns Applied discount details
2448
+ * @throws {StorefrontError} With code "CAMPAIGN_ACTIVE" if a BuyXPayY campaign is active
2449
+ *
2450
+ * @example
2451
+ * ```typescript
2452
+ * const result = await client.discountCode.apply({
2453
+ * code: "SUMMER20",
2454
+ * cartId: cartId,
2455
+ * cartItems: cart.items,
2456
+ * campaigns: storeConfig.campaigns,
2457
+ * });
2458
+ * ```
2459
+ */
2460
+ apply(params: ApplyDiscountParams): Promise<ApplyDiscountResponse>;
2461
+ /**
2462
+ * Get the currently applied discount code
2463
+ *
2464
+ * @param params - Session info (cartId or sessionId)
2465
+ * @returns Current discount or null if none applied
2466
+ *
2467
+ * @example
2468
+ * ```typescript
2469
+ * const { discount } = await client.discountCode.get({ cartId });
2470
+ * if (discount) {
2471
+ * console.log(`Code ${discount.code}: ${discount.discountValue}${discount.discountType === 'PERCENTAGE' ? '%' : '¢'} off`);
2472
+ * }
2473
+ * ```
2474
+ */
2475
+ get(params?: GetDiscountParams): Promise<GetDiscountResponse>;
2476
+ /**
2477
+ * Remove the currently applied discount code
2478
+ *
2479
+ * @param params - Session info (cartId or sessionId)
2480
+ * @returns Success status
2481
+ *
2482
+ * @example
2483
+ * ```typescript
2484
+ * await client.discountCode.remove({ cartId });
2485
+ * ```
2486
+ */
2487
+ remove(params?: RemoveDiscountParams): Promise<RemoveDiscountResponse>;
2488
+ };
2489
+ /**
2490
+ * Type for the discount code resource
2491
+ */
2492
+ type DiscountCodeResource = ReturnType<typeof createDiscountCodeResource>;
2493
+
2278
2494
  /**
2279
2495
  * The Storefront API client
2280
2496
  */
@@ -2315,6 +2531,10 @@ interface StorefrontClient {
2315
2531
  * Checkout resource for payment processing
2316
2532
  */
2317
2533
  readonly checkout: CheckoutResource;
2534
+ /**
2535
+ * Discount code resource for applying/removing discount codes
2536
+ */
2537
+ readonly discountCode: DiscountCodeResource;
2318
2538
  }
2319
2539
  /**
2320
2540
  * Create a new Storefront API client
@@ -2378,13 +2598,11 @@ declare function getPriceInfo(product: ProductDetail, variation?: ProductVariati
2378
2598
  /**
2379
2599
  * Calculate cart totals with campaign discounts applied.
2380
2600
  *
2381
- * Supports two campaign types:
2382
- * - **FREE_SHIPPING**: Free shipping when cart total exceeds minimum spend
2383
- * - **BUY_X_PAY_Y**: Buy X items, pay for Y (e.g., Buy 3 Pay 2 = 1 free item)
2601
+ * Supports BUY_X_PAY_Y campaigns: Buy X items, pay for Y (e.g., Buy 3 Pay 2 = 1 free item)
2384
2602
  *
2385
2603
  * @param items - Cart items to calculate
2386
2604
  * @param campaigns - Active campaigns to apply
2387
- * @returns Calculation result with totals, savings, and free shipping status
2605
+ * @returns Calculation result with totals and savings
2388
2606
  *
2389
2607
  * @example
2390
2608
  * ```typescript
@@ -2392,7 +2610,6 @@ declare function getPriceInfo(product: ProductDetail, variation?: ProductVariati
2392
2610
  *
2393
2611
  * console.log(result.cartTotal); // 4990 (cents)
2394
2612
  * console.log(result.totalSavings); // 1990 (cents)
2395
- * console.log(result.freeShipping.isEligible); // true
2396
2613
  *
2397
2614
  * // Render calculated items
2398
2615
  * result.calculatedItems.forEach(({ item, paidQuantity, freeQuantity }) => {
@@ -2402,6 +2619,126 @@ declare function getPriceInfo(product: ProductDetail, variation?: ProductVariati
2402
2619
  */
2403
2620
  declare function calculateCartWithCampaigns(items: CartItem[], campaigns: Campaign[]): CartCalculationResult;
2404
2621
 
2622
+ /**
2623
+ * Discount Code Utilities
2624
+ *
2625
+ * Helper functions for working with discount codes.
2626
+ */
2627
+
2628
+ /**
2629
+ * Options for formatting discount values
2630
+ */
2631
+ interface FormatDiscountOptions {
2632
+ /**
2633
+ * Currency symbol to use for fixed amount discounts
2634
+ * @default "€"
2635
+ */
2636
+ currencySymbol?: string;
2637
+ /**
2638
+ * Whether to show the currency symbol before or after the value
2639
+ * @default "after"
2640
+ */
2641
+ currencyPosition?: "before" | "after";
2642
+ /**
2643
+ * Number of decimal places for fixed amount
2644
+ * @default 2
2645
+ */
2646
+ decimals?: number;
2647
+ /**
2648
+ * Whether to include the minus sign prefix
2649
+ * @default true
2650
+ */
2651
+ showMinus?: boolean;
2652
+ }
2653
+ /**
2654
+ * Format a discount value for display.
2655
+ *
2656
+ * @param discount - The applied discount (or just type and value)
2657
+ * @param options - Formatting options
2658
+ * @returns Formatted discount string (e.g., "-20%" or "-5,00 €")
2659
+ *
2660
+ * @example
2661
+ * ```typescript
2662
+ * // Percentage discount
2663
+ * formatDiscountValue({ discountType: "PERCENTAGE", discountValue: 20 });
2664
+ * // Returns: "-20%"
2665
+ *
2666
+ * // Fixed amount discount (value in cents)
2667
+ * formatDiscountValue({ discountType: "FIXED_AMOUNT", discountValue: 500 });
2668
+ * // Returns: "-5,00 €"
2669
+ *
2670
+ * // Custom currency
2671
+ * formatDiscountValue(
2672
+ * { discountType: "FIXED_AMOUNT", discountValue: 1000 },
2673
+ * { currencySymbol: "$", currencyPosition: "before" }
2674
+ * );
2675
+ * // Returns: "-$10.00"
2676
+ * ```
2677
+ */
2678
+ declare function formatDiscountValue(discount: Pick<AppliedDiscount, "discountType" | "discountValue">, options?: FormatDiscountOptions): string;
2679
+ /**
2680
+ * Calculate the discount amount in cents.
2681
+ *
2682
+ * @param subtotal - Cart subtotal in cents (before discount)
2683
+ * @param discount - The applied discount
2684
+ * @returns Discount amount in cents (always positive)
2685
+ *
2686
+ * @example
2687
+ * ```typescript
2688
+ * // 20% off 100€ subtotal
2689
+ * calculateDiscountAmount(10000, { discountType: "PERCENTAGE", discountValue: 20 });
2690
+ * // Returns: 2000 (20€ in cents)
2691
+ *
2692
+ * // Fixed 5€ discount
2693
+ * calculateDiscountAmount(10000, { discountType: "FIXED_AMOUNT", discountValue: 500 });
2694
+ * // Returns: 500 (5€ in cents)
2695
+ *
2696
+ * // Fixed discount larger than subtotal (capped)
2697
+ * calculateDiscountAmount(300, { discountType: "FIXED_AMOUNT", discountValue: 500 });
2698
+ * // Returns: 300 (capped to subtotal)
2699
+ * ```
2700
+ */
2701
+ declare function calculateDiscountAmount(subtotal: number, discount: Pick<AppliedDiscount, "discountType" | "discountValue">): number;
2702
+ /**
2703
+ * Supported locales for error messages
2704
+ */
2705
+ type DiscountMessageLocale = "fi" | "en";
2706
+ /**
2707
+ * Get a user-friendly message for discount removal reason.
2708
+ *
2709
+ * @param reason - The removal reason from cart validation
2710
+ * @param locale - Locale for the message (fi or en)
2711
+ * @returns Localized user-friendly message
2712
+ *
2713
+ * @example
2714
+ * ```typescript
2715
+ * getDiscountRemovalMessage("CAMPAIGN_ACTIVE", "fi");
2716
+ * // Returns: "Alennuskoodi poistettu - kampanja-alennus aktivoitui"
2717
+ *
2718
+ * getDiscountRemovalMessage("MIN_ORDER_NOT_MET", "en");
2719
+ * // Returns: "Discount code removed - cart total below minimum order"
2720
+ * ```
2721
+ */
2722
+ declare function getDiscountRemovalMessage(reason: DiscountRemovalReason | undefined, locale?: DiscountMessageLocale): string;
2723
+ /**
2724
+ * Error codes from discount code apply endpoint
2725
+ */
2726
+ type DiscountApplyErrorCode = "NOT_FOUND" | "INACTIVE" | "NOT_STARTED" | "EXPIRED" | "MAX_USES_REACHED" | "MIN_ORDER_NOT_MET" | "CAMPAIGN_ACTIVE";
2727
+ /**
2728
+ * Get a user-friendly message for discount apply error.
2729
+ *
2730
+ * @param errorCode - The error code from apply endpoint
2731
+ * @param locale - Locale for the message (fi or en)
2732
+ * @returns Localized user-friendly message
2733
+ *
2734
+ * @example
2735
+ * ```typescript
2736
+ * getDiscountApplyErrorMessage("EXPIRED", "fi");
2737
+ * // Returns: "Alennuskoodi on vanhentunut"
2738
+ * ```
2739
+ */
2740
+ declare function getDiscountApplyErrorMessage(errorCode: DiscountApplyErrorCode | string | undefined, locale?: DiscountMessageLocale): string;
2741
+
2405
2742
  /**
2406
2743
  * Base error class for all Storefront API errors
2407
2744
  */
@@ -2445,4 +2782,4 @@ declare class VerificationRequiredError extends StorefrontError {
2445
2782
  constructor(message: string, customerId: string);
2446
2783
  }
2447
2784
 
2448
- export { type AddToCartParams, type AddToWishlistResponse, AuthError, type BuyXPayYCampaign, type CalculatedCartItem, type Campaign, type CampaignType, type CartCalculationResult, type CartItem, type CartResponse, type CartSessionOptions, type CartValidationChanges, type CartValidationResponse, type Category, type CategoryReference, type CategoryResponse, type CheckoutCustomerData, type CheckoutErrorCode, type CheckoutErrorDetails, type CheckoutOptions, type CheckoutParams, type CheckoutShipmentMethod, type ConfirmationItemType, type ConfirmationOrderCustomerData, type ConfirmationOrderLineItem, type ConfirmationOrderShipmentMethod, type Customer, type CustomerOrder, type DeleteAccountResponse, type FeatureFlags, type FetchOptions, type ForgotPasswordResponse, type FreeShippingStatus, type GetOrdersResponse, type GetUserResponse, type LoginOptions, type LoginResponse, type LoginVerificationRequiredResponse, type LogoutResponse, NotFoundError, type Order, type OrderLineItem, type OrderProductInfo, type OrderShipmentMethod, type OrderStatus, type PaymentConfig, type PaytrailCheckoutResponse, type PaytrailGroup, type PaytrailProvider, type PickupLocation, type PriceInfo, type Product, type ProductCountResponse, type ProductDetail, type ProductListParams, type ProductListResponse, type ProductSortOption, type ProductVariation, type ProductVariationListing, RateLimitError, type RegisterData, type RegisterResponse, type RemoveFromCartParams, type RemoveFromWishlistResponse, type ResendVerificationResponse, type ResetPasswordResponse, type ShipitShippingMethod, type ShipmentMethod, type ShipmentMethodsWithLocationsResponse, type StoreConfig, type StoreInfo, type StoreSeo, type StorefrontClient, type StorefrontClientConfig, StorefrontError, type StripeCheckoutResponse, type UpdateCartQuantityParams, type UpdateProfileData, type UpdateProfileResponse, ValidationError, type VariationOption, VerificationRequiredError, type VerifyEmailResponse, type WishlistItem, type WishlistProduct, type WishlistResponse, type WishlistVariation, type WishlistVariationOption, calculateCartWithCampaigns, createStorefrontClient, getPriceInfo, isSaleActive };
2785
+ export { type AddToCartParams, type AddToWishlistResponse, type AppliedDiscount, type ApplyDiscountParams, type ApplyDiscountResponse, AuthError, type BuyXPayYCampaign, type CalculatedCartItem, type Campaign, type CampaignType, type CartCalculationResult, type CartItem, type CartResponse, type CartSessionOptions, type CartValidationChanges, type CartValidationResponse, type Category, type CategoryReference, type CategoryResponse, type CheckoutCustomerData, type CheckoutErrorCode, type CheckoutErrorDetails, type CheckoutOptions, type CheckoutParams, type CheckoutShipmentMethod, type ConfirmationItemType, type ConfirmationOrderCustomerData, type ConfirmationOrderLineItem, type ConfirmationOrderShipmentMethod, type Customer, type CustomerOrder, type DeleteAccountResponse, type DiscountApplyErrorCode, type DiscountCodeError, type DiscountMessageLocale, type DiscountRemovalReason, type DiscountType, type FeatureFlags, type FetchOptions, type ForgotPasswordResponse, type FormatDiscountOptions, type GetDiscountParams, type GetDiscountResponse, type GetOrdersResponse, type GetUserResponse, type HomeDeliveryOption, type LoginOptions, type LoginResponse, type LoginVerificationRequiredResponse, type LogoutResponse, NotFoundError, type OpeningHours, type Order, type OrderLineItem, type OrderProductInfo, type OrderShipmentMethod, type OrderStatus, type PaymentConfig, type PaytrailCheckoutResponse, type PaytrailGroup, type PaytrailProvider, type PickupPointOption, type PriceInfo, type Product, type ProductCountResponse, type ProductDetail, type ProductListParams, type ProductListResponse, type ProductSortOption, type ProductVariation, type ProductVariationListing, RateLimitError, type RegisterData, type RegisterResponse, type RemoveDiscountParams, type RemoveDiscountResponse, type RemoveFromCartParams, type RemoveFromWishlistResponse, type ResendVerificationResponse, type ResetPasswordResponse, type ShipitShippingMethod, type ShipmentMethod, type ShipmentMethodsResponse, type StoreConfig, type StoreInfo, type StoreSeo, type StorefrontClient, type StorefrontClientConfig, StorefrontError, type StripeCheckoutResponse, type UpdateCartQuantityParams, type UpdateProfileData, type UpdateProfileResponse, ValidationError, type VariationOption, VerificationRequiredError, type VerifyEmailResponse, type WishlistItem, type WishlistProduct, type WishlistResponse, type WishlistVariation, type WishlistVariationOption, calculateCartWithCampaigns, calculateDiscountAmount, createStorefrontClient, formatDiscountValue, getDiscountApplyErrorMessage, getDiscountRemovalMessage, getPriceInfo, isSaleActive };