@merchi/cart 1.4.18 → 1.4.19

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.
@@ -19,13 +19,13 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
19
19
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
20
20
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
21
21
  import * as React from 'react';
22
- import { createContext, useContext, useEffect, useState } from 'react';
22
+ import { createContext, useContext, useEffect, useRef, useState } from 'react';
23
23
  import { faCheckCircle, faExclamationTriangle, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
24
24
  import { makeCart, makeCartItem, stripeIsValidAndActive } from './utilities/cart';
25
25
  import { cartEmbed } from './utilities/helpers';
26
26
  import { initTabs, tabIdItems, tabShipment } from './utilities/tabs';
27
27
  import { Merchi } from '@merchi/sdk';
28
- import { getCartCookie, getCartCookieToken, setCartCookie } from './utilities/cookie';
28
+ import { getCartCookie, getCartCookieToken, readHandoffCart, setCartCookie } from './utilities/cookie';
29
29
  import { makeAddress, sanitizeAddressFields } from './utilities/address';
30
30
  import { appendStyleSheetText } from './utilities/helpers';
31
31
  import { makeJob } from './utilities/job';
@@ -425,6 +425,7 @@ var CartProvider = function CartProvider(_ref) {
425
425
  _useState16 = _slicedToArray(_useState15, 2),
426
426
  cartSettingsInvalid = _useState16[0],
427
427
  setCartSettingsInvalid = _useState16[1];
428
+ var appliedHandoffCart = useRef(null);
428
429
  function attachSavedClientIfNeeded(_x) {
429
430
  return _attachSavedClientIfNeeded.apply(this, arguments);
430
431
  } // Create cart and save cart cookie
@@ -554,7 +555,8 @@ var CartProvider = function CartProvider(_ref) {
554
555
  }
555
556
  function getCart(_x3) {
556
557
  return _getCart.apply(this, arguments);
557
- }
558
+ } // A checkout link can name the cart. Remember it once, then keep using the cookie
559
+ // so a later refetch does not put that cart back after the shopper replaced it.
558
560
  function _getCart() {
559
561
  _getCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(cartIdAndToken) {
560
562
  var id, _cart2, cartJson, isTest, jobSource, saved, _t4;
@@ -634,28 +636,73 @@ var CartProvider = function CartProvider(_ref) {
634
636
  }));
635
637
  return _getCart.apply(this, arguments);
636
638
  }
639
+ function adoptHandoffCart() {
640
+ return _adoptHandoffCart.apply(this, arguments);
641
+ }
642
+ function _adoptHandoffCart() {
643
+ _adoptHandoffCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
644
+ var raw, handoff;
645
+ return _regenerator().w(function (_context4) {
646
+ while (1) switch (_context4.n) {
647
+ case 0:
648
+ if (!(typeof window === 'undefined' || !domainId)) {
649
+ _context4.n = 1;
650
+ break;
651
+ }
652
+ return _context4.a(2);
653
+ case 1:
654
+ raw = new URLSearchParams(window.location.search).get('cart');
655
+ if (!(!raw || raw === appliedHandoffCart.current)) {
656
+ _context4.n = 2;
657
+ break;
658
+ }
659
+ return _context4.a(2);
660
+ case 2:
661
+ handoff = readHandoffCart(window.location.search);
662
+ if (handoff) {
663
+ _context4.n = 3;
664
+ break;
665
+ }
666
+ return _context4.a(2);
667
+ case 3:
668
+ appliedHandoffCart.current = raw;
669
+ _context4.n = 4;
670
+ return setCartCookie(Number(domainId), {
671
+ id: handoff[0],
672
+ token: handoff[1]
673
+ });
674
+ case 4:
675
+ return _context4.a(2);
676
+ }
677
+ }, _callee4);
678
+ }));
679
+ return _adoptHandoffCart.apply(this, arguments);
680
+ }
637
681
  function actionGetMerchiCart() {
638
682
  return _actionGetMerchiCart.apply(this, arguments);
639
683
  }
640
684
  function _actionGetMerchiCart() {
641
- _actionGetMerchiCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
685
+ _actionGetMerchiCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
642
686
  var cartIdAndToken;
643
- return _regenerator().w(function (_context4) {
644
- while (1) switch (_context4.n) {
687
+ return _regenerator().w(function (_context5) {
688
+ while (1) switch (_context5.n) {
645
689
  case 0:
646
- _context4.n = 1;
647
- return getCartCookie(domainId);
690
+ _context5.n = 1;
691
+ return adoptHandoffCart();
648
692
  case 1:
649
- cartIdAndToken = _context4.v;
693
+ _context5.n = 2;
694
+ return getCartCookie(domainId);
695
+ case 2:
696
+ cartIdAndToken = _context5.v;
650
697
  if (cartIdAndToken) {
651
698
  getCart(cartIdAndToken);
652
699
  } else {
653
700
  createCartAndCookie();
654
701
  }
655
- case 2:
656
- return _context4.a(2);
702
+ case 3:
703
+ return _context5.a(2);
657
704
  }
658
- }, _callee4);
705
+ }, _callee5);
659
706
  }));
660
707
  return _actionGetMerchiCart.apply(this, arguments);
661
708
  }
@@ -663,19 +710,19 @@ var CartProvider = function CartProvider(_ref) {
663
710
  return _clearCart.apply(this, arguments);
664
711
  }
665
712
  function _clearCart() {
666
- _clearCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(callback) {
667
- return _regenerator().w(function (_context5) {
668
- while (1) switch (_context5.n) {
713
+ _clearCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(callback) {
714
+ return _regenerator().w(function (_context6) {
715
+ while (1) switch (_context6.n) {
669
716
  case 0:
670
- _context5.n = 1;
717
+ _context6.n = 1;
671
718
  return createCartAndCookie();
672
719
  case 1:
673
720
  if (callback) callback();
674
721
  closeClearCart();
675
722
  case 2:
676
- return _context5.a(2);
723
+ return _context6.a(2);
677
724
  }
678
- }, _callee5);
725
+ }, _callee6);
679
726
  }));
680
727
  return _clearCart.apply(this, arguments);
681
728
  }
