1// These helpers stay dependency-free so the payment timing and amount contract 2// can be executed directly in Playwright without opening a real wallet. 3function calculateQuickPayTotalMinorUnits( 4 cartBaseTotal, 5 processingFee, 6 shippingFeeMinorUnits, 7 usesZeroDecimalMinorUnits, 8 displayedCartTotal 9) { 10 var minorUnitMultiplier = usesZeroDecimalMinorUnits ? 1 : 100; 11 var approvedCartTotal = Number.isFinite(displayedCartTotal) 12 ? displayedCartTotal 13 : Number(cartBaseTotal || 0); 14 var calculatedAmount = 15 Math.round( 16 (approvedCartTotal + Number(processingFee || 0)) * 17 minorUnitMultiplier 18 ) + Math.round(Number(shippingFeeMinorUnits || 0)); 19 20 // The preferred DOM total contains promotions already shown to the customer. 21 // Shipping is selected inside the wallet sheet, so compose it on top. 22 return calculatedAmount; 23} 24 25function getQuickPayDisplayedCartTotal(documentRoot) { 26 if (!documentRoot || typeof documentRoot.querySelector != 'function') { 27 return null; 28 } 29 30 var displayedCartTotalElement = 31 documentRoot.querySelector('.quick_pay_approved_total') || 32 documentRoot.querySelector('.quick_pay_current_total') || 33 documentRoot.querySelector('.cart_total_price'); 34 if (!displayedCartTotalElement || !displayedCartTotalElement.innerText) { 35 return null; 36 } 37 38 var displayedCartTotal = parseFloat( 39 displayedCartTotalElement.innerText.replace(/[^\d.]/g, '') 40 ); 41 return Number.isFinite(displayedCartTotal) ? displayedCartTotal : null; 42} 43 44function getQuickPayInputValue(documentRoot, selector) { 45 if (!documentRoot || typeof documentRoot.querySelector != 'function') { 46 return ''; 47 } 48 49 var input = documentRoot.querySelector(selector); 50 return input && typeof input.value != 'undefined' ? input.value : ''; 51} 52 53function getQuickPayDigitalProductLabel( 54 cartData, 55 bundleLookup, 56 textTranslator 57) { 58 var translate = 59 typeof textTranslator == 'function' 60 ? textTranslator 61 : function (text) { 62 return text; 63 }; 64 var ticketQuantity = 0; 65 var annualPassQuantity = 0; 66 67 if (cartData && Array.isArray(cartData.items)) { 68 cartData.items.forEach(function (cartItem) { 69 var quantity = Math.max(1, Number(cartItem.quantity || 1)); 70 var bundle = 71 typeof bundleLookup == 'function' 72 ? bundleLookup(cartItem.bundleId || 1) || {} 73 : {}; 74 75 var hasAnnualPass = 76 Number( 77 cartItem.season_pass_length_override || 78 bundle.season_pass_length || 79 0 80 ) > 0 || 81 Number( 82 cartItem.season_pass_players_override || 83 bundle.season_pass_tickets || 84 0 85 ) > 0; 86 87 if (hasAnnualPass) { 88 annualPassQuantity += quantity; 89 } else { 90 ticketQuantity += quantity; 91 } 92 }); 93 } 94 95 var quantityParts = []; 96 if (ticketQuantity > 0) { 97 quantityParts.push( 98 ticketQuantity + 99 ' ' + 100 translate( 101 ticketQuantity == 1 102 ? 'Scavenger Hunt Ticket' 103 : 'Scavenger Hunt Tickets' 104 ) 105 ); 106 } 107 if (annualPassQuantity > 0) { 108 quantityParts.push( 109 annualPassQuantity + 110 ' ' + 111 translate(annualPassQuantity == 1 ? 'Annual Pass' : 'Annual Passes') 112 ); 113 } 114 115 var deliveryLabel = translate('Instant delivery - Digital Product'); 116 return quantityParts.length 117 ? deliveryLabel + ': ' + quantityParts.join(' + ') + '.' 118 : deliveryLabel + '.'; 119} 120 121function getQuickPayAvailablePaymentMethods( 122 canMakePaymentRequest, 123 shouldForceLink 124) { 125 // forceLink is an explicit end-to-end automation mode. Stripe can briefly 126 // return null for a fresh CI browser even though Link opens and completes a 127 // real test purchase. Keep shopper capability checks unchanged, but make 128 // the requested test wallet deterministic. 129 if (shouldForceLink) { 130 return { link: true }; 131 } 132 133 return canMakePaymentRequest || null; 134} 135 136async function submitQuickPayCheckout( 137 checkoutUrl, 138 formData, 139 requestFunction, 140 completeFunction, 141 retryOptions 142) { 143 retryOptions = retryOptions || {}; 144 var maxRequestAttempts = retryOptions.maxRequestAttempts || 8; 145 var retryDelayMilliseconds = retryOptions.retryDelayMilliseconds || 300; 146 var delayFunction = 147 retryOptions.delayFunction || 148 function (delayMilliseconds) { 149 return new Promise(function (resolve) { 150 setTimeout(resolve, delayMilliseconds); 151 }); 152 }; 153 var lastRequestError = null; 154 for ( 155 var requestAttempt = 0; 156 requestAttempt < maxRequestAttempts; 157 requestAttempt++ 158 ) { 159 try { 160 var formattedResponse = await requestFunction(checkoutUrl, formData); 161 var responseError = formattedResponse && formattedResponse.error; 162 var responseErrorCode = 163 responseError && typeof responseError == 'object' 164 ? responseError.code 165 : null; 166 var looksLikeNetworkFailure = 167 formattedResponse && 168 formattedResponse.status === false && 169 typeof formattedResponse.error == 'string'; 170 var checkoutIsStillProcessing = 171 responseErrorCode === 'quick_pay_checkout_in_progress'; 172 var responseIsRetryable = 173 responseError && 174 typeof responseError == 'object' && 175 responseError.retryable === true; 176 if ( 177 looksLikeNetworkFailure || 178 checkoutIsStillProcessing || 179 responseIsRetryable 180 ) { 181 lastRequestError = responseError || new Error('Network error occurred.'); 182 if (requestAttempt + 1 < maxRequestAttempts) { 183 await delayFunction(retryDelayMilliseconds); 184 continue; 185 } 186 break; 187 } 188 189 if (!formattedResponse) { 190 lastRequestError = new Error('Quickpay checkout returned no response.'); 191 if (requestAttempt + 1 < maxRequestAttempts) { 192 await delayFunction(retryDelayMilliseconds); 193 continue; 194 } 195 continue; 196 } 197 198 completeFunction( 199 formattedResponse && formattedResponse.status === true 200 ? 'success' 201 : 'fail' 202 ); 203 return formattedResponse; 204 } catch (paymentRequestError) { 205 lastRequestError = paymentRequestError; 206 if (requestAttempt + 1 < maxRequestAttempts) { 207 await delayFunction(retryDelayMilliseconds); 208 continue; 209 } 210 } 211 } 212 213 // Stripe's wallet API has no indeterminate completion state. Closing the 214 // sheet as submitted prevents a second authorization while the original 215 // same-attempt request may still finish and charge. 216 completeFunction('success'); 217 return { 218 status: false, 219 result: null, 220 error: { 221 code: 'quick_pay_checkout_status_unknown', 222 message: 'Quickpay checkout could not be confirmed.', 223 retryable: false, 224 cause: lastRequestError, 225 }, 226 }; 227} 228 229function createQuickPayCheckoutAttemptId() { 230 if ( 231 typeof crypto != 'undefined' && 232 typeof crypto.randomUUID == 'function' 233 ) { 234 return crypto.randomUUID(); 235 } 236 237 return ( 238 'quickpay_' + 239 Date.now().toString(36) + 240 '_' + 241 Math.random().toString(36).slice(2) + 242 Math.random().toString(36).slice(2) 243 ); 244} 245 246function normalizeQuickPayDebugWalletName(walletName) { 247 var normalizedWalletName = String(walletName || '') 248 .replace(/[^a-z]/gi, '') 249 .toLowerCase(); 250 251 if (normalizedWalletName == 'applepay') { 252 return 'apple_pay'; 253 } 254 if (normalizedWalletName == 'googlepay' || normalizedWalletName == 'gpay') { 255 return 'google_pay'; 256 } 257 if (normalizedWalletName == 'link') { 258 return 'link'; 259 } 260 return 'unknown'; 261} 262
263function redactQuickPayDebugText(value) { 264 return String(value || '') 265 .replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, '[redacted_email]') 266 .replace( 267 /(^|[^A-Za-z0-9])(?:tok|pm|src|pi|seti|cus|ch)_[A-Za-z0-9_-]+/gi, 268 '$1[redacted_payment_id]' 269 ); 270} 271 272function getQuickPayDebugErrorDetails(paymentError) { 273 var errorDetails = { 274 name: '', 275 code: '', 276 message: '', 277 }; 278 if (!paymentError) { 279 return errorDetails; 280 } 281 282 if (typeof paymentError == 'string') { 283 errorDetails.message = redactQuickPayDebugText(paymentError).slice(0, 300); 284 return errorDetails; 285 } 286 287 errorDetails.name = redactQuickPayDebugText(paymentError.name) 288 .replace(/[^A-Za-z0-9_.:-]/g, '') 289 .slice(0, 80); 290 errorDetails.code = redactQuickPayDebugText( 291 paymentError.code || paymentError.type 292 ) 293 .replace(/[^A-Za-z0-9_.:-]/g, '') 294 .slice(0, 100); 295 errorDetails.message = redactQuickPayDebugText(paymentError.message) 296 .slice(0, 300); 297 return errorDetails; 298} 299 300function quickPayCheckoutSupportsRecovery(checkoutUrl) { 301 // Corporate Quickpay has its own transaction flow and does not yet share the 302 // store checkout's attempt lock/idempotency contract. 303 return checkoutUrl !== '/api/payment/do_corporate_transaction'; 304} 305 306function quickPayUsesCorporateCheckout(documentRoot, hasEventUuid) { 307 return !!( 308 hasEventUuid || 309 (documentRoot && 310 typeof documentRoot.querySelector == 'function' && 311 documentRoot.querySelector('.eventQuickPayButton')) 312 ); 313} 314 315function quickPayRequiresCurrencyQuote(isCorporateCheckout) { 316 return !isCorporateCheckout; 317} 318 319function quickPayHasCheckoutControls(documentRoot) { 320 return !!( 321 documentRoot && 322 typeof documentRoot.querySelector == 'function' && 323 documentRoot.querySelector( 324 '.quickPayButton, .eventQuickPayButton, #payment-request-button, [data-test-id="link-pay-btn"], [onclick*="paymentRequest.show"]' 325 ) 326 ); 327} 328 329function quickPayCartHasItems(cartData) { 330 return !!( 331 cartData && 332 Array.isArray(cartData.items) && 333 cartData.items.length 334 ); 335} 336 337function quickPayCurrencyQuoteExpiresAt(currencyQuoteToken) { 338 var tokenParts = String(currencyQuoteToken || '').split(':'); 339 if ( 340 tokenParts.length !== 3 || 341 tokenParts[0] !== 'v1' || 342 !/^\d+$/.test(tokenParts[1]) || 343 !/^[a-f0-9]{64}$/.test(tokenParts[2]) 344 ) { 345 return 0; 346 } 347 348 return parseInt(tokenParts[1], 10); 349} 350 351function quickPayCurrencyQuoteIsUsable( 352 currencyQuoteToken, 353 nowSeconds, 354 minimumValiditySeconds 355) { 356 var expiresAt = quickPayCurrencyQuoteExpiresAt(currencyQuoteToken); 357 nowSeconds = Number.isFinite(nowSeconds) 358 ? nowSeconds 359 : Math.floor(Date.now() / 1000); 360 minimumValiditySeconds = Number.isFinite(minimumValiditySeconds) 361 ? Math.max(0, minimumValiditySeconds) 362 : 120; 363 return expiresAt > nowSeconds + minimumValiditySeconds; 364} 365 366var quickPayCurrencyQuoteRefreshPromise = null; 367var quickPayCurrencyQuoteRefreshTimer = null; 368var quickPayCurrencyQuotePageShowBound = false; 369 370function applyQuickPayCurrencyQuoteRefresh(responseData) { 371 var refreshedToken = responseData.currency_quote_token || ''; 372 var refreshedRates = responseData.currency_rates || {}; 373 var activeCurrency = String( 374 typeof cy != 'undefined' && cy ? cy : 'usd' 375 ).toLowerCase(); 376 var activeRate = Number(refreshedRates[activeCurrency]); 377 if (!quickPayCurrencyQuoteIsUsable(refreshedToken)) { 378 throw new Error('Quickpay currency quote refresh was invalid.'); 379 } 380 if (!Number.isFinite(activeRate) || activeRate <= 0) { 381 throw new Error('Quickpay currency rate refresh was invalid.'); 382 } 383 384 Object.keys(refreshedRates).forEach(function (currencyCode) { 385 var refreshedRate = Number(refreshedRates[currencyCode]); 386 if ( 387 !Number.isFinite(refreshedRate) || 388 refreshedRate <= 0 || 389 !currencies[currencyCode] 390 ) { 391 return; 392 } 393 currencies[currencyCode].currency_to_usd = refreshedRates[currencyCode]; 394 }); 395 if (Array.isArray(currenciesArray)) { 396 currenciesArray.forEach(function (currencyDefinition) { 397 var currencyCode = String(currencyDefinition.code || '').toLowerCase(); 398 if (currencies[currencyCode]) { 399 currencyDefinition.currency_to_usd = 400 currencies[currencyCode].currency_to_usd; 401 } 402 }); 403 } 404 checkoutCurrencyQuoteToken = refreshedToken; 405 return refreshedToken; 406} 407 408function refreshQuickPayCurrencyQuoteToken() { 409 if (quickPayCurrencyQuoteRefreshPromise) { 410 return quickPayCurrencyQuoteRefreshPromise; 411 } 412 413 quickPayCurrencyQuoteRefreshPromise = window 414 .fetch( 415 '/store/api/checkout/currency-quote?currency=' + 416 encodeURIComponent( 417 String(typeof cy != 'undefined' && cy ? cy : 'usd').toLowerCase() 418 ), 419 { credentials: 'same-origin', cache: 'no-store' } 420 ) 421 .then(function (response) { 422 if (!response.ok) { 423 throw new Error('Quickpay currency quote refresh failed.'); 424 } 425 return response.json(); 426 }) 427 .then(function (responseData) { 428 var refreshedToken = applyQuickPayCurrencyQuoteRefresh(responseData); 429 if (typeof updateCurrencies == 'function') { 430 updateCurrencies(cy); 431 } 432 armQuickPayCurrencyQuoteExpiration(refreshedToken); 433 if (typeof CartViewRender == 'function') { 434 CartViewRender(); 435 }
435 else if (typeof updateQuickPay == 'function') { 436 updateQuickPay(); 437 } 438 return refreshedToken; 439 }) 440 .finally(function () { 441 quickPayCurrencyQuoteRefreshPromise = null; 442 }); 443 444 return quickPayCurrencyQuoteRefreshPromise; 445} 446 447var quickPayPaymentRequestActive = false; 448 449function acquireQuickPayPaymentRequest() { 450 if (quickPayPaymentRequestActive) { 451 return false; 452 } 453 454 quickPayPaymentRequestActive = true; 455 return true; 456} 457 458function releaseQuickPayPaymentRequest() { 459 quickPayPaymentRequestActive = false; 460} 461 462function showQuickPayPaymentRequest() { 463 if (quickPayPaymentRequestActive) { 464 return null; 465 } 466 467 beginQuickPayDebugWalletInteraction(); 468 recordQuickPayDebugStage('wallet_show_requested'); 469 var isCorporateCheckout = quickPayUsesCorporateCheckout( 470 document, 471 typeof eventUUID != 'undefined' 472 ); 473 if ( 474 quickPayRequiresCurrencyQuote(isCorporateCheckout) && 475 !quickPayCurrencyQuoteIsUsable(checkoutCurrencyQuoteToken) 476 ) { 477 var currencyQuoteError = new Error('Quickpay currency quote expired.'); 478 var currencyQuoteDebugEvent = markQuickPayUnavailable( 479 currencyQuoteError, 480 'wallet_preflight_failed' 481 ); 482 showQuickPayFailureModal( 483 'wallet_preflight_failed', 484 currencyQuoteError, 485 currencyQuoteDebugEvent 486 ); 487 return null; 488 } 489 490 if ( 491 typeof paymentRequest == 'undefined' || 492 !paymentRequest || 493 typeof paymentRequest.show != 'function' 494 ) { 495 var unavailablePaymentRequestError = new Error( 496 'Quickpay payment request is unavailable.' 497 ); 498 var unavailablePaymentRequestDebugEvent = sendQuickPayDebugTerminalEvent( 499 'wallet_preflight_failed', 500 'unavailable', 501 unavailablePaymentRequestError 502 ); 503 showQuickPayFailureModal( 504 'wallet_preflight_failed', 505 unavailablePaymentRequestError, 506 unavailablePaymentRequestDebugEvent 507 ); 508 return null; 509 } 510 511 // Apple Pay rejects a second show() call while its first sheet is still 512 // active. Treat repeated clicks as the same interaction instead of creating 513 // a noisy InvalidAccessError and interrupting checkout recovery. 514 if (!acquireQuickPayPaymentRequest()) { 515 return null; 516 } 517 518 try { 519 var paymentRequestShowResult = paymentRequest.show(); 520 if ( 521 paymentRequestShowResult && 522 typeof paymentRequestShowResult.then == 'function' 523 ) { 524 paymentRequestShowResult 525 .then(function () { 526 recordQuickPayDebugStage('wallet_show_resolved'); 527 }) 528 .catch(function (paymentRequestShowError) { 529 releaseQuickPayPaymentRequest(); 530 var errorDetails = getQuickPayDebugErrorDetails( 531 paymentRequestShowError 532 ); 533 var walletShowStage = 534 errorDetails.name == 'AbortError' 535 ? 'wallet_cancelled' 536 : 'wallet_show_failed'; 537 var walletShowDebugEvent = sendQuickPayDebugTerminalEvent( 538 walletShowStage, 539 errorDetails.name == 'AbortError' ? 'cancelled' : 'failed', 540 paymentRequestShowError 541 ); 542 if (walletShowStage == 'wallet_show_failed') { 543 showQuickPayFailureModal( 544 walletShowStage, 545 paymentRequestShowError, 546 walletShowDebugEvent 547 ); 548 } 549 }); 550 } 551 return paymentRequestShowResult; 552 } catch (paymentRequestShowError) { 553 releaseQuickPayPaymentRequest(); 554 var walletShowDebugEvent = sendQuickPayDebugTerminalEvent( 555 'wallet_show_failed', 556 'failed', 557 paymentRequestShowError 558 ); 559 showQuickPayFailureModal( 560 'wallet_show_failed', 561 paymentRequestShowError, 562 walletShowDebugEvent 563 ); 564 return null; 565 } 566} 567 568function armQuickPayCurrencyQuoteExpiration(currencyQuoteToken) { 569 var expiresAt = quickPayCurrencyQuoteExpiresAt(currencyQuoteToken); 570 if (!quickPayCurrencyQuoteIsUsable(currencyQuoteToken)) { 571 return refreshQuickPayCurrencyQuoteToken(); 572 } 573 574 if (typeof setTimeout == 'function') { 575 if (quickPayCurrencyQuoteRefreshTimer) { 576 clearTimeout(quickPayCurrencyQuoteRefreshTimer); 577 } 578 quickPayCurrencyQuoteRefreshTimer = setTimeout(function () { 579 if (!quickPayCurrencyQuoteIsUsable(currencyQuoteToken)) { 580 refreshQuickPayCurrencyQuoteToken().catch(function (refreshError) { 581 markQuickPayUnavailable(refreshError, 'wallet_preflight_failed'); 582 }); 583 } 584 }, Math.max(0, expiresAt * 1000 - Date.now() - 120000 + 25)); 585 } 586 if ( 587 !quickPayCurrencyQuotePageShowBound && 588 typeof window != 'undefined' && 589 typeof window.addEventListener == 'function' 590 ) { 591 quickPayCurrencyQuotePageShowBound = true; 592 window.addEventListener('pageshow', function () { 593 if (!quickPayCurrencyQuoteIsUsable(checkoutCurrencyQuoteToken)) { 594 refreshQuickPayCurrencyQuoteToken().catch(function (refreshError) { 595 markQuickPayUnavailable(refreshError, 'wallet_preflight_failed'); 596 }); 597 } 598 }); 599 } 600 return Promise.resolve(currencyQuoteToken); 601} 602 603function disableQuickPayControls(documentRoot) { 604 if (!documentRoot || typeof documentRoot.querySelectorAll != 'function') { 605 return; 606 } 607 608 documentRoot 609 .querySelectorAll( 610 '.quickPayButton, .eventQuickPayButton, #payment-request-button, [data-test-id="link-pay-btn"], [onclick*="paymentRequest.show"]' 611 ) 612 .forEach(function (quickPayControl) { 613 var paypalContainer = null; 614 if ( 615 typeof quickPayControl.matches == 'function' && 616 quickPayControl.matches('[data-paypal-quick-pay-entry-point]') 617 ) { 618 paypalContainer = quickPayControl; 619 } else if (typeof quickPayControl.querySelector == 'function') { 620 paypalContainer = quickPayControl.querySelector(
621 '[data-paypal-quick-pay-entry-point]' 622 ); 623 } 624 625 if (paypalContainer) { 626 // Reason: modern wallet rows can share the legacy quickPayButton 627 // wrapper with PayPal. Stripe wallet unavailability must not hide a 628 // ready PayPal iframe, including after browser Back restores the page. 629 if (typeof quickPayControl.querySelectorAll == 'function') { 630 quickPayControl 631 .querySelectorAll( 632 '.sch-new-cart__wallet, [data-test-id="link-pay-btn"], [onclick*="paymentRequest.show"]' 633 ) 634 .forEach(function (stripeWalletControl) { 635 stripeWalletControl.style.display = 'none'; 636 stripeWalletControl.setAttribute('aria-disabled', 'true'); 637 }); 638 } 639 quickPayControl.style.display = ''; 640 if (typeof quickPayControl.removeAttribute == 'function') { 641 quickPayControl.removeAttribute('aria-disabled'); 642 } 643 return; 644 } 645 646 quickPayControl.style.display = 'none'; 647 quickPayControl.setAttribute('aria-disabled', 'true'); 648 }); 649} 650 651function getQuickPayFailureModalCopy(stage, paymentError) { 652 var walletNames = { 653 apple_pay: 'Apple Pay', 654 google_pay: 'Google Pay', 655 link: 'Link', 656 unknown: 'Quick Pay', 657 }; 658 var reasonLabels = { 659 submission_blocked: 'Checkout could not start', 660 lead_creation_failed: 'Event checkout setup failed', 661 checkout_request_failed: 'Checkout connection failed', 662 checkout_response_failed: 'Payment could not be confirmed', 663 post_token_failed: 'Checkout page error', 664 wallet_show_failed: 'Wallet could not open', 665 wallet_preflight_failed: 'Wallet is not ready', 666 setup_failed: 'Secure payment setup failed', 667 }; 668 var walletName = walletNames[ 669 normalizeQuickPayDebugWalletName( 670 quickPayDebugActiveWallet || window.quickPayMethod 671 ) 672 ]; 673 var errorDetails = getQuickPayDebugErrorDetails(paymentError); 674 var declineReasonLabels = { 675 insufficient_funds: 676 'This card does not have enough available funds. Please use another payment method.', 677 card_declined: 678 'This card was declined. Please use another payment method or contact your bank.', 679 }; 680 var cardDeclineMessage = 681 paymentError && 682 paymentError.type == 'card_error' && 683 errorDetails.message 684 ? errorDetails.message 685 : ''; 686 var reason = translateQuickPayFailureText( 687 cardDeclineMessage || 688 declineReasonLabels[errorDetails.code] || 689 reasonLabels[stage] || 690 'Payment could not be completed' 691 ); 692 if (errorDetails.code) { 693 reason += 694 ' (' + 695 translateQuickPayFailureText('code') + 696 ' ' + 697 errorDetails.code + 698 ')'; 699 } 700 701 if (stage == 'checkout_status_unknown') { 702 return { 703 title: translateQuickPayFailureText( 704 'Your payment is still being confirmed' 705 ), 706 message: translateQuickPayFailureText( 707 'Do not try to pay again. Check your email for a receipt, or contact support if it does not arrive.' 708 ), 709 reason: translateQuickPayFailureText( 710 'Payment status could not be confirmed' 711 ), 712 walletName: walletName, 713 statusUnknown: true, 714 }; 715 } 716 717 return { 718 title: 719 walletName + 720 ' ' + 721 translateQuickPayFailureText('could not complete checkout'), 722 message: translateQuickPayFailureText( 723 'Your order was not completed. Try your wallet again, or use regular checkout.' 724 ), 725 reason: reason, 726 walletName: walletName, 727 statusUnknown: false, 728 }; 729} 730 731function translateQuickPayFailureText(text) { 732 return typeof translateText == 'function' ? translateText(text) : text; 733} 734 735function ensureQuickPayFailureModalStyles() { 736 if ( 737 typeof document == 'undefined' || 738 document.getElementById('quick-pay-failure-modal-styles') 739 ) { 740 return; 741 } 742 743 var style = document.createElement('style'); 744 style.id = 'quick-pay-failure-modal-styles'; 745 // Reason: SCH's mobile ticket sheet uses the browser's maximum z-index. 746 // Matching it lets this later body child render above the checkout sheet. 747 style.textContent = 748 '.quick-pay-failure-modal{align-items:center;background:rgba(15,23,42,.58);display:flex;inset:0;justify-content:center;padding:20px;position:fixed;z-index:2147483647}' + 749 '.quick-pay-failure-modal__dialog{background:#fff;border-radius:18px;box-shadow:0 24px 64px rgba(15,23,42,.3);box-sizing:border-box;color:#172033;font-family:Arial,sans-serif;max-width:440px;padding:28px;text-align:center;width:100%}' + 750 '.quick-pay-failure-modal__icon{align-items:center;background:#fff2e8;border-radius:50%;color:#c45100;display:flex;font-size:28px;font-weight:800;height:56px;justify-content:center;margin:0 auto 16px;width:56px}' + 751 '.quick-pay-failure-modal__title{font-size:24px;line-height:1.25;margin:0 0 10px}' + 752 '.quick-pay-failure-modal__message{color:#475569;font-size:16px;line-height:1.5;margin:0 0 14px}' + 753 '.quick-pay-failure-modal__reason{background:#f8fafc;border:1px solid #e2e8f0;border-radius:9px;color:#475569;font-size:13px;line-height:1.45;margin:0 0 20px;padding:10px 12px;word-break:break-word}' + 754 '.quick-pay-failure-modal__actions{display:flex;flex-wrap:wrap;gap:10px}' + 755 '.quick-pay-failure-modal__button{border:0;border-radius:10px;cursor:pointer;flex:1 1 150px;font-size:16px;font-weight:700;min-height:48px;padding:12px 16px}' + 756 '.quick-pay-failure-modal__button--retry{background:#eef2f7;color:#172033}' + 757 '.quick-pay-failure-modal__button--checkout{background:#e87722;color:#fff}'; 758 (document.head || document.body).appendChild(style); 759} 760 761function showQuickPayFailureModal(stage, paymentError, debugEvent) { 762 if ( 763 typeof document == 'undefined' || 764 !document.body || 765 typeof document.createElement != 'function' 766 ) { 767 console.warn('Unable to show Quick Pay failure modal', { stage: stage }); 768 return; 769 } 770 771 if ( 772 stage != 'checkout_status_unknown' && 773 typeof window.schNewTicketFlowSetQuickPayLoading == 'function' 774 ) { 775 window.schNewTicketFlowSetQuickPayLoading(false); 776 } 777 778 ensureQuickPayFailureModalStyles(); 779 var existingModal = document.getElementById('quick-pay-failure-modal'); 780 if (existingModal) { 781 existingModal.remove(); 782 } 783 784 var copy = getQuickPayFailureModalCopy(stage, paymentError); 785 var referenceParts = [stage]; 786 if (debugEvent && debugEvent.event_id) { 787 referenceParts.push(String(debugEvent.event_id).slice(0, 8)); 788 } 789
790 var overlay = document.createElement('div'); 791 overlay.id = 'quick-pay-failure-modal'; 792 overlay.className = 'quick-pay-failure-modal'; 793 overlay.setAttribute('data-test-id', 'quick-pay-failure-modal'); 794 795 var dialog = document.createElement('section'); 796 dialog.className = 'quick-pay-failure-modal__dialog'; 797 dialog.setAttribute('role', 'dialog'); 798 dialog.setAttribute('aria-modal', 'true'); 799 dialog.setAttribute('aria-labelledby', 'quick-pay-failure-modal-title'); 800 801 var icon = document.createElement('div'); 802 icon.className = 'quick-pay-failure-modal__icon'; 803 icon.setAttribute('aria-hidden', 'true'); 804 icon.textContent = '!'; 805 806 var title = document.createElement('h2'); 807 title.id = 'quick-pay-failure-modal-title'; 808 title.className = 'quick-pay-failure-modal__title'; 809 title.textContent = copy.title; 810 811 var message = document.createElement('p'); 812 message.className = 'quick-pay-failure-modal__message'; 813 message.textContent = copy.message; 814 815 var reason = document.createElement('p'); 816 reason.className = 'quick-pay-failure-modal__reason'; 817 reason.textContent = 818 copy.reason + 819 ' · ' + 820 translateQuickPayFailureText('Reference') + 821 ': ' + 822 referenceParts.join(' / '); 823 824 var actions = document.createElement('div'); 825 actions.className = 'quick-pay-failure-modal__actions'; 826 827 function closeModal() { 828 overlay.remove(); 829 } 830 831 var retryButton = document.createElement('button'); 832 retryButton.type = 'button'; 833 retryButton.className = 834 'quick-pay-failure-modal__button quick-pay-failure-modal__button--retry'; 835 retryButton.textContent = translateQuickPayFailureText( 836 copy.statusUnknown ? 'Close' : 'Try wallet again' 837 ); 838 retryButton.onclick = closeModal; 839 actions.appendChild(retryButton); 840 841 if (!copy.statusUnknown && typeof goToCheckout == 'function') { 842 var checkoutButton = document.createElement('button'); 843 checkoutButton.type = 'button'; 844 checkoutButton.className = 845 'quick-pay-failure-modal__button quick-pay-failure-modal__button--checkout'; 846 checkoutButton.textContent = translateQuickPayFailureText( 847 'Use regular checkout' 848 ); 849 checkoutButton.onclick = function () { 850 closeModal(); 851 goToCheckout(); 852 }; 853 actions.appendChild(checkoutButton); 854 } 855 856 dialog.appendChild(icon); 857 dialog.appendChild(title); 858 dialog.appendChild(message); 859 dialog.appendChild(reason); 860 dialog.appendChild(actions); 861 overlay.appendChild(dialog); 862 overlay.onclick = function (event) { 863 if (event.target === overlay) { 864 closeModal(); 865 } 866 }; 867 document.body.appendChild(overlay); 868 retryButton.focus(); 869} 870 871// handling chrome and apple pay 872window.__quickPayScriptInitialized = true; 873 874var quickPayDebugQueueKey = 'lr_wallet_debug_queue_v1'; 875var quickPayDebugFeedbackKey = 'lr_wallet_debug_last_terminal_v1'; 876var quickPayDebugStartedAt = Date.now(); 877var quickPayDebugSessionId = createQuickPayCheckoutAttemptId(); 878var quickPayDebugWalletInteractionId = ''; 879var quickPayDebugCheckoutAttemptId = ''; 880var quickPayDebugActiveWallet = ''; 881var quickPayDebugTrail = []; 882var quickPayDebugMemoryQueue = []; 883var quickPayDebugFlushInProgress = false; 884var quickPayDebugInteractionTerminalLogged = false; 885var quickPayDebugWalletCompletionStatus = null; 886var quickPayDebugLoggedFallbackEvents = {}; 887 888function quickPayDebugLoggingEnabled() { 889 return window.STORE_QUICK_PAY_DIAGNOSTICS_ENABLED !== false; 890} 891 892function recordQuickPayDebugStage(stage, code) { 893 quickPayDebugTrail.push({ 894 stage: stage, 895 elapsed_ms: Math.max(0, Date.now() - quickPayDebugStartedAt), 896 code: String(code || '') 897 .replace(/[^A-Za-z0-9_.:-]/g, '') 898 .slice(0, 100), 899 }); 900 quickPayDebugTrail = quickPayDebugTrail.slice(-12); 901} 902 903function beginQuickPayDebugWalletInteraction() { 904 quickPayDebugWalletInteractionId = createQuickPayCheckoutAttemptId(); 905 quickPayDebugCheckoutAttemptId = ''; 906 quickPayDebugInteractionTerminalLogged = false; 907 quickPayDebugWalletCompletionStatus = null; 908 clearQuickPayDebugEventForFeedback(); 909} 910 911function getQuickPayDebugTimezone() { 912 try { 913 return Intl.DateTimeFormat().resolvedOptions().timeZone || ''; 914 } catch (timezoneError) { 915 return ''; 916 } 917} 918 919function getQuickPayDebugContext() { 920 var availablePaymentMethods = window.quickPayAvailablePaymentMethods || {};
921 var pagePath = window.location.pathname || '/'; 922 try { 923 pagePath = decodeURIComponent(pagePath); 924 } catch (pagePathDecodeError) { 925 // Keep the encoded path and redact any recognizable identifiers below. 926 } 927 pagePath = redactQuickPayDebugText(pagePath).slice(0, 300); 928 var browserLanguage = 929 typeof getCookie == 'function' 930 ? getCookie('language') || navigator.language || '' 931 : navigator.language || ''; 932 browserLanguage = /^[A-Za-z]{2,3}(?:[-_][A-Za-z0-9]{2,8})?$/.test( 933 browserLanguage 934 ) 935 ? browserLanguage 936 : ''; 937 return { 938 page_path: pagePath, 939 website_domain: 940 typeof WEBSITE_DOMAIN != 'undefined' ? WEBSITE_DOMAIN : 'unknown', 941 currency: typeof cy != 'undefined' ? cy : 'unknown', 942 language: browserLanguage, 943 timezone: getQuickPayDebugTimezone(), 944 online: navigator.onLine !== false, 945 viewport_width: window.innerWidth || 0, 946 viewport_height: window.innerHeight || 0, 947 apple_pay_available: !!availablePaymentMethods.applePay, 948 google_pay_available: !!availablePaymentMethods.googlePay, 949 link_available: !!availablePaymentMethods.link, 950 }; 951} 952 953function readQuickPayDebugQueue() { 954 try { 955 var storedQueue = JSON.parse( 956 window.localStorage.getItem(quickPayDebugQueueKey) || '[]' 957 ); 958 var combinedQueue = quickPayDebugMemoryQueue.concat( 959 Array.isArray(storedQueue) ? storedQueue : [] 960 ); 961 var seenEventIds = {}; 962 quickPayDebugMemoryQueue = combinedQueue 963 .filter(function (queuedEvent) { 964 if ( 965 !queuedEvent || 966 !queuedEvent.event_id || 967 seenEventIds[queuedEvent.event_id] 968 ) { 969 return false; 970 } 971 seenEventIds[queuedEvent.event_id] = true; 972 return true; 973 }) 974 .slice(-10); 975 return quickPayDebugMemoryQueue.slice(); 976 } catch (queueReadError) { 977 return quickPayDebugMemoryQueue.slice(); 978 } 979} 980 981function writeQuickPayDebugQueue(queue) { 982 quickPayDebugMemoryQueue = queue.slice(-10); 983 try { 984 window.localStorage.setItem( 985 quickPayDebugQueueKey, 986 JSON.stringify(quickPayDebugMemoryQueue) 987 ); 988 } catch (queueWriteError) { 989 // Wallet checkout must remain usable when storage is unavailable. 990 } 991} 992 993function enqueueQuickPayDebugEvent(debugEvent) { 994 var queue = readQuickPayDebugQueue().filter(function (queuedEvent) { 995 return queuedEvent && queuedEvent.event_id != debugEvent.event_id; 996 }); 997 queue.push(debugEvent); 998 writeQuickPayDebugQueue(queue); 999} 1000 1001function rememberQuickPayDebugEventForFeedback(debugEvent) { 1002 try { 1003 window.sessionStorage.setItem( 1004 quickPayDebugFeedbackKey, 1005 JSON.stringify({ 1006 event_id: debugEvent.event_id, 1007 stage: debugEvent.stage, 1008 wallet: debugEvent.wallet, 1009 occurred_at: Math.floor(Date.now() / 1000), 1010 }) 1011 ); 1012 } catch (feedbackStorageError) { 1013 // The diagnostic endpoint remains the source of truth when storage is blocked. 1014 } 1015} 1016 1017function clearQuickPayDebugEventForFeedback() { 1018 try { 1019 window.sessionStorage.removeItem(quickPayDebugFeedbackKey); 1020 } catch (feedbackStorageError) { 1021 // Session storage is optional diagnostic context. 1022 } 1023} 1024 1025function flushQuickPayDebugQueue() { 1026 if ( 1027 !quickPayDebugLoggingEnabled() || 1028 quickPayDebugFlushInProgress || 1029 typeof window.fetch != 'function' 1030 ) { 1031 return; 1032 } 1033 1034 var queue = readQuickPayDebugQueue(); 1035 if (!queue.length) { 1036 return; 1037 } 1038 1039 var debugEvent = queue[0]; 1040 quickPayDebugFlushInProgress = true; 1041 window 1042 .fetch('/store/api/checkout/wallet-debug-log', { 1043 method: 'POST', 1044 headers: { 'Content-Type': 'application/json' }, 1045 credentials: 'same-origin', 1046 keepalive: true, 1047 body: JSON.stringify(debugEvent), 1048 }) 1049 .then(function (response) { 1050 if (!response.ok) { 1051 throw new Error('Wallet diagnostic request failed.'); 1052 } 1053 var updatedQueue = readQuickPayDebugQueue().filter(function ( 1054 queuedEvent 1055 ) { 1056 return queuedEvent && queuedEvent.event_id != debugEvent.event_id; 1057 }); 1058 writeQuickPayDebugQueue(updatedQueue); 1059 quickPayDebugFlushInProgress = false;
1060 flushQuickPayDebugQueue(); 1061 }) 1062 .catch(function () { 1063 quickPayDebugFlushInProgress = false; 1064 }); 1065} 1066 1067function sendQuickPayDebugTerminalEvent(stage, outcome, paymentError) { 1068 if (!quickPayDebugLoggingEnabled()) { 1069 return null; 1070 } 1071 1072 var errorDetails = getQuickPayDebugErrorDetails(paymentError); 1073 recordQuickPayDebugStage(stage, errorDetails.code || errorDetails.name); 1074 1075 if (quickPayDebugWalletInteractionId) { 1076 if (quickPayDebugInteractionTerminalLogged) { 1077 return; 1078 } 1079 quickPayDebugInteractionTerminalLogged = true; 1080 } else { 1081 var fallbackEventKey = stage + ':' + outcome; 1082 if (quickPayDebugLoggedFallbackEvents[fallbackEventKey]) { 1083 return; 1084 } 1085 quickPayDebugLoggedFallbackEvents[fallbackEventKey] = true; 1086 } 1087 1088 var debugEvent = { 1089 event_id: createQuickPayCheckoutAttemptId(), 1090 debug_session_id: quickPayDebugSessionId, 1091 wallet_interaction_id: quickPayDebugWalletInteractionId, 1092 checkout_attempt_id: quickPayDebugCheckoutAttemptId, 1093 stage: stage, 1094 outcome: outcome, 1095 wallet: normalizeQuickPayDebugWalletName( 1096 quickPayDebugActiveWallet || window.quickPayMethod 1097 ), 1098 error: errorDetails, 1099 context: getQuickPayDebugContext(), 1100 trail: quickPayDebugTrail.slice(-12), 1101 }; 1102 1103 rememberQuickPayDebugEventForFeedback(debugEvent); 1104 enqueueQuickPayDebugEvent(debugEvent); 1105 flushQuickPayDebugQueue(); 1106 console.warn('quickPay diagnostic queued', { 1107 eventId: debugEvent.event_id, 1108 stage: stage, 1109 wallet: debugEvent.wallet, 1110 code: errorDetails.code || errorDetails.name, 1111 }); 1112 return debugEvent; 1113} 1114 1115if (quickPayDebugLoggingEnabled()) { 1116 recordQuickPayDebugStage('script_initialized'); 1117 flushQuickPayDebugQueue(); 1118 if (typeof window.addEventListener == 'function') { 1119 window.addEventListener('online', flushQuickPayDebugQueue); 1120 } 1121} 1122 1123// Reason: js.stripe.com is a third-party script that ad blockers, privacy 1124// extensions, scrapers, and flaky networks routinely block. When it never 1125// loads, wallet checkout is legitimately unavailable and the shopper still 1126// gets standard checkout, so it is an expected environment condition rather 1127// than a wallet failure. main.js already reloads this script once Stripe.js 1128// becomes available, so logging every blocked load as a terminal wallet error 1129// only floods dev_error_logs and hides real wallet regressions. 1130var quickPayStripeScriptUnavailable = 1131 typeof window.stripe == 'undefined' && typeof Stripe == 'undefined'; 1132var stripe; 1133var elements; 1134window.__quickPayStripeSetupReady = false; 1135if (quickPayStripeScriptUnavailable) { 1136 // Keep the local diagnostic trail so a later real failure still shows that 1137 // Stripe.js never loaded, without calling an undefined global or sending a 1138 // terminal event. Standard card checkout remains available. 1139 recordQuickPayDebugStage('stripe_script_unavailable', 'ReferenceError'); 1140 console.warn('quickPay unavailable: Stripe.js did not load'); 1141} else { 1142 try { 1143 stripe = 1144 typeof window.stripe != 'undefined' 1145 ? window.stripe 1146 : Stripe(strpk, { locale: getCookie('language') || 'en' }); 1147 elements = stripe.elements(); 1148 window.__quickPayStripeSetupReady = true; 1149 } catch (quickPayStripeSetupError) { 1150 sendQuickPayDebugTerminalEvent( 1151 'setup_failed', 1152 'unavailable', 1153 quickPayStripeSetupError 1154 ); 1155 } 1156} 1157 1158window.canMakePaymentRequest = false; 1159window.canNotMakePaymentRequest = false; 1160window.quickPayAvailablePaymentMethods = null; 1161window.quickPayCapabilityPending = false; 1162window.quickPayCapabilitySettled = false; 1163 1164function settleQuickPayCapability() { 1165 window.quickPayCapabilityPending = false; 1166 window.quickPayCapabilitySettled = true; 1167 window.dispatchEvent(new CustomEvent('quickpaycapabilitysettled')); 1168} 1169 1170function markQuickPayUnavailable(paymentError, diagnosticStage) { 1171 var diagnosticEvent = null; 1172 if (paymentError) { 1173 console.warn('quickPay unavailable', { paymentError }); 1174 } 1175 1176 if (diagnosticStage) { 1177 diagnosticEvent = sendQuickPayDebugTerminalEvent( 1178 diagnosticStage, 1179 'unavailable', 1180 paymentError 1181 ); 1182 } 1183 1184 window.quickPayReady = false;
1185 window.quickPayMethod = 'none'; 1186 window.canMakePaymentRequest = false; 1187 window.canNotMakePaymentRequest = true; 1188 window.quickPayAvailablePaymentMethods = null; 1189 1190 if (typeof setCookie == 'function') { 1191 setCookie('canNotMakePaymentRequest', 'true'); 1192 } 1193 1194 disableQuickPayControls(document); 1195 return diagnosticEvent; 1196} 1197 1198var cartTotalAmount; 1199var quickPayApprovedAmount = null; 1200 1201var lastPages = getCookie('last_pages') || ''; 1202 1203if (!cart.shippingOptions && typeof shippingOptions != 'undefined') { 1204 cart.shippingOptions = shippingOptions; 1205} 1206var processingFee = 0; 1207function calculateProcessingFee() { 1208 // only charges processing fee on checkout 1209 processingFee = 0; 1210 if (WEBSITE_DOMAIN != 'afs' && window.location.href.includes('/checkout')) { 1211 processingFee = money_round(cart.total * 0.1); 1212 } 1213 1214 if ( 1215 WEBSITE_DOMAIN == 'sch' && 1216 window.location.href.includes('/checkout') && 1217 !lastPages.includes('products') 1218 ) { 1219 processingFee = money_round(cart.total * 0.08); 1220 } 1221 // processing fee is 2 cents less than 10 percent 1222 if (processingFee > 0.02) { 1223 processingFee = processingFee - 0.02; 1224 } 1225} 1226calculateProcessingFee(); 1227 1228function money_round(num) { 1229 return (Math.round(num * 100) / 100).toFixed(2); 1230} 1231 1232// https://stripe.com/docs/currencies 1233function fixStripeZeroPointCorrection(total_in_cents) { 1234 let fixedTotal = total_in_cents; 1235 if ( 1236 currencies && 1237 currencies[cy] && 1238 parseInt(currencies[cy].non_zero_point_currency, 10) 1239 ) { 1240 // won and yen 1241 fixedTotal = Math.round(total_in_cents / 100); 1242 } else { 1243 // usd and cad and most dollar curreincies 1244 fixedTotal = parseFloat((total_in_cents / 100).toFixed(2)); 1245 } 1246 return fixedTotal; 1247} 1248 1249console.log('quickPay: calculating pricing', { 1250 processingFee, 1251 total: cart.total, 1252 initialShippingFee, 1253 processingFee, 1254}); 1255 1256var getCartTotalAmount = function () { 1257 cartBaseTotal = cart.total; 1258 var eventTotal = getQuickPayInputValue(document, '#eventTotal'); 1259 var quickPayTotalOverride = getQuickPayInputValue( 1260 document, 1261 '#quickPayTotalOverride' 1262 ); 1263 1264 if (eventTotal || quickPayTotalOverride) { 1265 return Math.ceil( 1266 getLocalPrice(eventTotal || quickPayTotalOverride || 0, cy, false), 1267 cy, 1268 false 1269 ); 1270 } 1271 1272 if (typeof cartBaseTotalOverride != 'undefined') { 1273 cartBaseTotal = cartBaseTotalOverride; 1274 } 1275 1276 var usesZeroDecimalMinorUnits = 1277 currencies && 1278 currencies[cy] && 1279 parseInt(currencies[cy].non_zero_point_currency, 10); 1280 var displayedCartTotal = getQuickPayDisplayedCartTotal(document); 1281 1282 cartTotalAmount = calculateQuickPayTotalMinorUnits( 1283 cartBaseTotal, 1284 processingFee, 1285 initialShippingFee, 1286 usesZeroDecimalMinorUnits, 1287 displayedCartTotal 1288 ); 1289 1290 console.log('quickPay: calculated price 1', { cartTotalAmount }); 1291 1292 console.log('quickPay: calculated price 1', { 1293 cartTotalAmount, 1294 }); 1295 1296 return cartTotalAmount; 1297}; 1298 1299function rememberQuickPayApprovedAmount(amount) { 1300 quickPayApprovedAmount = parseInt(amount, 10); 1301 return quickPayApprovedAmount; 1302} 1303 1304var handleEventQuickPay = function () { 1305 var quickPayButtonContent = getQuickPayButton(); 1306 console.log('handleEventQuickPay', { quickPayButtonContent }); 1307 document 1308 .querySelectorAll('.eventQuickPayButton') 1309 .forEach(function (quickPayEventBuuton) { 1310 quickPayEventBuuton.innerHTML = quickPayButtonContent; 1311 }); 1312 1313 if (typeof paymentRequest != 'undefined') { 1314 const approvedAmount = rememberQuickPayApprovedAmount(getCartTotalAmount()); 1315 const updatedQuickPayOptions = { 1316 total: { 1317 label: translateText('Scavenger Hunt Party Tickets'), 1318 displayItems: !!(processingFee || useAddressFields) 1319 ? [ 1320 { 1321 amount: approvedAmount, 1322 label: translateText('Scavenger Hunt Party Tickets'), 1323 }, 1324 ] 1325 : undefined, 1326 amount: approvedAmount, 1327 }, 1328 }; 1329 //If LinkBtn exists then set data-amount for our quickpay E2E test 1330 const linkBtn = document.querySelector('[data-test-id="link-pay-btn"]'); 1331 if (linkBtn) { 1332 //set data-amount of linkBtn to be getCartTotalAmount() 1333 linkBtn.setAttribute('data-amount', approvedAmount); 1334 } 1335 1336 console.log({ 1337 updatedQuickPayOptions, 1338 initialShippingFee, 1339 processingFee, 1340 }); 1341 paymentRequest.update(updatedQuickPayOptions); 1342 } 1343}; 1344
1345var updateQuickPay = function () { 1346 console.log('updateQuickPay'); 1347 if (document.querySelector('.eventQuickPayButton')) { 1348 return handleEventQuickPay(); 1349 } 1350 1351 if ( 1352 quickPayRequiresCurrencyQuote( 1353 quickPayUsesCorporateCheckout( 1354 document, 1355 typeof eventUUID != 'undefined' 1356 ) 1357 ) && 1358 quickPayHasCheckoutControls(document) 1359 ) { 1360 var quickPayRenderedQuoteWasUsable = quickPayCurrencyQuoteIsUsable( 1361 checkoutCurrencyQuoteToken 1362 ); 1363 var quickPayRenderedQuoteReady = armQuickPayCurrencyQuoteExpiration( 1364 checkoutCurrencyQuoteToken 1365 ); 1366 if (!quickPayRenderedQuoteWasUsable) { 1367 disableQuickPayControls(document); 1368 quickPayRenderedQuoteReady 1369 .then(function () { 1370 if (typeof CartViewRender == 'function') { 1371 CartViewRender(); 1372 } 1373 }) 1374 .catch(function (refreshError) { 1375 markQuickPayUnavailable(refreshError, 'wallet_preflight_failed'); 1376 }); 1377 return; 1378 } 1379 } 1380 1381 try { 1382 initialShippingFee = cart.shippingOptions 1383 ? cart.shippingOptions[0].amount 1384 : 0; 1385 calculateProcessingFee(); 1386 if (typeof paymentRequest != 'undefined') { 1387 const approvedAmount = rememberQuickPayApprovedAmount(getCartTotalAmount()); 1388 const updatedQuickPayOptions = { 1389 currency: cy, 1390 total: { 1391 label: getQuickPayDigitalProductLabel(cart, getBundleInfo, translateText), 1392 displayItems: !!(processingFee || useAddressFields) 1393 ? [ 1394 { 1395 amount: fixStripeZeroPointCorrection( 1396 parseInt(getValue('#schTotal') || 0) 1397 ), 1398 label: translateText('Free Instant Digital Gift Card'), 1399 }, 1400 ] 1401 : undefined, 1402 amount: approvedAmount, 1403 }, 1404 }; 1405 //If LinkBtn exists then set data-amount for our quickpay E2E test 1406 const linkBtn = document.querySelector('[data-test-id="link-pay-btn"]'); 1407 if (linkBtn) { 1408 //set data-amount of linkBtn to be getCartTotalAmount() 1409 linkBtn.setAttribute('data-amount', approvedAmount); 1410 } 1411 1412 if (WEBSITE_DOMAIN == 'sch') { 1413 cartBaseTotal = cart.total; 1414 if (typeof cartBaseTotalOverride != 'undefined') { 1415 cartBaseTotal = cartBaseTotalOverride; 1416 } 1417 googlePayShippingInfo['displayItems'] = [ 1418 { 1419 amount: fixStripeZeroPointCorrection( 1420 100 * parseInt(Math.round(100 * cartBaseTotal)) 1421 ), 1422 label: translateText('Cart Items'), 1423 }, 1424 { 1425 amount: fixStripeZeroPointCorrection( 1426 100 * parseInt(parseInt(Math.round(processingFee * 100))) 1427 ), 1428 label: translateText('Duties & Fees'), // Display the label for the fee 1429 }, 1430 ]; 1431 } 1432 1433 console.log({ 1434 updatedQuickPayOptions, 1435 initialShippingFee, 1436 processingFee, 1437 }); 1438 paymentRequest.update(updatedQuickPayOptions); 1439 } 1440 } catch (updateQuickPayError) { 1441 console.error({ updateQuickPayError }); 1442 } 1443}; 1444 1445useAddressFields = 1446 typeof useAddressFields != 'undefined' 1447 ? useAddressFields 1448 : cart.hasPhysicalProduct; 1449 1450var initialShippingFee = cart.shippingOptions 1451 ? cart.shippingOptions[0].amount 1452 : 0; 1453 1454var disableWallets = window.location.href.includes('disableWallets') 1455 ? ['googlePay', 'link', 'applePay'] 1456 : ['link']; 1457 1458console.log({ disableWallets }); 1459 1460var initialQuickPayApprovedAmount = rememberQuickPayApprovedAmount( 1461 getCartTotalAmount() 1462); 1463 1464// https://stripe.com/docs/js#stripe-payment-request 1465var googlePayShippingInfo = { 1466 country: 'US', 1467 currency: cy, 1468 disableWallets: disableWallets, 1469 1470 total: { 1471 label: getQuickPayDigitalProductLabel(cart, getBundleInfo, translateText), 1472 amount: initialQuickPayApprovedAmount, 1473 }, 1474 1475 requestPayerName: true, 1476 requestPayerEmail: true, 1477 requestShipping: false, 1478}; 1479 1480if (WEBSITE_DOMAIN == 'sch') { 1481 cartBaseTotal = cart.total; 1482 if (typeof cartBaseTotalOverride != 'undefined') { 1483 cartBaseTotal = cartBaseTotalOverride; 1484 } 1485 1486 googlePayShippingInfo['displayItems'] = [ 1487 { 1488 amount: fixStripeZeroPointCorrection( 1489 100 * parseInt(Math.round(100 * cartBaseTotal)) 1490 ), 1491 label: 'Cart Items', 1492 }, 1493 { 1494 amount: fixStripeZeroPointCorrection( 1495 100 * parseInt(parseInt(Math.round(processingFee * 100))) 1496 ), 1497 label: 'Duties & Fees', // Display the label for the fee 1498 }, 1499 ]; 1500} 1501 1502console.log({ googlePayShippingInfo }); 1503 1504var paymentRequest; 1505var prButton; 1506var skipLegacyPaymentRequestButtonElement = !!window.isTest; 1507// Reason: a blocked Stripe.js load is already handled above as an expected
1508// environment condition, so the follow-on setup failure must not be logged as 1509// a wallet regression either. 1510var quickPaySetupDiagnosticStage = quickPayStripeScriptUnavailable 1511 ? '' 1512 : 'setup_failed'; 1513var quickPayCurrencyQuoteReady = Promise.resolve(checkoutCurrencyQuoteToken); 1514 1515try { 1516 var quickPayIsCorporateCheckout = quickPayUsesCorporateCheckout( 1517 document, 1518 typeof eventUUID != 'undefined' 1519 ); 1520 if ( 1521 quickPayRequiresCurrencyQuote(quickPayIsCorporateCheckout) && 1522 cart && 1523 cart.hasSubscription 1524 ) { 1525 // Subscriptions intentionally use the standard checkout, so this is not a 1526 // wallet failure and should not dilute the diagnostic signal. 1527 quickPaySetupDiagnosticStage = ''; 1528 throw new Error('Quickpay is unavailable for subscription carts.'); 1529 } 1530 if ( 1531 quickPayRequiresCurrencyQuote(quickPayIsCorporateCheckout) && 1532 (quickPayHasCheckoutControls(document) || quickPayCartHasItems(cart)) 1533 ) { 1534 quickPayCurrencyQuoteReady = armQuickPayCurrencyQuoteExpiration( 1535 checkoutCurrencyQuoteToken 1536 ); 1537 } 1538 paymentRequest = stripe.paymentRequest(googlePayShippingInfo); 1539 if (!skipLegacyPaymentRequestButtonElement) { 1540 prButton = elements.create('paymentRequestButton', { 1541 paymentRequest: paymentRequest, 1542 }); 1543 } 1544} catch (quickPaySetupError) { 1545 console.error('quickPay setup error', { quickPaySetupError }); 1546 markQuickPayUnavailable(quickPaySetupError, quickPaySetupDiagnosticStage); 1547} 1548 1549if (typeof paymentRequest != 'undefined') { 1550 function getQuickPaySelectedShippingOption(tokenEvent) { 1551 var selectedShippingOption = tokenEvent && tokenEvent.shippingOption; 1552 if ( 1553 (!selectedShippingOption || selectedShippingOption.amount == null) && 1554 cart && 1555 Array.isArray(cart.shippingOptions) && 1556 cart.shippingOptions.length 1557 ) { 1558 selectedShippingOption = cart.shippingOptions[0]; 1559 } 1560 1561 return selectedShippingOption || null; 1562 } 1563 1564 function getQuickPayPostedShippingAmount(tokenEvent) { 1565 var selectedShippingOption = getQuickPaySelectedShippingOption(tokenEvent); 1566 1567 if (!selectedShippingOption || selectedShippingOption.amount == null) { 1568 return 0; 1569 } 1570 1571 var usesZeroDecimalMinorUnits = 1572 currencies && 1573 currencies[cy] && 1574 parseInt(currencies[cy].non_zero_point_currency, 10); 1575 return usesZeroDecimalMinorUnits 1576 ? selectedShippingOption.amount 1577 : selectedShippingOption.amount / 100; 1578 } 1579 1580 function getQuickPayPostedShippingService(tokenEvent) { 1581 var selectedShippingOption = getQuickPaySelectedShippingOption(tokenEvent); 1582 return selectedShippingOption && selectedShippingOption.id != null 1583 ? String(selectedShippingOption.id).trim() 1584 : ''; 1585 } 1586 1587 // check availability and mount the button when the promise resolves 1588 console.log('seeing if quickPay: canMakePayment'); 1589 recordQuickPayDebugStage('capability_check_started'); 1590 // PayPal uses this signal to hold one continuous loading skeleton while the 1591 // floating row learns whether Apple Pay / Google Pay needs another column. 1592 window.quickPayCapabilityPending = true; 1593 Promise.all([paymentRequest.canMakePayment(), quickPayCurrencyQuoteReady]) 1594 .then(function (quickPaySetupResults) { 1595 var canMakePaymentRequest = quickPaySetupResults[0]; 1596 console.log({ canMakePaymentRequest }); 1597 var availablePaymentMethods = getQuickPayAvailablePaymentMethods( 1598 canMakePaymentRequest, 1599 false 1600 ); 1601 window.quickPayAvailablePaymentMethods = availablePaymentMethods; 1602 1603 if (availablePaymentMethods) { 1604 recordQuickPayDebugStage('capability_available'); 1605 if (availablePaymentMethods.applePay) { 1606 window.quickPayReady = true; 1607 window.quickPayMethod = 'applePay'; 1608 setCookie('quickPayMethod', 'applePay'); 1609 } else if (availablePaymentMethods.googlePay) { 1610 window.quickPayReady = true; 1611 window.quickPayMethod = 'googlePay'; 1612 setCookie('quickPayMethod', 'googlePay'); 1613 // no on uses link 1614 // so disabling 1615 } else { 1616 markQuickPayUnavailable(); 1617 if (typeof CartViewRender != 'undefined') { 1618 CartViewRender(); 1619 } 1620 return console.warn('no quick pay methods', { 1621 availablePaymentMethods, 1622 }); 1623 } 1624 1625 quickPayDebugActiveWallet = window.quickPayMethod; 1626 1627 window.canMakePaymentRequest = 1;
1628 setCookie('canNotMakePaymentRequest', 'false'); 1629 1630 const usingCustomQuickPayButton = 1631 !!document.querySelector('.quickPayButton') || 1632 !!document.querySelector('.eventQuickPayButton'); 1633 1634 if (document.querySelector('#quick-pay')) { 1635 document.querySelector('#quick-pay').style.display = 'block'; 1636 1637 // When we render our own quick-pay button, Stripe recommends using 1638 // paymentRequest.show() directly instead of also mounting the 1639 // paymentRequestButton Element for the same PaymentRequest instance. 1640 if (prButton && !usingCustomQuickPayButton) { 1641 prButton.mount('#payment-request-button'); 1642 } 1643 } 1644 1645 // PayPal owns a cross-origin iframe inside the integrated wallet row. 1646 // Replacing quickPayButton.innerHTML here destroys a ready PayPal 1647 // button and causes a second skeleton. The cart render immediately 1648 // below consumes the settled wallet capability and builds the final 1649 // Apple/Google Pay + PayPal columns without mutating a mounted iframe. 1650 if ( 1651 document.querySelector('.quickPayButton') && 1652 window.PAYPAL_QUICK_PAY_VARIANT_ENABLED !== true 1653 ) { 1654 document 1655 .querySelectorAll('.quickPayButton') 1656 .forEach(function (quickPayEventBuuton) { 1657 var quickPayButtonContent = getQuickPayButton(); 1658 quickPayEventBuuton.innerHTML = quickPayButtonContent; 1659 }); 1660 } 1661 1662 if (document.querySelector('.eventQuickPayButton')) { 1663 handleEventQuickPay(); 1664 } else if (typeof CartViewRender != 'undefined') { 1665 CartViewRender(); 1666 } 1667 } else { 1668 markQuickPayUnavailable(); 1669 } 1670 }) 1671 .catch(function (paymentCapabilityError) { 1672 paymentRequest = undefined; 1673 prButton = undefined; 1674 markQuickPayUnavailable( 1675 paymentCapabilityError, 1676 'capability_check_failed' 1677 ); 1678 }) 1679 .then(function () { 1680 settleQuickPayCapability(); 1681 }); 1682 1683 paymentRequest.on('shippingoptionchange', function (event) { 1684 console.log({ event }); 1685 window.event = event; 1686 var updateWith = event.updateWith; 1687 var shippingOption = event.shippingOption; 1688 // handle shippingoptionchange event 1689 if (shippingOption && shippingOption.amount != null) { 1690 initialShippingFee = shippingOption.amount; 1691 } 1692 1693 const shippingOptionApprovedAmount = rememberQuickPayApprovedAmount( 1694 getCartTotalAmount() 1695 ); 1696 const shippingOptionChangeUpdateData = { 1697 status: 'success', 1698 total: { 1699 amount: shippingOptionApprovedAmount, 1700 label: translateText('Free Instant Digital Gift Card'), 1701 }, 1702 }; 1703 1704 console.log({ shippingOptionChangeUpdateData }); 1705 1706 // call event.updateWith within 30 seconds 1707 updateWith(shippingOptionChangeUpdateData); 1708 console.log('update called'); 1709 }); 1710 1711 paymentRequest.on('token', async function (tokenEvent) { 1712 // A new wallet authorization gets a new attempt. Any network retry below 1713 // reuses this same ID, token, and payload so Stripe can safely deduplicate it. 1714 var quickPayCheckoutAttemptId = createQuickPayCheckoutAttemptId(); 1715 if (!quickPayDebugWalletInteractionId) { 1716 beginQuickPayDebugWalletInteraction(); 1717 } 1718 quickPayDebugCheckoutAttemptId = quickPayCheckoutAttemptId; 1719 quickPayDebugActiveWallet = tokenEvent.walletName || window.quickPayMethod; 1720 recordQuickPayDebugStage('token_received'); 1721 var quickPayCompletionStatus = null; 1722 function completeQuickPay(status) { 1723 if (quickPayCompletionStatus !== null) { 1724 return; 1725 } 1726 1727 quickPayCompletionStatus = status; 1728 quickPayDebugWalletCompletionStatus = status; 1729 releaseQuickPayPaymentRequest(); 1730 if (typeof tokenEvent.complete == 'function') { 1731 tokenEvent.complete(status); 1732 } 1733 } 1734 1735 try { 1736 // Send the token to your server to charge it 1737 if ( 1738 typeof window.schNewTicketFlowMarkQuickPaySubmitting == 'function' && 1739 !window.schNewTicketFlowMarkQuickPaySubmitting() 1740 ) { 1741 completeQuickPay('fail'); 1742 var submissionBlockedError = new Error( 1743 'Quickpay submission was blocked before checkout.' 1744 ); 1745 var submissionBlockedDebugEvent = sendQuickPayDebugTerminalEvent( 1746 'submission_blocked', 1747 'failed', 1748 submissionBlockedError 1749 ); 1750 if (typeof window.schNewTicketFlowSetQuickPayLoading == 'function') { 1751 window.schNewTicketFlowSetQuickPayLoading(false); 1752 } 1753 showQuickPayFailureModal( 1754 'submission_blocked', 1755 submissionBlockedError, 1756 submissionBlockedDebugEvent 1757 ); 1758 return; 1759 } 1760 1761 const firstName = (tokenEvent.payerName || '').split(' ')[0]; 1762 const lastName = (tokenEvent.payerName || '').split(' ')[0]; 1763 const payerEmail = tokenEvent.payerEmail || ''; 1764 1765 if (typeof quickPayOverride == 'function') { 1766 return quickPayOverride(tokenEvent); 1767 } 1768 1769 var formData = { 1770 bchName: firstName, 1771 bchLastname: lastName, 1772 bchEmail: payerEmail, 1773 bchSource: getValue('#bchSource'), 1774 bchHostalPromo: getValue('#bchHostalPromo'), 1775 cart_fees: processingFee, 1776 bchTransactionType: 'cart',
1777 bchSubscribe: 'yes', 1778 stripeToken: tokenEvent.token.id, 1779 shippingAmount: getQuickPayPostedShippingAmount(tokenEvent), 1780 shippingService: getQuickPayPostedShippingService(tokenEvent), 1781 is_quick_pay: true, 1782 submission_url: window.location.href, 1783 quick_pay_type: (tokenEvent.walletName ?? '') + '_store_page', 1784 quick_pay_approved_amount: quickPayApprovedAmount, 1785 checkout_attempt_id: quickPayCheckoutAttemptId, 1786 currency_quote_token: 1787 typeof checkoutCurrencyQuoteToken != 'undefined' 1788 ? checkoutCurrencyQuoteToken 1789 : '', 1790 quick_pay_contract_version: 'currency_quote_v1', 1791 }; 1792 1793 if (typeof cart != 'undefined') { 1794 cart.cy = cy; 1795 cart.currency = cy; 1796 if (typeof priceCart == 'function') { 1797 cart.total = priceCart('discounted'); 1798 cart.regularTotal = priceCart('regular'); 1799 cart.fullTotal = priceCart('full'); 1800 } 1801 formData.cartOverride = JSON.stringify(cart); 1802 } 1803 1804 formData.chargeShipping = getValue('#shippingCheck1'); 1805 1806 if (tokenEvent.shippingAddress) { 1807 // submit form w/address fields 1808 1809 formData.includes_address = true; 1810 formData.address_first_name = firstName; 1811 formData.address_last_name = lastName; 1812 if (tokenEvent.shippingAddress.addressLine) { 1813 formData.address = tokenEvent.shippingAddress.addressLine[0]; 1814 formData.address2 = tokenEvent.shippingAddress.addressLine[1]; 1815 } 1816 formData.address_city = tokenEvent.shippingAddress.city; 1817 formData.address_state = tokenEvent.shippingAddress.region; 1818 formData.address_zip = tokenEvent.shippingAddress.postalCode; 1819 } 1820 var checkout_url = `/store/api/checkout/send`; 1821 1822 if (window.location.href.includes('app/checkout')) { 1823 checkout_url = `/store/api/app/purchase?testing_website_domain=letsroam`; 1824 } 1825 1826 // birth bach quick pay 1827 if ( 1828 document.querySelector('.eventQuickPayButton') || 1829 typeof eventUUID != 'undefined' 1830 ) { 1831 if (typeof eventUUID != 'undefined') { 1832 formData.uuid = eventUUID; 1833 } else { 1834 const modalForm = document.querySelector('#bday-booking-modal'); 1835 const rightForm = document.querySelector('#bday-booking'); 1836 1837 const formRoot = isHidden(modalForm) ? rightForm : modalForm; 1838 1839 const locationInput = formRoot.querySelector('#clead_GE'); 1840 // if on the city page we want to use that city if they don't change it 1841 const fallbackLocation = 1842 formRoot.querySelector('#fallbackLocation').innerText; 1843 1844 let date_of_event_text = formRoot.querySelector('#date_of_event_text'); 1845 1846 if (date_of_event_text) { 1847 date_of_event_text = date_of_event_text.value; 1848 } 1849 1850 groupSize = formRoot.querySelector('#group_size').value || 20; 1851 1852 var eventLocation = locationInput.value || fallbackLocation.trim(); 1853 if (eventLocation) { 1854 eventLocation = eventLocation.replace('Search a city', ''); 1855 } 1856 1857 const leadData = { 1858 email: payerEmail, 1859 full_name: firstName + ' ' + lastName, 1860 company: firstName + ' Custom Event', 1861 location: eventLocation, 1862 package_type: 'scavenger_hunt', 1863 group_size: groupSize, 1864 city: eventLocation, 1865 category: window.location.href.includes('bach') 1866 ? 'bachelorette' 1867 : 'birthday', 1868 event_date: 1869 date_of_event_text || new Date().toISOString().split('T')[0], 1870 currency: cy, 1871 cookies: typeof getTrackingCookies === 'function' ? getTrackingCookies() : {}, 1872 form_page: 'quick_pay_event_page', 1873 submitting_url: window.location.href, 1874 }; 1875 1876 console.log({ 1877 leadData, 1878 formRoot, 1879 fallbackLocation, 1880 eventLocation, 1881 groupSize, 1882 }); 1883 1884 const newLead = await fetchWithParams( 1885 '/api/your_event/create_lead_endpoint', 1886 leadData, 1887 {}, 1888 'POST', 1889 function (error) { 1890 console.log('error', error); 1891 } 1892 ); 1893 1894 console.log({ newLead }); 1895 1896 if (!newLead || !newLead.company || !newLead.company.UUID) { 1897 completeQuickPay('fail'); 1898 var leadCreationError = new Error( 1899 'Quickpay event lead creation failed.' 1900 ); 1901 var leadCreationDebugEvent = sendQuickPayDebugTerminalEvent( 1902 'lead_creation_failed', 1903 'failed', 1904 leadCreationError 1905 ); 1906 showQuickPayFailureModal( 1907 'lead_creation_failed', 1908 leadCreationError, 1909 leadCreationDebugEvent 1910 ); 1911 return; 1912 } 1913 1914 formData.uuid = newLead.company.UUID; 1915 } 1916 1917 checkout_url = '/api/payment/do_corporate_transaction'; 1918 } 1919 recordQuickPayDebugStage('checkout_request_started'); 1920 console.log('quickPay: submitting checkout', { 1921 checkoutAttemptId: quickPayCheckoutAttemptId, 1922 checkoutUrl: checkout_url, 1923 wallet: normalizeQuickPayDebugWalletName(quickPayDebugActiveWallet), 1924 }); 1925 var formattedResponse; 1926 try { 1927 if (!quickPayCheckoutSupportsRecovery(checkout_url)) { 1928 // Preserve the established corporate flow; this card only hardens the 1929 // idempotent store checkout path.
1930 completeQuickPay('success'); 1931 formattedResponse = await fetchWithParams(checkout_url, formData); 1932 } else { 1933 formattedResponse = await submitQuickPayCheckout( 1934 checkout_url, 1935 formData, 1936 function (requestUrl, requestData) { 1937 return fetchWithParams( 1938 requestUrl, 1939 requestData, 1940 {}, 1941 'POST', 1942 null, 1943 true 1944 ); 1945 }, 1946 completeQuickPay 1947 ); 1948 } 1949 } catch (paymentFetchError) { 1950 console.error('no response', { paymentFetchError }); 1951 var checkoutRequestDebugEvent = sendQuickPayDebugTerminalEvent( 1952 'checkout_request_failed', 1953 'failed', 1954 paymentFetchError 1955 ); 1956 if (typeof window.schNewTicketFlowSetQuickPayLoading == 'function') { 1957 window.schNewTicketFlowSetQuickPayLoading(false); 1958 } 1959 showQuickPayFailureModal( 1960 'checkout_request_failed', 1961 paymentFetchError, 1962 checkoutRequestDebugEvent 1963 ); 1964 return; 1965 } 1966 1967 recordQuickPayDebugStage( 1968 'checkout_response_received', 1969 formattedResponse && formattedResponse.error 1970 ? formattedResponse.error.code || formattedResponse.error.type 1971 : '' 1972 ); 1973 if (formattedResponse && formattedResponse.status === true) { 1974 recordQuickPayDebugStage('checkout_succeeded'); 1975 clearQuickPayDebugEventForFeedback(); 1976 fb_track_safe('CartQuickPay'); 1977 setTimeout(function () { 1978 if (formattedResponse.redirect_url) { 1979 window.location.href = base_url(formattedResponse.redirect_url); 1980 return; 1981 } 1982 1983 var tid = formattedResponse.result.tid; 1984 console.log('successful payment'); 1985 window.location.href = base_url(`/store/receipt/${tid}?firstLoad=1`); 1986 }, 500); 1987 return; 1988 } 1989 1990 if ( 1991 formattedResponse && 1992 formattedResponse.error && 1993 formattedResponse.error.code === 'quick_pay_checkout_status_unknown' 1994 ) { 1995 var checkoutStatusUnknownDebugEvent = sendQuickPayDebugTerminalEvent( 1996 'checkout_status_unknown', 1997 'failed', 1998 formattedResponse.error 1999 ); 2000 markQuickPayUnavailable(formattedResponse.error); 2001 showQuickPayFailureModal( 2002 'checkout_status_unknown', 2003 formattedResponse.error, 2004 checkoutStatusUnknownDebugEvent 2005 ); 2006 return; 2007 } 2008 2009 console.error('no response', { formattedResponse }); 2010 var checkoutResponseError = 2011 formattedResponse && formattedResponse.error 2012 ? formattedResponse.error 2013 : new Error('Quickpay checkout returned no usable response.'); 2014 var checkoutResponseDebugEvent = sendQuickPayDebugTerminalEvent( 2015 'checkout_response_failed', 2016 'failed', 2017 checkoutResponseError 2018 ); 2019 if (typeof window.schNewTicketFlowSetQuickPayLoading == 'function') { 2020 window.schNewTicketFlowSetQuickPayLoading(false); 2021 } 2022 showQuickPayFailureModal( 2023 'checkout_response_failed', 2024 checkoutResponseError, 2025 checkoutResponseDebugEvent 2026 ); 2027 } catch (quickPayPostTokenError) { 2028 completeQuickPay('fail'); 2029 console.error({ quickPayPostTokenError }); 2030 var postTokenDebugEvent = sendQuickPayDebugTerminalEvent( 2031 'post_token_failed', 2032 'failed', 2033 quickPayPostTokenError 2034 ); 2035 if (typeof window.schNewTicketFlowSetQuickPayLoading == 'function') { 2036 window.schNewTicketFlowSetQuickPayLoading(false); 2037 } 2038 showQuickPayFailureModal( 2039 'post_token_failed', 2040 quickPayPostTokenError, 2041 postTokenDebugEvent 2042 ); 2043 } 2044 }); 2045 2046 paymentRequest.on('cancel', function () { 2047 releaseQuickPayPaymentRequest(); 2048 if (quickPayDebugWalletCompletionStatus !== null) { 2049 return; 2050 } 2051 if (!quickPayDebugWalletInteractionId) { 2052 beginQuickPayDebugWalletInteraction(); 2053 } 2054 var walletCancelledError = new Error('Wallet sheet was cancelled.'); 2055 walletCancelledError.name = 'AbortError'; 2056 sendQuickPayDebugTerminalEvent( 2057 'wallet_cancelled', 2058 'cancelled', 2059 walletCancelledError 2060 ); 2061 if (typeof window.schNewTicketFlowHandleQuickPayCancel == 'function') { 2062 window.schNewTicketFlowHandleQuickPayCancel(); 2063 } else if (typeof window.schNewTicketFlowSetQuickPayLoading == 'function') { 2064 window.schNewTicketFlowSetQuickPayLoading(false); 2065 } 2066 }); 2067} else { 2068 // PayPal must not remain behind a loading skeleton when Stripe could not 2069 // initialize a PaymentRequest at all. 2070 settleQuickPayCapability(); 2071} 2072 2073ready(function () { 2074 console.log('quickPay: ready');
2075 updateQuickPay(); 2076}); 2077 2078function isHidden(el) { 2079 return el.offsetParent === null; 2080} 2081 2082if ( 2083 typeof cart !== 'undefined' && 2084 cart && 2085 Array.isArray(cart.items) && 2086 cart.items.length > 0 2087) { 2088 var quickPayAttempts = 0; 2089 var quickPayInterval = setInterval(function () { 2090 if (typeof updateQuickPay === 'function') { 2091 updateQuickPay(); 2092 } 2093 2094 quickPayAttempts++; 2095 2096 if (quickPayAttempts === 3) { 2097 clearInterval(quickPayInterval); 2098 } 2099 }, 3000); 2100}
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.