// 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 },
});
});
});
Checkout with {{app.name}}
Choose from any one of our options below.
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)
}