@@ -687,55 +734,55 @@ var CartProvider = function CartProvider(_ref) {
687
734
  return _updateCartShipmentAddress.apply(this, arguments);
688
735
  }
689
736
  function _updateCartShipmentAddress() {
690
- _updateCartShipmentAddress = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(address) {
737
+ _updateCartShipmentAddress = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(address) {
691
738
  var token, addressFields, receiverAddress, cartEnt, cartJson, query, cartWithShipmentGroups, _t5;
692
- return _regenerator().w(function (_context6) {
693
- while (1) switch (_context6.p = _context6.n) {
739
+ return _regenerator().w(function (_context7) {
740
+ while (1) switch (_context7.p = _context7.n) {
694
741
  case 0:
695
- _context6.n = 1;
742
+ _context7.n = 1;
696
743
  return getCartCookieToken(domainId);
697
744
  case 1:
698
- token = _context6.v;
745
+ token = _context7.v;
699
746
  addressFields = sanitizeAddressFields(address);
700
747
  receiverAddress = makeAddress(addressFields, true);
701
748
  cartEnt = makeCart(cart, false, token);
702
749
  setFetchingShipmentGroups(true);
703
- _context6.p = 2;
750
+ _context7.p = 2;
704
751
  cartEnt.receiverAddress = receiverAddress;
705
- _context6.n = 3;
752
+ _context7.n = 3;
706
753
  return cartEnt.save({
707
754
  embed: cartEmbed
708
755
  });
709
756
  case 3:
710
- cartEnt = _context6.v;
757
+ cartEnt = _context7.v;
711
758
  cartJson = cartEnt.toJson();
712
759
  saveCheckoutAddress(domainId, addressFields);
713
760
  query = [];
714
761
  query.push(['cart_token', cart.token]);
715
- _context6.n = 4;
762
+ _context7.n = 4;
716
763
  return merchi.authenticatedFetch("/generate-cart-shipment-quotes/".concat(cart.id, "/"), {
717
764
  method: 'GET',
718
765
  query: query
719
766
  });
720
767
  case 4:
721
- cartWithShipmentGroups = _context6.v;
768
+ cartWithShipmentGroups = _context7.v;
722
769
  setCart(_objectSpread(_objectSpread({}, cartJson), {}, {
723
770
  shipmentGroups: cartWithShipmentGroups.shipmentGroups || []
724
771
  }));
725
- _context6.n = 6;
772
+ _context7.n = 6;
726
773
  break;
727
774
  case 5:
728
- _context6.p = 5;
729
- _t5 = _context6.v;
775
+ _context7.p = 5;
776
+ _t5 = _context7.v;
730
777
  alertError(_t5.errorMessage || _t5.message || 'Unable to set address');
731
778
  case 6:
732
- _context6.p = 6;
779
+ _context7.p = 6;
733
780
  setFetchingShipmentGroups(false);
734
- return _context6.f(6);
781
+ return _context7.f(6);
735
782
  case 7:
736
- return _context6.a(2);
783
+ return _context7.a(2);
737
784
  }
738
- }, _callee6, null, [[2, 5, 6, 7]]);
785
+ }, _callee7, null, [[2, 5, 6, 7]]);
739
786
  }));
740
787
  return _updateCartShipmentAddress.apply(this, arguments);
741
788
  }
@@ -743,39 +790,39 @@ var CartProvider = function CartProvider(_ref) {
743
790
  return _getCartShipmentOptions.apply(this, arguments);
744
791
  }
745
792
  function _getCartShipmentOptions() {
746
- _getCartShipmentOptions = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
793
+ _getCartShipmentOptions = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
747
794
  var query, cartWithGroups, _t6;
748
- return _regenerator().w(function (_context7) {
749
- while (1) switch (_context7.p = _context7.n) {
795
+ return _regenerator().w(function (_context8) {
796
+ while (1) switch (_context8.p = _context8.n) {
750
797
  case 0:
751
798
  setFetchingShipmentGroups(true);
752
- _context7.p = 1;
799
+ _context8.p = 1;
753
800
  query = [];
754
801
  query.push(['cart_token', cart.token]);
755
- _context7.n = 2;
802
+ _context8.n = 2;
756
803
  return merchi.authenticatedFetch("/generate-cart-shipment-quotes/".concat(cart.id, "/"), {
757
804
  method: 'GET',
758
805
  query: query
759
806
  });
760
807
  case 2:
761
- cartWithGroups = _context7.v;
808
+ cartWithGroups = _context8.v;
762
809
  setCart(_objectSpread(_objectSpread({}, cart), {}, {
763
810
  shipmentGroups: cartWithGroups.shipmentGroups || []
764
811
  }));
765
- _context7.n = 4;
812
+ _context8.n = 4;
766
813
  break;
767
814
  case 3:
768
- _context7.p = 3;
769
- _t6 = _context7.v;
815
+ _context8.p = 3;
816
+ _t6 = _context8.v;
770
817
  alertError(_t6.errorMessage || _t6.message || 'Unable to fetch shipment options');
771
818
  case 4:
772
- _context7.p = 4;
819
+ _context8.p = 4;
773
820
  setFetchingShipmentGroups(false);
774
- return _context7.f(4);
821
+ return _context8.f(4);
775
822
  case 5:
776
- return _context7.a(2);
823
+ return _context8.a(2);
777
824
  }
778
- }, _callee7, null, [[1, 3, 4, 5]]);
825
+ }, _callee8, null, [[1, 3, 4, 5]]);
779
826
  }));
780
827
  return _getCartShipmentOptions.apply(this, arguments);
781
828
  }
@@ -783,14 +830,14 @@ var CartProvider = function CartProvider(_ref) {
783
830
  return _getMerchiCartValues.apply(this, arguments);
784
831
  }
