@merchi/cart 1.4.4 → 1.4.6
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/esm/CartProvider.js +58 -20
- package/esm/forms/FormShipmentAddressAndNotes.js +24 -3
- package/esm/stripe/LegacyStripeCardForm.js +11 -4
- package/esm/stripe/StripeCardForm.js +122 -16
- package/esm/stripe/actions.js +155 -57
- package/esm/stripe/actions.test.js +120 -0
- package/esm/utilities/cookie.js +15 -12
- package/esm/utilities/index.js +2 -0
- package/esm/utilities/job_source.js +139 -0
- package/esm/utilities/job_source.test.js +75 -0
- package/esm/utilities/shipment.js +22 -0
- package/esm/utilities/shipment.test.js +60 -0
- package/esm/utilities/test_checkout.js +25 -0
- package/esm/utilities/test_checkout.test.js +31 -0
- package/lib/CartProvider.js +58 -20
- package/lib/forms/FormShipmentAddressAndNotes.js +22 -1
- package/lib/stripe/LegacyStripeCardForm.js +10 -3
- package/lib/stripe/StripeCardForm.js +123 -17
- package/lib/stripe/actions.d.ts +2 -0
- package/lib/stripe/actions.js +157 -57
- package/lib/stripe/actions.test.d.ts +1 -0
- package/lib/utilities/cookie.d.ts +3 -3
- package/lib/utilities/cookie.js +15 -12
- package/lib/utilities/index.d.ts +2 -0
- package/lib/utilities/index.js +22 -0
- package/lib/utilities/job_source.d.ts +23 -0
- package/lib/utilities/job_source.js +152 -0
- package/lib/utilities/job_source.test.d.ts +1 -0
- package/lib/utilities/shipment.d.ts +15 -0
- package/lib/utilities/shipment.js +24 -0
- package/lib/utilities/shipment.test.d.ts +1 -0
- package/lib/utilities/test_checkout.d.ts +6 -0
- package/lib/utilities/test_checkout.js +34 -0
- package/lib/utilities/test_checkout.test.d.ts +1 -0
- package/package.json +8 -5
- package/src/CartProvider.tsx +42 -4
- package/src/forms/FormShipmentAddressAndNotes.tsx +34 -2
- package/src/stripe/LegacyStripeCardForm.tsx +9 -3
- package/src/stripe/StripeCardForm.tsx +92 -8
- package/src/stripe/actions.test.ts +50 -0
- package/src/stripe/actions.ts +41 -0
- package/src/utilities/cookie.ts +9 -6
- package/src/utilities/index.ts +2 -0
- package/src/utilities/job_source.test.ts +86 -0
- package/src/utilities/job_source.ts +165 -0
- package/src/utilities/shipment.test.ts +68 -0
- package/src/utilities/shipment.ts +34 -0
- package/src/utilities/test_checkout.test.ts +39 -0
- package/src/utilities/test_checkout.ts +27 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
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; } }
|
|
4
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
5
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
6
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
7
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
8
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
9
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
10
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
|
11
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
12
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
13
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
14
|
+
export var JOB_SOURCE_STORAGE_KEY = 'merchi_job_source';
|
|
15
|
+
var CLICK_ID_PARAMS = ['gclid', 'gbraid', 'wbraid', 'fbclid', 'msclkid'];
|
|
16
|
+
var UTM_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
|
|
17
|
+
function trimTo(value, limit) {
|
|
18
|
+
if (!value) return undefined;
|
|
19
|
+
var text = value.trim().slice(0, limit);
|
|
20
|
+
return text || undefined;
|
|
21
|
+
}
|
|
22
|
+
function readSearchParams() {
|
|
23
|
+
if (typeof window === 'undefined') return new URLSearchParams();
|
|
24
|
+
try {
|
|
25
|
+
return new URLSearchParams(window.location.search);
|
|
26
|
+
} catch (_unused) {
|
|
27
|
+
return new URLSearchParams();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function readStoredSource() {
|
|
31
|
+
if (typeof window === 'undefined') return null;
|
|
32
|
+
try {
|
|
33
|
+
var raw = window.localStorage.getItem(JOB_SOURCE_STORAGE_KEY);
|
|
34
|
+
if (!raw) return null;
|
|
35
|
+
var parsed = JSON.parse(raw);
|
|
36
|
+
if (!parsed || _typeof(parsed) !== 'object') return null;
|
|
37
|
+
return parsed;
|
|
38
|
+
} catch (_unused2) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function writeStoredSource(source) {
|
|
43
|
+
if (typeof window === 'undefined') return;
|
|
44
|
+
try {
|
|
45
|
+
window.localStorage.setItem(JOB_SOURCE_STORAGE_KEY, JSON.stringify(source));
|
|
46
|
+
} catch (_unused3) {
|
|
47
|
+
// Ignore quota / private-mode failures.
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function currentLanding() {
|
|
51
|
+
if (typeof window === 'undefined') return undefined;
|
|
52
|
+
try {
|
|
53
|
+
var path = "".concat(window.location.pathname || '/').concat(window.location.search || '');
|
|
54
|
+
return trimTo(path, 512);
|
|
55
|
+
} catch (_unused4) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function currentReferrer() {
|
|
60
|
+
if (typeof document === 'undefined') return undefined;
|
|
61
|
+
try {
|
|
62
|
+
return trimTo(document.referrer, 512);
|
|
63
|
+
} catch (_unused5) {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function sourceFromLocation() {
|
|
68
|
+
var params = readSearchParams();
|
|
69
|
+
var source = {
|
|
70
|
+
utmSource: trimTo(params.get('utm_source'), 256),
|
|
71
|
+
utmMedium: trimTo(params.get('utm_medium'), 256),
|
|
72
|
+
utmCampaign: trimTo(params.get('utm_campaign'), 256),
|
|
73
|
+
utmContent: trimTo(params.get('utm_content'), 256),
|
|
74
|
+
utmTerm: trimTo(params.get('utm_term'), 256),
|
|
75
|
+
sourceLanding: currentLanding(),
|
|
76
|
+
sourceReferrer: currentReferrer()
|
|
77
|
+
};
|
|
78
|
+
var _iterator = _createForOfIteratorHelper(CLICK_ID_PARAMS),
|
|
79
|
+
_step;
|
|
80
|
+
try {
|
|
81
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
82
|
+
var key = _step.value;
|
|
83
|
+
var value = trimTo(params.get(key), 256);
|
|
84
|
+
if (value) {
|
|
85
|
+
source.sourceClickId = value;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
_iterator.e(err);
|
|
91
|
+
} finally {
|
|
92
|
+
_iterator.f();
|
|
93
|
+
}
|
|
94
|
+
return source;
|
|
95
|
+
}
|
|
96
|
+
export function hasPaidClick(source) {
|
|
97
|
+
return Boolean(source === null || source === void 0 ? void 0 : source.sourceClickId);
|
|
98
|
+
}
|
|
99
|
+
export function hasSourceData(source) {
|
|
100
|
+
if (!source) return false;
|
|
101
|
+
return Boolean(source.utmSource || source.utmMedium || source.utmCampaign || source.utmContent || source.utmTerm || source.sourceClickId || source.sourceLanding || source.sourceReferrer);
|
|
102
|
+
}
|
|
103
|
+
export function captureAndReadJobSource() {
|
|
104
|
+
var incoming = sourceFromLocation();
|
|
105
|
+
var stored = readStoredSource();
|
|
106
|
+
if (hasPaidClick(incoming) || !hasSourceData(stored)) {
|
|
107
|
+
var next = _objectSpread(_objectSpread({}, stored || {}), Object.fromEntries(Object.entries(incoming).filter(function (_ref) {
|
|
108
|
+
var _ref2 = _slicedToArray(_ref, 2),
|
|
109
|
+
value = _ref2[1];
|
|
110
|
+
return Boolean(value);
|
|
111
|
+
})));
|
|
112
|
+
if (hasSourceData(next)) writeStoredSource(next);
|
|
113
|
+
return next;
|
|
114
|
+
}
|
|
115
|
+
return stored || incoming;
|
|
116
|
+
}
|
|
117
|
+
export function jobSourceFieldsForApi(source) {
|
|
118
|
+
var resolved = source || captureAndReadJobSource();
|
|
119
|
+
var out = {};
|
|
120
|
+
Object.keys(resolved).forEach(function (key) {
|
|
121
|
+
var value = resolved[key];
|
|
122
|
+
if (value) out[key] = value;
|
|
123
|
+
});
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
export function cartNeedsSourceUpdate(cart, source) {
|
|
127
|
+
if (!hasSourceData(source)) return false;
|
|
128
|
+
if (!(cart !== null && cart !== void 0 && cart.utmSource) && !(cart !== null && cart !== void 0 && cart.sourceClickId) && !(cart !== null && cart !== void 0 && cart.sourceChannel)) return true;
|
|
129
|
+
return Boolean(source.sourceClickId && !(cart !== null && cart !== void 0 && cart.sourceClickId));
|
|
130
|
+
}
|
|
131
|
+
export var JOB_SOURCE_URL_KEYS = [].concat(UTM_PARAMS, CLICK_ID_PARAMS);
|
|
132
|
+
export function appendJobSourceToFormData(data, source) {
|
|
133
|
+
var fields = jobSourceFieldsForApi(source);
|
|
134
|
+
Object.keys(fields).forEach(function (key) {
|
|
135
|
+
var value = fields[key];
|
|
136
|
+
if (value) data.append(key, value);
|
|
137
|
+
});
|
|
138
|
+
return data;
|
|
139
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { JOB_SOURCE_STORAGE_KEY, captureAndReadJobSource, cartNeedsSourceUpdate, hasPaidClick } from './job_source';
|
|
2
|
+
describe('job source capture', function () {
|
|
3
|
+
var originalLocation = window.location;
|
|
4
|
+
var originalReferrer = document.referrer;
|
|
5
|
+
beforeEach(function () {
|
|
6
|
+
window.localStorage.clear();
|
|
7
|
+
delete window.location;
|
|
8
|
+
window.location = {
|
|
9
|
+
search: '',
|
|
10
|
+
pathname: '/tyvek-wristbands'
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(document, 'referrer', {
|
|
13
|
+
configurable: true,
|
|
14
|
+
value: ''
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
afterEach(function () {
|
|
18
|
+
window.location = originalLocation;
|
|
19
|
+
Object.defineProperty(document, 'referrer', {
|
|
20
|
+
configurable: true,
|
|
21
|
+
value: originalReferrer
|
|
22
|
+
});
|
|
23
|
+
window.localStorage.clear();
|
|
24
|
+
});
|
|
25
|
+
it('captures google ads click ids and landing path', function () {
|
|
26
|
+
window.location = {
|
|
27
|
+
search: '?utm_source=google&utm_medium=cpc&gclid=EAIa&utm_campaign=bands',
|
|
28
|
+
pathname: '/tyvek-wristbands'
|
|
29
|
+
};
|
|
30
|
+
var source = captureAndReadJobSource();
|
|
31
|
+
expect(source.utmSource).toBe('google');
|
|
32
|
+
expect(source.utmMedium).toBe('cpc');
|
|
33
|
+
expect(source.utmCampaign).toBe('bands');
|
|
34
|
+
expect(source.sourceClickId).toBe('EAIa');
|
|
35
|
+
expect(source.sourceLanding).toBe('/tyvek-wristbands?utm_source=google&utm_medium=cpc&gclid=EAIa&utm_campaign=bands');
|
|
36
|
+
expect(hasPaidClick(source)).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('keeps first-touch UTMs unless a paid click arrives later', function () {
|
|
39
|
+
window.location = {
|
|
40
|
+
search: '?utm_source=newsletter&utm_medium=email',
|
|
41
|
+
pathname: '/'
|
|
42
|
+
};
|
|
43
|
+
captureAndReadJobSource();
|
|
44
|
+
window.location = {
|
|
45
|
+
search: '?utm_source=google&utm_medium=cpc',
|
|
46
|
+
pathname: '/product'
|
|
47
|
+
};
|
|
48
|
+
expect(captureAndReadJobSource().utmSource).toBe('newsletter');
|
|
49
|
+
window.location = {
|
|
50
|
+
search: '?gclid=NEWCLICK&utm_source=google',
|
|
51
|
+
pathname: '/product'
|
|
52
|
+
};
|
|
53
|
+
expect(captureAndReadJobSource().sourceClickId).toBe('NEWCLICK');
|
|
54
|
+
expect(window.localStorage.getItem(JOB_SOURCE_STORAGE_KEY)).toContain('NEWCLICK');
|
|
55
|
+
});
|
|
56
|
+
it('knows when an existing cart still needs source fields', function () {
|
|
57
|
+
expect(cartNeedsSourceUpdate({}, {
|
|
58
|
+
utmSource: 'google',
|
|
59
|
+
sourceClickId: 'x'
|
|
60
|
+
})).toBe(true);
|
|
61
|
+
expect(cartNeedsSourceUpdate({
|
|
62
|
+
utmSource: 'google'
|
|
63
|
+
}, {
|
|
64
|
+
utmSource: 'google',
|
|
65
|
+
sourceClickId: 'x'
|
|
66
|
+
})).toBe(true);
|
|
67
|
+
expect(cartNeedsSourceUpdate({
|
|
68
|
+
utmSource: 'google',
|
|
69
|
+
sourceClickId: 'x'
|
|
70
|
+
}, {
|
|
71
|
+
utmSource: 'google',
|
|
72
|
+
sourceClickId: 'x'
|
|
73
|
+
})).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addressHasContent } from './local_storage';
|
|
1
2
|
export var shipmentFormId = '_shipment-address-form-id';
|
|
2
3
|
export function cartItemsNeedShipment(cart) {
|
|
3
4
|
var _cart$cartItems = cart.cartItems,
|
|
@@ -8,4 +9,25 @@ export function cartItemsNeedShipment(cart) {
|
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
return false;
|
|
12
|
+
}
|
|
13
|
+
export function shipmentGroupsHaveQuotes(groups) {
|
|
14
|
+
if (!(groups !== null && groups !== void 0 && groups.length)) return false;
|
|
15
|
+
return groups.some(function (group) {
|
|
16
|
+
return Array.isArray(group === null || group === void 0 ? void 0 : group.quotes) && group.quotes.length > 0;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* When checkout hydrates a saved address (localStorage) the form shows it,
|
|
21
|
+
* but quotes are only fetched after the user edits the field. Returning
|
|
22
|
+
* `apply-saved-address` means the cart still needs that address applied so
|
|
23
|
+
* quotes can be generated; `refresh-quotes` means the cart already has an
|
|
24
|
+
* address but no usable shipment options.
|
|
25
|
+
*/
|
|
26
|
+
export function checkoutShipmentHydrationAction(options) {
|
|
27
|
+
var hasCartAddress = addressHasContent(options.cartAddress);
|
|
28
|
+
var hasSavedAddress = addressHasContent(options.savedAddress);
|
|
29
|
+
var hasQuotes = shipmentGroupsHaveQuotes(options.shipmentGroups);
|
|
30
|
+
if (!hasCartAddress && hasSavedAddress) return 'apply-saved-address';
|
|
31
|
+
if (hasCartAddress && !hasQuotes) return 'refresh-quotes';
|
|
32
|
+
return null;
|
|
11
33
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { checkoutShipmentHydrationAction, shipmentGroupsHaveQuotes } from './shipment';
|
|
2
|
+
var savedAddress = {
|
|
3
|
+
lineOne: '32 Bridge St',
|
|
4
|
+
city: 'Brighton',
|
|
5
|
+
state: 'VIC',
|
|
6
|
+
postcode: '3186',
|
|
7
|
+
country: 'AU'
|
|
8
|
+
};
|
|
9
|
+
describe('shipmentGroupsHaveQuotes', function () {
|
|
10
|
+
it('is false when groups are missing or empty', function () {
|
|
11
|
+
expect(shipmentGroupsHaveQuotes(undefined)).toBe(false);
|
|
12
|
+
expect(shipmentGroupsHaveQuotes([])).toBe(false);
|
|
13
|
+
expect(shipmentGroupsHaveQuotes([{
|
|
14
|
+
quotes: []
|
|
15
|
+
}])).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
it('is true when any group has quotes', function () {
|
|
18
|
+
expect(shipmentGroupsHaveQuotes([{
|
|
19
|
+
quotes: []
|
|
20
|
+
}, {
|
|
21
|
+
quotes: [{
|
|
22
|
+
id: 1
|
|
23
|
+
}]
|
|
24
|
+
}])).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('checkoutShipmentHydrationAction', function () {
|
|
28
|
+
it('applies a localStorage address when the cart has none', function () {
|
|
29
|
+
expect(checkoutShipmentHydrationAction({
|
|
30
|
+
cartAddress: {},
|
|
31
|
+
savedAddress: savedAddress,
|
|
32
|
+
shipmentGroups: []
|
|
33
|
+
})).toBe('apply-saved-address');
|
|
34
|
+
});
|
|
35
|
+
it('refreshes quotes when the cart already has an address but no options', function () {
|
|
36
|
+
expect(checkoutShipmentHydrationAction({
|
|
37
|
+
cartAddress: savedAddress,
|
|
38
|
+
savedAddress: savedAddress,
|
|
39
|
+
shipmentGroups: []
|
|
40
|
+
})).toBe('refresh-quotes');
|
|
41
|
+
});
|
|
42
|
+
it('does nothing when quotes are already available', function () {
|
|
43
|
+
expect(checkoutShipmentHydrationAction({
|
|
44
|
+
cartAddress: savedAddress,
|
|
45
|
+
savedAddress: savedAddress,
|
|
46
|
+
shipmentGroups: [{
|
|
47
|
+
quotes: [{
|
|
48
|
+
id: 9
|
|
49
|
+
}]
|
|
50
|
+
}]
|
|
51
|
+
})).toBe(null);
|
|
52
|
+
});
|
|
53
|
+
it('does nothing when neither cart nor localStorage has an address', function () {
|
|
54
|
+
expect(checkoutShipmentHydrationAction({
|
|
55
|
+
cartAddress: {},
|
|
56
|
+
savedAddress: {},
|
|
57
|
+
shipmentGroups: []
|
|
58
|
+
})).toBe(null);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export var TEST_CHECKOUT_QUERY = 'merchi_test_checkout';
|
|
2
|
+
export var TEST_CHECKOUT_STORAGE_KEY = 'merchi_test_checkout';
|
|
3
|
+
export var TEST_CARD_NUMBER = '4242 4242 4242 4242';
|
|
4
|
+
export function readTestCheckoutFlag() {
|
|
5
|
+
if (typeof window === 'undefined') {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
var params = new URLSearchParams(window.location.search);
|
|
10
|
+
if (params.get(TEST_CHECKOUT_QUERY) === '1') {
|
|
11
|
+
window.localStorage.setItem(TEST_CHECKOUT_STORAGE_KEY, '1');
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
return window.localStorage.getItem(TEST_CHECKOUT_STORAGE_KEY) === '1';
|
|
15
|
+
} catch (_unused) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function persistTestCheckoutFlag() {
|
|
20
|
+
return readTestCheckoutFlag();
|
|
21
|
+
}
|
|
22
|
+
export function cartCookieName(domainId) {
|
|
23
|
+
var isTest = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
24
|
+
return isTest ? "cart-".concat(domainId, "-test") : "cart-".concat(domainId);
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { TEST_CARD_NUMBER, TEST_CHECKOUT_QUERY, TEST_CHECKOUT_STORAGE_KEY, cartCookieName, readTestCheckoutFlag } from './test_checkout';
|
|
2
|
+
describe('test checkout helpers', function () {
|
|
3
|
+
var originalLocation = window.location;
|
|
4
|
+
beforeEach(function () {
|
|
5
|
+
window.localStorage.clear();
|
|
6
|
+
delete window.location;
|
|
7
|
+
window.location = {
|
|
8
|
+
search: ''
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
afterEach(function () {
|
|
12
|
+
window.location = originalLocation;
|
|
13
|
+
window.localStorage.clear();
|
|
14
|
+
});
|
|
15
|
+
it('builds a separate cookie name for test carts', function () {
|
|
16
|
+
expect(cartCookieName(12)).toBe('cart-12');
|
|
17
|
+
expect(cartCookieName(12, true)).toBe('cart-12-test');
|
|
18
|
+
});
|
|
19
|
+
it('persists the query flag and exposes the test card number', function () {
|
|
20
|
+
window.location = {
|
|
21
|
+
search: "?".concat(TEST_CHECKOUT_QUERY, "=1")
|
|
22
|
+
};
|
|
23
|
+
expect(readTestCheckoutFlag()).toBe(true);
|
|
24
|
+
expect(window.localStorage.getItem(TEST_CHECKOUT_STORAGE_KEY)).toBe('1');
|
|
25
|
+
expect(TEST_CARD_NUMBER).toBe('4242 4242 4242 4242');
|
|
26
|
+
});
|
|
27
|
+
it('reads the stored flag when the query is absent', function () {
|
|
28
|
+
window.localStorage.setItem(TEST_CHECKOUT_STORAGE_KEY, '1');
|
|
29
|
+
expect(readTestCheckoutFlag()).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
});
|
package/lib/CartProvider.js
CHANGED
|
@@ -18,6 +18,8 @@ var _job = require("./utilities/job");
|
|
|
18
18
|
var _product = require("./utilities/product");
|
|
19
19
|
var _local_storage = require("./utilities/local_storage");
|
|
20
20
|
var _user = require("./utilities/user");
|
|
21
|
+
var _test_checkout = require("./utilities/test_checkout");
|
|
22
|
+
var _job_source = require("./utilities/job_source");
|
|
21
23
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
22
24
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, "default": e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t9 in e) "default" !== _t9 && {}.hasOwnProperty.call(e, _t9) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t9)) && (i.get || i.set) ? o(f, _t9, i) : f[_t9] = e[_t9]); return f; })(e, t); }
|
|
23
25
|
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
@@ -503,27 +505,36 @@ var CartProvider = function CartProvider(_ref) {
|
|
|
503
505
|
}
|
|
504
506
|
function _createCartAndCookie() {
|
|
505
507
|
_createCartAndCookie = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(callback) {
|
|
506
|
-
var _cart, cartJson, _t3;
|
|
508
|
+
var isTest, jobSource, _cart, query, created, cartJson, _t3;
|
|
507
509
|
return _regenerator().w(function (_context2) {
|
|
508
510
|
while (1) switch (_context2.p = _context2.n) {
|
|
509
511
|
case 0:
|
|
510
512
|
setFetchingCart(true);
|
|
511
513
|
_context2.p = 1;
|
|
514
|
+
isTest = (0, _test_checkout.readTestCheckoutFlag)();
|
|
515
|
+
jobSource = (0, _job_source.jobSourceFieldsForApi)((0, _job_source.captureAndReadJobSource)());
|
|
512
516
|
_cart = (0, _cart3.makeCart)({
|
|
513
517
|
domain: {
|
|
514
518
|
id: domainId
|
|
515
|
-
}
|
|
519
|
+
},
|
|
520
|
+
isTest: isTest
|
|
516
521
|
}, true);
|
|
522
|
+
query = [['embed', JSON.stringify(_helpers.cartEmbed)], ['skip_rights', 'y']];
|
|
523
|
+
if (isTest) query.push(['is_test', 'true']);
|
|
517
524
|
_context2.n = 2;
|
|
518
|
-
return
|
|
519
|
-
|
|
525
|
+
return merchi.authenticatedFetch('/carts/', {
|
|
526
|
+
method: 'POST',
|
|
527
|
+
body: (0, _job_source.appendJobSourceToFormData)(_cart.toFormData(), jobSource),
|
|
528
|
+
query: query
|
|
520
529
|
});
|
|
521
530
|
case 2:
|
|
531
|
+
created = _context2.v;
|
|
532
|
+
_cart.fromJson(created.cart);
|
|
522
533
|
_context2.n = 3;
|
|
523
534
|
return _cart.toJson();
|
|
524
535
|
case 3:
|
|
525
536
|
cartJson = _context2.v;
|
|
526
|
-
if (domainId) (0, _cookie.setCartCookie)(Number(domainId), cartJson, undefined);
|
|
537
|
+
if (domainId) (0, _cookie.setCartCookie)(Number(domainId), cartJson, undefined, isTest);
|
|
527
538
|
_context2.n = 4;
|
|
528
539
|
return attachSavedClientIfNeeded(cartJson);
|
|
529
540
|
case 4:
|
|
@@ -552,7 +563,7 @@ var CartProvider = function CartProvider(_ref) {
|
|
|
552
563
|
}
|
|
553
564
|
function _getCart() {
|
|
554
565
|
_getCart = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(cartIdAndToken) {
|
|
555
|
-
var id, _cart2, cartJson, _t4;
|
|
566
|
+
var id, _cart2, cartJson, isTest, jobSource, saved, _t4;
|
|
556
567
|
return _regenerator().w(function (_context3) {
|
|
557
568
|
while (1) switch (_context3.p = _context3.n) {
|
|
558
569
|
case 0:
|
|
@@ -570,36 +581,62 @@ var CartProvider = function CartProvider(_ref) {
|
|
|
570
581
|
return _cart2.toJson();
|
|
571
582
|
case 3:
|
|
572
583
|
cartJson = _context3.v;
|
|
573
|
-
|
|
584
|
+
isTest = (0, _test_checkout.readTestCheckoutFlag)();
|
|
585
|
+
if (!(Boolean(cartJson.isTest) !== isTest)) {
|
|
574
586
|
_context3.n = 5;
|
|
575
587
|
break;
|
|
576
588
|
}
|
|
577
589
|
_context3.n = 4;
|
|
578
|
-
return
|
|
590
|
+
return createCartAndCookie();
|
|
579
591
|
case 4:
|
|
592
|
+
return _context3.a(2);
|
|
593
|
+
case 5:
|
|
594
|
+
jobSource = (0, _job_source.jobSourceFieldsForApi)((0, _job_source.captureAndReadJobSource)());
|
|
595
|
+
if (!(0, _job_source.cartNeedsSourceUpdate)(cartJson, jobSource)) {
|
|
596
|
+
_context3.n = 7;
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
_context3.n = 6;
|
|
600
|
+
return merchi.authenticatedFetch("/carts/".concat(_cart2.id, "/"), {
|
|
601
|
+
method: 'PATCH',
|
|
602
|
+
body: (0, _job_source.appendJobSourceToFormData)(_cart2.toFormData(), jobSource),
|
|
603
|
+
query: [['embed', JSON.stringify(_helpers.cartEmbed)], ['skip_rights', 'y']]
|
|
604
|
+
});
|
|
605
|
+
case 6:
|
|
606
|
+
saved = _context3.v;
|
|
607
|
+
_cart2.fromJson(saved.cart);
|
|
608
|
+
cartJson = _cart2.toJson();
|
|
609
|
+
case 7:
|
|
610
|
+
if (!((0, _cart3.stripeIsValidAndActive)(cartJson) || isTest)) {
|
|
611
|
+
_context3.n = 9;
|
|
612
|
+
break;
|
|
613
|
+
}
|
|
614
|
+
_context3.n = 8;
|
|
615
|
+
return attachSavedClientIfNeeded(cartJson);
|
|
616
|
+
case 8:
|
|
580
617
|
cartJson = _context3.v;
|
|
581
618
|
setCart(_objectSpread({}, cartJson));
|
|
582
|
-
_context3.n =
|
|
619
|
+
_context3.n = 10;
|
|
583
620
|
break;
|
|
584
|
-
case
|
|
621
|
+
case 9:
|
|
585
622
|
console.error("MErhci cart error: Stripe payment " + "options have not been correctly set up. " + "Check the company profile payment options tab " + "to set and edit stripe payment options.");
|
|
586
623
|
setCartSettingsInvalid(true);
|
|
587
|
-
case
|
|
588
|
-
_context3.n =
|
|
624
|
+
case 10:
|
|
625
|
+
_context3.n = 12;
|
|
589
626
|
break;
|
|
590
|
-
case
|
|
591
|
-
_context3.p =
|
|
627
|
+
case 11:
|
|
628
|
+
_context3.p = 11;
|
|
592
629
|
_t4 = _context3.v;
|
|
593
|
-
_context3.n =
|
|
630
|
+
_context3.n = 12;
|
|
594
631
|
return createCartAndCookie();
|
|
595
|
-
case
|
|
596
|
-
_context3.p =
|
|
632
|
+
case 12:
|
|
633
|
+
_context3.p = 12;
|
|
597
634
|
setFetchingCart(false);
|
|
598
|
-
return _context3.f(
|
|
599
|
-
case
|
|
635
|
+
return _context3.f(12);
|
|
636
|
+
case 13:
|
|
600
637
|
return _context3.a(2);
|
|
601
638
|
}
|
|
602
|
-
}, _callee3, null, [[1,
|
|
639
|
+
}, _callee3, null, [[1, 11, 12, 13]]);
|
|
603
640
|
}));
|
|
604
641
|
return _getCart.apply(this, arguments);
|
|
605
642
|
}
|
|
@@ -962,6 +999,7 @@ var CartProvider = function CartProvider(_ref) {
|
|
|
962
999
|
window.createCartAndCookie = createCartAndCookie;
|
|
963
1000
|
}
|
|
964
1001
|
(0, _react.useEffect)(function () {
|
|
1002
|
+
(0, _test_checkout.persistTestCheckoutFlag)();
|
|
965
1003
|
if (domainId) actionGetMerchiCart();
|
|
966
1004
|
if (domainId && includeTheme) actionFetchTheme();
|
|
967
1005
|
}, [domainId]);
|
|
@@ -129,7 +129,9 @@ function ActiveFormShipmentAddressAndNotes() {
|
|
|
129
129
|
cart = _useCartContext2.cart,
|
|
130
130
|
setCart = _useCartContext2.setCart,
|
|
131
131
|
setLoadingTotals = _useCartContext2.setLoadingTotals,
|
|
132
|
-
setActiveTabAndEditDisabled = _useCartContext2.setActiveTabAndEditDisabled
|
|
132
|
+
setActiveTabAndEditDisabled = _useCartContext2.setActiveTabAndEditDisabled,
|
|
133
|
+
updateCartShipmentAddress = _useCartContext2.updateCartShipmentAddress,
|
|
134
|
+
getCartShipmentOptions = _useCartContext2.getCartShipmentOptions;
|
|
133
135
|
var saved = (0, _local_storage.getSavedCheckoutAddress)(domainId);
|
|
134
136
|
var cartAddress = cart === null || cart === void 0 ? void 0 : cart.receiverAddress;
|
|
135
137
|
var receiverAddress = (0, _local_storage.addressHasContent)(cartAddress) ? cartAddress : saved.address || {};
|
|
@@ -138,6 +140,25 @@ function ActiveFormShipmentAddressAndNotes() {
|
|
|
138
140
|
_useState2 = _slicedToArray(_useState, 2),
|
|
139
141
|
addressFieldsOpen = _useState2[0],
|
|
140
142
|
setAddressFieldsOpen = _useState2[1];
|
|
143
|
+
var hydratedShipmentOptionsRef = (0, _react.useRef)(false);
|
|
144
|
+
(0, _react.useEffect)(function () {
|
|
145
|
+
if (hydratedShipmentOptionsRef.current || !(cart !== null && cart !== void 0 && cart.id)) return;
|
|
146
|
+
var draft = (0, _local_storage.getSavedCheckoutAddress)(domainId);
|
|
147
|
+
var action = (0, _shipment.checkoutShipmentHydrationAction)({
|
|
148
|
+
cartAddress: cart === null || cart === void 0 ? void 0 : cart.receiverAddress,
|
|
149
|
+
savedAddress: draft.address,
|
|
150
|
+
shipmentGroups: cart === null || cart === void 0 ? void 0 : cart.shipmentGroups
|
|
151
|
+
});
|
|
152
|
+
if (!action) return;
|
|
153
|
+
hydratedShipmentOptionsRef.current = true;
|
|
154
|
+
if (action === "apply-saved-address" && draft.address) {
|
|
155
|
+
updateCartShipmentAddress(draft.address);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (action === "refresh-quotes") {
|
|
159
|
+
getCartShipmentOptions === null || getCartShipmentOptions === void 0 || getCartShipmentOptions();
|
|
160
|
+
}
|
|
161
|
+
}, [cart === null || cart === void 0 ? void 0 : cart.id, cart === null || cart === void 0 ? void 0 : cart.receiverAddress, cart === null || cart === void 0 ? void 0 : cart.shipmentGroups, domainId, getCartShipmentOptions, updateCartShipmentAddress]);
|
|
141
162
|
function saveCartShipmentAddressAndGoToNextTab(_x) {
|
|
142
163
|
return _saveCartShipmentAddressAndGoToNextTab.apply(this, arguments);
|
|
143
164
|
}
|
|
@@ -56,6 +56,7 @@ function StripeCardForm() {
|
|
|
56
56
|
error = _useState2[0],
|
|
57
57
|
setError = _useState2[1];
|
|
58
58
|
var company = domain.company;
|
|
59
|
+
var isTestCheckout = Boolean(cart.isTest);
|
|
59
60
|
var hasCompanyPubKey = Boolean(company.isStripeValid && (0, _utils.companyStripePubKeyOrTestPubKey)(company));
|
|
60
61
|
var companyPubKey = hasCompanyPubKey ? (0, _utils.companyStripePubKeyOrTestPubKey)(company) : '';
|
|
61
62
|
var canUseConnect = !!company.stripeAccountId;
|
|
@@ -142,15 +143,21 @@ function StripeCardForm() {
|
|
|
142
143
|
}));
|
|
143
144
|
return _doStripePaymentRequestForButton.apply(this, arguments);
|
|
144
145
|
}
|
|
145
|
-
var _useState7 = (0, _react.useState)(companyPubKey),
|
|
146
|
+
var _useState7 = (0, _react.useState)(isTestCheckout ? '' : companyPubKey),
|
|
146
147
|
_useState8 = _slicedToArray(_useState7, 2),
|
|
147
148
|
stripePublicKey = _useState8[0],
|
|
148
149
|
setStripePublicKey = _useState8[1];
|
|
149
150
|
(0, _react.useEffect)(function () {
|
|
151
|
+
if (isTestCheckout) {
|
|
152
|
+
(0, _actions.fetchTestPublishableKey)(url).then(setStripePublicKey)["catch"](function () {
|
|
153
|
+
return setStripePublicKey('');
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
150
157
|
if (!stripePublicKey && canUseConnect) (0, _actions.stripeInitPromise)(url).then(setStripePublicKey);
|
|
151
|
-
}, [stripePublicKey, canUseConnect]);
|
|
158
|
+
}, [stripePublicKey, canUseConnect, isTestCheckout, url]);
|
|
152
159
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
153
|
-
children: [company.isTesting && badgeTestMode, /*#__PURE__*/(0, _jsxRuntime.jsx)(_StripeCardFields["default"], {
|
|
160
|
+
children: [!isTestCheckout && company.isTesting && badgeTestMode, /*#__PURE__*/(0, _jsxRuntime.jsx)(_StripeCardFields["default"], {
|
|
154
161
|
doStripePayment: doStripePayment,
|
|
155
162
|
doStripePaymentRequestForButton: doStripePaymentRequestForButton,
|
|
156
163
|
cart: cart,
|