PageSourceSearch

https://volumeonetickets.org/js/tickets_page_stripe.1788259773.js

js volumeonetickets.org collected 2026-10-03 19:38:58 UTC 19,410 bytes, 583 lines download raw bytes

1//fallback if translated strings are not in memory
2if (typeof textError == 'undefined') {
3	var textError = 'Error ';
4	var textTryAgain = ' Please try again.';
5	var processingPayment = 'Processing your payment...';
6	var paymentDeclined1 = 'Payment Declined';
7	var paymentDeclined2 = 'You must approve the payment to complete the order. Click the button to authenticate the payment.';
8}
9
10// used currently only for stripe iDEAL payments. Will remain blank, unless the
11// pop up window is generated for the iDEAL payment confirmation
12var newWindow;
13var updateControl = 0;
14var paymentPlanType;
15
16// True while a caller is managing the PaymentIntent itself, so the
17// recalc-driven refresh in orders.js stays out of the way. The timer
18// self-clears the flag in case the consumer never sees the recalc.
19let suppressPaymentIntentRefresh = false;
20let suppressPaymentIntentRefreshTimer = null;
21const SUPPRESS_PAYMENT_INTENT_REFRESH_TIMEOUT_MS = 5000;
22
23function armSuppressPaymentIntentRefresh() {
24	suppressPaymentIntentRefresh = true;
25	if (suppressPaymentIntentRefreshTimer) {
26		clearTimeout(suppressPaymentIntentRefreshTimer);
27	}
28	suppressPaymentIntentRefreshTimer = setTimeout(() => {
29		suppressPaymentIntentRefresh = false;
30		suppressPaymentIntentRefreshTimer = null;
31	}, SUPPRESS_PAYMENT_INTENT_REFRESH_TIMEOUT_MS);
32}
33
34function clearSuppressPaymentIntentRefresh() {
35	suppressPaymentIntentRefresh = false;
36	if (suppressPaymentIntentRefreshTimer) {
37		clearTimeout(suppressPaymentIntentRefreshTimer);
38		suppressPaymentIntentRefreshTimer = null;
39	}
40}
41
42
43/**
44 * @returns  the `clientSecret` from the new payment intent that was created
45 */
46function stripeCreatePaymentIntent() {
47	// if already has a payment intent, see if it can be canceled.
48	if($("#orderForm #OrderPaymentIntentSecret").val()){
49		// Remove old fields synchronously before any async calls, so the
50		// cancel callback can't accidentally remove the new fields created below.
51		let oldId = $("#orderForm #OrderPaymentIntentId").val();
52		let oldSecret = $("#orderForm #OrderPaymentIntentSecret").val();
53		$("#orderForm #OrderPaymentIntentId").remove();
54		$("#orderForm #OrderPaymentIntentSecret").remove();
55		cancelPaymentIntent(oldId, oldSecret);
56	}
57	return new Promise((resolve) => {
58		var clientSecret = [];
59		$.ajax({
60			type: "POST",
61			url: buildURL("/orders/ajax_create_payment_intent/"),
62			data: $("#orderForm").serialize(),
63			dataType: "json",
64			success: function (data) {
65				paymentPlanType = data.paymentPlanType;
66				switch (data.status) {
67					case "success":
68						//create fields for payment intent
69						var $form = $("#orderForm");
70						$form.append($('<input type="hidden" name="data[Order][payment_intent_id]" id="OrderPaymentIntentId">').val(data.id));
71						$form.append($('<input type="hidden" name="data[Order][payment_intent_secret]" id="OrderPaymentIntentSecret">').val(data.client_secret));
72						clientSecret = data.client_secret;
73						break;
74					case "fail":
75					case "error":
76						break;
77				}
78			},
79			complete: function () {
80				resolve(clientSecret);
81			},
82		});
83	});
84}
85
86// will check if the payment intent that was created can be canceled. Can only
87// cancel with specific status defined in the stripe controller
88// When called with oldId/oldSecret, the hidden fields have already been removed
89// by the caller to prevent a race condition with the new PI fields.
90function cancelPaymentIntent(oldId, oldSecret){
91	let id = oldId || $("#orderForm #OrderPaymentIntentId").val();
92	let secret = oldSecret || $("#orderForm #OrderPaymentIntentSecret").val();
93	if(secret && secret.length){
94		let formData = $("#orderForm").serialize();
95		if (id) {
96			formData += "&data[Order][payment_intent_id]=" + encodeURIComponent(id);
97		}
98		formData += "&data[Order][payment_intent_secret]=" + encodeURIComponent(secret);
99		$.ajax({
100			type: "POST",
101			url: buildURL("/orders/ajax_cancel_payment_intent"),
102			data: formData,
103			dataType: "json",
104			error: function(xhr, status, err) {
105				// Cancel is best-effort cleanup; log for debugging but don't interrupt buyer flow.
106				console.error('cancelPaymentIntent failed:', status, err);
107			},
108		});
109	}
110}
111
112/**
113 * Stripe Elements Implementation below
114 */
115var stripe = Stripe(stripePublicKey);
116let elements;
117var clientSecret;
118
119/**
120 * Fetches a payment intent and captures the client secret
121 */
122async function initialize() {
123	clientSecret = await stripeCreatePaymentIntent();
124
125	var layout = {
126		type: 'accordion',
127		defaultCollapsed: false,
128		radios: true,
129		spacedAccordionItems: false
130	};
131	var theme = 'stripe';
132	var vars = {
133		fontFamily: 'Inter, Open Sans, sans-serif',
134		colorTextPlaceholder: '#ccc'
135	};
136
137	// check if in admin - remove wallet options for admin
138	if($('#OrderPurchaseLocation').val() === 'back-end'){
139		appleWalletPayments = 'never';
140		googleWalletPayments = 'never';
141	} else {
142		// get the text color for the page and use that on the payment element
143		// also make the background transparent so that it matches the page
144		var eventThemeColor = $('body').data('theme-color');
145		if (eventThemeColor == 'dark') {
146			theme = 'night';
147			vars.colorPrimary = '#767994';
148		}
149		var eventTheme = $('body').attr('class');
150		if (eventTheme == 'modern-v2') {
151			layout = {
152				type: 'tabs'
153			};
154		}
155	}
156
157	const appearance = {
158		theme: theme,
159		labels: 'above',
160		variables: vars,
161	};
162
163	const loader = 'always';
164
165	elements = stripe.elements({ clientSecret, appearance, loader });
166	const paymentElement = elements.create("payment", {
167		wallets: {
168			applePay: appleWalletPayments,
169			googlePay: googleWalletPayments,
170		},
171		layout: layout
172	});
173
174	// Mount paymentElement if element exists on the page (not waitlist)
175	var paymentElementExists = document.getElementById("payment-element");
176	if (paymentElementExists) {
177		paymentElement.mount("#payment-element");
178	}
179
180	paymentElement.on('change', function(event) {
181		if (event.complete) {
182			// element data seems to be correct complete - enable payment button
183			enableSubmit(origSubmitButtonText);
184		} else {
185			// element data does not seem to be complete - disables the payment button
186			disableSubmit(origSubmitButtonText);
187		}
188
189		// save for other functions to use to determine if it is filled out.
190		paymentElementLastStatus = event;
191	});
192
193	// checks if it is on the secondary payment complete page
194	if ($('#submit-new-payment').length) {
195		$('#submit-new-payment').slideDown('slow');
196	}
197
198	// if message area exists, then show the payment plan messaging
199	if ($('.payment-plan-message').length) {
200		// messaging
201		showPaymentPlanMessage();
202	}
203
204}
205
206async function unmountIntent(){
207	// cancel the intent and remove its hidden fields
208	cancelPaymentIntent();
209	$("#orderForm #OrderPaymentIntentId").remove();
210	$("#orderForm #OrderPaymentIntentSecret").remove();
211
212	// clear payment intent html
213	$("#payment-element").html('');
214}
215
216// if it already has a payment intent but the total is changed or other things have impacted the calculate order method
217// we need to update the total on the payment intent instead of making a new one.
218async function updatePaymentIntent(){
219	$.ajax({
220		type: "POST",
221		url: buildURL("/orders/ajax_update_payment_intent"),
222		data: $("#orderForm").serialize(),
223		dataType: "json",
224		success: function (data) {
225			// this little chunk is needed to update the payment element
226			// that the payment intent has been updated so it can provide the correct information.
227			elements.fetchUpdates();
228			// if message area exists, then show the payment plan messaging
229			if ($('.payment-plan-message').length) {
230				// messaging
231				showPaymentPlanMessage();
232			}
233		}
234	});
235}
236
237/**
238 * Displays a payment plan prompt under the order summary with the payment breakdown
239 */
240function showPaymentPlanMessage() {
241	if(paymentPlanType == 'none'){
242		// no need to add messaging but may want to add something in the future
243	} else {
244		// Get the text color for the page and use that on the payment element
245		var theme = 'stripe';
246		var colorText = '#000000';
247		var eventTheme = $('body').data('theme-color');
248
249		if (eventTheme == 'dark') {
250			theme = 'night';
251			colorText = '#ffffff';
252		}
253
254		const appearance = {
255			theme: theme,
256			variables: {
257				fontFamily: 'Inter, Open Sans, sans-serif, system-ui',
258				colorText: colorText,
259			},
260			rules: {
261				'.PaymentMethodMessaging': {
262					textAlign: 'center',
263				}
264			}
265		};
266
267		const elements = stripe.elements({appearance});
268		let proposedTotal = Math.round($('#OrderProposedTotal').val() * 100);
269		const options = {
270			amount: proposedTotal,
271			currency: currency,
272			paymentMethodTypes: [paymentPlanType],
273			countryCode: countryCode,
274		};
275
276		const paymentPlanMessageElement = elements.create('paymentMethodMessaging', options);
277		paymentPlanMessageElement.mount('#payment-plan-message');
278
279		const paymentPlanMessageElementMobile = elements.create('paymentMethodMessaging', options);
280		paymentPlanMessageElementMobile.mount('#payment-plan-message-mobile');
281
282		$('#payment-plan-message').slideDown('fast');
283		$('#payment-plan-message-mobile').slideDown('fast');
284
285	}
286}
286
287
288
289/**
290 * handles the start of the payment process and processing of the funds
291 */
292 async function handleSubmit() {
293	// Update a Payment Element after creation
294	const { error } = await stripe.confirmPayment({ // initiate and process the payment
295		elements,
296		clientSecret,
297		confirmParams: {
298			// Make sure to change this to your payment completion page
299			return_url: encodeURI("https://" + document.location.host + buildURL("/e/" + orderData.event_slug + "/payment/" + orderData.order_id + "/" + orderData.lang)),
300		},
301		redirect: "if_required",
302	});
303	let data = new Array();
304	if (error) {
305        switch (error.type) {
306			case "api_error": // request error
307			case "api_connection_error": // issue during submission, hit back in browser, etc
308				stripe.retrievePaymentIntent(clientSecret).then(function (result) {
309					if (result.paymentIntent && result.paymentIntent.status && result.paymentIntent.status === 'succeeded') {
310						handlePaymentIntentSucceeded(result, orderData.order_id);
311					} else {
312						data.reason = "There was a problem processing your payment and your cart has expired. Please refresh the page to restart your order. [tpsjs"+ getLineNumber() + "]";
313						data.code = "tpsjs" + getLineNumber();
314						data.clearPrefill = true;
315						data.refresh = true;
316						submittingAlert.close();
317						alertAndClose(data);
318					}
319				});
320				break;
321			case "invalid_request_error": // payment processing failed.
322				if(error.payment_intent.status == 'canceled'){
323					data.reason = "There was a problem processing your payment and your cart has expired. Please refresh the page to restart your order. [tpsjs"+ getLineNumber() + "]";
324					data.code = "tpsjs" + getLineNumber();
325					data.clearPrefill = true;
326					data.refresh = true;
327					submittingAlert.close();
328					alertAndClose(data);
329					break;
330				}
331			case "card_error": // declined for funds or card status
332			case "validation_error": // for things like invalid CC number
333			default:
334				submittingAlert.close();
335				data.reason = error.message;
336				data.refresh = false;
337				alertAndClose(data);
338				$('#payment-response').text(error.message).addClass('error'); //Sets in-line payment response banner
339				ignoreLeaveAttempts();
340				orderSubmissionInProgress = false;
341				enableSubmit(origSubmitButtonText);
342				stripe.retrievePaymentIntent(clientSecret).then(function (result) {
343					if (result.paymentIntent.status != 'succeeded') {
344						handlePaymentIntentFailure(orderData.order_id, '_confirm_payment_' + error.type + '_' + error.code + '_' + getLineNumber());
345					}
346				});
347				break;
348		}
349	} else {
350		// get the current status of the intent
351		stripe.retrievePaymentIntent(clientSecret).then(function (result) {
352			if (result.paymentIntent.status == 'succeeded') {
353				handlePaymentIntentSucceeded(result, orderData.order_id);
354			}
355		});
356	}
357}
358
359/**
360 *
361 * @param {*} clientSecret
362 * @param {*} orderId
363 */
364function completePaymentIntent(clientSecret, orderId) {
365	const maxRetry = 5;
366	let retryCount = 0;
367
368	const executePaymentIntent = async () => {
369		try {
370			const result = await stripe.retrievePaymentIntent(clientSecret);
371			switch (result.paymentIntent.status) {
372				case "processing":
373					// TODO: handle checking again or placing this order is a pool to check later.
374					break;
375				case "succeeded":
376					// payment was successful
377					handlePaymentIntentSucceeded(result, orderId);
378					break;
379				case "amount_capturable_updated":
380					// TODO: handle rerunning the capture process for this charge
381					break;
382				case "requires_source":
383				case "canceled":
384				case "payment_failed":
385				default:
386
387					// payment was not successful
388					handlePaymentIntentFailure(orderId, '_completePaymentIntent_' + result.paymentIntent.status + '_' + getLineNumber());
389					const data = {
390						redirect: true,
391						redirect_url: buildURL("/e/" + eventSlug + "/tickets/"),
392						code: 'tpsjs' + getLineNumber(),
393						clearPrefill: true,
394						reason: 'There was a problem processing your payment and your cart has expired. Please restart your order. [tpsjs' + getLineNumber() + ']'
395					};
396					alertAndClose(data); // Show an alert and close the window
397					break;
398			}
399		} catch (error) {
400			// 400	Bad Request	The request was unacceptable, often due to missing a required parameter.
401			// 401	Unauthorized	No valid API key provided.
402			// 403	Forbidden	The API key doesn’t have permissions to perform the request.
403
404			// 402	Request Failed	The parameters were valid but the request failed.
405			// 404	Not Found	The requested resource doesn’t exist.
406			// 409	Conflict	The request conflicts with another request (perhaps due to using the same idempotent key).
407			// 429	Too Many Requests	Too many requests hit the API too quickly. We recommend an exponential backoff of your requests.
408			// 500, 502, 503, 504	Server Errors	Something went wrong on Stripe’s end.
409			const tryAgainStatusCodes = [402, 404, 409, 429, 500, 502, 503];
410			const endItStatusCodes = [400, 401, 403];
411			if (tryAgainStatusCodes.includes(error.statusCode) && retryCount < maxRetry) {
412				retryCount++;
413				setTimeout(executePaymentIntent, retryCount * 100);
414			} else if (endItStatusCodes.includes(error.statusCode) || retryCount >= maxRetry) {
415				handlePaymentIntentFailure(orderId, 'completePaymentIntent_catch_error_' + getLineNumber());
416				const data = {
417						redirect: true,
418						redirect_url: buildURL("/e/" + eventSlug + "/tickets/"),
419						code: 'tpsjs' + getLineNumber(),
420						clearPrefill: true,
421						reason: 'There was a problem processing your payment and your cart has expired. Please restart your order. [tpsjs' + getLineNumber() + ']'
422					};
423				alertAndClose(data);
424			}
425		}
426	};
427
428	executePaymentIntent();
429}
430
431/**
432 *
433 * @param {*} paymentInformation
434 * @param {*} orderId
435 */
436function handlePaymentIntentSucceeded(paymentInformation, orderId) {
437	// Gather all order form data
438	var orderFormData = $('#orderForm').serializeArray();
439
440	// Convert the order form data into an object
441	var orderDataObj = {};
442	$.each(orderFormData, function (index, element) {
443		orderDataObj[element.name] = element.value;
444	});
445
446	// Merge the payment information with the order data object
447	var dataToSend = $.extend({}, paymentInformation, orderDataObj);
448
449	$.ajax({
450		type: "POST",
451		url: buildURL("/orders/ajax_save_completed_payment_intent_information/" + orderId),
452		data: dataToSend,
453		dataType: "json",
454		success: function (data) {
455			if (data.status == 'fail') {
456				data.code = "tpsjs" + getLineNumber();
457				data.clearPrefill = true;
458				data.refresh = true;
459				submittingAlert.close();
460				alertAndClose(data);
461				return;
462			} 
463			// get redirect information from the initial order submission.
464			if (typeof orderData !== 'undefined') {
465				data.redirect = orderData.redirect;
466			}
467			ignoreLeaveAttempts();
468			handlePaymentRedirect(data);
469		}
470	});
471}
472
473/**
474 * Records a failed payment on the order and resets the checkout form so the buyer can try again.
475 *
476 * @param {string} orderId
477 * @param {string} lineInformation Tag identifying where the failure was detected, saved with the order
478 */
479function handlePaymentIntentFailure(orderId, lineInformation) {
480	orderSubmissionInProgress = false;
481	clearOutSeatedFields();
482	$.ajax({
483		type: "POST",
484		url: buildURL("/orders/ajax_save_payment_failed_information/" + orderId + "/tpsjs" + lineInformation),
485		data: $("#orderForm").serialize(),
486		dataType: "json",
487		success: function (data) {
488			$('#OrderId').remove();
489			enableSubmit(origSubmitButtonText);
490			return false;
491		},
492		failed: function (data) {
493			$('#OrderId').remove();
494			enableSubmit(origSubmitButtonText);
495			return false;
496		}
497	});
498}
499
500/**
501 *
502 * @param {*} type
503 * @returns
504 */
505function formatOrderData(type) {
506	//gather the name and billing address (if exists) to pass to the function
507	firstName = ($('#OrderFirstName').val() == 'undefined' ? '' : $('#OrderFirstName').val());
508	lastName = ($('#OrderFirstName').val() == 'undefined' ? '' : $('#OrderLastName').val());
509	billName = firstName + ' ' + lastName;
510	billName = $.trim(billName);
511	address1 = ($('#OrderAddress').val() == 'undefined' ? '' : $('#OrderAddress').val());
512	address2 = ($('#OrderAddress2').val() == 'undefined' ? '' : $('#OrderAddress2').val());
513	city = ($('#OrderCity').val() == 'undefined' ? '' : $('#OrderCity').val());
514	postalCode = ($('#OrderPostalCode').val() == 'undefined' ? '' : $('#OrderPostalCode').val());
515	country = ($('#OrderCountry').val() == 'undefined' ? '' : $('#OrderOrderCountry').val());
516
517	//special handling for state, since there's 4 possible entry fields
518	if ($('#OrderState').val() != 'undefined' && $('#OrderState').val() != '') {
519		state = $('#OrderState').val();
520	} else if ($('#OrderProvince').val() != 'undefined' && $('#OrderProvince').val() != '') {
521		state = $('#OrderProvince').val();
522	} else if ($('#OrderAustralianState').val() != 'undefined' && $('#OrderAustralianState').val() != '') {
523		state = $('#OrderAustralianState').val();
524	} else if ($('#OrderMxDistrict').val() != 'undefined' && $('#OrderMxDistrict').val() != '') {
525		state = $('#OrderMxDistrict').val();
526	} else {
527		state = '';
528	}
529
530	return {
531		payment_method_data: {
532			billing_details: {
533				name: billName,
534				address: {
535					city: city,
536					country: country,
537					line1: address1,
538					line2: address2,
539					postal_code: postalCode,
540					state: state,
541
542				},
543			},
544			shipping: {
545				name: billName,
546				address: {
547					city: city,
548					country: country,
549					line1: address1,
550					line2: address2,
551					postal_code: postalCode,
552					state: state,
553
554				}
555			}
556		}
557	};
558}
559
560/**
561 * Get the line number of the current execution point.
562 *
563 * This function retrieves the line number of the current execution point by parsing the call stack.
564 * It creates a new Error object to capture the stack trace, then extracts the line number from the stack trace.
565 * The line number is obtained by splitting the stack trace and using regular expression matching.
566 *
567 * @return int|string The line number of the current execution point if found, otherwise 'unknown'.
568 * @throws \Exception If an error occurs while retrieving the line number.
569 */
570function getLineNumber() {
571	try {
572		throw new Error();
573	} catch (e) {
574		if (!e.stack) {
575			return 'unknown';
576		}
577		const stack = e.stack.split("\n");
578		const callerLine = stack[2] || stack[1];
579		const match = callerLine.match(/:(\d+):\d+/); // Regular expression to extract line number
580
581		return match ? match[1] : 'unknown';
582	}
583}

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.