785
832
  function _getMerchiCartValues() {
786
- _getMerchiCartValues = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
833
+ _getMerchiCartValues = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
787
834
  var _ref2, cartItems, currency, subtotalCost, taxAmount, totalCost, cartItemsCount;
788
- return _regenerator().w(function (_context8) {
789
- while (1) switch (_context8.n) {
835
+ return _regenerator().w(function (_context9) {
836
+ while (1) switch (_context9.n) {
790
837
  case 0:
791
838
  _ref2 = cart, cartItems = _ref2.cartItems, currency = _ref2.currency, subtotalCost = _ref2.subtotalCost, taxAmount = _ref2.taxAmount, totalCost = _ref2.totalCost;
792
839
  cartItemsCount = cartItems ? cartItems.length : 0;
793
- return _context8.a(2, {
840
+ return _context9.a(2, {
794
841
  cart: cart,
795
842
  cartItemsCount: cartItemsCount,
796
843
  currency: currency || '',
@@ -799,7 +846,7 @@ var CartProvider = function CartProvider(_ref) {
799
846
  totalCost: totalCost || 0
800
847
  });
801
848
  }
802
- }, _callee8);
849
+ }, _callee9);
803
850
  }));
804
851
  return _getMerchiCartValues.apply(this, arguments);
805
852
  }
@@ -807,13 +854,13 @@ var CartProvider = function CartProvider(_ref) {
807
854
  return _isMerchiCartFetching.apply(this, arguments);
808
855
  }
809
856
  function _isMerchiCartFetching() {
810
- _isMerchiCartFetching = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
811
- return _regenerator().w(function (_context9) {
812
- while (1) switch (_context9.n) {
857
+ _isMerchiCartFetching = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
858
+ return _regenerator().w(function (_context0) {
859
+ while (1) switch (_context0.n) {
813
860
  case 0:
814
- return _context9.a(2, fetchingCart);
861
+ return _context0.a(2, fetchingCart);
815
862
  }
816
- }, _callee9);
863
+ }, _callee0);
817
864
  }));
818
865
  return _isMerchiCartFetching.apply(this, arguments);
819
866
  }
@@ -827,14 +874,14 @@ var CartProvider = function CartProvider(_ref) {
827
874
  return _actionFetchTheme.apply(this, arguments);
828
875
  }
829
876
  function _actionFetchTheme() {
830
- _actionFetchTheme = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
877
+ _actionFetchTheme = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
831
878
  var domain, theme, _t7;
832
- return _regenerator().w(function (_context0) {
833
- while (1) switch (_context0.p = _context0.n) {
879
+ return _regenerator().w(function (_context1) {
880
+ while (1) switch (_context1.p = _context1.n) {
834
881
  case 0:
835
882
  setLoading(true);
836
- _context0.p = 1;
837
- _context0.n = 2;
883
+ _context1.p = 1;
884
+ _context1.n = 2;
838
885
  return merchi.Domain.get(domainId, {
839
886
  embed: {
840
887
  activeTheme: {
@@ -843,24 +890,24 @@ var CartProvider = function CartProvider(_ref) {
843
890
  }
844
891
  });
845
892
  case 2:
846
- domain = _context0.v;
893
+ domain = _context1.v;
847
894
  theme = domain.activeTheme;
848
- _context0.n = 3;
895
+ _context1.n = 3;
849
896
  return appendStyleSheetText(theme.mainCss, function () {
850
897
  return setLoading(false);
851
898
  });
852
899
  case 3:
853
- _context0.n = 5;
900
+ _context1.n = 5;
854
901
  break;
855
902
  case 4:
856
- _context0.p = 4;
857
- _t7 = _context0.v;
903
+ _context1.p = 4;
904
+ _t7 = _context1.v;
858
905
  alertError(_t7.errorMessage || _t7.message || 'Error fetching domain theme.');
859
906
  setLoading(false);
860
907
  case 5:
861
- return _context0.a(2);
908
+ return _context1.a(2);
862
909
  }
863
- }, _callee0, null, [[1, 4]]);
910
+ }, _callee1, null, [[1, 4]]);
864
911
  }));
865
912
  return _actionFetchTheme.apply(this, arguments);
866
913
  }
@@ -868,18 +915,18 @@ var CartProvider = function CartProvider(_ref) {
868
915
  return _setCartComplete.apply(this, arguments);
869
916
  }
870
917
  function _setCartComplete() {
871
- _setCartComplete = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
872
- return _regenerator().w(function (_context1) {
873
- while (1) switch (_context1.n) {
918
+ _setCartComplete = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
919
+ return _regenerator().w(function (_context10) {
920
+ while (1) switch (_context10.n) {
874
921
  case 0:
875
- _context1.n = 1;
922
+ _context10.n = 1;
876
923
  return createCartAndCookie();
877
924
  case 1:
878
925
  location.reload();
879
926
  case 2:
880
- return _context1.a(2);
927
+ return _context10.a(2);
881
928
  }
882
- }, _callee1);
929
+ }, _callee10);
883
930
  }));
884
931
  return _setCartComplete.apply(this, arguments);
885
932
  }
@@ -887,16 +934,16 @@ var CartProvider = function CartProvider(_ref) {
887
934
  return _refetchCart.apply(this, arguments);
888
935
  }
889
936
  function _refetchCart() {
890
- _refetchCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
891
- return _regenerator().w(function (_context10) {
892
- while (1) switch (_context10.n) {
937
+ _refetchCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11() {
938
+ return _regenerator().w(function (_context11) {
939
+ while (1) switch (_context11.n) {
893
940
  case 0:
894
- _context10.n = 1;
941
+ _context11.n = 1;
895
942
  return actionGetMerchiCart();
896
943
  case 1:
897
- return _context10.a(2);
944
+ return _context11.a(2);
898
945
  }
899
- }, _callee10);
946
+ }, _callee11);
900
947
  }));
901
948
  return _refetchCart.apply(this, arguments);
902
949
  }
@@ -912,17 +959,17 @@ var CartProvider = function CartProvider(_ref) {
912
959
  return _addCartItem.apply(this, arguments);
913
960
  }
