PageSourceSearch

https://www.endthekilling.ca/wp-content/plugins/squarewoosync/assets/js/square-gateway.js?ver=7.1.1.1787094944

js endthekilling.ca collected 2026-10-02 07:28:45 UTC 14,690 bytes, 498 lines download raw bytes

1( function ( $ ) {
2	let payments = null;
3	let walletPaymentRequest = null;
4	let paymentInProgress = false;
5	let initializationInProgress = false;
6	let sdkLoadPromise = null;
7
8	let alreadyInitialized = false; // Guard for initial load
9
10	// Debounce timer
11	let squareInitTimeout = null;
12	const DEBOUNCE_DELAY = 300; // Increased from 20ms to 300ms for better stability
13
14	// Cached jQuery selectors
15	const $cachedSelectors = {};
16
17	// Helper to get cached jQuery selector
18	function getCached( selector ) {
19		if ( ! $cachedSelectors[ selector ] ) {
20			$cachedSelectors[ selector ] = $( selector );
21		}
22		return $cachedSelectors[ selector ];
23	}
24
25	// Clear cached selectors on checkout update
26	function clearSelectorCache() {
27		for ( const key in $cachedSelectors ) {
28			delete $cachedSelectors[ key ];
29		}
30	}
31
32	// We remove lastKnownTotal logic & the booleans, allowing each partial refresh to re-init
33
34	/**
35	 * Wait for Square SDK to be loaded
36	 */
37	function waitForSquareSDK() {
38		if ( sdkLoadPromise ) {
39			return sdkLoadPromise;
40		}
41
42		sdkLoadPromise = new Promise( ( resolve, reject ) => {
43			let attempts = 0;
44			const maxAttempts = 50; // 5 seconds total wait time
45
46			const checkSquare = () => {
47				if ( typeof Square !== 'undefined' && Square.payments ) {
48					resolve( Square );
49				} else if ( attempts >= maxAttempts ) {
50					reject(
51						new Error( 'Square SDK failed to load after 5 seconds' )
52					);
53				} else {
54					attempts++;
55					setTimeout( checkSquare, 100 );
56				}
57			};
58
59			checkSquare();
60		} );
61
62		return sdkLoadPromise;
63	}
64
65	/**
66	 * Debounces the Square init calls so multiple near-simultaneous triggers
67	 * only cause a single actual initialization attempt.
68	 */
69	function scheduleSquareInit() {
70		if ( squareInitTimeout ) {
71			clearTimeout( squareInitTimeout );
72		}
73		squareInitTimeout = setTimeout( handleSquareInit, DEBOUNCE_DELAY );
74	}
75
76	/**
77	 * Main initialization if squaresync_credit is chosen.
78	 * Called after the debounce timer.
79	 */
80	async function handleSquareInit() {
81		// Prevent concurrent initialization attempts
82		if ( initializationInProgress ) {
83			console.log(
84				'Square initialization already in progress, skipping...'
85			);
86			return;
87		}
88
89		const selectedMethod = $(
90			'#payment input[name="payment_method"]:checked'
91		).val();
92		if ( selectedMethod !== 'squaresync_credit' ) {
93			return;
94		}
95
96		initializationInProgress = true;
97		clearStoredTokens();
98
99		try {
100			// Wait for Square SDK to be fully loaded
101			await waitForSquareSDK();
102			console.log( 'Square SDK loaded successfully' );
103			// Create 'payments' once if not done yet
104			if ( ! payments ) {
105				const applicationId = SquareConfig.applicationId;
106				const locationId = SquareConfig.locationId;
107
108				if ( ! applicationId || ! locationId ) {
109					throw new Error(
110						'Square configuration missing: applicationId or locationId'
111					);
112				}
113
114				payments = await Square.payments( applicationId, locationId );
115				console.log( 'Square payments instance created:', payments );
116				if ( ! payments ) {
117					throw new Error( 'Square payments init failed' );
118				}
119			}
120
121			const needsShipping = await checkNeedsShipping();
122			const couponApplied = false;
123			// Build or refresh the PaymentRequest object
124			walletPaymentRequest = await initPaymentRequest(
125				payments,
126				needsShipping,
127				couponApplied
128			);
129
130			// Show/hide card fields
131			toggleCardFields();
132
133			// Only clean and reinitialize card if it doesn't exist or is broken
134			const cardContainer = document.getElementById( 'card-container' );
135			const existingCardIframe = cardContainer
136				? cardContainer.querySelector(
137						'.sq-card-iframe-container iframe'
138				  )
139				: null;
140
141			if ( ! existingCardIframe || ! window.squareCard ) {
142				// Card needs initialization
143				forceCleanCardContainer();
144				initCreditCardPayment( payments, walletPaymentRequest );
145			} else {
146				// Card already exists and is working, skip reinitialization
147				console.log(
148					'Credit card form already initialized, keeping existing instance'
149				);
150			}
151
152			// Also forcibly remove leftover wallet buttons, then re-init
153			if ( walletPaymentRequest ) {
154				forceCleanWalletButtons();
155				initWalletPayments(
156					payments,
157					walletPaymentRequest,
158					needsShipping,
159					couponApplied
160				);
161			}
162
163			alreadyInitialized = true;
164		} catch ( err ) {
165			console.error( 'Square init error:', err );
166			// Reset payments on error to allow retry
167			if ( err.message && err.message.includes( 'Square SDK' ) ) {
168				sdkLoadPromise = null; // Allow retry of SDK loading
169			}
170			logPaymentError(
171				'Payment system initialization failed. Please refresh the page.'
172			);
173		} finally {
174			initializationInProgress = false;
175		}
176	}
177
178	/**
179	 * Remove any leftover .sq-card-wrapper from partial refresh
180	 * Only call this when you actually need to reinitialize the card
181	 */
182	function forceCleanCardContainer() {
183		const $container = $( '#payment #card-container' );
184		if ( $container.length ) {
185			// Check if card instance exists and properly detach it
186			if (
187				window.squareCard &&
188				typeof window.squareCard.destroy === 'function'
189			) {
190				try {
191					window.squareCard.destroy();
192					window.squareCard = null;
193					console.log( 'Destroyed existing card instance' );
194				} catch ( e ) {
195					console.warn( 'Error destroying card instance:', e );
196				}
197			}
198			$container.find( '.sq-card-wrapper' ).remove();
199			$container.find( '.sq-card-iframe-container' ).remove();
200		}
201	}
202
203	/**
204	 * Remove leftover wallet button elements
205	 */
206	function forceCleanWalletButtons() {
207		// Clean up wallet instances if cleanup function exists
208		if ( typeof cleanupWalletInstances === 'function' ) {
209			cleanupWalletInstances();
210		}
211
212		// Don't empty Google Pay if button already exists
213		const googlePayExists = document.querySelector(
214			'#gpay-button-online-api-id'
215		);
216		if ( ! googlePayExists ) {
217			jQuery( '#google-pay-button' ).empty();
218		}
219
220		// Don't empty Afterpay if button already exists
221		const afterpayExists = document.querySelector( '.sq-ap__button' );
222		if ( ! afterpayExists ) {
223			jQuery( '#afterpay-button' ).empty();
224		}
225
226		// Apple Pay can be safely emptied as it doesn't persist
227		jQuery( '#apple-pay-button' ).empty();
228
229		// Also re-allow initWalletPayments() if you use a guard inside it
230		// e.g. `walletPaymentsInitialized = false;`
231		window.walletPaymentsInitialized = false;
232		walletPaymentsInitialized = false;
233	}
234
235	/**
236	 * Intercept place_order if squaresync_credit is chosen, so we can tokenize.
237	 */
238	$( document ).on( 'click', '#place_order', function ( e ) {
239		const method = $(
240			'#payment input[name="payment_method"]:checked'
241		).val();
242		if ( method !== 'squaresync_credit' || paymentInProgress ) return;
243
244		e.preventDefault();
245		paymentInProgress = true;
246		$( document.body ).trigger( 'checkout_place_order_squaresync_credit' );
247	} );
248
249	/**
250	 * Actually tokenize the card, store nonce, then re-submit.
251	 */
252	$( document.body ).on(
253		'checkout_place_order_squaresync_credit',
254		async function () {
255			// Clear any stale nonces before generating a new one
256			clearStoredTokens();
257			console.log(
258				'Starting payment processing - cleared any stale nonces'
259			);
260
261			const success = await processCreditCardPayment(
262				payments,
263				walletPaymentRequest
264			);
265			paymentInProgress = false;
266			if ( success ) {
267				if ( SquareConfig.context === 'account' ) {
268					$( '#add_payment_method' ).trigger( 'submit' );
269				} else {
270					// Handle both checkout and pay-for-order forms
271					const $form = $( 'form.checkout, form#order_review' );
272					if ( $form.length ) {
273						$form.trigger( 'submit' );
274					} else {
275						console.error(
276							'Could not find checkout form to submit'
277						);
278					}
279				}
280			} else {
281				console.log( 'Payment processing failed - nonces cleared' );
282				clearStoredTokens();
283			}
284		}
285	);
286
287	$( document.body ).on( 'wc_payment_method_selected', scheduleSquareInit );
288
289	/**
290	 * Show/hide the new card fields vs. saved tokens
291	 */
292	function toggleCardFields() {
293		const token = $(
294			'#payment input[name="wc-squaresync_credit-payment-token"]:checked'
295		).val();
296		if ( token && token !== 'new' ) {
297			$( '#payment-form' ).hide();
298			$( '.woocommerce-SavedPaymentMethods-saveNew' ).hide();
299		} else {
300			$( '#payment-form' ).show();
301			$( '.woocommerce-SavedPaymentMethods-saveNew' ).show();
302		}
303	}
304
305	// Debounced triggers from WooCommerce
306	$( document.body ).on( 'updated_checkout', function ( e, data ) {
307		// Check if this is a minor update (like coupon application)
308		const isMinorUpdate =
309			data && ( data.coupon_applied || data.coupon_removed );
310
311		if ( ! isMinorUpdate ) {
312			clearSelectorCache();
313		}
314
315		// Always schedule init but it will skip if card already exists
316		scheduleSquareInit();
317	} );
318	$( document.body ).on(
319		'change',
320		'#payment input[name="payment_method"]',
321		function () {
322			// Payment method changed, need full reinit
323			clearSelectorCache();
324			scheduleSquareInit();
325		}
326	);
327
328	$( document ).on(
329		'click',
330		'#wc-squaresync_credit-payment-token-new',
331		function ( e ) {
332			$( '#payment-form' ).show();
333			// Initialize Square when switching to new payment method
334			// Only trigger on pay-for-order pages where updated_checkout doesn't fire
335			const method = $(
336				'#payment input[name="payment_method"]:checked'
337			).val();
338			const isPayForOrderPage = $( 'body' ).hasClass(
339				'woocommerce-order-pay'
340			);
341			if ( method === 'squaresync_credit' && isPayForOrderPage ) {
342				scheduleSquareInit();
343			}
344		}
345	);
346	$( document ).on(
347		'click',
348		'.woocommerce-SavedPaymentMethods-token input',
349		function ( e ) {
350			$( '#payment-form' ).hide();
351		}
352	);
353
354	// If user picks "Use a new card," re-check
355	// $(document).on('change', '#payment input[name="wc-squaresync_credit-payment-token"]', function () {
356	//   toggleCardFields();
357	//   const method = $('#payment input[name="payment_method"]:checked').val();
358	//   if ($(this).val() === 'new' && method === 'squaresync_credit') {
359	//     forceCleanCardContainer()
360	//     forceCleanWalletButtons()
361	//     scheduleSquareInit();
362	//   }
363	// });
364
365	// On account page "Add Payment Method", do an init once
366	$( document ).ready( function () {
367		if ( SquareConfig.context === 'account' ) {
368			$( document.body ).trigger( 'wc_payment_method_selected' );
369		}
370
371		// On pay-for-order pages, initialize Square if the payment method is already selected
372		const isPayForOrderPage = $( 'body' ).hasClass(
373			'woocommerce-order-pay'
374		);
375		const isSquareSelected = $( '#payment_method_squaresync_credit' ).is(
376			':checked'
377		);
378		if ( isPayForOrderPage && isSquareSelected ) {
379			scheduleSquareInit();
380		}
381	} );
382
383	// On checkout_error, clear tokens and fully re-initialize card + wallets
384	$( document.body ).on( 'checkout_error', function () {
385		try {
386			console.log(
387				'checkout_error event fired - clearing nonces and reinitializing'
388			);
389			clearStoredTokens();
390			// Clean existing UI/instances to avoid stale nonce or stuck state
391			forceCleanCardContainer();
392			forceCleanWalletButtons();
393			// Re-run initialization to mount fresh instances
394			scheduleSquareInit();
395			paymentInProgress = false;
396		} catch ( e ) {
397			console.warn( 'Square re-init after checkout_error failed:', e );
398		}
399	} );
400
401	// Additional handler: Reset on form submission failure (defense-in-depth for customized checkouts)
402	// This catches cases where checkout_error might not fire consistently
403	let formSubmissionAttempted = false;
404	$( document ).on(
405		'submit',
406		'form.checkout, form#order_review',
407		function () {
408			const method = $(
409				'#payment input[name="payment_method"]:checked'
410			).val();
411			if ( method === 'squaresync_credit' ) {
412				formSubmissionAttempted = true;
413				console.log( 'Form submission attempted with Square payment' );
414			}
415		}
416	);
417
418	// Monitor for WooCommerce error notices appearing after form submission
419	const observer = new MutationObserver( function ( mutations ) {
420		mutations.forEach( function ( mutation ) {
421			if ( mutation.addedNodes.length ) {
422				mutation.addedNodes.forEach( function ( node ) {
423					// Check if error notices were added to the page
424					if (
425						node.nodeType === 1 &&
426						( $( node ).hasClass( 'woocommerce-error' ) ||
427							$( node ).hasClass(
428								'woocommerce-NoticeGroup-checkout'
429							) ||
430							$( node ).find( '.woocommerce-error' ).length )
431					) {
432						const method = $(
433							'#payment input[name="payment_method"]:checked'
434						).val();
435						if (
436							formSubmissionAttempted &&
437							method === 'squaresync_credit'
438						) {
439							console.log(
440								'Payment error detected via notice - clearing nonces for retry'
441							);
442							clearStoredTokens();
443							paymentInProgress = false;
444							formSubmissionAttempted = false;
445							// Card instance should remain for retry, but nonces are cleared
446						}
447					}
448				} );
449			}
450		} );
451	} );
452
453	// Start observing the checkout form area for error notices
454	$( document ).ready( function () {
455		const checkoutForm = document.querySelector(
456			'.woocommerce-checkout, .woocommerce-order-pay'
457		);
458		if ( checkoutForm ) {
459			observer.observe( checkoutForm, {
460				childList: true,
461				subtree: true,
462			} );
463		}
464
465		// Clear stale nonces when page is shown from browser cache (back button)
466		window.addEventListener( 'pageshow', function ( event ) {
467			if ( event.persisted ) {
468				// Page was loaded from cache (back/forward button)
469				console.log(
470					'Page shown from cache - clearing any stale payment nonces'
471				);
472				clearStoredTokens();
473				paymentInProgress = false;
474				formSubmissionAttempted = false;
475			}
476		} );
477	} );
478
479	// Handle coupon-specific events to prevent card destruction
480	$( document.body ).on( 'applied_coupon removed_coupon', function ( e ) {
481		console.log( 'Coupon event detected, preserving card fields' );
482		// Just update payment request without destroying card
483		if ( payments && walletPaymentRequest ) {
484			checkNeedsShipping().then( ( needsShipping ) => {
485				initPaymentRequest( payments, needsShipping, true ).then(
486					( updatedRequest ) => {
487						if ( updatedRequest ) {
488							walletPaymentRequest = updatedRequest;
489							console.log(
490								'Payment request updated for coupon change'
491							);
492						}
493					}
494				);
495			} );
496		}
497	} );
498} )( jQuery );

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.