{{terminalErrorHeading}}

An error has occurred

{{terminalError}}

// Get Angular injector from the checkout app var injector = angular.element(document.querySelector('.checkout-component')).injector() var ngEventService = injector.get('ngEventService') var eventService = injector.get('eventService') // Listen for when a payment method is selected ngEventService.subscribe('EVENT_CHECKOUT_PAYMENT_METHODS_CHANGED', function () { eventService.customEvent('customEvent', { eventName: 'payment-methods-ready', params: {}, }) }) // Bridge form errors to the host app. Piano's errorService fires EVENT_ERRORS_CHANGED // for any form error (empty CC fields, unchecked TnC, payment declines), but checkoutPaymentError // only covers payment processing errors and validation failures — NOT TnC consent errors. // The host shows its own error banner overlay (cloning the Piano message-block concept) // so it fully controls visibility without fighting Angular's errorService auto-clear. var $rootScope = injector.get("$rootScope"); var errorService = injector.get("errorService"); var $timeout = injector.get("$timeout"); // Traverse the Angular scope tree directly instead of relying on errorService's internal // children tree — child scopes that register before $rootScope become orphans in that tree. function collectAllErrors(scope) { var errors = (errorService(scope).errors(true) || []).slice(); var child = scope.$$childHead; while (child) { errors = errors.concat(collectAllErrors(child)); child = child.$$nextSibling; } return errors; } ngEventService.subscribe("EVENT_ERRORS_CHANGED", function (eventData) { // Wrap in $timeout to let Angular's digest cycle propagate the error to the scope tree // before reading — matches the pattern Piano's own error display directive uses. $timeout(function () { var errorDetails = []; try { var errors = collectAllErrors($rootScope); console.log('>>> errors', errors) errorDetails = errors.map(function (e) { return { type: e.type, field: e.field, name: e.name, message: e.message || e.msg || "", }; }); } catch (err) { console.error("checkout-form-error bridge", err); } eventService.customEvent("customEvent", { eventName: "checkout-form-error", params: { errors: errorDetails }, }); }); });
if (!document.getElementById('addTemplateScript')) { const BRANCH_KEY = 'qaBranch' const DEFAULT_FOLDER_NAME = 'staging' function getDevelopmentTemplateFolderName(branch) { if (branch === 'clear') { deleteBranchFromStorage() return DEFAULT_FOLDER_NAME } function validateAndSaveBranchToStorage(targetBranch) { if (targetBranch.startsWith('SCMPWEB-')) sessionStorage.setItem(BRANCH_KEY, targetBranch) return targetBranch } function getBranchFromStorage() { return sessionStorage.getItem(BRANCH_KEY) } function deleteBranchFromStorage() { return sessionStorage.removeItem(BRANCH_KEY) } if (!branch) { const branch = getBranchFromStorage() if (branch) return branch else return 'staging' } else { return validateAndSaveBranchToStorage(branch) } } const currentUrl = new URL(window.location.href) const isInsidePianoIframe = ['tinypass.com', 'piano.io'].some(pianoDomain => currentUrl.host.includes(pianoDomain), ) const rootUrl = isInsidePianoIframe ? new URL(currentUrl.searchParams.get('url')) : currentUrl const isProduction = rootUrl.host.includes('scmp.com') var script = document.createElement('script') const src = (() => { if (isProduction) { const timestamp = Math.round(Date.now().valueOf() / 1000 / 60 / 60) //invalidate cache every hour a value return `https://assets.i-scmp.com/subscription-template/shoppingcart.js?v=${timestamp}` } else { const targetBranch = rootUrl.searchParams.get('branch') if (targetBranch === 'localhost') { return `https://rosetta-template.local.product-web.dev-2.scmp.tech/dev/shoppingcart.js` } else { const folderName = getDevelopmentTemplateFolderName(targetBranch) console.info('TEMPLATE FOLDER NAME:', folderName) return `https://product-web-scmp-pwa-dev.oss-cn-hongkong.aliyuncs.com/subscription-template/${folderName}/shoppingcart.js` } } })() script.id = 'addTemplateScript' script.async = true script.defer = true script.src = src document.head.appendChild(script) }