914
961
  function _addCartItem() {
915
- _addCartItem = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11(jobJson, onSuccess, onError) {
962
+ _addCartItem = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee12(jobJson, onSuccess, onError) {
916
963
  var jobEnt, cartToken, cartEnt, cartItemEnt, cartItemProduct, item, itemJson, _t8;
917
- return _regenerator().w(function (_context11) {
918
- while (1) switch (_context11.p = _context11.n) {
964
+ return _regenerator().w(function (_context12) {
965
+ while (1) switch (_context12.p = _context12.n) {
919
966
  case 0:
920
967
  setFetchingCart(true);
921
968
  jobEnt = makeJob(jobJson, true);
922
- _context11.n = 1;
969
+ _context12.n = 1;
923
970
  return getCartCookieToken(domainId);
924
971
  case 1:
925
- cartToken = _context11.v;
972
+ cartToken = _context12.v;
926
973
  cartEnt = makeCart(cart, false, cartToken);
927
974
  cartItemEnt = makeCartItem({}, true);
928
975
  cartItemProduct = makeProduct({
@@ -937,30 +984,30 @@ var CartProvider = function CartProvider(_ref) {
937
984
  cartItemEnt.ownDrafts = jobEnt.ownDrafts;
938
985
  cartItemEnt.clientFiles = jobEnt.clientFiles;
939
986
  cartItemEnt.tags = jobEnt.tags;
940
- _context11.p = 2;
941
- _context11.n = 3;
987
+ _context12.p = 2;
988
+ _context12.n = 3;
942
989
  return cartItemEnt.create();
943
990
  case 3:
944
- item = _context11.v;
945
- _context11.n = 4;
991
+ item = _context12.v;
992
+ _context12.n = 4;
946
993
  return item.toJson();
947
994
  case 4:
948
- itemJson = _context11.v;
995
+ itemJson = _context12.v;
949
996
  onSuccess(itemJson);
950
- _context11.n = 6;
997
+ _context12.n = 6;
951
998
  break;
952
999
  case 5:
953
- _context11.p = 5;
954
- _t8 = _context11.v;
1000
+ _context12.p = 5;
1001
+ _t8 = _context12.v;
955
1002
  onError(_t8);
956
1003
  case 6:
957
- _context11.p = 6;
1004
+ _context12.p = 6;
958
1005
  setFetchingCart(false);
959
- return _context11.f(6);
1006
+ return _context12.f(6);
960
1007
  case 7:
961
- return _context11.a(2);
1008
+ return _context12.a(2);
962
1009
  }
963
- }, _callee11, null, [[2, 5, 6, 7]]);
1010
+ }, _callee12, null, [[2, 5, 6, 7]]);
964
1011
  }));
965
1012
  return _addCartItem.apply(this, arguments);
966
1013
  }
@@ -4,6 +4,15 @@ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.
4
4
  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
5
5
  import { isBrowser } from "browser-or-node";
6
6
  import { cartCookieName, readTestCheckoutFlag } from "./test_checkout";
7
+
8
+ // id,token from a checkout link. The token is url-safe and has no padding.
9
+ var HANDOFF_CART = /^\d+,[A-Za-z0-9_-]{8,}$/;
10
+ export function readHandoffCart(search) {
11
+ var cart = new URLSearchParams(search).get("cart");
12
+ if (!cart || !HANDOFF_CART.test(cart)) return null;
13
+ var comma = cart.indexOf(",");
14
+ return [cart.slice(0, comma), cart.slice(comma + 1)];
15
+ }
7
16
  export function getCookie(_x, _x2) {
8
17
  return _getCookie.apply(this, arguments);
9
18
  }
@@ -0,0 +1,14 @@
1
+ import { readHandoffCart } from './cookie';
2
+ describe('checkout cart handoff', function () {
3
+ it('reads an id and token from the checkout query', function () {
4
+ expect(readHandoffCart('?cart=530141,AbcdEfgh_123-xyz')).toEqual(['530141', 'AbcdEfgh_123-xyz']);
5
+ });
6
+ it('reads a query where the comma was encoded', function () {
7
+ expect(readHandoffCart('?cart=5%2Cabcdefgh')).toEqual(['5', 'abcdefgh']);
8
+ });
9
+ it('ignores a cart value that is not an id and token', function () {
10
+ expect(readHandoffCart('?cart=530141')).toBeNull();
11
+ expect(readHandoffCart('?cart=nope,short')).toBeNull();
12
+ expect(readHandoffCart('')).toBeNull();
13
+ });
14
+ });
@@ -431,6 +431,7 @@ var CartProvider = function CartProvider(_ref) {
431
431
  _useState16 = _slicedToArray(_useState15, 2),
432
432
  cartSettingsInvalid = _useState16[0],
433
433
  setCartSettingsInvalid = _useState16[1];
434
+ var appliedHandoffCart = (0, _react.useRef)(null);
434
435
  function attachSavedClientIfNeeded(_x) {
435
436
  return _attachSavedClientIfNeeded.apply(this, arguments);
436
437
  } // Create cart and save cart cookie
@@ -560,7 +561,8 @@ var CartProvider = function CartProvider(_ref) {
560
561
  }
561
562
  function getCart(_x3) {
562
563
  return _getCart.apply(this, arguments);
563
- }
564
+ } // A checkout link can name the cart. Remember it once, then keep using the cookie
565
+ // so a later refetch does not put that cart back after the shopper replaced it.
564
566
  function _getCart() {
565
567
  _getCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(cartIdAndToken) {
566
568
  var id, _cart2, cartJson, isTest, jobSource, saved, _t4;
@@ -640,28 +642,73 @@ var CartProvider = function CartProvider(_ref) {
640
642
  }));
641
643
  return _getCart.apply(this, arguments);
642
644
  }
645
+ function adoptHandoffCart() {
646
+ return _adoptHandoffCart.apply(this, arguments);
647
+ }
648
+ function _adoptHandoffCart() {
649
+ _adoptHandoffCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
650
+ var raw, handoff;
651
+ return _regenerator().w(function (_context4) {
652
+ while (1) switch (_context4.n) {
653
+ case 0:
654
+ if (!(typeof window === 'undefined' || !domainId)) {
655
+ _context4.n = 1;
656
+ break;
657
+ }
658
+ return _context4.a(2);
659
+ case 1:
660
+ raw = new URLSearchParams(window.location.search).get('cart');
661
+ if (!(!raw || raw === appliedHandoffCart.current)) {
662
+ _context4.n = 2;
663
+ break;
664
+ }
665
+ return _context4.a(2);
666
+ case 2:
667
+ handoff = (0, _cookie.readHandoffCart)(window.location.search);
668
+ if (handoff) {
669
+ _context4.n = 3;
670
+ break;
671
+ }
672
+ return _context4.a(2);
673
+ case 3:
674
+ appliedHandoffCart.current = raw;
675
+ _context4.n = 4;
676
+ return (0, _cookie.setCartCookie)(Number(domainId), {
677
+ id: handoff[0],
678
+ token: handoff[1]
679
+ });
680
+ case 4:
681
+ return _context4.a(2);
682
+ }
683
+ }, _callee4);
684
+ }));
685
+ return _adoptHandoffCart.apply(this, arguments);
686
+ }
643
687
  function actionGetMerchiCart() {
644
688
  return _actionGetMerchiCart.apply(this, arguments);
645
689
  }
