PageSourceSearch

https://www.roamans.com/on/demandware.static/Sites-oss-Site/-/defa…/internal/jscript/applepay.js

js roamans.com collected 2026-09-25 21:02:10 UTC 14,297 bytes, 489 lines download raw bytes

1/* global fetch, CustomEvent */
2if (window.dw && window.dw.applepay && window.ApplePaySession && window.ApplePaySession.canMakePayments() && typeof fetch === 'function') {
3	(function (config, location, ApplePaySession) {
4		/** Used version of ApplePaySession */
5		var APPLE_PAY_VERSION = 1;
6
7		/** CSS class name indicating an element was processed by a directive */
8		var CLASS_PROCESSED = 'dw-apple-pay-processed';
9
10		/** Name of the SKU element attribute indicating add to basket */
11		var ATTR_SKU = 'sku';
12
13		/** Left hand side of cookie indicating to update the Apple Pay request. */
14		var UPDATE_REQUEST_COOKIE_LHS = '; dwapupreq=';
15
16		/** Map of Demandware status names to Apple Pay status codes */
17		var STATUSES = {
18			Failure: ApplePaySession.STATUS_FAILURE,
19			InvalidBillingPostalAddress: ApplePaySession.STATUS_INVALID_BILLING_POSTAL_ADDRESS,
20			InvalidShippingPostalAddress: ApplePaySession.STATUS_INVALID_SHIPPING_POSTAL_ADDRESS,
21			InvalidShippingContact: ApplePaySession.STATUS_INVALID_SHIPPING_CONTACT,
22			PINRequired: ApplePaySession.STATUS_PIN_REQUIRED,
23			PINIncorrect: ApplePaySession.STATUS_PIN_INCORRECT,
24			PINLockout: ApplePaySession.STATUS_PIN_LOCKOUT
25		};
26
27		if (location.protocol !== 'https:' || !ApplePaySession.supportsVersion(APPLE_PAY_VERSION)) {
28			return;
29		}
30		var action = config.action;
31		var inject = config.inject;
32
33		/** Last value of the cookie indicating to update the Apple Pay request. */
34		var lastUpdateRequestCookie;
35
36		/** URL to which to redirect if the Apple Pay session is aborted. */
37		var redirect;
38
39		/** Latest Apple Pay request data to use to create a session. */
40		var request;
41		
42		/** Request object that gets updated with new data from server responses. */
43		var updatedRequest;
44
45		/** Current Apple Pay session. */
46		var session;
47
48		function hasClass (element, className) {
49			return (' ' + element.className + ' ').replace(/[\t\r\n\f]/g, ' ').indexOf(' ' + className + ' ') > -1;
50		}
51
52		function mapStatus (status) {
53			if (status && STATUSES[status]) {
54				return STATUSES[status];
55			}
56			return ApplePaySession.STATUS_FAILURE;
57		}
58
59		function handleResponse (response) {
60			return response.json()
61				.then(function (json) {
62					if (response.status >= 200 && response.status < 300) {
63						// Return success JSON response
64						return json;
65					}
66
67					// Throw error with response status
68					var err = new Error(json ? json.status : 'Request error');
69					err.response = json;
70					throw err;
71				});
72		}
73
74		function postJson (url, data) {
75			var json = data;
76			if (typeof data === 'object') {
77				json = JSON.stringify(data);
78			} else if (typeof data !== 'string') {
79				throw new Error('Data must be an object or a JSON string.');
80			}
81			return fetch(url, {
82				method: 'POST',
83				credentials: 'include',
84				headers: {
85					'Content-Type': 'application/json',
86					'Accept': 'application/json'
87				},
88				body: json
89			}).then(handleResponse);
90		}
91
92		function getJson (url) {
93			return fetch(url, {
94				credentials: 'include',
95				headers: {
96					'Accept': 'application/json'
97				}
98			}).then(handleResponse);
99		}
100
101		/* Filter out properties that are not from Event prototype,
102		 * and include all others as ownProperty to a new object.
103		 * @param {Event} e
104		 * @return {Object} object with relevant properties as ownProperty
105		 */
106		function filterEvent (e) {
107			var filteredEvent = {};
108			for (var prop in e) {
109				/* global Event */
110				if (!Event.prototype.hasOwnProperty(prop)) {
111					filteredEvent[prop] = e[prop];
112				}
113			}
114			return filteredEvent;
115		}
116
117		/**
118		 * On successful creation of an order, submit it through a form
119		 * @param {String} formAction URL to which to POST form to
120		 */
121		function submitOrder (formAction) {
122			var form = document.createElement('form');
123			form.action = formAction;
124			form.method = 'post';
125
126			document.body.appendChild(form);
127			form.submit();
128		}
129
130		function doRedirect () {
131			if (redirect) {
132				// Redirect to last responded redirect
133				location.href = redirect;
134			}
135		}
136
137		function dispatchEvent (event) {
138			if (!event || !event.name) {
139				return;
140			}
141			document.body.dispatchEvent(new CustomEvent(event.name, {
142				bubbles: true,
143				detail: event.detail
144			}));
145		}
146
147		function processServerResponse (response) {
148			if (!response) {
149				return;
150			}
151			// redirect could be an empty string
152			if (typeof response.redirect !== 'undefined') {
153				// set redirect URL
154				redirect = response.redirect;
155			}
156			dispatchEvent(response.event);
157		}
158
159		function oncancelHandler (e) {
160			setSession(null);
161
162			postJson(action.cancel, {}).then(function (response) {
163				processServerResponse(response);
164				doRedirect();
165			}, function (error) {
166				processServerResponse(error.response);
167				doRedirect();
168			}).catch(function (err) {
169				console.error(err);
170			});
171		}
172
173		function onpaymentauthorizedHandler (e) {
174			postJson(action.onpaymentauthorized, filterEvent(e)).then(function (response) {
175				session.completePayment(ApplePaySession.STATUS_SUCCESS);
176				processServerResponse(response);
177				setSession(null);
178				submitOrder(response.redirect);
179			}, function (error) {
180				session.completePayment(mapStatus(error.message));
181				processServerResponse(error.response);
182				doRedirect();
183			}).catch(function (err) {
184				console.error(err);
185			});
186		}
187
188		function onpaymentmethodselectedHandler (e) {
189			postJson(action.onpaymentmethodselected, filterEvent(e)).then(function (response) {
190				// update updatedRequest with new server response
191				updatedRequest = Object.assign(updatedRequest, response);
192				session.completePaymentMethodSelection(response.total, response.lineItems);
193				processServerResponse(response);
194			}, function (error) {
195				// No way to communicate error status on this event to Apple Pay
196				session.completePaymentMethodSelection(
197						updatedRequest.total, updatedRequest.lineItems);
198				processServerResponse(error.response);
199			}).catch(function (err) {
200				console.error(err);
201			});
202		}
203
204		function onshippingcontactselectedHandler (e) {
205			postJson(action.onshippingcontactselected, filterEvent(e)).then(function (response) {
206				// update updatedRequest with new server response
207				updatedRequest = Object.assign(updatedRequest, response);
208				session.completeShippingContactSelection(ApplePaySession.STATUS_SUCCESS,
209					response.shippingMethods, response.total, response.lineItems);
210				processServerResponse(response);
211			}, function (error) {
212				session.completeShippingContactSelection(mapStatus(error.message),
213						[], updatedRequest.total, updatedRequest.lineItems);
214				processServerResponse(error.response);
215			}).catch(function (err) {
216				console.error(err);
217			});
218		}
219
220		function onshippingmethodselectedHandler (e) {
221			postJson(action.onshippingmethodselected, filterEvent(e)).then(function (response) {
222				// update updatedRequest with new server response
223				updatedRequest = Object.assign(updatedRequest, response);
224				session.completeShippingMethodSelection(ApplePaySession.STATUS_SUCCESS,
225					response.total, response.lineItems);
226				processServerResponse(response);
227			}, function (error) {
228				session.completeShippingMethodSelection(mapStatus(error.message),
229						updatedRequest.total, updatedRequest.lineItems);
230				processServerResponse(error.response);
231			}).catch(function (err) {
232				console.error(err);
233			});
234		}
235
236		function onvalidatemerchantHandler (e) {
237			postJson(action.onvalidatemerchant, Object.assign(filterEvent(e), {
238				hostname: window.location.hostname
239			})).then(function (response) {
240				session.completeMerchantValidation(response.session);
241			}, function (error) {
242				session.abort();
243			}).catch(function (err) {
244				console.error(err);
245			});
246		}
247
248		function setSession (s) {
249			if (session) {
250				session.oncancel = null;
251				session.onpaymentauthorized = null;
252				session.onpaymentmethodselected = null;
253				session.onshippingcontactselected = null;
254				session.onshippingmethodselected = null;
255				session.onvalidatemerchant = null;
256			}
257
258			session = s;
259
260			if (session) {
261				session.oncancel = oncancelHandler;
262				session.onpaymentauthorized = onpaymentauthorizedHandler;
263				session.onpaymentmethodselected = onpaymentmethodselectedHandler;
264				session.onshippingcontactselected = onshippingcontactselectedHandler;
265				session.onshippingmethodselected = onshippingmethodselectedHandler;
266				session.onvalidatemerchant = onvalidatemerchantHandler;
267			}
268		}
269
270		/**
271		 * Begins the ApplePaySession.
272		 */
273		function createSession () {
274			// fake the total amount to make it a valid request
275			// if the basket is empty.
276			// @TODO some basic addition should be calculated instead
277			// to reflect a more reasonable amount
278			// this relies on the SKU price to be available on render
279			if (parseFloat(request.total.amount) === 0) {
280				request.total.amount = '0.01';
281			}
282			setSession(new ApplePaySession(APPLE_PAY_VERSION, request));
283			session.begin();
284
285			// set updatedRequest to request
286			updatedRequest = Object.assign({}, request);
287		}
288
289		/**
290		 * Prepares an Apple Pay basket, optionally
291		 * containing exclusively the product with the given SKU to buy.
292		 * @param {String} [sku] SKU of product to buy
293		 */
294		function prepareBasket (sku) {
295			postJson(action.prepareBasket, {
296				sku: sku
297			}).then(function (response) {
298				processServerResponse(response);
299			}, function (error) {
300				try {
301					session.abort();
302				} catch (e) {
303					// Swallow an error if Apple Pay throws when aborting session
304					console.error(e);
305				}
306				processServerResponse(error.response);
307				doRedirect();
308			}).catch(function (err) {
309				console.error(err);
310			});
311		}
312
313		function validateInject (element, directive) {
314			if (element.hasAttribute(ATTR_SKU)) {
315				// No minimum total price required to inject for add to cart
316				return !!request;
317			}
318
319			// Inject only if request total price is positive
320			return request && request.total && request.total.amount &&
321				parseFloat(request.total.amount) > 0;
322		}
323
324		function createButton (element, directive) {
325			// Create button element
326			var button = document.createElement('button');
327			button.type = 'button';
328
329			// Compose button class name
330			var className = '';
331			if (directive.css) {
332				className += directive.css;
333			}
334
335			// Copy attributes to button
336			if (directive.copy) {
337				if (element.getAttribute(ATTR_SKU)) {
338					button.setAttribute(ATTR_SKU, element.getAttribute(ATTR_SKU));
339				}
340				if (element.className) {
341					className += ' ' + element.className;
342				}
343				if (element.id) {
344					var id = element.id;
345					element.id = '';
346					button.id = id;
347				}
348			}
349
350			if (className) {
351				button.className = className;
352			}
353
354			// Dynamically style the button
355			if (directive.style) {
356				var styleElement;
357				if (directive.style.ref === 'this') {
358					// Style reference is same element
359					styleElement = element;
360				} else if (directive.style.ref) {
361					// Query style reference element
362					styleElement = document.querySelector(directive.style.ref);
363				}
364
365				if (styleElement && directive.style.attr && 'getComputedStyle' in window) {
366					// Copy declared style attributes to button
367					var computedStyle = window.getComputedStyle(styleElement);
368					var style = '';
369					for (var i = 0; i < directive.style.attr.length; i++) {
370						style += directive.style.attr[i] + ':' + computedStyle.getPropertyValue(directive.style.attr[i]) + ';';
371					}
372					button.style = style;
373				}
374			}
375
376			// Add button click handler
377			button.onclick = function () {
378				// Create Apple Pay basket
379				prepareBasket(button.getAttribute(ATTR_SKU));
380
381				// Create Apple Pay session synchronously
382				createSession();
383
384				// Quit further event handling
385				return false;
386			};
387
388			return button;
389		}
390
391		function process (element, directive) {
392			if (hasClass(element, CLASS_PROCESSED)) {
393				// Element has already been processed
394				return;
395			}
396
397			if (!validateInject(element, directive)) {
398				// Element is not valid for this directive
399				return;
400			}
401
402			// Create button for element
403			var button = createButton(element, directive);
404
405			// Execute action for button
406			switch (directive.action) {
407				case 'after':
408					element.parentNode.appendChild(button, element.nextSibling);
409					break;
410				case 'before':
411					element.parentNode.insertBefore(button, element);
412					break;
413				case 'replace':
414					element.parentNode.insertBefore(button, element);
415					element.parentNode.removeChild(element);
416					break;
417				case 'append':
418					element.appendChild(button);
419					break;
420			}
421
422			// Mark element as processed
423			element.className += ' ' + CLASS_PROCESSED;
424		}
425
426		function processDirectives () {
427			if (!request) {
428				// Quit processing without a valid request
429				return;
430			}
431
432			var directive, elements;
433			for (var i = 0; i < inject.directives.length; i++) {
434				directive = inject.directives[i];
435				elements = document.querySelectorAll(directive.query);
436				if (elements && elements.length > 0) {
437					for (var j = 0; j < elements.length; j++) {
438						process(elements[j], directive);
439					}
440				}
441			}
442
443			if (inject.directives.length) {
444				// Repeat processing in one second
445				setTimeout(processDirectives, 1000);
446			}
447		}
448
449		/**
450		 * Retrieves Apple Pay request info from the server.
451		 */
452		function getRequest () {
453			getJson(action.getRequest).then(function (response) {
454				request = Object.assign({}, response.request);
455				processDirectives();
456				processServerResponse(response);
457			}).catch(function (err) {
458				console.error(err);
459				processServerResponse(err.response);
460			});
461		}
462
463		function getUpdateRequestCookie () {
464			var cookie = '; ' + (document.cookie || '') + '; ';
465			var start = cookie.indexOf(UPDATE_REQUEST_COOKIE_LHS);
466			if (start < 0) {
467				return '';
468			}
469
470			start += UPDATE_REQUEST_COOKIE_LHS.length;
471			return cookie.substring(start, cookie.indexOf('; ', start));
472		}
473
474		function pollCookies () {
475			var value = getUpdateRequestCookie();
476			if (value && value !== lastUpdateRequestCookie) {
477				lastUpdateRequestCookie = value;
478				getRequest();
479			}
480		}
481
482		// Kick off XHR to get initial Apple Pay request
483		getRequest();
484
485		// Poll for cookie to update Apple Pay request
486		lastUpdateRequestCookie = getUpdateRequestCookie();
487		setInterval(pollCookies, 200);
488	})(window.dw.applepay, window.location, window.ApplePaySession);
489}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.