omni-sync-sdk 0.20.2 → 0.21.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/LICENSE ADDED
File without changes
package/dist/index.d.mts CHANGED
@@ -1692,15 +1692,27 @@ interface ShippingRate {
1692
1692
  * **Note:** Order is created automatically via webhook when payment succeeds.
1693
1693
  * No need to call `completeCheckout()` - it happens automatically.
1694
1694
  *
1695
+ * **IMPORTANT: Order Summary Display**
1696
+ * Always use `checkout.lineItems` for displaying the Order Summary during checkout!
1697
+ * This is especially important for **partial checkout** (AliExpress-style) where the user
1698
+ * selects only some items from their cart. The `lineItems` array contains ONLY the items
1699
+ * being purchased in this checkout, NOT the entire cart.
1700
+ *
1695
1701
  * **IMPORTANT: Price fields are strings**
1696
1702
  * All monetary fields (subtotal, discountAmount, shippingAmount, taxAmount, total)
1697
1703
  * are strings to preserve decimal precision. Use parseFloat() for calculations.
1698
1704
  *
1699
1705
  * @example
1700
1706
  * ```typescript
1701
- * // Display checkout summary
1707
+ * // Display checkout summary - use checkout.lineItems, NOT localCart!
1702
1708
  * const checkout = await omni.getCheckout(checkoutId);
1703
1709
  *
1710
+ * // Order Summary - ALWAYS use checkout.lineItems
1711
+ * checkout.lineItems.forEach(item => {
1712
+ * console.log(item.product.name, item.quantity, item.unitPrice);
1713
+ * });
1714
+ *
1715
+ * // Totals
1704
1716
  * const subtotal = parseFloat(checkout.subtotal);
1705
1717
  * const discount = parseFloat(checkout.discountAmount);
1706
1718
  * const shipping = parseFloat(checkout.shippingAmount);
@@ -1712,11 +1724,6 @@ interface ShippingRate {
1712
1724
  * console.log(`Shipping: $${shipping.toFixed(2)}`);
1713
1725
  * console.log(`Tax: $${tax.toFixed(2)}`);
1714
1726
  * console.log(`Total: $${total.toFixed(2)}`);
1715
- *
1716
- * // Access line items (nested structure)
1717
- * checkout.lineItems.forEach(item => {
1718
- * console.log(item.product.name, item.quantity);
1719
- * });
1720
1727
  * ```
1721
1728
  *
1722
1729
  * @see CheckoutLineItem for item structure
@@ -3057,6 +3064,11 @@ declare class OmniSyncClient {
3057
3064
  * When a cart has this ID, operations use localStorage instead of server API.
3058
3065
  */
3059
3066
  private readonly VIRTUAL_LOCAL_CART_ID;
3067
+ /**
3068
+ * localStorage key for persisting active checkout across page redirects.
3069
+ * This is needed because Stripe redirects lose in-memory state.
3070
+ */
3071
+ private readonly ACTIVE_CHECKOUT_KEY;
3060
3072
  private readonly onAuthError?;
3061
3073
  constructor(options: OmniSyncClientOptions);
3062
3074
  /**
@@ -4185,6 +4197,11 @@ declare class OmniSyncClient {
4185
4197
  * @internal
4186
4198
  */
4187
4199
  private getOrCreateCustomerCart;
4200
+ /**
4201
+ * Fetch the customer's existing cart from server
4202
+ * @internal
4203
+ */
4204
+ private fetchCustomerCart;
4188
4205
  /**
4189
4206
  * Smart add to cart - automatically uses localStorage or server based on auth state
4190
4207
  *
@@ -4743,6 +4760,11 @@ declare class OmniSyncClient {
4743
4760
  * Pass `selectedIndices` to checkout only specific items from the local cart.
4744
4761
  * Use the index of each item in the cart.items array.
4745
4762
  *
4763
+ * **IMPORTANT - Order Summary Display:**
4764
+ * After calling this function, use `getCheckout(checkoutId)` to get the checkout
4765
+ * and display `checkout.lineItems` in your Order Summary - NOT the local cart items!
4766
+ * The checkout.lineItems contains ONLY the selected items for this checkout.
4767
+ *
4746
4768
  * @param options.selectedIndices - Optional array of item indices for partial checkout
4747
4769
  * @returns Tracking result with checkoutId if enabled
4748
4770
  *
@@ -4755,19 +4777,22 @@ declare class OmniSyncClient {
4755
4777
  * const result = await omni.startGuestCheckout({ selectedIndices: [0, 2] });
4756
4778
  *
4757
4779
  * if (result.tracked) {
4758
- * // Store checkoutId for later use
4759
- * console.log('Checkout tracked:', result.checkoutId);
4780
+ * // IMPORTANT: Fetch checkout and use checkout.lineItems for Order Summary!
4781
+ * const checkout = await omni.getCheckout(result.checkoutId);
4782
+ *
4783
+ * // Display Order Summary using checkout.lineItems (NOT localCart.items!)
4784
+ * // checkout.lineItems contains ONLY the selected items
4785
+ * checkout.lineItems.forEach(item => {
4786
+ * console.log(item.product.name, item.quantity, item.unitPrice);
4787
+ * });
4760
4788
  *
4761
4789
  * // Update checkout with address
4762
- * await omni.updateGuestCheckout(result.checkoutId, {
4790
+ * await omni.updateGuestCheckoutAddress(result.checkoutId, {
4763
4791
  * shippingAddress: { ... },
4764
4792
  * });
4765
4793
  *
4766
- * // Complete checkout
4767
- * const order = await omni.completeGuestCheckout(result.checkoutId);
4768
- *
4769
- * // For partial checkout: remove only purchased items
4770
- * omni.removeLocalCartItemsByIndex([0, 2]);
4794
+ * // After payment success, call handlePaymentSuccess() to clear cart
4795
+ * omni.handlePaymentSuccess(result.checkoutId);
4771
4796
  * } else {
4772
4797
  * // Tracking not enabled, use regular submitGuestOrder
4773
4798
  * const order = await omni.submitGuestOrder();
@@ -4777,6 +4802,21 @@ declare class OmniSyncClient {
4777
4802
  startGuestCheckout(options?: {
4778
4803
  selectedIndices?: number[];
4779
4804
  }): Promise<GuestCheckoutStartResponse>;
4805
+ /**
4806
+ * Save active checkout to localStorage (persists across page redirects)
4807
+ * @internal
4808
+ */
4809
+ private saveActiveCheckout;
4810
+ /**
4811
+ * Load active checkout from localStorage
4812
+ * @internal
4813
+ */
4814
+ private loadActiveCheckout;
4815
+ /**
4816
+ * Clear active checkout from localStorage
4817
+ * @internal
4818
+ */
4819
+ private clearActiveCheckoutStorage;
4780
4820
  /**
4781
4821
  * Remove specific items from local cart by their indices.
4782
4822
  * Use after partial checkout to remove only the purchased items.
@@ -4824,6 +4864,83 @@ declare class OmniSyncClient {
4824
4864
  clearCartOnSuccess?: boolean;
4825
4865
  selectedIndices?: number[];
4826
4866
  }): Promise<GuestOrderResponse>;
4867
+ /**
4868
+ * Get the active guest checkout session info.
4869
+ * Use this to check if there's an active checkout before handling payment success.
4870
+ *
4871
+ * @returns The active checkout info or null if no checkout is active
4872
+ *
4873
+ * @example
4874
+ * ```typescript
4875
+ * const activeCheckout = omni.getActiveGuestCheckout();
4876
+ * if (activeCheckout) {
4877
+ * console.log('Active checkout:', activeCheckout.checkoutId);
4878
+ * console.log('Partial checkout:', activeCheckout.selectedIndices?.length);
4879
+ * }
4880
+ * ```
4881
+ */
4882
+ getActiveGuestCheckout(): {
4883
+ checkoutId: string;
4884
+ cartId: string;
4885
+ selectedIndices?: number[];
4886
+ } | null;
4887
+ /**
4888
+ * Handle payment success - automatically clears the cart (both local and server).
4889
+ *
4890
+ * Call this after Stripe payment succeeds (payment_intent.succeeded or confirmPayment success).
4891
+ * This handles ALL checkout scenarios:
4892
+ *
4893
+ * **For guest users (local cart):**
4894
+ * - Full checkout: clears entire localStorage cart
4895
+ * - Partial checkout: removes only the purchased items
4896
+ *
4897
+ * **For logged-in users (server cart):**
4898
+ * - Clears the cached cart ID so next `smartGetCart()` fetches fresh data
4899
+ * - The server cart is already marked as CONVERTED by the webhook
4900
+ *
4901
+ * **IMPORTANT:** Call this from your payment success handler (e.g., after stripe.confirmPayment succeeds,
4902
+ * or on your success page after redirect). This works for both guests AND logged-in users!
4903
+ *
4904
+ * @param checkoutId - Optional checkout ID for validation
4905
+ * @returns Object indicating whether cart was cleared and how
4906
+ *
4907
+ * @example
4908
+ * ```typescript
4909
+ * // After stripe.confirmPayment() succeeds (works for guests AND logged-in users)
4910
+ * const { error, paymentIntent } = await stripe.confirmPayment({
4911
+ * elements,
4912
+ * confirmParams: { return_url: '/checkout/success' },
4913
+ * redirect: 'if_required',
4914
+ * });
4915
+ *
4916
+ * if (!error && paymentIntent?.status === 'succeeded') {
4917
+ * // Clear the cart automatically - handles all scenarios!
4918
+ * const result = omni.handlePaymentSuccess(checkoutId);
4919
+ * console.log('Cart cleared:', result.cleared);
4920
+ * console.log('User type:', result.userType); // 'guest' or 'customer'
4921
+ * }
4922
+ * ```
4923
+ *
4924
+ * @example
4925
+ * ```typescript
4926
+ * // On success page (after redirect)
4927
+ * const checkoutId = new URLSearchParams(location.search).get('checkout_id');
4928
+ * if (checkoutId) {
4929
+ * omni.handlePaymentSuccess(checkoutId);
4930
+ * }
4931
+ * ```
4932
+ */
4933
+ handlePaymentSuccess(checkoutId?: string): {
4934
+ cleared: boolean;
4935
+ mode: 'full' | 'partial' | 'none';
4936
+ userType: 'guest' | 'customer';
4937
+ itemsRemoved?: number;
4938
+ };
4939
+ /**
4940
+ * Clear the active guest checkout tracking without clearing the cart.
4941
+ * Use this if the user abandons checkout or navigates away.
4942
+ */
4943
+ clearActiveGuestCheckout(): void;
4827
4944
  /**
4828
4945
  * Create order from custom data (not from local cart)
4829
4946
  * Use this if you manage cart state yourself
package/dist/index.d.ts CHANGED
@@ -1692,15 +1692,27 @@ interface ShippingRate {
1692
1692
  * **Note:** Order is created automatically via webhook when payment succeeds.
1693
1693
  * No need to call `completeCheckout()` - it happens automatically.
1694
1694
  *
1695
+ * **IMPORTANT: Order Summary Display**
1696
+ * Always use `checkout.lineItems` for displaying the Order Summary during checkout!
1697
+ * This is especially important for **partial checkout** (AliExpress-style) where the user
1698
+ * selects only some items from their cart. The `lineItems` array contains ONLY the items
1699
+ * being purchased in this checkout, NOT the entire cart.
1700
+ *
1695
1701
  * **IMPORTANT: Price fields are strings**
1696
1702
  * All monetary fields (subtotal, discountAmount, shippingAmount, taxAmount, total)
1697
1703
  * are strings to preserve decimal precision. Use parseFloat() for calculations.
1698
1704
  *
1699
1705
  * @example
1700
1706
  * ```typescript
1701
- * // Display checkout summary
1707
+ * // Display checkout summary - use checkout.lineItems, NOT localCart!
1702
1708
  * const checkout = await omni.getCheckout(checkoutId);
1703
1709
  *
1710
+ * // Order Summary - ALWAYS use checkout.lineItems
1711
+ * checkout.lineItems.forEach(item => {
1712
+ * console.log(item.product.name, item.quantity, item.unitPrice);
1713
+ * });
1714
+ *
1715
+ * // Totals
1704
1716
  * const subtotal = parseFloat(checkout.subtotal);
1705
1717
  * const discount = parseFloat(checkout.discountAmount);
1706
1718
  * const shipping = parseFloat(checkout.shippingAmount);
@@ -1712,11 +1724,6 @@ interface ShippingRate {
1712
1724
  * console.log(`Shipping: $${shipping.toFixed(2)}`);
1713
1725
  * console.log(`Tax: $${tax.toFixed(2)}`);
1714
1726
  * console.log(`Total: $${total.toFixed(2)}`);
1715
- *
1716
- * // Access line items (nested structure)
1717
- * checkout.lineItems.forEach(item => {
1718
- * console.log(item.product.name, item.quantity);
1719
- * });
1720
1727
  * ```
1721
1728
  *
1722
1729
  * @see CheckoutLineItem for item structure
@@ -3057,6 +3064,11 @@ declare class OmniSyncClient {
3057
3064
  * When a cart has this ID, operations use localStorage instead of server API.
3058
3065
  */
3059
3066
  private readonly VIRTUAL_LOCAL_CART_ID;
3067
+ /**
3068
+ * localStorage key for persisting active checkout across page redirects.
3069
+ * This is needed because Stripe redirects lose in-memory state.
3070
+ */
3071
+ private readonly ACTIVE_CHECKOUT_KEY;
3060
3072
  private readonly onAuthError?;
3061
3073
  constructor(options: OmniSyncClientOptions);
3062
3074
  /**
@@ -4185,6 +4197,11 @@ declare class OmniSyncClient {
4185
4197
  * @internal
4186
4198
  */
4187
4199
  private getOrCreateCustomerCart;
4200
+ /**
4201
+ * Fetch the customer's existing cart from server
4202
+ * @internal
4203
+ */
4204
+ private fetchCustomerCart;
4188
4205
  /**
4189
4206
  * Smart add to cart - automatically uses localStorage or server based on auth state
4190
4207
  *
@@ -4743,6 +4760,11 @@ declare class OmniSyncClient {
4743
4760
  * Pass `selectedIndices` to checkout only specific items from the local cart.
4744
4761
  * Use the index of each item in the cart.items array.
4745
4762
  *
4763
+ * **IMPORTANT - Order Summary Display:**
4764
+ * After calling this function, use `getCheckout(checkoutId)` to get the checkout
4765
+ * and display `checkout.lineItems` in your Order Summary - NOT the local cart items!
4766
+ * The checkout.lineItems contains ONLY the selected items for this checkout.
4767
+ *
4746
4768
  * @param options.selectedIndices - Optional array of item indices for partial checkout
4747
4769
  * @returns Tracking result with checkoutId if enabled
4748
4770
  *
@@ -4755,19 +4777,22 @@ declare class OmniSyncClient {
4755
4777
  * const result = await omni.startGuestCheckout({ selectedIndices: [0, 2] });
4756
4778
  *
4757
4779
  * if (result.tracked) {
4758
- * // Store checkoutId for later use
4759
- * console.log('Checkout tracked:', result.checkoutId);
4780
+ * // IMPORTANT: Fetch checkout and use checkout.lineItems for Order Summary!
4781
+ * const checkout = await omni.getCheckout(result.checkoutId);
4782
+ *
4783
+ * // Display Order Summary using checkout.lineItems (NOT localCart.items!)
4784
+ * // checkout.lineItems contains ONLY the selected items
4785
+ * checkout.lineItems.forEach(item => {
4786
+ * console.log(item.product.name, item.quantity, item.unitPrice);
4787
+ * });
4760
4788
  *
4761
4789
  * // Update checkout with address
4762
- * await omni.updateGuestCheckout(result.checkoutId, {
4790
+ * await omni.updateGuestCheckoutAddress(result.checkoutId, {
4763
4791
  * shippingAddress: { ... },
4764
4792
  * });
4765
4793
  *
4766
- * // Complete checkout
4767
- * const order = await omni.completeGuestCheckout(result.checkoutId);
4768
- *
4769
- * // For partial checkout: remove only purchased items
4770
- * omni.removeLocalCartItemsByIndex([0, 2]);
4794
+ * // After payment success, call handlePaymentSuccess() to clear cart
4795
+ * omni.handlePaymentSuccess(result.checkoutId);
4771
4796
  * } else {
4772
4797
  * // Tracking not enabled, use regular submitGuestOrder
4773
4798
  * const order = await omni.submitGuestOrder();
@@ -4777,6 +4802,21 @@ declare class OmniSyncClient {
4777
4802
  startGuestCheckout(options?: {
4778
4803
  selectedIndices?: number[];
4779
4804
  }): Promise<GuestCheckoutStartResponse>;
4805
+ /**
4806
+ * Save active checkout to localStorage (persists across page redirects)
4807
+ * @internal
4808
+ */
4809
+ private saveActiveCheckout;
4810
+ /**
4811
+ * Load active checkout from localStorage
4812
+ * @internal
4813
+ */
4814
+ private loadActiveCheckout;
4815
+ /**
4816
+ * Clear active checkout from localStorage
4817
+ * @internal
4818
+ */
4819
+ private clearActiveCheckoutStorage;
4780
4820
  /**
4781
4821
  * Remove specific items from local cart by their indices.
4782
4822
  * Use after partial checkout to remove only the purchased items.
@@ -4824,6 +4864,83 @@ declare class OmniSyncClient {
4824
4864
  clearCartOnSuccess?: boolean;
4825
4865
  selectedIndices?: number[];
4826
4866
  }): Promise<GuestOrderResponse>;
4867
+ /**
4868
+ * Get the active guest checkout session info.
4869
+ * Use this to check if there's an active checkout before handling payment success.
4870
+ *
4871
+ * @returns The active checkout info or null if no checkout is active
4872
+ *
4873
+ * @example
4874
+ * ```typescript
4875
+ * const activeCheckout = omni.getActiveGuestCheckout();
4876
+ * if (activeCheckout) {
4877
+ * console.log('Active checkout:', activeCheckout.checkoutId);
4878
+ * console.log('Partial checkout:', activeCheckout.selectedIndices?.length);
4879
+ * }
4880
+ * ```
4881
+ */
4882
+ getActiveGuestCheckout(): {
4883
+ checkoutId: string;
4884
+ cartId: string;
4885
+ selectedIndices?: number[];
4886
+ } | null;
4887
+ /**
4888
+ * Handle payment success - automatically clears the cart (both local and server).
4889
+ *
4890
+ * Call this after Stripe payment succeeds (payment_intent.succeeded or confirmPayment success).
4891
+ * This handles ALL checkout scenarios:
4892
+ *
4893
+ * **For guest users (local cart):**
4894
+ * - Full checkout: clears entire localStorage cart
4895
+ * - Partial checkout: removes only the purchased items
4896
+ *
4897
+ * **For logged-in users (server cart):**
4898
+ * - Clears the cached cart ID so next `smartGetCart()` fetches fresh data
4899
+ * - The server cart is already marked as CONVERTED by the webhook
4900
+ *
4901
+ * **IMPORTANT:** Call this from your payment success handler (e.g., after stripe.confirmPayment succeeds,
4902
+ * or on your success page after redirect). This works for both guests AND logged-in users!
4903
+ *
4904
+ * @param checkoutId - Optional checkout ID for validation
4905
+ * @returns Object indicating whether cart was cleared and how
4906
+ *
4907
+ * @example
4908
+ * ```typescript
4909
+ * // After stripe.confirmPayment() succeeds (works for guests AND logged-in users)
4910
+ * const { error, paymentIntent } = await stripe.confirmPayment({
4911
+ * elements,
4912
+ * confirmParams: { return_url: '/checkout/success' },
4913
+ * redirect: 'if_required',
4914
+ * });
4915
+ *
4916
+ * if (!error && paymentIntent?.status === 'succeeded') {
4917
+ * // Clear the cart automatically - handles all scenarios!
4918
+ * const result = omni.handlePaymentSuccess(checkoutId);
4919
+ * console.log('Cart cleared:', result.cleared);
4920
+ * console.log('User type:', result.userType); // 'guest' or 'customer'
4921
+ * }
4922
+ * ```
4923
+ *
4924
+ * @example
4925
+ * ```typescript
4926
+ * // On success page (after redirect)
4927
+ * const checkoutId = new URLSearchParams(location.search).get('checkout_id');
4928
+ * if (checkoutId) {
4929
+ * omni.handlePaymentSuccess(checkoutId);
4930
+ * }
4931
+ * ```
4932
+ */
4933
+ handlePaymentSuccess(checkoutId?: string): {
4934
+ cleared: boolean;
4935
+ mode: 'full' | 'partial' | 'none';
4936
+ userType: 'guest' | 'customer';
4937
+ itemsRemoved?: number;
4938
+ };
4939
+ /**
4940
+ * Clear the active guest checkout tracking without clearing the cart.
4941
+ * Use this if the user abandons checkout or navigates away.
4942
+ */
4943
+ clearActiveGuestCheckout(): void;
4827
4944
  /**
4828
4945
  * Create order from custom data (not from local cart)
4829
4946
  * Use this if you manage cart state yourself
package/dist/index.js CHANGED
@@ -54,6 +54,11 @@ var OmniSyncClient = class {
54
54
  * When a cart has this ID, operations use localStorage instead of server API.
55
55
  */
56
56
  this.VIRTUAL_LOCAL_CART_ID = "__local__";
57
+ /**
58
+ * localStorage key for persisting active checkout across page redirects.
59
+ * This is needed because Stripe redirects lose in-memory state.
60
+ */
61
+ this.ACTIVE_CHECKOUT_KEY = "omni_active_checkout";
57
62
  // -------------------- Local Cart (Client-Side for Guests) --------------------
58
63
  // These methods store cart data in localStorage - NO API calls!
59
64
  // Use for guest users in vibe-coded sites
@@ -2032,6 +2037,14 @@ var OmniSyncClient = class {
2032
2037
  this.customerCartId = null;
2033
2038
  }
2034
2039
  }
2040
+ try {
2041
+ const cart2 = await this.fetchCustomerCart();
2042
+ if (cart2.status === "ACTIVE") {
2043
+ this.customerCartId = cart2.id;
2044
+ return cart2;
2045
+ }
2046
+ } catch {
2047
+ }
2035
2048
  const cart = await this.createCart();
2036
2049
  this.customerCartId = cart.id;
2037
2050
  if (this.customerToken) {
@@ -2042,6 +2055,22 @@ var OmniSyncClient = class {
2042
2055
  }
2043
2056
  return cart;
2044
2057
  }
2058
+ /**
2059
+ * Fetch the customer's existing cart from server
2060
+ * @internal
2061
+ */
2062
+ async fetchCustomerCart() {
2063
+ if (!this.customerToken) {
2064
+ throw new OmniSyncError("Customer token required", 401);
2065
+ }
2066
+ if (this.isVibeCodedMode()) {
2067
+ return this.vibeCodedRequest("GET", "/customers/me/cart");
2068
+ }
2069
+ if (this.storeId && !this.apiKey) {
2070
+ return this.storefrontRequest("GET", "/customers/me/cart");
2071
+ }
2072
+ throw new OmniSyncError("fetchCustomerCart requires vibe-coded or storefront mode", 400);
2073
+ }
2045
2074
  /**
2046
2075
  * Smart add to cart - automatically uses localStorage or server based on auth state
2047
2076
  *
@@ -3045,6 +3074,11 @@ var OmniSyncClient = class {
3045
3074
  * Pass `selectedIndices` to checkout only specific items from the local cart.
3046
3075
  * Use the index of each item in the cart.items array.
3047
3076
  *
3077
+ * **IMPORTANT - Order Summary Display:**
3078
+ * After calling this function, use `getCheckout(checkoutId)` to get the checkout
3079
+ * and display `checkout.lineItems` in your Order Summary - NOT the local cart items!
3080
+ * The checkout.lineItems contains ONLY the selected items for this checkout.
3081
+ *
3048
3082
  * @param options.selectedIndices - Optional array of item indices for partial checkout
3049
3083
  * @returns Tracking result with checkoutId if enabled
3050
3084
  *
@@ -3057,19 +3091,22 @@ var OmniSyncClient = class {
3057
3091
  * const result = await omni.startGuestCheckout({ selectedIndices: [0, 2] });
3058
3092
  *
3059
3093
  * if (result.tracked) {
3060
- * // Store checkoutId for later use
3061
- * console.log('Checkout tracked:', result.checkoutId);
3094
+ * // IMPORTANT: Fetch checkout and use checkout.lineItems for Order Summary!
3095
+ * const checkout = await omni.getCheckout(result.checkoutId);
3096
+ *
3097
+ * // Display Order Summary using checkout.lineItems (NOT localCart.items!)
3098
+ * // checkout.lineItems contains ONLY the selected items
3099
+ * checkout.lineItems.forEach(item => {
3100
+ * console.log(item.product.name, item.quantity, item.unitPrice);
3101
+ * });
3062
3102
  *
3063
3103
  * // Update checkout with address
3064
- * await omni.updateGuestCheckout(result.checkoutId, {
3104
+ * await omni.updateGuestCheckoutAddress(result.checkoutId, {
3065
3105
  * shippingAddress: { ... },
3066
3106
  * });
3067
3107
  *
3068
- * // Complete checkout
3069
- * const order = await omni.completeGuestCheckout(result.checkoutId);
3070
- *
3071
- * // For partial checkout: remove only purchased items
3072
- * omni.removeLocalCartItemsByIndex([0, 2]);
3108
+ * // After payment success, call handlePaymentSuccess() to clear cart
3109
+ * omni.handlePaymentSuccess(result.checkoutId);
3073
3110
  * } else {
3074
3111
  * // Tracking not enabled, use regular submitGuestOrder
3075
3112
  * const order = await omni.submitGuestOrder();
@@ -3097,8 +3134,48 @@ var OmniSyncClient = class {
3097
3134
  customer: cart.customer
3098
3135
  }
3099
3136
  );
3137
+ if (response.tracked && response.checkoutId && response.cartId) {
3138
+ this.saveActiveCheckout({
3139
+ checkoutId: response.checkoutId,
3140
+ cartId: response.cartId,
3141
+ selectedIndices: options?.selectedIndices
3142
+ });
3143
+ }
3100
3144
  return response;
3101
3145
  }
3146
+ /**
3147
+ * Save active checkout to localStorage (persists across page redirects)
3148
+ * @internal
3149
+ */
3150
+ saveActiveCheckout(checkout) {
3151
+ if (typeof window !== "undefined" && window.localStorage) {
3152
+ localStorage.setItem(this.ACTIVE_CHECKOUT_KEY, JSON.stringify(checkout));
3153
+ }
3154
+ }
3155
+ /**
3156
+ * Load active checkout from localStorage
3157
+ * @internal
3158
+ */
3159
+ loadActiveCheckout() {
3160
+ if (typeof window === "undefined" || !window.localStorage) {
3161
+ return null;
3162
+ }
3163
+ try {
3164
+ const data = localStorage.getItem(this.ACTIVE_CHECKOUT_KEY);
3165
+ return data ? JSON.parse(data) : null;
3166
+ } catch {
3167
+ return null;
3168
+ }
3169
+ }
3170
+ /**
3171
+ * Clear active checkout from localStorage
3172
+ * @internal
3173
+ */
3174
+ clearActiveCheckoutStorage() {
3175
+ if (typeof window !== "undefined" && window.localStorage) {
3176
+ localStorage.removeItem(this.ACTIVE_CHECKOUT_KEY);
3177
+ }
3178
+ }
3102
3179
  /**
3103
3180
  * Remove specific items from local cart by their indices.
3104
3181
  * Use after partial checkout to remove only the purchased items.
@@ -3189,6 +3266,113 @@ var OmniSyncClient = class {
3189
3266
  }
3190
3267
  return result;
3191
3268
  }
3269
+ /**
3270
+ * Get the active guest checkout session info.
3271
+ * Use this to check if there's an active checkout before handling payment success.
3272
+ *
3273
+ * @returns The active checkout info or null if no checkout is active
3274
+ *
3275
+ * @example
3276
+ * ```typescript
3277
+ * const activeCheckout = omni.getActiveGuestCheckout();
3278
+ * if (activeCheckout) {
3279
+ * console.log('Active checkout:', activeCheckout.checkoutId);
3280
+ * console.log('Partial checkout:', activeCheckout.selectedIndices?.length);
3281
+ * }
3282
+ * ```
3283
+ */
3284
+ getActiveGuestCheckout() {
3285
+ return this.loadActiveCheckout();
3286
+ }
3287
+ /**
3288
+ * Handle payment success - automatically clears the cart (both local and server).
3289
+ *
3290
+ * Call this after Stripe payment succeeds (payment_intent.succeeded or confirmPayment success).
3291
+ * This handles ALL checkout scenarios:
3292
+ *
3293
+ * **For guest users (local cart):**
3294
+ * - Full checkout: clears entire localStorage cart
3295
+ * - Partial checkout: removes only the purchased items
3296
+ *
3297
+ * **For logged-in users (server cart):**
3298
+ * - Clears the cached cart ID so next `smartGetCart()` fetches fresh data
3299
+ * - The server cart is already marked as CONVERTED by the webhook
3300
+ *
3301
+ * **IMPORTANT:** Call this from your payment success handler (e.g., after stripe.confirmPayment succeeds,
3302
+ * or on your success page after redirect). This works for both guests AND logged-in users!
3303
+ *
3304
+ * @param checkoutId - Optional checkout ID for validation
3305
+ * @returns Object indicating whether cart was cleared and how
3306
+ *
3307
+ * @example
3308
+ * ```typescript
3309
+ * // After stripe.confirmPayment() succeeds (works for guests AND logged-in users)
3310
+ * const { error, paymentIntent } = await stripe.confirmPayment({
3311
+ * elements,
3312
+ * confirmParams: { return_url: '/checkout/success' },
3313
+ * redirect: 'if_required',
3314
+ * });
3315
+ *
3316
+ * if (!error && paymentIntent?.status === 'succeeded') {
3317
+ * // Clear the cart automatically - handles all scenarios!
3318
+ * const result = omni.handlePaymentSuccess(checkoutId);
3319
+ * console.log('Cart cleared:', result.cleared);
3320
+ * console.log('User type:', result.userType); // 'guest' or 'customer'
3321
+ * }
3322
+ * ```
3323
+ *
3324
+ * @example
3325
+ * ```typescript
3326
+ * // On success page (after redirect)
3327
+ * const checkoutId = new URLSearchParams(location.search).get('checkout_id');
3328
+ * if (checkoutId) {
3329
+ * omni.handlePaymentSuccess(checkoutId);
3330
+ * }
3331
+ * ```
3332
+ */
3333
+ handlePaymentSuccess(checkoutId) {
3334
+ const isLoggedIn = this.isCustomerLoggedIn();
3335
+ if (isLoggedIn) {
3336
+ this.customerCartId = null;
3337
+ }
3338
+ const activeCheckout = this.loadActiveCheckout();
3339
+ if (checkoutId && activeCheckout && activeCheckout.checkoutId !== checkoutId) {
3340
+ console.warn(
3341
+ `handlePaymentSuccess: checkoutId mismatch. Expected ${activeCheckout.checkoutId}, got ${checkoutId}. Proceeding with cleanup.`
3342
+ );
3343
+ }
3344
+ this.clearActiveCheckoutStorage();
3345
+ if (isLoggedIn) {
3346
+ this.clearLocalCart();
3347
+ return {
3348
+ cleared: true,
3349
+ mode: "full",
3350
+ userType: "customer"
3351
+ };
3352
+ }
3353
+ if (activeCheckout?.selectedIndices && activeCheckout.selectedIndices.length > 0) {
3354
+ this.removeLocalCartItemsByIndex(activeCheckout.selectedIndices);
3355
+ return {
3356
+ cleared: true,
3357
+ mode: "partial",
3358
+ userType: "guest",
3359
+ itemsRemoved: activeCheckout.selectedIndices.length
3360
+ };
3361
+ }
3362
+ this.clearLocalCart();
3363
+ return {
3364
+ cleared: true,
3365
+ mode: "full",
3366
+ userType: "guest"
3367
+ };
3368
+ }
3369
+ /**
3370
+ * Clear the active guest checkout tracking without clearing the cart.
3371
+ * Use this if the user abandons checkout or navigates away.
3372
+ */
3373
+ clearActiveGuestCheckout() {
3374
+ this.clearActiveCheckoutStorage();
3375
+ }
3192
3376
  /**
3193
3377
  * Create order from custom data (not from local cart)
3194
3378
  * Use this if you manage cart state yourself
package/dist/index.mjs CHANGED
@@ -17,6 +17,11 @@ var OmniSyncClient = class {
17
17
  * When a cart has this ID, operations use localStorage instead of server API.
18
18
  */
19
19
  this.VIRTUAL_LOCAL_CART_ID = "__local__";
20
+ /**
21
+ * localStorage key for persisting active checkout across page redirects.
22
+ * This is needed because Stripe redirects lose in-memory state.
23
+ */
24
+ this.ACTIVE_CHECKOUT_KEY = "omni_active_checkout";
20
25
  // -------------------- Local Cart (Client-Side for Guests) --------------------
21
26
  // These methods store cart data in localStorage - NO API calls!
22
27
  // Use for guest users in vibe-coded sites
@@ -1995,6 +2000,14 @@ var OmniSyncClient = class {
1995
2000
  this.customerCartId = null;
1996
2001
  }
1997
2002
  }