646
690
  function _actionGetMerchiCart() {
647
- _actionGetMerchiCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
691
+ _actionGetMerchiCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
648
692
  var cartIdAndToken;
649
- return _regenerator().w(function (_context4) {
650
- while (1) switch (_context4.n) {
693
+ return _regenerator().w(function (_context5) {
694
+ while (1) switch (_context5.n) {
651
695
  case 0:
652
- _context4.n = 1;
653
- return (0, _cookie.getCartCookie)(domainId);
696
+ _context5.n = 1;
697
+ return adoptHandoffCart();
654
698
  case 1:
655
- cartIdAndToken = _context4.v;
699
+ _context5.n = 2;
700
+ return (0, _cookie.getCartCookie)(domainId);
701
+ case 2:
702
+ cartIdAndToken = _context5.v;
656
703
  if (cartIdAndToken) {
657
704
  getCart(cartIdAndToken);
658
705
  } else {
659
706
  createCartAndCookie();
660
707
  }
661
- case 2:
662
- return _context4.a(2);
708
+ case 3:
709
+ return _context5.a(2);
663
710
  }
664
- }, _callee4);
711
+ }, _callee5);
665
712
  }));
666
713
  return _actionGetMerchiCart.apply(this, arguments);
667
714
  }
@@ -669,19 +716,19 @@ var CartProvider = function CartProvider(_ref) {
669
716
  return _clearCart.apply(this, arguments);
670
717
  }
671
718
  function _clearCart() {
672
- _clearCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(callback) {
673
- return _regenerator().w(function (_context5) {
674
- while (1) switch (_context5.n) {
719
+ _clearCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(callback) {
720
+ return _regenerator().w(function (_context6) {
721
+ while (1) switch (_context6.n) {
675
722
  case 0:
676
- _context5.n = 1;
723
+ _context6.n = 1;
677
724
  return createCartAndCookie();
678
725
  case 1:
679
726
  if (callback) callback();
680
727
  closeClearCart();
681
728
  case 2:
682
- return _context5.a(2);
729
+ return _context6.a(2);
683
730
  }
684
- }, _callee5);
731
+ }, _callee6);
685
732
  }));
686
733
  return _clearCart.apply(this, arguments);
687
734
  }
@@ -693,55 +740,55 @@ var CartProvider = function CartProvider(_ref) {
693
740
  return _updateCartShipmentAddress.apply(this, arguments);
694
741
  }
695
742
  function _updateCartShipmentAddress() {
696
- _updateCartShipmentAddress = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(address) {
743
+ _updateCartShipmentAddress = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(address) {
697
744
  var token, addressFields, receiverAddress, cartEnt, cartJson, query, cartWithShipmentGroups, _t5;
698
- return _regenerator().w(function (_context6) {
699
- while (1) switch (_context6.p = _context6.n) {
745
+ return _regenerator().w(function (_context7) {
746
+ while (1) switch (_context7.p = _context7.n) {
700
747
  case 0:
701
- _context6.n = 1;
748
+ _context7.n = 1;
702
749
  return (0, _cookie.getCartCookieToken)(domainId);
703
750
  case 1:
704
- token = _context6.v;
751
+ token = _context7.v;
705
752
  addressFields = (0, _address.sanitizeAddressFields)(address);
706
753
  receiverAddress = (0, _address.makeAddress)(addressFields, true);
707
754
  cartEnt = (0, _cart3.makeCart)(cart, false, token);
708
755
  setFetchingShipmentGroups(true);
709
- _context6.p = 2;
756
+ _context7.p = 2;
710
757
  cartEnt.receiverAddress = receiverAddress;
711
- _context6.n = 3;
758
+ _context7.n = 3;
712
759
  return cartEnt.save({
713
760
  embed: _helpers.cartEmbed
714
761
  });
715
762
  case 3:
716
- cartEnt = _context6.v;
763
+ cartEnt = _context7.v;
717
764
  cartJson = cartEnt.toJson();
718
765
  (0, _local_storage.saveCheckoutAddress)(domainId, addressFields);
719
766
  query = [];
720
767
  query.push(['cart_token', cart.token]);
721
- _context6.n = 4;
768
+ _context7.n = 4;
722
769
  return merchi.authenticatedFetch("/generate-cart-shipment-quotes/".concat(cart.id, "/"), {
723
770
  method: 'GET',
724
771
  query: query
725
772
  });
726
773
  case 4:
727
- cartWithShipmentGroups = _context6.v;
774
+ cartWithShipmentGroups = _context7.v;
728
775
  setCart(_objectSpread(_objectSpread({}, cartJson), {}, {
729
776
  shipmentGroups: cartWithShipmentGroups.shipmentGroups || []
730
777
  }));
731
- _context6.n = 6;
778
+ _context7.n = 6;
732
779
  break;
733
780
  case 5:
734
- _context6.p = 5;
735
- _t5 = _context6.v;
781
+ _context7.p = 5;
782
+ _t5 = _context7.v;
736
783
  alertError(_t5.errorMessage || _t5.message || 'Unable to set address');
737
784
  case 6:
738
- _context6.p = 6;
785
+ _context7.p = 6;
739
786
  setFetchingShipmentGroups(false);
740
- return _context6.f(6);
787
+ return _context7.f(6);
741
788
  case 7:
742
- return _context6.a(2);
789
+ return _context7.a(2);
743
790
  }
744
- }, _callee6, null, [[2, 5, 6, 7]]);
791
+ }, _callee7, null, [[2, 5, 6, 7]]);
745
792
  }));
746
793
  return _updateCartShipmentAddress.apply(this, arguments);
747
794
  }
