PageSourceSearch

https://at.diesel.com/on/demandware.static/Sites-DieselDEAT-Site/-…/jscript/applepay-order-tracking.js

js diesel.com collected 2026-09-24 08:40:23 UTC 14,694 bytes, 503 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 mapPaymentAuthorizedResult(response) {
174			if (response.result) {
175				if (response.result.errors) {
176					response.result.errors = response.result.errors.map(function (err) {
177						return new ApplePayError(err.code, err.contactField, err.message);
178					});
179				}
180				return response.result;
181			}
182			
183			// Fall back to legacy result
184			return ApplePaySession.STATUS_SUCCESS;
185		}
186
187		function onpaymentauthorizedHandler (e) {
188			postJson(action.onpaymentauthorized, filterEvent(e)).then(function (response) {
189				session.completePayment(mapPaymentAuthorizedResult(response));
190				processServerResponse(response);
191				setSession(null);
192				submitOrder(response.redirect);
193			}, function (error) {
194				session.completePayment(mapStatus(error.message));
195				processServerResponse(error.response);
196				doRedirect();
197			}).catch(function (err) {
198				console.error(err);
199			});
200		}
201
202		function onpaymentmethodselectedHandler (e) {
203			postJson(action.onpaymentmethodselected, filterEvent(e)).then(function (response) {
204				// update updatedRequest with new server response
205				updatedRequest = Object.assign(updatedRequest, response);
206				session.completePaymentMethodSelection(response.total, response.lineItems);
207				processServerResponse(response);
208			}, function (error) {
209				// No way to communicate error status on this event to Apple Pay
210				session.completePaymentMethodSelection(
211						updatedRequest.total, updatedRequest.lineItems);
212				processServerResponse(error.response);
213			}).catch(function (err) {
214				console.error(err);
215			});
216		}
217
218		function onshippingcontactselectedHandler (e) {
219			postJson(action.onshippingcontactselected, filterEvent(e)).then(function (response) {
220				// update updatedRequest with new server response
221				updatedRequest = Object.assign(updatedRequest, response);
222				session.completeShippingContactSelection(ApplePaySession.STATUS_SUCCESS,
223					response.shippingMethods, response.total, response.lineItems);
224				processServerResponse(response);
225			}, function (error) {
226				session.completeShippingContactSelection(mapStatus(error.message),
227						[], updatedRequest.total, updatedRequest.lineItems);
228				processServerResponse(error.response);
229			}).catch(function (err) {
230				console.error(err);
231			});
232		}
233
234		function onshippingmethodselectedHandler (e) {
235			postJson(action.onshippingmethodselected, filterEvent(e)).then(function (response) {
236				// update updatedRequest with new server response
237				updatedRequest = Object.assign(updatedRequest, response);
238				session.completeShippingMethodSelection(ApplePaySession.STATUS_SUCCESS,
239					response.total, response.lineItems);
240				processServerResponse(response);
241			}, function (error) {
242				session.completeShippingMethodSelection(mapStatus(error.message),
243						updatedRequest.total, updatedRequest.lineItems);
244				processServerResponse(error.response);
245			}).catch(function (err) {
246				console.error(err);
247			});
248		}
249
250		function onvalidatemerchantHandler (e) {
251			postJson(action.onvalidatemerchant, Object.assign(filterEvent(e), {
252				hostname: window.location.hostname
253			})).then(function (response) {
254				session.completeMerchantValidation(response.session);
255			}, function (error) {
256				session.abort();
257			}).catch(function (err) {
258				console.error(err);
259			});
260		}
261
262		function setSession (s) {
263			if (session) {
264				session.oncancel = null;
265				session.onpaymentauthorized = null;
266				session.onpaymentmethodselected = null;
267				session.onshippingcontactselected = null;
268				session.onshippingmethodselected = null;
269				session.onvalidatemerchant = null;
270			}
271
272			session = s;
273
274			if (session) {
275				session.oncancel = oncancelHandler;
276				session.onpaymentauthorized = onpaymentauthorizedHandler;
277				session.onpaymentmethodselected = onpaymentmethodselectedHandler;
278				session.onshippingcontactselected = onshippingcontactselectedHandler;
279				session.onshippingmethodselected = onshippingmethodselectedHandler;
280				session.onvalidatemerchant = onvalidatemerchantHandler;
281			}
282		}
283
284		/**
285		 * Begins the ApplePaySession.
286		 */
287		function createSession () {
288			// fake the total amount to make it a valid request
289			// if the basket is empty.
290			// @TODO some basic addition should be calculated instead
291			// to reflect a more reasonable amount
292			// this relies on the SKU price to be available on render
293			if (parseFloat(request.total.amount) === 0) {
294				request.total.amount = '0.01';
295			}
296			setSession(new ApplePaySession(APPLE_PAY_VERSION, request));
297			session.begin();
298
299			// set updatedRequest to request
300			updatedRequest = Object.assign({}, request);
301		}
302
303		/**
304		 * Prepares an Apple Pay basket, optionally
305		 * containing exclusively the product with the given SKU to buy.
306		 * @param {String} [sku] SKU of product to buy
307		 */
308		function prepareBasket (sku) {
309			postJson(action.prepareBasket, {
310				sku: sku
311			}).then(function (response) {
312				processServerResponse(response);
313			}, function (error) {
314				try {
315					session.abort();
316				} catch (e) {
317					// Swallow an error if Apple Pay throws when aborting session
318					console.error(e);
319				}
320				processServerResponse(error.response);
321				doRedirect();
322			}).catch(function (err) {
323				console.error(err);
324			});
325		}
326
327		function validateInject (element, directive) {
328			if (element.hasAttribute(ATTR_SKU)) {
329				// No minimum total price required to inject for add to cart
330				return !!request;
331			}
332
333			// Inject only if request total price is positive
334			return request && request.total && request.total.amount &&
335				parseFloat(request.total.amount) > 0;
336		}
337
338		function createButton (element, directive) {
339			// Create button element
340			var button = document.createElement('button');
341			button.type = 'button';
342
343			// Compose button class name
344			var className = '';
345			if (directive.css) {
346				className += directive.css;
347			}
348
349			// Copy attributes to button
350			if (directive.copy) {
351				if (element.getAttribute(ATTR_SKU)) {
352					button.setAttribute(ATTR_SKU, element.getAttribute(ATTR_SKU));
353				}
354				if (element.className) {
355					className += ' ' + element.className;
356				}
357				if (element.id) {
358					var id = element.id;
359					element.id = '';
360					button.id = id;
361				}
362			}
363
364			if (className) {
365				button.className = className;
366			}
367
368			// Dynamically style the button
369			if (directive.style) {
370				var styleElement;
371				if (directive.style.ref === 'this') {
372					// Style reference is same element
373					styleElement = element;
374				} else if (directive.style.ref) {
375					// Query style reference element
376					styleElement = document.querySelector(directive.style.ref);
377				}
378
379				if (styleElement && directive.style.attr && 'getComputedStyle' in window) {
380					// Copy declared style attributes to button
381					var computedStyle = window.getComputedStyle(styleElement);
382					var style = '';
383					for (var i = 0; i < directive.style.attr.length; i++) {
384						style += directive.style.attr[i] + ':' + computedStyle.getPropertyValue(directive.style.attr[i]) + ';';
385					}
386					button.style = style;
387				}
388			}
389
390			// Add button click handler
391			button.onclick = function () {
392				// Create Apple Pay basket
393				prepareBasket(button.getAttribute(ATTR_SKU));
394
395				// Create Apple Pay session synchronously
396				createSession();
397
398				// Quit further event handling
399				return false;
400			};
401
402			return button;
403		}
404
405		function process (element, directive) {
406			if (hasClass(element, CLASS_PROCESSED)) {
407				// Element has already been processed
408				return;
409			}
410
411			if (!validateInject(element, directive)) {
412				// Element is not valid for this directive
413				return;
414			}
415
416			// Create button for element
417			var button = createButton(element, directive);
418
419			// Execute action for button
420			switch (directive.action) {
421				case 'after':
422					element.parentNode.appendChild(button, element.nextSibling);
423					break;
424				case 'before':
425					element.parentNode.insertBefore(button, element);
426					break;
427				case 'replace':
428					element.parentNode.insertBefore(button, element);
429					element.parentNode.removeChild(element);
430					break;
431				case 'append':
432					element.appendChild(button);
433					break;
434			}
435
436			// Mark element as processed
437			element.className += ' ' + CLASS_PROCESSED;
438		}
439
440		function processDirectives () {
441			if (!request) {
442				// Quit processing without a valid request
443				return;
444			}
445
446			var directive, elements;
447			for (var i = 0; i < inject.directives.length; i++) {
448				directive = inject.directives[i];
449				elements = document.querySelectorAll(directive.query);
450				if (elements && elements.length > 0) {
451					for (var j = 0; j < elements.length; j++) {
452						process(elements[j], directive);
453					}
454				}
455			}
456
457			if (inject.directives.length) {
458				// Repeat processing in one second
459				setTimeout(processDirectives, 1000);
460			}
461		}
462
463		/**
464		 * Retrieves Apple Pay request info from the server.
465		 */
466		function getRequest () {
467			getJson(action.getRequest).then(function (response) {
468				request = Object.assign({}, response.request);
469				processDirectives();
470				processServerResponse(response);
471			}).catch(function (err) {
472				console.error(err);
473				processServerResponse(err.response);
474			});
475		}
476
477		function getUpdateRequestCookie () {
478			var cookie = '; ' + (document.cookie || '') + '; ';
479			var start = cookie.indexOf(UPDATE_REQUEST_COOKIE_LHS);
480			if (start < 0) {
481				return '';
482			}
483
484			start += UPDATE_REQUEST_COOKIE_LHS.length;
485			return cookie.substring(start, cookie.indexOf('; ', start));
486		}
487
488		function pollCookies () {
489			var value = getUpdateRequestCookie();
490			if (value && value !== lastUpdateRequestCookie) {
491				lastUpdateRequestCookie = value;
492				getRequest();
493			}
494		}
495
496		// Kick off XHR to get initial Apple Pay request
497		getRequest();
498
499		// Poll for cookie to update Apple Pay request
500		lastUpdateRequestCookie = getUpdateRequestCookie();
501		setInterval(pollCookies, 200);
502	})(window.dw.applepay, window.location, window.ApplePaySession);
503}

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.