2003
+ try {
2004
+ const cart2 = await this.fetchCustomerCart();
2005
+ if (cart2.status === "ACTIVE") {
2006
+ this.customerCartId = cart2.id;
2007
+ return cart2;
2008
+ }
2009
+ } catch {
2010
+ }
1998
2011
  const cart = await this.createCart();
1999
2012
  this.customerCartId = cart.id;
2000
2013
  if (this.customerToken) {
@@ -2005,6 +2018,22 @@ var OmniSyncClient = class {
2005
2018
  }
2006
2019
  return cart;
2007
2020
  }
2021
+ /**
2022
+ * Fetch the customer's existing cart from server
2023
+ * @internal
2024
+ */
2025
+ async fetchCustomerCart() {
2026
+ if (!this.customerToken) {
2027
+ throw new OmniSyncError("Customer token required", 401);
2028
+ }
2029
+ if (this.isVibeCodedMode()) {
2030
+ return this.vibeCodedRequest("GET", "/customers/me/cart");
2031
+ }
2032
+ if (this.storeId && !this.apiKey) {
2033
+ return this.storefrontRequest("GET", "/customers/me/cart");
2034
+ }
2035
+ throw new OmniSyncError("fetchCustomerCart requires vibe-coded or storefront mode", 400);
2036
+ }
2008
2037
  /**
2009
2038
  * Smart add to cart - automatically uses localStorage or server based on auth state
2010
2039
  *
@@ -3008,6 +3037,11 @@ var OmniSyncClient = class {
3008
3037
  * Pass `selectedIndices` to checkout only specific items from the local cart.
3009
3038
  * Use the index of each item in the cart.items array.
3010
3039
  *
3040
+ * **IMPORTANT - Order Summary Display:**
3041
+ * After calling this function, use `getCheckout(checkoutId)` to get the checkout
3042
+ * and display `checkout.lineItems` in your Order Summary - NOT the local cart items!
3043
+ * The checkout.lineItems contains ONLY the selected items for this checkout.
3044
+ *
3011
3045
  * @param options.selectedIndices - Optional array of item indices for partial checkout
3012
3046
  * @returns Tracking result with checkoutId if enabled
3013
3047
  *
@@ -3020,19 +3054,22 @@ var OmniSyncClient = class {
3020
3054
  * const result = await omni.startGuestCheckout({ selectedIndices: [0, 2] });
3021
3055
  *
3022
3056
  * if (result.tracked) {
3023
- * // Store checkoutId for later use
3024
- * console.log('Checkout tracked:', result.checkoutId);
3057
+ * // IMPORTANT: Fetch checkout and use checkout.lineItems for Order Summary!
3058
+ * const checkout = await omni.getCheckout(result.checkoutId);
3059
+ *
3060
+ * // Display Order Summary using checkout.lineItems (NOT localCart.items!)
3061
+ * // checkout.lineItems contains ONLY the selected items
3062
+ * checkout.lineItems.forEach(item => {
3063
+ * console.log(item.product.name, item.quantity, item.unitPrice);
3064
+ * });
3025
3065
  *
3026
3066
  * // Update checkout with address
3027
- * await omni.updateGuestCheckout(result.checkoutId, {
3067
+ * await omni.updateGuestCheckoutAddress(result.checkoutId, {
3028
3068
  * shippingAddress: { ... },
3029
3069
  * });
3030
3070
  *
3031
- * // Complete checkout
3032
- * const order = await omni.completeGuestCheckout(result.checkoutId);
3033
- *
3034
- * // For partial checkout: remove only purchased items
3035
- * omni.removeLocalCartItemsByIndex([0, 2]);
3071
+ * // After payment success, call handlePaymentSuccess() to clear cart
3072
+ * omni.handlePaymentSuccess(result.checkoutId);
3036
3073
  * } else {
3037
3074
  * // Tracking not enabled, use regular submitGuestOrder
3038
3075
  * const order = await omni.submitGuestOrder();
@@ -3060,8 +3097,48 @@ var OmniSyncClient = class {
3060
3097
  customer: cart.customer
3061
3098
  }
3062
3099
  );
3100
+ if (response.tracked && response.checkoutId && response.cartId) {
3101
+ this.saveActiveCheckout({
3102
+ checkoutId: response.checkoutId,
3103
+ cartId: response.cartId,
3104
+ selectedIndices: options?.selectedIndices
3105
+ });
3106
+ }
3063
3107
  return response;
3064
3108
  }
3109
+ /**
3110
+ * Save active checkout to localStorage (persists across page redirects)
3111
+ * @internal
3112
+ */
3113
+ saveActiveCheckout(checkout) {
3114
+ if (typeof window !== "undefined" && window.localStorage) {
3115
+ localStorage.setItem(this.ACTIVE_CHECKOUT_KEY, JSON.stringify(checkout));
3116
+ }
3117
+ }
3118
+ /**
3119
+ * Load active checkout from localStorage
3120
+ * @internal
3121
+ */
3122
+ loadActiveCheckout() {
3123
+ if (typeof window === "undefined" || !window.localStorage) {
3124
+ return null;
3125
+ }
3126
+ try {
3127
+ const data = localStorage.getItem(this.ACTIVE_CHECKOUT_KEY);
3128
+ return data ? JSON.parse(data) : null;
3129
+ } catch {
3130
+ return null;
3131
+ }
3132
+ }
3133
+ /**
3134
+ * Clear active checkout from localStorage
3135
+ * @internal
3136
+ */
3137
+ clearActiveCheckoutStorage() {
3138
+ if (typeof window !== "undefined" && window.localStorage) {
3139
+ localStorage.removeItem(this.ACTIVE_CHECKOUT_KEY);
3140
+ }
3141
+ }
3065
3142
  /**
3066
3143
  * Remove specific items from local cart by their indices.
3067
3144
  * Use after partial checkout to remove only the purchased items.
@@ -3152,6 +3229,113 @@ var OmniSyncClient = class {
3152
3229
  }
3153
3230
  return result;
3154
3231
  }
3232
+ /**
3233
+ * Get the active guest checkout session info.
3234
+ * Use this to check if there's an active checkout before handling payment success.
3235
+ *
3236
+ * @returns The active checkout info or null if no checkout is active
3237
+ *
3238
+ * @example
3239
+ * ```typescript
3240
+ * const activeCheckout = omni.getActiveGuestCheckout();
3241
+ * if (activeCheckout) {
3242
+ * console.log('Active checkout:', activeCheckout.checkoutId);
3243
+ * console.log('Partial checkout:', activeCheckout.selectedIndices?.length);
3244
+ * }
3245
+ * ```
3246
+ */
3247
+ getActiveGuestCheckout() {
3248
+ return this.loadActiveCheckout();
3249
+ }
3250
+ /**
3251
+ * Handle payment success - automatically clears the cart (both local and server).
3252
+ *
3253
+ * Call this after Stripe payment succeeds (payment_intent.succeeded or confirmPayment success).
3254
+ * This handles ALL checkout scenarios:
3255
+ *
3256
+ * **For guest users (local cart):**
3257
+ * - Full checkout: clears entire localStorage cart
3258
+ * - Partial checkout: removes only the purchased items
3259
+ *
3260
+ * **For logged-in users (server cart):**
3261
+ * - Clears the cached cart ID so next `smartGetCart()` fetches fresh data
3262
+ * - The server cart is already marked as CONVERTED by the webhook
3263
+ *
3264
+ * **IMPORTANT:** Call this from your payment success handler (e.g., after stripe.confirmPayment succeeds,
3265
+ * or on your success page after redirect). This works for both guests AND logged-in users!
3266
+ *
3267
+ * @param checkoutId - Optional checkout ID for validation
3268
+ * @returns Object indicating whether cart was cleared and how
3269
+ *
3270
+ * @example
3271
+ * ```typescript
3272
+ * // After stripe.confirmPayment() succeeds (works for guests AND logged-in users)
3273
+ * const { error, paymentIntent } = await stripe.confirmPayment({
3274
+ * elements,
3275
+ * confirmParams: { return_url: '/checkout/success' },
3276
+ * redirect: 'if_required',
3277
+ * });
3278
+ *
3279
+ * if (!error && paymentIntent?.status === 'succeeded') {
3280
+ * // Clear the cart automatically - handles all scenarios!
3281
+ * const result = omni.handlePaymentSuccess(checkoutId);
3282
+ * console.log('Cart cleared:', result.cleared);
3283
+ * console.log('User type:', result.userType); // 'guest' or 'customer'
3284
+ * }
3285
+ * ```
3286
+ *
3287
+ * @example
3288
+ * ```typescript
3289
+ * // On success page (after redirect)
3290
+ * const checkoutId = new URLSearchParams(location.search).get('checkout_id');
3291
+ * if (checkoutId) {
3292
+ * omni.handlePaymentSuccess(checkoutId);
3293
+ * }
3294
+ * ```
3295
+ */
3296
+ handlePaymentSuccess(checkoutId) {
3297
+ const isLoggedIn = this.isCustomerLoggedIn();
3298
+ if (isLoggedIn) {
3299
+ this.customerCartId = null;
3300
+ }
3301
+ const activeCheckout = this.loadActiveCheckout();
3302
+ if (checkoutId && activeCheckout && activeCheckout.checkoutId !== checkoutId) {
3303
+ console.warn(
3304
+ `handlePaymentSuccess: checkoutId mismatch. Expected ${activeCheckout.checkoutId}, got ${checkoutId}. Proceeding with cleanup.`
3305
+ );
3306
+ }
3307
+ this.clearActiveCheckoutStorage();
3308
+ if (isLoggedIn) {
3309
+ this.clearLocalCart();
3310
+ return {
3311
+ cleared: true,
3312
+ mode: "full",
3313
+ userType: "customer"
3314
+ };
3315
+ }
3316
+ if (activeCheckout?.selectedIndices && activeCheckout.selectedIndices.length > 0) {
3317
+ this.removeLocalCartItemsByIndex(activeCheckout.selectedIndices);
3318
+ return {
3319
+ cleared: true,
3320
+ mode: "partial",
3321
+ userType: "guest",
3322
+ itemsRemoved: activeCheckout.selectedIndices.length
3323
+ };
3324
+ }
3325
+ this.clearLocalCart();
3326
+ return {
3327
+ cleared: true,
3328
+ mode: "full",
3329
+ userType: "guest"
3330
+ };
3331
+ }
3332
+ /**
3333
+ * Clear the active guest checkout tracking without clearing the cart.
3334
+ * Use this if the user abandons checkout or navigates away.
3335
+ */
3336
+ clearActiveGuestCheckout() {
3337
+ this.clearActiveCheckoutStorage();
3338
+ }
3155
3339
  /**
3156
3340
  * Create order from custom data (not from local cart)
3157
3341
  * Use this if you manage cart state yourself
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omni-sync-sdk",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
4
4
  "description": "Official SDK for building e-commerce storefronts with OmniSync Platform. Perfect for vibe-coded sites, AI-built stores (Cursor, Lovable, v0), and custom storefronts.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -16,14 +16,6 @@
16
16
  "dist",
17
17
  "README.md"
18
18
  ],
19
- "scripts": {
20
- "build": "tsup src/index.ts --format cjs,esm --dts",
21
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
22
- "lint": "eslint \"src/**/*.ts\"",
23
- "test": "vitest run",
24
- "test:watch": "vitest",
25
- "prepublishOnly": "pnpm build"
26
- },
27
19
  "keywords": [
28
20
  "omni-sync",
29
21
  "e-commerce",
@@ -72,5 +64,12 @@
72
64
  "typescript": {
73
65
  "optional": true
74
66
  }
67
+ },
68
+ "scripts": {
69
+ "build": "tsup src/index.ts --format cjs,esm --dts",
70
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
71
+ "lint": "eslint \"src/**/*.ts\"",
72
+ "test": "vitest run",
73
+ "test:watch": "vitest"
75
74
  }
76
- }
75
+ }