@@ -749,39 +796,39 @@ var CartProvider = function CartProvider(_ref) {
749
796
  return _getCartShipmentOptions.apply(this, arguments);
750
797
  }
751
798
  function _getCartShipmentOptions() {
752
- _getCartShipmentOptions = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
799
+ _getCartShipmentOptions = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
753
800
  var query, cartWithGroups, _t6;
754
- return _regenerator().w(function (_context7) {
755
- while (1) switch (_context7.p = _context7.n) {
801
+ return _regenerator().w(function (_context8) {
802
+ while (1) switch (_context8.p = _context8.n) {
756
803
  case 0:
757
804
  setFetchingShipmentGroups(true);
758
- _context7.p = 1;
805
+ _context8.p = 1;
759
806
  query = [];
760
807
  query.push(['cart_token', cart.token]);
761
- _context7.n = 2;
808
+ _context8.n = 2;
762
809
  return merchi.authenticatedFetch("/generate-cart-shipment-quotes/".concat(cart.id, "/"), {
763
810
  method: 'GET',
764
811
  query: query
765
812
  });
766
813
  case 2:
767
- cartWithGroups = _context7.v;
814
+ cartWithGroups = _context8.v;
768
815
  setCart(_objectSpread(_objectSpread({}, cart), {}, {
769
816
  shipmentGroups: cartWithGroups.shipmentGroups || []
770
817
  }));
771
- _context7.n = 4;
818
+ _context8.n = 4;
772
819
  break;
773
820
  case 3:
774
- _context7.p = 3;
775
- _t6 = _context7.v;
821
+ _context8.p = 3;
822
+ _t6 = _context8.v;
776
823
  alertError(_t6.errorMessage || _t6.message || 'Unable to fetch shipment options');
777
824
  case 4:
778
- _context7.p = 4;
825
+ _context8.p = 4;
779
826
  setFetchingShipmentGroups(false);
780
- return _context7.f(4);
827
+ return _context8.f(4);
781
828
  case 5:
782
- return _context7.a(2);
829
+ return _context8.a(2);
783
830
  }
784
- }, _callee7, null, [[1, 3, 4, 5]]);
831
+ }, _callee8, null, [[1, 3, 4, 5]]);
785
832
  }));
786
833
  return _getCartShipmentOptions.apply(this, arguments);
787
834
  }
@@ -789,14 +836,14 @@ var CartProvider = function CartProvider(_ref) {
789
836
  return _getMerchiCartValues.apply(this, arguments);
790
837
  }
791
838
  function _getMerchiCartValues() {
792
- _getMerchiCartValues = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
839
+ _getMerchiCartValues = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
793
840
  var _ref2, cartItems, currency, subtotalCost, taxAmount, totalCost, cartItemsCount;
794
- return _regenerator().w(function (_context8) {
795
- while (1) switch (_context8.n) {
841
+ return _regenerator().w(function (_context9) {
842
+ while (1) switch (_context9.n) {
796
843
  case 0:
797
844
  _ref2 = cart, cartItems = _ref2.cartItems, currency = _ref2.currency, subtotalCost = _ref2.subtotalCost, taxAmount = _ref2.taxAmount, totalCost = _ref2.totalCost;
798
845
  cartItemsCount = cartItems ? cartItems.length : 0;
799
- return _context8.a(2, {
846
+ return _context9.a(2, {
800
847
  cart: cart,
801
848
  cartItemsCount: cartItemsCount,
802
849
  currency: currency || '',
@@ -805,7 +852,7 @@ var CartProvider = function CartProvider(_ref) {
805
852
  totalCost: totalCost || 0
806
853
  });
807
854
  }
808
- }, _callee8);
855
+ }, _callee9);
809
856
  }));
810
857
  return _getMerchiCartValues.apply(this, arguments);
811
858
  }
@@ -813,13 +860,13 @@ var CartProvider = function CartProvider(_ref) {
813
860
  return _isMerchiCartFetching.apply(this, arguments);
814
861
  }
815
862
  function _isMerchiCartFetching() {
816
- _isMerchiCartFetching = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
817
- return _regenerator().w(function (_context9) {
818
- while (1) switch (_context9.n) {
863
+ _isMerchiCartFetching = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
864
+ return _regenerator().w(function (_context0) {
865
+ while (1) switch (_context0.n) {
819
866
  case 0:
820
- return _context9.a(2, fetchingCart);
867
+ return _context0.a(2, fetchingCart);
821
868
  }
822
- }, _callee9);
869
+ }, _callee0);
823
870
  }));
824
871
  return _isMerchiCartFetching.apply(this, arguments);
825
872
  }
@@ -833,14 +880,14 @@ var CartProvider = function CartProvider(_ref) {
833
880
  return _actionFetchTheme.apply(this, arguments);
834
881
  }
835
882
  function _actionFetchTheme() {
836
- _actionFetchTheme = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
883
+ _actionFetchTheme = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
837
884
  var domain, theme, _t7;
838
- return _regenerator().w(function (_context0) {
839
- while (1) switch (_context0.p = _context0.n) {
885
+ return _regenerator().w(function (_context1) {
886
+ while (1) switch (_context1.p = _context1.n) {
840
887
  case 0:
841
888
  setLoading(true);
842
- _context0.p = 1;
843
- _context0.n = 2;
889
+ _context1.p = 1;
890
+ _context1.n = 2;
844
891
  return merchi.Domain.get(domainId, {
845
892
  embed: {
846
893
  activeTheme: {
@@ -849,24 +896,24 @@ var CartProvider = function CartProvider(_ref) {
849
896
  }
850
897
  });
851
898
  case 2:
852
- domain = _context0.v;
899
+ domain = _context1.v;
853
900
  theme = domain.activeTheme;
854
- _context0.n = 3;
901
+ _context1.n = 3;
855
902
  return (0, _helpers.appendStyleSheetText)(theme.mainCss, function () {
856
903
  return setLoading(false);
857
904
  });
858
905
  case 3:
859
- _context0.n = 5;
906
+ _context1.n = 5;
860
907
  break;
861
908
  case 4:
862
- _context0.p = 4;
863
- _t7 = _context0.v;
909
+ _context1.p = 4;
910
+ _t7 = _context1.v;
864
911
  alertError(_t7.errorMessage || _t7.message || 'Error fetching domain theme.');
865
912
  setLoading(false);
866
913
  case 5:
867
- return _context0.a(2);
914
+ return _context1.a(2);
868
915
  }
869
- }, _callee0, null, [[1, 4]]);
916
+ }, _callee1, null, [[1, 4]]);
870
917
  }));
871
918
  return _actionFetchTheme.apply(this, arguments);
872
919
  }
@@ -874,18 +921,18 @@ var CartProvider = function CartProvider(_ref) {
874
921
  return _setCartComplete.apply(this, arguments);
875
922
  }
876
923
  function _setCartComplete() {
877
- _setCartComplete = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
878
- return _regenerator().w(function (_context1) {
879
- while (1) switch (_context1.n) {
924
+ _setCartComplete = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
925
+ return _regenerator().w(function (_context10) {
926
+ while (1) switch (_context10.n) {
880
927
  case 0:
881
- _context1.n = 1;
928
+ _context10.n = 1;
882
929
  return createCartAndCookie();
883
930
  case 1:
884
931
  location.reload();
885
932
  case 2:
886
- return _context1.a(2);
933
+ return _context10.a(2);
887
934
  }
888
- }, _callee1);
935
+ }, _callee10);
889
936
  }));
890
937
  return _setCartComplete.apply(this, arguments);
891
938
  }
@@ -893,16 +940,16 @@ var CartProvider = function CartProvider(_ref) {
893
940
  return _refetchCart.apply(this, arguments);
894
941
  }
895
942
  function _refetchCart() {
896
- _refetchCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10() {
897
- return _regenerator().w(function (_context10) {
898
- while (1) switch (_context10.n) {
943
+ _refetchCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11() {
944
+ return _regenerator().w(function (_context11) {
945
+ while (1) switch (_context11.n) {
899
946
  case 0:
900
- _context10.n = 1;
947
+ _context11.n = 1;
901
948
  return actionGetMerchiCart();
902
949
  case 1:
903
- return _context10.a(2);
950
+ return _context11.a(2);
904
951
  }
905
- }, _callee10);
952
+ }, _callee11);
906
953
  }));
907
954
  return _refetchCart.apply(this, arguments);
908
955
  }
@@ -918,17 +965,17 @@ var CartProvider = function CartProvider(_ref) {
918
965
  return _addCartItem.apply(this, arguments);
919
966
  }
920
967
  function _addCartItem() {
921
- _addCartItem = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee11(jobJson, onSuccess, onError) {
968
+ _addCartItem = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee12(jobJson, onSuccess, onError) {
922
969
  var jobEnt, cartToken, cartEnt, cartItemEnt, cartItemProduct, item, itemJson, _t8;
923
- return _regenerator().w(function (_context11) {
924
- while (1) switch (_context11.p = _context11.n) {
970
+ return _regenerator().w(function (_context12) {
971
+ while (1) switch (_context12.p = _context12.n) {
925
972
  case 0:
926
973
  setFetchingCart(true);
927
974
  jobEnt = (0, _job.makeJob)(jobJson, true);
928
- _context11.n = 1;
975
+ _context12.n = 1;
929
976
  return (0, _cookie.getCartCookieToken)(domainId);
930
977
  case 1:
931
- cartToken = _context11.v;
978
+ cartToken = _context12.v;
932
979
  cartEnt = (0, _cart3.makeCart)(cart, false, cartToken);
933
980
  cartItemEnt = (0, _cart3.makeCartItem)({}, true);
934
981
  cartItemProduct = (0, _product.makeProduct)({
@@ -943,30 +990,30 @@ var CartProvider = function CartProvider(_ref) {
943
990
  cartItemEnt.ownDrafts = jobEnt.ownDrafts;
944
991
  cartItemEnt.clientFiles = jobEnt.clientFiles;
945
992
  cartItemEnt.tags = jobEnt.tags;
946
- _context11.p = 2;
947
- _context11.n = 3;
993
+ _context12.p = 2;
994
+ _context12.n = 3;
948
995
  return cartItemEnt.create();
949
996
  case 3:
950
- item = _context11.v;
951
- _context11.n = 4;
997
+ item = _context12.v;
998
+ _context12.n = 4;
952
999
  return item.toJson();
953
1000
  case 4:
954
- itemJson = _context11.v;
1001
+ itemJson = _context12.v;
955
1002
  onSuccess(itemJson);
956
- _context11.n = 6;
1003
+ _context12.n = 6;
957
1004
  break;
958
1005
  case 5:
959
- _context11.p = 5;
960
- _t8 = _context11.v;
1006
+ _context12.p = 5;
1007
+ _t8 = _context12.v;
961
1008
  onError(_t8);
962
1009
  case 6:
963
- _context11.p = 6;
1010
+ _context12.p = 6;
964
1011
  setFetchingCart(false);
965
- return _context11.f(6);
1012
+ return _context12.f(6);
966
1013
  case 7:
967
- return _context11.a(2);
1014
+ return _context12.a(2);
968
1015
  }
969
- }, _callee11, null, [[2, 5, 6, 7]]);
1016
+ }, _callee12, null, [[2, 5, 6, 7]]);
970
1017
  }));
971
1018
  return _addCartItem.apply(this, arguments);
972
1019
  }
@@ -1,3 +1,4 @@
1
+ export declare function readHandoffCart(search: string): string[] | null;
1
2
  export declare function getCookie(name: string, defaultValue: any): Promise<any>;
2
3
  export declare function getCartCookie(domainId: number | string, isTest?: boolean): Promise<string[]>;
3
4
  export declare function getCartCookieToken(domainId: number | string, isTest?: boolean): Promise<string>;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.getCartCookie = getCartCookie;
7
7
  exports.getCartCookieToken = getCartCookieToken;
8
8
  exports.getCookie = getCookie;
9
+ exports.readHandoffCart = readHandoffCart;
9
10
  exports.setCartCookie = void 0;
10
11
  var _browserOrNode = require("browser-or-node");
11
12
  var _test_checkout = require("./test_checkout");
@@ -13,6 +14,14 @@ function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present,
13
14
  function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
14
15
  function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
15
16
  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
17
+ // id,token from a checkout link. The token is url-safe and has no padding.
18
+ var HANDOFF_CART = /^\d+,[A-Za-z0-9_-]{8,}$/;
19
+ function readHandoffCart(search) {
20
+ var cart = new URLSearchParams(search).get("cart");
21
+ if (!cart || !HANDOFF_CART.test(cart)) return null;
22
+ var comma = cart.indexOf(",");
23
+ return [cart.slice(0, comma), cart.slice(comma + 1)];
24
+ }
16
25
  function getCookie(_x, _x2) {
17
26
  return _getCookie.apply(this, arguments);
18
27
  }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@merchi/cart",
3
- "version": "1.4.18",
3
+ "version": "1.4.19",
4
4
  "description": "Merchi's cart",
5
5
  "source": "src/index.ts",
6
6
  "main": "lib/index.js",
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { createContext, ReactNode, useContext, useEffect, useState } from 'react';
2
+ import { createContext, ReactNode, useContext, useEffect, useRef, useState } from 'react';
3
3
  import { faCheckCircle, faExclamationTriangle, faInfoCircle } from '@fortawesome/free-solid-svg-icons';
4
4
  import { Alert, Tab } from './types';
5
5
  import {
@@ -10,7 +10,7 @@ import {
10
10
  import { cartEmbed } from './utilities/helpers';
11
11
  import { initTabs, tabIdItems, tabShipment } from './utilities/tabs';
12
12
  import { Merchi } from '@merchi/sdk';
13
- import { getCartCookie, getCartCookieToken, setCartCookie } from './utilities/cookie';
13
+ import { getCartCookie, getCartCookieToken, readHandoffCart, setCartCookie } from './utilities/cookie';
14
14
  import { makeAddress, sanitizeAddressFields } from './utilities/address';
15
15
  import { appendStyleSheetText } from './utilities/helpers';
16
16
  import { makeJob } from './utilities/job';
@@ -593,6 +593,7 @@ const CartProvider = ({
593
593
 
594
594
  const [fetchingCart, setFetchingCart] = useState(false);
595
595
  const [cartSettingsInvalid, setCartSettingsInvalid] = useState(false);
596
+ const appliedHandoffCart = useRef<string | null>(null);
596
597
 
597
598
  async function attachSavedClientIfNeeded(cartJson: any) {
598
599
  if (cartJson?.client?.id) {
@@ -699,7 +700,20 @@ const CartProvider = ({
699
700
  }
700
701
  }
701
702
 
703
+ // A checkout link can name the cart. Remember it once, then keep using the cookie
704
+ // so a later refetch does not put that cart back after the shopper replaced it.
705
+ async function adoptHandoffCart() {
706
+ if (typeof window === 'undefined' || !domainId) return;
707
+ const raw = new URLSearchParams(window.location.search).get('cart');
708
+ if (!raw || raw === appliedHandoffCart.current) return;
709
+ const handoff = readHandoffCart(window.location.search);
710
+ if (!handoff) return;
711
+ appliedHandoffCart.current = raw;
712
+ await setCartCookie(Number(domainId), { id: handoff[0], token: handoff[1] });
713
+ }
714
+
702
715
  async function actionGetMerchiCart() {
716
+ await adoptHandoffCart();
703
717
  const cartIdAndToken = await getCartCookie((domainId as number));
704
718
  if (cartIdAndToken) {
705
719
  getCart(cartIdAndToken);
@@ -0,0 +1,20 @@
1
+ import { readHandoffCart } from './cookie';
2
+
3
+ describe('checkout cart handoff', () => {
4
+ it('reads an id and token from the checkout query', () => {
5
+ expect(readHandoffCart('?cart=530141,AbcdEfgh_123-xyz')).toEqual([
6
+ '530141',
7
+ 'AbcdEfgh_123-xyz',
8
+ ]);
9
+ });
10
+
11
+ it('reads a query where the comma was encoded', () => {
12
+ expect(readHandoffCart('?cart=5%2Cabcdefgh')).toEqual(['5', 'abcdefgh']);
13
+ });
14
+
15
+ it('ignores a cart value that is not an id and token', () => {
16
+ expect(readHandoffCart('?cart=530141')).toBeNull();
17
+ expect(readHandoffCart('?cart=nope,short')).toBeNull();
18
+ expect(readHandoffCart('')).toBeNull();
19
+ });
20
+ });
@@ -1,6 +1,16 @@
1
1
  import { isBrowser } from "browser-or-node";
2
2
  import { cartCookieName, readTestCheckoutFlag } from "./test_checkout";
3
3
 
4
+ // id,token from a checkout link. The token is url-safe and has no padding.
5
+ const HANDOFF_CART = /^\d+,[A-Za-z0-9_-]{8,}$/;
6
+
7
+ export function readHandoffCart(search: string): string[] | null {
8
+ const cart = new URLSearchParams(search).get("cart");
9
+ if (!cart || !HANDOFF_CART.test(cart)) return null;
10
+ const comma = cart.indexOf(",");
11
+ return [cart.slice(0, comma), cart.slice(comma + 1)];
12
+ }
13
+
4
14
  export async function getCookie(name: string, defaultValue: any) {
5
15
  if (!isBrowser) {
6
16
  return '';