@economist/web-apple-pay 1.5.1 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +962 -51
- package/dist/src/apple-pay-button.d.ts +13 -1
- package/dist/src/apple-pay-button.js +962 -51
- package/dist/src/apple-pay-button.stories.js +969 -67
- package/dist/src/apple-pay-modal.js +18 -1
- package/dist/src/apple-pay-modal.stories.js +962 -51
- package/dist/src/clients/payment-checkout/apple-session.d.ts +18 -0
- package/dist/src/clients/payment-checkout/apple-session.js +227 -0
- package/dist/src/clients/payment-checkout/billing-contact-details.d.ts +21 -0
- package/dist/src/clients/payment-checkout/billing-contact-details.js +109 -0
- package/dist/src/clients/payment-checkout/config.d.ts +1 -0
- package/dist/src/clients/payment-checkout/contact-validation.d.ts +21 -0
- package/dist/src/clients/payment-checkout/contact-validation.js +50 -0
- package/dist/src/clients/payment-checkout/index.d.ts +4 -0
- package/dist/src/clients/payment-checkout/index.js +25 -1
- package/dist/src/clients/payment-checkout/messages.d.ts +33 -0
- package/dist/src/clients/payment-checkout/messages.js +19 -0
- package/dist/src/clients/payment-checkout/payment-handler.d.ts +36 -0
- package/dist/src/clients/payment-checkout/payment-handler.js +375 -0
- package/dist/src/clients/payment-checkout/shipping-contact-details.d.ts +10 -0
- package/dist/src/clients/payment-checkout/shipping-contact-details.js +83 -0
- package/dist/src/clients/payment-checkout/types.d.ts +102 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +962 -51
- package/dist/src/utils/billing-countries.d.ts +19 -0
- package/dist/src/utils/billing-countries.js +263 -0
- package/dist/src/utils/expressCheckoutRedirect.d.ts +55 -0
- package/dist/src/utils/expressCheckoutRedirect.js +46 -0
- package/dist/src/utils/expressCheckoutTracking.d.ts +64 -0
- package/dist/src/utils/expressCheckoutTracking.js +84 -0
- package/dist/src/utils/recaptcha.d.ts +22 -0
- package/dist/src/utils/recaptcha.js +54 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -76,7 +76,7 @@ function ensureApplePayLogoSymbol() {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// src/templates/apple-pay-modal.template.html
|
|
79
|
-
var apple_pay_modal_template_default = '<style>\n .apm-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 97;\n background: rgba(13, 13, 13, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--wall-gutter, 2rem);\n box-sizing: border-box;\n }\n\n .apm {\n position: relative;\n background: #fff;\n border-radius: 0.5rem 0.5rem 0 0;\n border: 0.0625rem solid #d9d9d9;\n padding: var(--mb-spacing-lg, 1.5rem);\n width: 25rem;\n max-width: 25rem;\n box-sizing: border-box;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n box-shadow: 0 0.25rem 1rem 0 rgba(13, 13, 13, 0.15);\n }\n\n .apm__close {\n position: absolute;\n top: 1.5rem;\n right: 1.3125rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .apm__close:focus {\n outline: solid 0.125rem var(--focus-border-color, #121212);\n border-radius: 0.25rem;\n }\n\n .apm__offer-name {\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-title-3-size, 1.25rem);\n font-weight: 500;\n color: var(--mb-colour-greyscale-london-5, #0d0d0d);\n line-height: var(--mb-typestyle-title-3-leading, 1.75rem);\n font-style: normal;\n margin: 0 1.5rem 0.5rem 0;\n }\n\n .apm__due-today {\n color: #1a1a1a;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n font-style: normal;\n font-weight: 400;\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n margin: 0 0 0.75rem 0;\n }\n\n .apm__rule {\n border: none;\n border-top: 0.0625rem solid #d9d9d9;\n margin: 0 0 1rem 0;\n }\n\n .apm__terms {\n color: #1a1a1a;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n font-style: normal;\n font-weight: 400;\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n margin: 0 0 1rem 0;\n }\n\n .apm__terms-bullets {\n list-style: disc outside;\n padding-left: 1.25rem;\n margin: 0 0 0.75rem 0;\n }\n\n .apm__terms-bullets li {\n margin-bottom: 0.375rem;\n }\n\n .apm__terms-legal {\n margin: 0;\n }\n\n .apm__checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: var(--mb-spacing-sm, 0.5rem);\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n color: var(--mb-colour-greyscale-london-20, #1a1a1a);\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n cursor: pointer;\n margin: 0 0 1.5rem 0;\n }\n\n .apm__checkbox-label--error {\n margin-bottom: 0.25rem;\n }\n\n .apm__checkbox-wrapper {\n position: relative;\n flex-shrink: 0;\n width: 1.25rem;\n height: 1.25rem;\n margin-top: 0.0625rem;\n }\n\n .apm__checkbox-input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n cursor: pointer;\n margin: 0;\n z-index: 1;\n }\n\n .apm__checkbox-custom {\n position: absolute;\n top: 0;\n left: 0;\n width: 1.25rem;\n height: 1.25rem;\n pointer-events: none;\n }\n\n .apm__checkbox-custom svg {\n display: none;\n width: 1.25rem;\n height: 1.25rem;\n }\n\n .apm__checkbox-input:not(:checked)~.apm__checkbox-custom .svg-unchecked {\n display: block;\n }\n\n .apm__checkbox-input:checked~.apm__checkbox-custom .svg-checked {\n display: block;\n }\n\n .apm__checkbox-label--error .apm__checkbox-input:not(:checked)~.apm__checkbox-custom .svg-unchecked {\n display: none;\n }\n\n .apm__checkbox-label--error .apm__checkbox-input:not(:checked)~.apm__checkbox-custom .svg-error {\n display: block;\n }\n\n .apm__checkbox-input:focus-visible~.apm__checkbox-custom {\n outline: solid 0.125rem #c91d42;\n outline-offset: 0.125rem;\n border-radius: 0.25rem;\n }\n\n .apm__checkbox-label a {\n color: #1a1a1a;\n font-weight: 700;\n text-decoration: underline;\n }\n\n .apm__error {\n display: none;\n align-items: center;\n gap: 0.75rem;\n color: var(--mb-colour-greyscale-london-5, #0d0d0d);\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: 1.0625rem;\n font-style: normal;\n font-weight: 500;\n line-height: 1.5rem;\n margin-bottom: 1rem;\n }\n\n .apm__error--visible {\n display: flex;\n }\n\n .apm__cta {\n display: flex;\n width: 100%;\n height: 2.75rem;\n padding: 0.375rem 1rem;\n justify-content: center;\n align-items: center;\n gap: 0.375rem;\n border-radius: 0.25rem;\n background: #000;\n border: none;\n cursor: pointer;\n color: #fff;\n text-align: center;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans, system-ui, sans-serif));\n font-size: 1.25rem;\n font-style: normal;\n font-weight: 500;\n line-height: normal;\n }\n\n .apm__cta svg {\n flex-shrink: 0;\n display: block;\n }\n\n @media (min-width: 22.5rem) and (max-width: 37.5rem) {\n .apm {\n width: 24.375rem;\n max-width: 24.375rem;\n }\n }\n\n @media (min-width: 37.5625rem) {\n .apm {\n border-radius: 0.5rem;\n }\n }\n\n @media (max-width: 22.4375rem) {\n .apm {\n width: 20rem;\n max-width: 20rem;\n }\n }\n\n @media (max-width: 26.1875rem) {\n .apm__cta {\n font-size: 1.125rem;\n }\n }\n</style>\n\n<div class="apm-overlay" id="apple-pay-modal-overlay">\n <div class="apm" role="dialog" aria-modal="true" aria-labelledby="apple-pay-modal-offer-name" id="apple-pay-modal"\n tabindex="-1">\n <button class="apm__close" id="apple-pay-modal-close" aria-label="Close">\n <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">\n <g clip-path="url(#clip0_15712_93054)">\n <rect width="24" height="24" fill="white" fill-opacity="0.01" />\n <path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H24V24H0V0Z" fill="white" fill-opacity="0.01" />\n <path fill-rule="evenodd" clip-rule="evenodd"\n d="M18.75 6.60964L17.3904 5.25L12 10.6404L6.60964 5.25L5.25 6.60964L10.6404 12L5.25 17.3904L6.60964 18.75L12 13.3596L17.3904 18.75L18.75 17.3904L13.3596 12L18.75 6.60964Z"\n fill="#333333" />\n </g>\n <defs>\n <clipPath id="clip0_15712_93054">\n <rect width="24" height="24" fill="white" />\n </clipPath>\n </defs>\n </svg>\n </button>\n <p class="apm__offer-name" id="apple-pay-modal-offer-name"></p>\n <p class="apm__due-today" id="apple-pay-modal-due-today"></p>\n <hr class="apm__rule" />\n <div class="apm__terms" id="apple-pay-modal-terms"></div>\n <label class="apm__checkbox-label" id="apple-pay-modal-checkbox-label" for="apple-pay-modal-checkbox">\n <div class="apm__checkbox-wrapper">\n <input class="apm__checkbox-input" type="checkbox" id="apple-pay-modal-checkbox" />\n <span class="apm__checkbox-custom">\n <svg class="svg-unchecked" width="24" height="24" viewBox="0 0 24 24" fill="none"\n xmlns="http://www.w3.org/2000/svg">\n <path\n d="M6 2.5H18C19.933 2.5 21.5 4.067 21.5 6V18C21.5 19.933 19.933 21.5 18 21.5H6C4.067 21.5 2.5 19.933 2.5 18V6C2.5 4.067 4.067 2.5 6 2.5Z"\n fill="white" stroke="#595959" />\n </svg>\n <svg class="svg-checked" width="24" height="24" viewBox="0 0 24 24" fill="none"\n xmlns="http://www.w3.org/2000/svg">\n <g clip-path="url(#clip0_20014_9555)">\n <path\n d="M2 6C2 3.79086 3.79086 2 6 2H18C20.2091 2 22 3.79086 22 6V18C22 20.2091 20.2091 22 18 22H6C3.79086 22 2 20.2091 2 18V6Z"\n fill="#333333" />\n <path d="M10.2857 17L6 12.6814L7.20857 11.4635L10.2857 14.5557L16.7914 8L18 9.22649L10.2857 17Z"\n fill="white" />\n </g>\n <defs>\n <clipPath id="clip0_20014_9555">\n <path\n d="M2 6C2 3.79086 3.79086 2 6 2H18C20.2091 2 22 3.79086 22 6V18C22 20.2091 20.2091 22 18 22H6C3.79086 22 2 20.2091 2 18V6Z"\n fill="white" />\n </clipPath>\n </defs>\n </svg>\n <svg class="svg-error" width="24" height="24" viewBox="0 0 24 24" fill="none"\n xmlns="http://www.w3.org/2000/svg">\n <path\n d="M6 2.5H18C19.933 2.5 21.5 4.067 21.5 6V18C21.5 19.933 19.933 21.5 18 21.5H6C4.067 21.5 2.5 19.933 2.5 18V6C2.5 4.067 4.067 2.5 6 2.5Z"\n fill="white" stroke="#C91D42" stroke-width="2" />\n </svg>\n </span>\n </div>\n <span id="apple-pay-modal-checkbox-text"></span>\n </label>\n <div class="apm__error" id="apple-pay-modal-error" role="alert">\n <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">\n <path fill-rule="evenodd" clip-rule="evenodd"\n d="M10 2C5.58172 2 2 5.58172 2 10C2 14.4183 5.58172 18 10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2ZM0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10ZM11 13C11 13.5523 10.5523 14 10 14C9.44772 14 9 13.5523 9 13C9 12.4477 9.44772 12 10 12C10.5523 12 11 12.4477 11 13ZM10 6C10.5523 6 11 6.44772 11 7V10C11 10.5523 10.5523 11 10 11C9.44772 11 9 10.5523 9 10V7C9 6.44772 9.44772 6 10 6Z"\n fill="#C91D42" />\n </svg>\n Please accept the terms to continue\n </div>\n <button class="apm__cta" id="apple-pay-modal-cta">\n Continue with\n <svg width="51" height="22" aria-label="Apple Pay" role="img">\n <use href="#apple-pay-logo" />\n </svg>\n </button>\n </div>\n</div>';
|
|
79
|
+
var apple_pay_modal_template_default = '<style>\n .apm-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 97;\n background: rgba(13, 13, 13, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--wall-gutter, 2rem);\n box-sizing: border-box;\n }\n\n .apm {\n position: relative;\n background: #fff;\n border-radius: 0.5rem 0.5rem 0 0;\n border: 0.0625rem solid #d9d9d9;\n padding: var(--mb-spacing-lg, 1.5rem);\n width: 25rem;\n max-width: 25rem;\n box-sizing: border-box;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n box-shadow: 0 0.25rem 1rem 0 rgba(13, 13, 13, 0.15);\n }\n\n .apm__close {\n position: absolute;\n top: 1.5rem;\n right: 1.3125rem;\n background: none;\n border: none;\n cursor: pointer;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .apm__close:focus {\n outline: solid 0.125rem var(--focus-border-color, #121212);\n border-radius: 0.25rem;\n }\n\n .apm__offer-name {\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-title-3-size, 1.25rem);\n font-weight: 500;\n color: var(--mb-colour-greyscale-london-5, #0d0d0d);\n line-height: var(--mb-typestyle-title-3-leading, 1.75rem);\n font-style: normal;\n margin: 0 1.5rem 0.5rem 0;\n }\n\n .apm__due-today {\n color: #1a1a1a;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n font-style: normal;\n font-weight: 400;\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n margin: 0 0 0.75rem 0;\n }\n\n .apm__rule {\n border: none;\n border-top: 0.0625rem solid #d9d9d9;\n margin: 0 0 1rem 0;\n }\n\n .apm__terms {\n color: #1a1a1a;\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n font-style: normal;\n font-weight: 400;\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n margin: 0 0 1rem 0;\n }\n\n .apm__terms-bullets {\n list-style: disc outside;\n padding-left: 1.25rem;\n margin: 0 0 0.75rem 0;\n }\n\n .apm__terms-bullets li {\n margin-bottom: 0.375rem;\n }\n\n .apm__terms-legal {\n margin: 0;\n }\n\n .apm__passive-terms {\n font-family: var(--wall-type-system-sans);\n font-size: 0.9375rem;\n color: #0d0d0d;\n line-height: 1.4;\n margin: 0 0 8px 0;\n }\n\n .apm__passive-terms a {\n color: #1a1a1a;\n font-weight: 700;\n text-decoration: underline;\n }\n\n .apm__checkbox-label {\n display: flex;\n align-items: flex-start;\n gap: var(--mb-spacing-sm, 0.5rem);\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: var(--mb-typestyle-label-2-size, 0.9375rem);\n color: var(--mb-colour-greyscale-london-20, #1a1a1a);\n line-height: var(--mb-typestyle-label-2-leading, 1.25rem);\n cursor: pointer;\n margin: 0 0 1.5rem 0;\n }\n\n .apm__checkbox-label--error {\n margin-bottom: 0.25rem;\n }\n\n .apm__checkbox-wrapper {\n position: relative;\n flex-shrink: 0;\n width: 1.25rem;\n height: 1.25rem;\n margin-top: 0.0625rem;\n }\n\n .apm__checkbox-input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n cursor: pointer;\n margin: 0;\n z-index: 1;\n }\n\n .apm__checkbox-custom {\n position: absolute;\n top: 0;\n left: 0;\n width: 1.25rem;\n height: 1.25rem;\n pointer-events: none;\n }\n\n .apm__checkbox-custom svg {\n display: none;\n width: 1.25rem;\n height: 1.25rem;\n }\n\n .apm__checkbox-input:not(:checked) ~ .apm__checkbox-custom .svg-unchecked {\n display: block;\n }\n\n .apm__checkbox-input:checked ~ .apm__checkbox-custom .svg-checked {\n display: block;\n }\n\n .apm__checkbox-label--error\n .apm__checkbox-input:not(:checked)\n ~ .apm__checkbox-custom\n .svg-unchecked {\n display: none;\n }\n\n .apm__checkbox-label--error\n .apm__checkbox-input:not(:checked)\n ~ .apm__checkbox-custom\n .svg-error {\n display: block;\n }\n\n .apm__checkbox-input:focus-visible ~ .apm__checkbox-custom {\n outline: solid 0.125rem #c91d42;\n outline-offset: 0.125rem;\n border-radius: 0.25rem;\n }\n\n .apm__checkbox-label a {\n color: #1a1a1a;\n font-weight: 700;\n text-decoration: underline;\n }\n\n .apm__error {\n display: none;\n align-items: center;\n gap: 0.75rem;\n color: var(--mb-colour-greyscale-london-5, #0d0d0d);\n font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));\n font-size: 1.0625rem;\n font-style: normal;\n font-weight: 500;\n line-height: 1.5rem;\n margin-bottom: 1rem;\n }\n\n .apm__error--visible {\n display: flex;\n }\n\n .apm__cta {\n display: flex;\n width: 100%;\n height: 2.75rem;\n padding: 0.375rem 1rem;\n justify-content: center;\n align-items: center;\n gap: 0.375rem;\n border-radius: 0.25rem;\n background: #000;\n border: none;\n cursor: pointer;\n color: #fff;\n text-align: center;\n font-family: var(\n --mb-typeface-sans,\n var(--wall-type-system-sans, system-ui, sans-serif)\n );\n font-size: 1.25rem;\n font-style: normal;\n font-weight: 500;\n line-height: normal;\n }\n\n .apm__cta svg {\n flex-shrink: 0;\n display: block;\n }\n\n @media (min-width: 22.5rem) and (max-width: 37.5rem) {\n .apm {\n width: 24.375rem;\n max-width: 24.375rem;\n }\n }\n\n @media (min-width: 37.5625rem) {\n .apm {\n border-radius: 0.5rem;\n }\n }\n\n @media (max-width: 22.4375rem) {\n .apm {\n width: 20rem;\n max-width: 20rem;\n }\n }\n\n @media (max-width: 26.1875rem) {\n .apm__cta {\n font-size: 1.125rem;\n }\n }\n</style>\n\n<div class="apm-overlay" id="apple-pay-modal-overlay">\n <div\n class="apm"\n role="dialog"\n aria-modal="true"\n aria-labelledby="apple-pay-modal-offer-name"\n id="apple-pay-modal"\n tabindex="-1"\n >\n <button class="apm__close" id="apple-pay-modal-close" aria-label="Close">\n <svg\n aria-hidden="true"\n xmlns="http://www.w3.org/2000/svg"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n >\n <g clip-path="url(#clip0_15712_93054)">\n <rect width="24" height="24" fill="white" fill-opacity="0.01" />\n <path\n fill-rule="evenodd"\n clip-rule="evenodd"\n d="M0 0H24V24H0V0Z"\n fill="white"\n fill-opacity="0.01"\n />\n <path\n fill-rule="evenodd"\n clip-rule="evenodd"\n d="M18.75 6.60964L17.3904 5.25L12 10.6404L6.60964 5.25L5.25 6.60964L10.6404 12L5.25 17.3904L6.60964 18.75L12 13.3596L17.3904 18.75L18.75 17.3904L13.3596 12L18.75 6.60964Z"\n fill="#333333"\n />\n </g>\n <defs>\n <clipPath id="clip0_15712_93054">\n <rect width="24" height="24" fill="white" />\n </clipPath>\n </defs>\n </svg>\n </button>\n <p class="apm__offer-name" id="apple-pay-modal-offer-name"></p>\n <p class="apm__due-today" id="apple-pay-modal-due-today"></p>\n <hr class="apm__rule" />\n <div class="apm__terms" id="apple-pay-modal-terms"></div>\n <p class="apm__passive-terms" id="apple-pay-modal-passive-terms"></p>\n <label\n class="apm__checkbox-label"\n id="apple-pay-modal-checkbox-label"\n for="apple-pay-modal-checkbox"\n >\n <div class="apm__checkbox-wrapper">\n <input\n class="apm__checkbox-input"\n type="checkbox"\n id="apple-pay-modal-checkbox"\n />\n <span class="apm__checkbox-custom">\n <svg\n aria-hidden="true"\n class="svg-unchecked"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n >\n <path\n d="M6 2.5H18C19.933 2.5 21.5 4.067 21.5 6V18C21.5 19.933 19.933 21.5 18 21.5H6C4.067 21.5 2.5 19.933 2.5 18V6C2.5 4.067 4.067 2.5 6 2.5Z"\n fill="white"\n stroke="#595959"\n />\n </svg>\n <svg\n aria-hidden="true"\n class="svg-checked"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n >\n <g clip-path="url(#clip0_20014_9555)">\n <path\n d="M2 6C2 3.79086 3.79086 2 6 2H18C20.2091 2 22 3.79086 22 6V18C22 20.2091 20.2091 22 18 22H6C3.79086 22 2 20.2091 2 18V6Z"\n fill="#333333"\n />\n <path\n d="M10.2857 17L6 12.6814L7.20857 11.4635L10.2857 14.5557L16.7914 8L18 9.22649L10.2857 17Z"\n fill="white"\n />\n </g>\n <defs>\n <clipPath id="clip0_20014_9555">\n <path\n d="M2 6C2 3.79086 3.79086 2 6 2H18C20.2091 2 22 3.79086 22 6V18C22 20.2091 20.2091 22 18 22H6C3.79086 22 2 20.2091 2 18V6Z"\n fill="white"\n />\n </clipPath>\n </defs>\n </svg>\n <svg\n aria-hidden="true"\n class="svg-error"\n width="24"\n height="24"\n viewBox="0 0 24 24"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n >\n <path\n d="M6 2.5H18C19.933 2.5 21.5 4.067 21.5 6V18C21.5 19.933 19.933 21.5 18 21.5H6C4.067 21.5 2.5 19.933 2.5 18V6C2.5 4.067 4.067 2.5 6 2.5Z"\n fill="white"\n stroke="#C91D42"\n stroke-width="2"\n />\n </svg>\n </span>\n </div>\n <span id="apple-pay-modal-checkbox-text"></span>\n </label>\n <div class="apm__error" id="apple-pay-modal-error" role="alert">\n <svg\n aria-hidden="true"\n width="20"\n height="20"\n viewBox="0 0 20 20"\n fill="none"\n xmlns="http://www.w3.org/2000/svg"\n >\n <path\n fill-rule="evenodd"\n clip-rule="evenodd"\n d="M10 2C5.58172 2 2 5.58172 2 10C2 14.4183 5.58172 18 10 18C14.4183 18 18 14.4183 18 10C18 5.58172 14.4183 2 10 2ZM0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10ZM11 13C11 13.5523 10.5523 14 10 14C9.44772 14 9 13.5523 9 13C9 12.4477 9.44772 12 10 12C10.5523 12 11 12.4477 11 13ZM10 6C10.5523 6 11 6.44772 11 7V10C11 10.5523 10.5523 11 10 11C9.44772 11 9 10.5523 9 10V7C9 6.44772 9.44772 6 10 6Z"\n fill="#C91D42"\n />\n </svg>\n Please accept the terms to continue\n </div>\n <button class="apm__cta" id="apple-pay-modal-cta">\n Continue with\n <svg width="51" height="22" aria-label="Apple Pay" role="img">\n <use href="#apple-pay-logo" />\n </svg>\n </button>\n </div>\n</div>\n\n';
|
|
80
80
|
|
|
81
81
|
// src/apple-pay-modal.ts
|
|
82
82
|
function getRenewalDateText(unit, duration) {
|
|
@@ -142,6 +142,12 @@ function getCheckboxTermsHtml(isNYT, isLoggedIn, autoRenewal) {
|
|
|
142
142
|
}
|
|
143
143
|
return `I accept the ${TERMS_LINK}${autoRenewalClause} and acknowledge the ${PRIVACY_LINK}, and understand that by continuing, an account will be created for me if I don\u2019t already have one.`;
|
|
144
144
|
}
|
|
145
|
+
function getPassiveTermsHtml(isNYT, isLoggedIn, autoRenewal) {
|
|
146
|
+
const accountClause = isLoggedIn ? " " : "\u2019ll get an account and ";
|
|
147
|
+
const autoRenewalClause = autoRenewal ? "above auto-renewal terms, " : "";
|
|
148
|
+
const nytPrefix = isNYT ? " your subscription to The Economist," : "";
|
|
149
|
+
return `By purchasing${nytPrefix} you${accountClause}agree to the ${autoRenewalClause}${TERMS_LINK} and acknowledge the ${PRIVACY_LINK}.`;
|
|
150
|
+
}
|
|
145
151
|
function renewalPeriodText(unit, duration) {
|
|
146
152
|
switch (unit) {
|
|
147
153
|
case "week":
|
|
@@ -364,10 +370,21 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
364
370
|
const errorElement = this.querySelector(
|
|
365
371
|
"#apple-pay-modal-error"
|
|
366
372
|
);
|
|
373
|
+
const passiveTermsElement = this.querySelector(
|
|
374
|
+
"#apple-pay-modal-passive-terms"
|
|
375
|
+
);
|
|
367
376
|
if (!isUS) {
|
|
368
377
|
checkboxLabel?.remove();
|
|
369
378
|
errorElement?.remove();
|
|
379
|
+
if (passiveTermsElement) {
|
|
380
|
+
passiveTermsElement.innerHTML = getPassiveTermsHtml(
|
|
381
|
+
isOfferNYT,
|
|
382
|
+
isLoggedIn,
|
|
383
|
+
isAutoRenewal
|
|
384
|
+
);
|
|
385
|
+
}
|
|
370
386
|
} else {
|
|
387
|
+
passiveTermsElement?.remove();
|
|
371
388
|
checkbox?.addEventListener("change", () => {
|
|
372
389
|
if (checkbox.checked) {
|
|
373
390
|
errorElement?.classList.remove("apm__error--visible");
|
|
@@ -524,6 +541,27 @@ var apiFetch = async (path, options = {}) => {
|
|
|
524
541
|
}
|
|
525
542
|
return response.json();
|
|
526
543
|
};
|
|
544
|
+
var getPaymentOptions = (data) => {
|
|
545
|
+
const { sku, countryCode, currencyCode, checkoutUrl } = data;
|
|
546
|
+
const params = new URLSearchParams({ checkoutUrl });
|
|
547
|
+
return apiFetch(
|
|
548
|
+
`/v1/wallet/payment-options/sku/${sku}/${countryCode}/${currencyCode}?${params}`,
|
|
549
|
+
{ method: "GET" }
|
|
550
|
+
);
|
|
551
|
+
};
|
|
552
|
+
var validateAppleMerchant = (data) => {
|
|
553
|
+
return apiFetch("/v1/wallet/validate-merchant", {
|
|
554
|
+
method: "POST",
|
|
555
|
+
body: JSON.stringify(data)
|
|
556
|
+
});
|
|
557
|
+
};
|
|
558
|
+
var performPurchase = (data) => {
|
|
559
|
+
return apiFetch("/v1/wallet/purchase", {
|
|
560
|
+
method: "POST",
|
|
561
|
+
body: JSON.stringify(data),
|
|
562
|
+
credentials: "include"
|
|
563
|
+
});
|
|
564
|
+
};
|
|
527
565
|
|
|
528
566
|
// src/clients/payment-checkout/session.ts
|
|
529
567
|
var getExpressCheckoutSession = () => {
|
|
@@ -533,9 +571,732 @@ var getExpressCheckoutSession = () => {
|
|
|
533
571
|
});
|
|
534
572
|
};
|
|
535
573
|
|
|
574
|
+
// src/utils/expressCheckoutRedirect.ts
|
|
575
|
+
var captureOriginUrl = () => window.location.href;
|
|
576
|
+
var buildQueryString = () => {
|
|
577
|
+
const countryCode = new URLSearchParams(window.location.search).get(
|
|
578
|
+
"country"
|
|
579
|
+
);
|
|
580
|
+
return countryCode ? `&country=${encodeURIComponent(countryCode)}` : "";
|
|
581
|
+
};
|
|
582
|
+
var decodeSku = (sku) => {
|
|
583
|
+
try {
|
|
584
|
+
return atob(sku.substring(sku.indexOf("-") + 1));
|
|
585
|
+
} catch {
|
|
586
|
+
return sku;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
var redirectToCheckoutComplete = (params) => {
|
|
590
|
+
const url = new URL("/checkout/complete", window.location.origin);
|
|
591
|
+
url.searchParams.set("basketId", params.basketId);
|
|
592
|
+
if (params.queryString) {
|
|
593
|
+
const qs = params.queryString.startsWith("?") ? params.queryString.slice(1) : params.queryString;
|
|
594
|
+
new URLSearchParams(qs).forEach((v, k) => url.searchParams.set(k, v));
|
|
595
|
+
}
|
|
596
|
+
url.searchParams.set("lmo_offer", params.skuId);
|
|
597
|
+
url.searchParams.set("status", params.status);
|
|
598
|
+
url.searchParams.set("redirectToCheckout", "true");
|
|
599
|
+
url.searchParams.set("returnUrl", params.returnUrl);
|
|
600
|
+
return url.pathname + url.search;
|
|
601
|
+
};
|
|
602
|
+
var redirectToCheckoutFallback = (params) => {
|
|
603
|
+
const url = new URL(window.location.href);
|
|
604
|
+
url.pathname = "/checkout";
|
|
605
|
+
url.searchParams.set("lmo_offer", params.skuId);
|
|
606
|
+
if (params.inlineError)
|
|
607
|
+
url.searchParams.set("inlineError", params.inlineError);
|
|
608
|
+
if (params.email) url.searchParams.set("email", params.email);
|
|
609
|
+
if (params.emailError) url.searchParams.set("emailError", params.emailError);
|
|
610
|
+
url.searchParams.set("returnUrl", params.returnUrl);
|
|
611
|
+
return url.pathname + url.search;
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
// src/utils/recaptcha.ts
|
|
615
|
+
var loadRecaptchaScript = (siteKey) => {
|
|
616
|
+
return new Promise((resolve, reject) => {
|
|
617
|
+
if (typeof grecaptcha !== "undefined") return resolve();
|
|
618
|
+
const existingScript = document.querySelector(
|
|
619
|
+
'script[src*="recaptcha/api.js"]'
|
|
620
|
+
);
|
|
621
|
+
if (existingScript) {
|
|
622
|
+
existingScript.addEventListener("load", () => resolve());
|
|
623
|
+
existingScript.addEventListener(
|
|
624
|
+
"error",
|
|
625
|
+
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
626
|
+
);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const script = document.createElement("script");
|
|
630
|
+
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
631
|
+
script.async = true;
|
|
632
|
+
script.defer = true;
|
|
633
|
+
script.onload = () => resolve();
|
|
634
|
+
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
635
|
+
document.head.appendChild(script);
|
|
636
|
+
});
|
|
637
|
+
};
|
|
638
|
+
var getRecaptchaToken = async (action) => {
|
|
639
|
+
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
640
|
+
if (!siteKey) {
|
|
641
|
+
throw new Error(
|
|
642
|
+
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
await loadRecaptchaScript(siteKey);
|
|
646
|
+
return new Promise((resolve, reject) => {
|
|
647
|
+
grecaptcha.ready(() => {
|
|
648
|
+
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
};
|
|
652
|
+
var getPurchaseRecaptchaTokens = async () => {
|
|
653
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
654
|
+
getRecaptchaToken("checkEligibility"),
|
|
655
|
+
getRecaptchaToken("newBasket"),
|
|
656
|
+
getRecaptchaToken("registerUser")
|
|
657
|
+
]);
|
|
658
|
+
return { checkEligibility, newBasket, registerUser };
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
// src/utils/expressCheckoutTracking.ts
|
|
662
|
+
var EXPRESS_CHECKOUT_EVENTS = {
|
|
663
|
+
/** Wallet button displayed */
|
|
664
|
+
IMPRESSION: "express_wallet_impression",
|
|
665
|
+
/** Wallet button clicked */
|
|
666
|
+
BUTTON_CLICK: "express_wallet_click",
|
|
667
|
+
/** Apple Pay sheet opened */
|
|
668
|
+
SHEET_OPEN: "wallet_payment_sheet_open",
|
|
669
|
+
/** Payment authorised by the user in the Apple Pay sheet */
|
|
670
|
+
AUTHORISED: "wallet_payment_authorised",
|
|
671
|
+
/** Purchase completed — BFF returned success */
|
|
672
|
+
SUCCESS: "wallet_payment_success",
|
|
673
|
+
/** Payment failed — BFF returned error */
|
|
674
|
+
FAILURE: "wallet_payment_failure",
|
|
675
|
+
/** Redirect to standard checkout (any fallback path) */
|
|
676
|
+
FALLBACK_CHECKOUT: "fallback_checkout"
|
|
677
|
+
};
|
|
678
|
+
var getDevice = () => {
|
|
679
|
+
const ua = navigator.userAgent;
|
|
680
|
+
if (/tablet|ipad/i.test(ua)) return "tablet";
|
|
681
|
+
if (/mobile|iphone|android/i.test(ua)) return "mobile";
|
|
682
|
+
return "desktop";
|
|
683
|
+
};
|
|
684
|
+
var emitTedlEvent = (event) => {
|
|
685
|
+
window.tedl = window.tedl ?? {};
|
|
686
|
+
window.tedl.events = window.tedl.events ?? [];
|
|
687
|
+
window.tedl.events.push(event);
|
|
688
|
+
};
|
|
689
|
+
var track = (action, extra) => {
|
|
690
|
+
emitTedlEvent({
|
|
691
|
+
name: "user.journey",
|
|
692
|
+
type: "checkout",
|
|
693
|
+
action,
|
|
694
|
+
info: "applepay",
|
|
695
|
+
...extra
|
|
696
|
+
});
|
|
697
|
+
};
|
|
698
|
+
var trackExpressWalletImpression = (skuId, { entryPoint, country }) => {
|
|
699
|
+
track(EXPRESS_CHECKOUT_EVENTS.IMPRESSION, {
|
|
700
|
+
wallet_type: "apple_pay",
|
|
701
|
+
entry_point: entryPoint,
|
|
702
|
+
sku_id: skuId,
|
|
703
|
+
country,
|
|
704
|
+
device: getDevice()
|
|
705
|
+
});
|
|
706
|
+
};
|
|
707
|
+
var trackExpressWalletClick = (skuId) => {
|
|
708
|
+
track(EXPRESS_CHECKOUT_EVENTS.BUTTON_CLICK, { sku_id: skuId });
|
|
709
|
+
};
|
|
710
|
+
var trackWalletPaymentSheetOpen = (skuId) => {
|
|
711
|
+
track(EXPRESS_CHECKOUT_EVENTS.SHEET_OPEN, { sku_id: skuId });
|
|
712
|
+
};
|
|
713
|
+
var trackWalletPaymentAuthorised = (skuId) => {
|
|
714
|
+
track(EXPRESS_CHECKOUT_EVENTS.AUTHORISED, { sku_id: skuId });
|
|
715
|
+
};
|
|
716
|
+
var trackWalletPaymentSuccess = (skuId, basketId) => {
|
|
717
|
+
track(EXPRESS_CHECKOUT_EVENTS.SUCCESS, {
|
|
718
|
+
sku_id: skuId,
|
|
719
|
+
basket_id: basketId
|
|
720
|
+
});
|
|
721
|
+
};
|
|
722
|
+
var trackWalletPaymentFailure = (skuId, reason) => {
|
|
723
|
+
track(EXPRESS_CHECKOUT_EVENTS.FAILURE, {
|
|
724
|
+
sku_id: skuId,
|
|
725
|
+
failure_reason: reason
|
|
726
|
+
});
|
|
727
|
+
};
|
|
728
|
+
var trackFallbackCheckout = (skuId) => {
|
|
729
|
+
track(EXPRESS_CHECKOUT_EVENTS.FALLBACK_CHECKOUT, { sku_id: skuId });
|
|
730
|
+
};
|
|
731
|
+
var trackValidationFieldError = (field, errorType) => {
|
|
732
|
+
track(`validation_${field}_${errorType}`, {});
|
|
733
|
+
};
|
|
734
|
+
|
|
735
|
+
// src/clients/payment-checkout/messages.ts
|
|
736
|
+
var MSG_MISSING_REQUIRED_FIELD = "Missing required field";
|
|
737
|
+
var MSG_EMAIL_WRONG_FORMAT = "That email doesn\u2019t look right. Please enter a valid email address.";
|
|
738
|
+
var MSG_EMAIL_TOO_LONG = "This field must be 80 characters or less.";
|
|
739
|
+
var MSG_UNSUPPORTED_BILLING_COUNTRY = "Billing country does not match offer price zone";
|
|
740
|
+
var MSG_PAYMENT_GENERIC_ERROR = "Something went wrong. Please check your payment and billing details and try again or contact us.";
|
|
741
|
+
var MSG_RECAPTCHA_VALIDATION_FAILED = "Security check failed. Please try again.";
|
|
742
|
+
var MSG_EMAIL_COLLISION = "This email address is already linked to an account. Please log in, or try a different email address.";
|
|
743
|
+
var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
|
|
744
|
+
|
|
745
|
+
// src/clients/payment-checkout/apple-session.ts
|
|
746
|
+
function startApplePaySession(config) {
|
|
747
|
+
const paymentRequest = {
|
|
748
|
+
countryCode: config.countryCode,
|
|
749
|
+
currencyCode: config.currencyCode,
|
|
750
|
+
merchantCapabilities: ["supports3DS"],
|
|
751
|
+
supportedNetworks: config.supportedNetworks,
|
|
752
|
+
total: {
|
|
753
|
+
label: config.totalLabel,
|
|
754
|
+
amount: config.totalAmount
|
|
755
|
+
},
|
|
756
|
+
requiredBillingContactFields: ["postalAddress", "name"],
|
|
757
|
+
// Only request email from Apple Pay sheet if user is NOT logged in
|
|
758
|
+
...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
|
|
759
|
+
};
|
|
760
|
+
const session = new ApplePaySession(3, paymentRequest);
|
|
761
|
+
session.onvalidatemerchant = async (event) => {
|
|
762
|
+
try {
|
|
763
|
+
const validationHost = new URL(event.validationURL).host;
|
|
764
|
+
if (!validationHost.endsWith("apple.com")) {
|
|
765
|
+
trackFallbackCheckout(config.skuId);
|
|
766
|
+
session.abort();
|
|
767
|
+
window.location.href = redirectToCheckoutFallback({
|
|
768
|
+
skuId: config.skuId,
|
|
769
|
+
returnUrl: config.returnUrl,
|
|
770
|
+
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
771
|
+
});
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
775
|
+
const response = await validateAppleMerchant({
|
|
776
|
+
validationURL: event.validationURL,
|
|
777
|
+
recaptchaToken
|
|
778
|
+
});
|
|
779
|
+
if (!response?.merchantSession) {
|
|
780
|
+
trackFallbackCheckout(config.skuId);
|
|
781
|
+
session.abort();
|
|
782
|
+
window.location.href = redirectToCheckoutFallback({
|
|
783
|
+
skuId: config.skuId,
|
|
784
|
+
returnUrl: config.returnUrl,
|
|
785
|
+
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
786
|
+
});
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
session.completeMerchantValidation(response.merchantSession);
|
|
790
|
+
} catch {
|
|
791
|
+
trackFallbackCheckout(config.skuId);
|
|
792
|
+
session.abort();
|
|
793
|
+
window.location.href = redirectToCheckoutFallback({
|
|
794
|
+
skuId: config.skuId,
|
|
795
|
+
returnUrl: config.returnUrl,
|
|
796
|
+
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
session.onpaymentauthorized = (event) => {
|
|
801
|
+
trackWalletPaymentAuthorised(config.skuId);
|
|
802
|
+
config.onPaymentAuthorized(event, session);
|
|
803
|
+
};
|
|
804
|
+
session.oncancel = () => {
|
|
805
|
+
config.onCancel?.();
|
|
806
|
+
};
|
|
807
|
+
trackWalletPaymentSheetOpen(config.skuId);
|
|
808
|
+
session.begin();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// src/clients/payment-checkout/contact-validation.ts
|
|
812
|
+
var pushFieldError = (errors, billingOrShippingContact = "billing", field, errorType, errorText) => {
|
|
813
|
+
const errorCode = billingOrShippingContact === "billing" ? "billingContactInvalid" : "shippingContactInvalid";
|
|
814
|
+
errors.push(new ApplePayError(errorCode, field, errorText));
|
|
815
|
+
trackValidationFieldError(field, errorType);
|
|
816
|
+
return "";
|
|
817
|
+
};
|
|
818
|
+
var pushRequiredFieldError = (errors, billingOrShippingContact = "billing", field) => {
|
|
819
|
+
return pushFieldError(
|
|
820
|
+
errors,
|
|
821
|
+
billingOrShippingContact,
|
|
822
|
+
field,
|
|
823
|
+
"required",
|
|
824
|
+
MSG_MISSING_REQUIRED_FIELD
|
|
825
|
+
);
|
|
826
|
+
};
|
|
827
|
+
var ContactValidationError = class extends Error {
|
|
828
|
+
constructor(errors) {
|
|
829
|
+
super(MSG_CONTACT_VALIDATION_INTERNAL);
|
|
830
|
+
this.errors = errors;
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
// src/clients/payment-checkout/billing-contact-details.ts
|
|
835
|
+
var countriesWithoutCityField = ["KY", "CK", "NR", "TC"];
|
|
836
|
+
var countriesRequiringAllFields = ["US", "CA", "GB"];
|
|
837
|
+
var resolveCountryConfig = (country) => {
|
|
838
|
+
const hasCityField = !countriesWithoutCityField.includes(country);
|
|
839
|
+
const requiresStateAndPostcode = countriesRequiringAllFields.includes(country);
|
|
840
|
+
return {
|
|
841
|
+
hasCityField,
|
|
842
|
+
requiresStateAndPostcode,
|
|
843
|
+
// if there is no city field, we require state and use it in place of city
|
|
844
|
+
requiresStateField: !hasCityField || requiresStateAndPostcode
|
|
845
|
+
};
|
|
846
|
+
};
|
|
847
|
+
var validateAddressFields = (errors, contact, { hasCityField, requiresStateAndPostcode, requiresStateField }) => {
|
|
848
|
+
const locality = !hasCityField ? contact.administrativeArea : contact.locality;
|
|
849
|
+
if (!contact.addressLines?.at(0)?.trim()) {
|
|
850
|
+
pushRequiredFieldError(errors, "billing", "addressLines");
|
|
851
|
+
}
|
|
852
|
+
if (!locality?.trim() && hasCityField) {
|
|
853
|
+
pushRequiredFieldError(errors, "billing", "locality");
|
|
854
|
+
}
|
|
855
|
+
if (!contact.administrativeArea?.trim() && requiresStateField) {
|
|
856
|
+
pushRequiredFieldError(errors, "billing", "administrativeArea");
|
|
857
|
+
}
|
|
858
|
+
if (!contact.postalCode?.trim() && requiresStateAndPostcode) {
|
|
859
|
+
pushRequiredFieldError(errors, "billing", "postalCode");
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
var validateNameFields = (errors, contact) => {
|
|
863
|
+
const firstName = contact.givenName?.trim() || "";
|
|
864
|
+
const lastName = contact.familyName?.trim() || "";
|
|
865
|
+
if (!firstName || !lastName) {
|
|
866
|
+
pushRequiredFieldError(errors, "billing", "name");
|
|
867
|
+
}
|
|
868
|
+
return { firstName, lastName };
|
|
869
|
+
};
|
|
870
|
+
var validateCountrySupport = (errors, country, supportedBillingCountries) => {
|
|
871
|
+
if (!supportedBillingCountries.includes(country)) {
|
|
872
|
+
pushFieldError(
|
|
873
|
+
errors,
|
|
874
|
+
"billing",
|
|
875
|
+
"countryCode",
|
|
876
|
+
"not-supported",
|
|
877
|
+
MSG_UNSUPPORTED_BILLING_COUNTRY
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
var billingContactDetails = (contact, supportedBillingCountries) => {
|
|
882
|
+
const errors = [];
|
|
883
|
+
const country = contact.countryCode?.toUpperCase() || pushRequiredFieldError(errors, "billing", "country");
|
|
884
|
+
const config = resolveCountryConfig(country);
|
|
885
|
+
validateAddressFields(errors, contact, config);
|
|
886
|
+
const { firstName, lastName } = validateNameFields(errors, contact);
|
|
887
|
+
validateCountrySupport(errors, country, supportedBillingCountries);
|
|
888
|
+
if (errors.length > 0) {
|
|
889
|
+
throw new ContactValidationError(errors);
|
|
890
|
+
}
|
|
891
|
+
return { firstName, lastName };
|
|
892
|
+
};
|
|
893
|
+
|
|
894
|
+
// src/clients/payment-checkout/shipping-contact-details.ts
|
|
895
|
+
var EMAIL_FORMAT_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
896
|
+
var shippingContactDetails = (shippingContact) => {
|
|
897
|
+
const errors = [];
|
|
898
|
+
let emailAddress = "";
|
|
899
|
+
if (shippingContact?.emailAddress === void 0 || shippingContact?.emailAddress === "") {
|
|
900
|
+
pushRequiredFieldError(errors, "shipping", "emailAddress");
|
|
901
|
+
} else {
|
|
902
|
+
emailAddress = shippingContact.emailAddress.trim();
|
|
903
|
+
if (emailAddress.length > 80) {
|
|
904
|
+
pushFieldError(
|
|
905
|
+
errors,
|
|
906
|
+
"shipping",
|
|
907
|
+
"emailAddress",
|
|
908
|
+
"format",
|
|
909
|
+
MSG_EMAIL_TOO_LONG
|
|
910
|
+
);
|
|
911
|
+
} else if (!EMAIL_FORMAT_REGEX.test(emailAddress)) {
|
|
912
|
+
pushFieldError(
|
|
913
|
+
errors,
|
|
914
|
+
"shipping",
|
|
915
|
+
"emailAddress",
|
|
916
|
+
"format",
|
|
917
|
+
MSG_EMAIL_WRONG_FORMAT
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
if (errors.length > 0) {
|
|
922
|
+
throw new ContactValidationError(errors);
|
|
923
|
+
}
|
|
924
|
+
return { emailAddress };
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
// src/clients/payment-checkout/payment-handler.ts
|
|
928
|
+
var MSG_ELIGIBILITY = "Something went wrong, please try again";
|
|
929
|
+
var MSG_REGISTRATION = "We couldn't create your account. Please enter your email or try a different login method.";
|
|
930
|
+
var failApplePaySession = (session, errors = []) => {
|
|
931
|
+
session.completePayment({
|
|
932
|
+
status: ApplePaySession.STATUS_FAILURE,
|
|
933
|
+
errors
|
|
934
|
+
});
|
|
935
|
+
};
|
|
936
|
+
function parseWalletApiError(err) {
|
|
937
|
+
const body = err instanceof ApiError ? err.body : err;
|
|
938
|
+
if (body && typeof body === "object" && "errorType" in body && "message" in body) {
|
|
939
|
+
return body;
|
|
940
|
+
}
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
function validateContacts(event, sessionData, supportedBillingCountries) {
|
|
944
|
+
billingContactDetails(
|
|
945
|
+
event.payment.billingContact,
|
|
946
|
+
supportedBillingCountries
|
|
947
|
+
);
|
|
948
|
+
if (!sessionData.loggedIn) {
|
|
949
|
+
return shippingContactDetails(event.payment.shippingContact).emailAddress;
|
|
950
|
+
}
|
|
951
|
+
return void 0;
|
|
952
|
+
}
|
|
953
|
+
function redirectFallback(skuId, returnUrl, options) {
|
|
954
|
+
trackFallbackCheckout(skuId);
|
|
955
|
+
window.location.href = redirectToCheckoutFallback({
|
|
956
|
+
skuId,
|
|
957
|
+
returnUrl,
|
|
958
|
+
...options
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
var ERROR_HANDLERS = {
|
|
962
|
+
EMAIL_COLLISION: ({ skuId, returnUrl, validatedEmail }) => redirectFallback(skuId, returnUrl, {
|
|
963
|
+
emailError: MSG_EMAIL_COLLISION,
|
|
964
|
+
email: validatedEmail
|
|
965
|
+
}),
|
|
966
|
+
PAYMENT_DECLINED: ({ skuId, returnUrl }) => (
|
|
967
|
+
// Matches sma-checkout catch-all: always redirect with generic inlineError.
|
|
968
|
+
redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR })
|
|
969
|
+
),
|
|
970
|
+
ELIGIBILITY_REJECTED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_ELIGIBILITY }),
|
|
971
|
+
REGISTRATION_FAILED: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_REGISTRATION }),
|
|
972
|
+
VALIDATION_ERROR: ({ skuId, returnUrl }) => redirectFallback(skuId, returnUrl, { inlineError: MSG_RECAPTCHA_VALIDATION_FAILED })
|
|
973
|
+
};
|
|
974
|
+
function handleStructuredError(ctx) {
|
|
975
|
+
const handler = ERROR_HANDLERS[ctx.walletError.errorType];
|
|
976
|
+
if (handler) {
|
|
977
|
+
handler(ctx);
|
|
978
|
+
} else {
|
|
979
|
+
redirectFallback(ctx.skuId, ctx.returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
983
|
+
const { skuId, returnUrl, queryString } = config;
|
|
984
|
+
let validatedEmail;
|
|
985
|
+
try {
|
|
986
|
+
validatedEmail = validateContacts(
|
|
987
|
+
event,
|
|
988
|
+
sessionData,
|
|
989
|
+
config.supportedBillingCountries
|
|
990
|
+
);
|
|
991
|
+
const result = await performPurchase({
|
|
992
|
+
applePayToken: event.payment.token,
|
|
993
|
+
skuId: config.skuId,
|
|
994
|
+
billingContact: event.payment.billingContact,
|
|
995
|
+
...event.payment.shippingContact && {
|
|
996
|
+
shippingContact: event.payment.shippingContact
|
|
997
|
+
},
|
|
998
|
+
recaptchaTokens: config.recaptchaTokens
|
|
999
|
+
});
|
|
1000
|
+
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1001
|
+
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1002
|
+
const userStatus = sessionData.loggedIn ? "lex" : "nex";
|
|
1003
|
+
window.location.href = redirectToCheckoutComplete({
|
|
1004
|
+
basketId: result.basketId,
|
|
1005
|
+
status: userStatus,
|
|
1006
|
+
skuId: decodeSku(skuId),
|
|
1007
|
+
returnUrl,
|
|
1008
|
+
queryString
|
|
1009
|
+
});
|
|
1010
|
+
} catch (err) {
|
|
1011
|
+
if (err instanceof ContactValidationError) {
|
|
1012
|
+
failApplePaySession(session, err.errors);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
|
|
1016
|
+
const walletError = parseWalletApiError(err);
|
|
1017
|
+
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1018
|
+
failApplePaySession(session);
|
|
1019
|
+
if (!walletError) {
|
|
1020
|
+
redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// src/data/price-zones.json
|
|
1028
|
+
var price_zones_default = {
|
|
1029
|
+
"AS-AS": [
|
|
1030
|
+
"BD",
|
|
1031
|
+
"BT",
|
|
1032
|
+
"CK",
|
|
1033
|
+
"FJ",
|
|
1034
|
+
"ID",
|
|
1035
|
+
"KH",
|
|
1036
|
+
"KI",
|
|
1037
|
+
"LA",
|
|
1038
|
+
"LK",
|
|
1039
|
+
"MH",
|
|
1040
|
+
"MM",
|
|
1041
|
+
"MN",
|
|
1042
|
+
"MV",
|
|
1043
|
+
"NC",
|
|
1044
|
+
"NF",
|
|
1045
|
+
"NP",
|
|
1046
|
+
"NR",
|
|
1047
|
+
"PF",
|
|
1048
|
+
"PG",
|
|
1049
|
+
"PK",
|
|
1050
|
+
"PW",
|
|
1051
|
+
"SB",
|
|
1052
|
+
"TH",
|
|
1053
|
+
"TL",
|
|
1054
|
+
"TO",
|
|
1055
|
+
"TV",
|
|
1056
|
+
"VN",
|
|
1057
|
+
"VU",
|
|
1058
|
+
"WS"
|
|
1059
|
+
],
|
|
1060
|
+
"AS-AU": ["AU"],
|
|
1061
|
+
"AS-CN": ["CN"],
|
|
1062
|
+
"AS-HK": ["HK", "MO"],
|
|
1063
|
+
"AS-IN": ["IN"],
|
|
1064
|
+
"AS-JP": ["JP"],
|
|
1065
|
+
"AS-KR": ["KR"],
|
|
1066
|
+
"AS-MY": ["MY"],
|
|
1067
|
+
"AS-NZ": ["NZ"],
|
|
1068
|
+
"AS-PH": ["PH"],
|
|
1069
|
+
"AS-SG": ["BN", "SG"],
|
|
1070
|
+
"AS-TW": ["TW"],
|
|
1071
|
+
"CE-AF": [
|
|
1072
|
+
"DZ",
|
|
1073
|
+
"AO",
|
|
1074
|
+
"BJ",
|
|
1075
|
+
"BW",
|
|
1076
|
+
"BF",
|
|
1077
|
+
"BI",
|
|
1078
|
+
"CM",
|
|
1079
|
+
"CV",
|
|
1080
|
+
"CF",
|
|
1081
|
+
"TD",
|
|
1082
|
+
"CG",
|
|
1083
|
+
"CD",
|
|
1084
|
+
"CI",
|
|
1085
|
+
"DJ",
|
|
1086
|
+
"EG",
|
|
1087
|
+
"GQ",
|
|
1088
|
+
"ER",
|
|
1089
|
+
"ET",
|
|
1090
|
+
"GA",
|
|
1091
|
+
"GM",
|
|
1092
|
+
"GH",
|
|
1093
|
+
"GN",
|
|
1094
|
+
"GW",
|
|
1095
|
+
"IL",
|
|
1096
|
+
"JO",
|
|
1097
|
+
"KE",
|
|
1098
|
+
"LB",
|
|
1099
|
+
"LS",
|
|
1100
|
+
"LR",
|
|
1101
|
+
"LY",
|
|
1102
|
+
"MG",
|
|
1103
|
+
"MW",
|
|
1104
|
+
"ML",
|
|
1105
|
+
"MR",
|
|
1106
|
+
"MU",
|
|
1107
|
+
"MA",
|
|
1108
|
+
"MZ",
|
|
1109
|
+
"NA",
|
|
1110
|
+
"NE",
|
|
1111
|
+
"NG",
|
|
1112
|
+
"PS",
|
|
1113
|
+
"RE",
|
|
1114
|
+
"RW",
|
|
1115
|
+
"ST",
|
|
1116
|
+
"SN",
|
|
1117
|
+
"SC",
|
|
1118
|
+
"SL",
|
|
1119
|
+
"ZA",
|
|
1120
|
+
"SZ",
|
|
1121
|
+
"TZ",
|
|
1122
|
+
"TG",
|
|
1123
|
+
"TN",
|
|
1124
|
+
"UG",
|
|
1125
|
+
"ZM",
|
|
1126
|
+
"ZW"
|
|
1127
|
+
],
|
|
1128
|
+
"CE-CH": ["LI", "CH"],
|
|
1129
|
+
"CE-DK": ["FO", "GL", "DK"],
|
|
1130
|
+
"CE-EU": [
|
|
1131
|
+
"AL",
|
|
1132
|
+
"AD",
|
|
1133
|
+
"AM",
|
|
1134
|
+
"AT",
|
|
1135
|
+
"AZ",
|
|
1136
|
+
"BY",
|
|
1137
|
+
"BE",
|
|
1138
|
+
"BA",
|
|
1139
|
+
"BG",
|
|
1140
|
+
"HR",
|
|
1141
|
+
"CY",
|
|
1142
|
+
"CZ",
|
|
1143
|
+
"EE",
|
|
1144
|
+
"FI",
|
|
1145
|
+
"FR",
|
|
1146
|
+
"GE",
|
|
1147
|
+
"DE",
|
|
1148
|
+
"GI",
|
|
1149
|
+
"GR",
|
|
1150
|
+
"HU",
|
|
1151
|
+
"IS",
|
|
1152
|
+
"IE",
|
|
1153
|
+
"IT",
|
|
1154
|
+
"KZ",
|
|
1155
|
+
"XK",
|
|
1156
|
+
"KG",
|
|
1157
|
+
"LV",
|
|
1158
|
+
"LT",
|
|
1159
|
+
"LU",
|
|
1160
|
+
"MT",
|
|
1161
|
+
"MD",
|
|
1162
|
+
"MC",
|
|
1163
|
+
"ME",
|
|
1164
|
+
"NL",
|
|
1165
|
+
"PL",
|
|
1166
|
+
"PT",
|
|
1167
|
+
"MK",
|
|
1168
|
+
"RO",
|
|
1169
|
+
"RS",
|
|
1170
|
+
"SK",
|
|
1171
|
+
"SI",
|
|
1172
|
+
"ES",
|
|
1173
|
+
"TJ",
|
|
1174
|
+
"TR",
|
|
1175
|
+
"TM",
|
|
1176
|
+
"UA",
|
|
1177
|
+
"UZ"
|
|
1178
|
+
],
|
|
1179
|
+
"CE-ME": ["AE", "BH", "KW", "OM", "QA", "SA"],
|
|
1180
|
+
"CE-NO": ["NO"],
|
|
1181
|
+
"CE-SE": ["SE"],
|
|
1182
|
+
"LA-LA": [
|
|
1183
|
+
"AI",
|
|
1184
|
+
"AG",
|
|
1185
|
+
"AR",
|
|
1186
|
+
"AW",
|
|
1187
|
+
"BS",
|
|
1188
|
+
"BB",
|
|
1189
|
+
"BZ",
|
|
1190
|
+
"BM",
|
|
1191
|
+
"BO",
|
|
1192
|
+
"BR",
|
|
1193
|
+
"KY",
|
|
1194
|
+
"CL",
|
|
1195
|
+
"CO",
|
|
1196
|
+
"CR",
|
|
1197
|
+
"DM",
|
|
1198
|
+
"DO",
|
|
1199
|
+
"EC",
|
|
1200
|
+
"SV",
|
|
1201
|
+
"FK",
|
|
1202
|
+
"GF",
|
|
1203
|
+
"GD",
|
|
1204
|
+
"GT",
|
|
1205
|
+
"GY",
|
|
1206
|
+
"HT",
|
|
1207
|
+
"HN",
|
|
1208
|
+
"JM",
|
|
1209
|
+
"MQ",
|
|
1210
|
+
"MX",
|
|
1211
|
+
"MS",
|
|
1212
|
+
"NI",
|
|
1213
|
+
"MP",
|
|
1214
|
+
"PA",
|
|
1215
|
+
"PY",
|
|
1216
|
+
"PE",
|
|
1217
|
+
"KN",
|
|
1218
|
+
"LC",
|
|
1219
|
+
"VC",
|
|
1220
|
+
"SR",
|
|
1221
|
+
"TT",
|
|
1222
|
+
"TC",
|
|
1223
|
+
"UY",
|
|
1224
|
+
"VG",
|
|
1225
|
+
"VI"
|
|
1226
|
+
],
|
|
1227
|
+
"NA-CA": ["CA"],
|
|
1228
|
+
"NA-US": ["AS", "GU", "FM", "PR", "US"],
|
|
1229
|
+
"UK-UK": ["GB"]
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
// src/data/eea-countries.json
|
|
1233
|
+
var eea_countries_default = [
|
|
1234
|
+
"AT",
|
|
1235
|
+
"BE",
|
|
1236
|
+
"BG",
|
|
1237
|
+
"HR",
|
|
1238
|
+
"CY",
|
|
1239
|
+
"CZ",
|
|
1240
|
+
"DK",
|
|
1241
|
+
"EE",
|
|
1242
|
+
"FO",
|
|
1243
|
+
"FI",
|
|
1244
|
+
"FR",
|
|
1245
|
+
"DE",
|
|
1246
|
+
"GR",
|
|
1247
|
+
"GL",
|
|
1248
|
+
"HU",
|
|
1249
|
+
"IS",
|
|
1250
|
+
"IE",
|
|
1251
|
+
"IT",
|
|
1252
|
+
"LV",
|
|
1253
|
+
"LI",
|
|
1254
|
+
"LT",
|
|
1255
|
+
"LU",
|
|
1256
|
+
"MT",
|
|
1257
|
+
"NL",
|
|
1258
|
+
"NO",
|
|
1259
|
+
"PL",
|
|
1260
|
+
"PT",
|
|
1261
|
+
"RO",
|
|
1262
|
+
"SK",
|
|
1263
|
+
"SI",
|
|
1264
|
+
"ES",
|
|
1265
|
+
"SE"
|
|
1266
|
+
];
|
|
1267
|
+
|
|
1268
|
+
// src/utils/billing-countries.ts
|
|
1269
|
+
var priceZones = price_zones_default;
|
|
1270
|
+
var eeaCountries = eea_countries_default;
|
|
1271
|
+
var ALL_COUNTRY_CODES = Object.values(priceZones).flat();
|
|
1272
|
+
var getZoneByCountry = (country) => Object.keys(priceZones).find((key) => priceZones[key].includes(country)) ?? null;
|
|
1273
|
+
var getSupportedBillingCountries = (offerCountry, offerVariant) => {
|
|
1274
|
+
if (offerVariant === "bundle" || offerVariant === "insider_print") {
|
|
1275
|
+
return [...ALL_COUNTRY_CODES];
|
|
1276
|
+
}
|
|
1277
|
+
const zone = getZoneByCountry(offerCountry);
|
|
1278
|
+
if (!zone) {
|
|
1279
|
+
return [...ALL_COUNTRY_CODES];
|
|
1280
|
+
}
|
|
1281
|
+
const zoneCountries = priceZones[zone];
|
|
1282
|
+
if (eeaCountries.includes(offerCountry)) {
|
|
1283
|
+
return Array.from(/* @__PURE__ */ new Set([...zoneCountries, ...eeaCountries]));
|
|
1284
|
+
}
|
|
1285
|
+
return [...zoneCountries];
|
|
1286
|
+
};
|
|
1287
|
+
|
|
536
1288
|
// src/apple-pay-button.ts
|
|
537
1289
|
var FEATURE_APPLE_PAY_EXPRESS_CHECKOUT = "FEATURE_APPLE_PAY_EXPRESS_CHECKOUT";
|
|
538
|
-
var
|
|
1290
|
+
var TOTAL_LABEL = "The Economist";
|
|
1291
|
+
var DEFAULT_CURRENCY = "USD";
|
|
1292
|
+
var DEFAULT_TOTAL = "0.00";
|
|
1293
|
+
var ERROR_INITIATE = "Failed to initiate Apple Pay session:";
|
|
1294
|
+
var SEL_CONTAINER = ".apple-pay-container";
|
|
1295
|
+
var SEL_BUTTON = ".apple-pay-btn";
|
|
1296
|
+
var APPLE_PAY_EXCLUDED_VARIANTS = [
|
|
1297
|
+
"bundle",
|
|
1298
|
+
"insider_print"
|
|
1299
|
+
];
|
|
539
1300
|
var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
540
1301
|
static tag = "teg-apple-pay-button";
|
|
541
1302
|
static define() {
|
|
@@ -544,29 +1305,39 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
544
1305
|
/**
|
|
545
1306
|
* Creates and configures an ApplePayButton if the feature flag is present.
|
|
546
1307
|
*/
|
|
547
|
-
static createIfEnabled(offer, country) {
|
|
548
|
-
if (!offer)
|
|
1308
|
+
static createIfEnabled(offer, country, entryPoint) {
|
|
1309
|
+
if (!offer) return null;
|
|
1310
|
+
if (APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant))
|
|
549
1311
|
return null;
|
|
550
|
-
|
|
551
|
-
const isExcludedVariant = APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant);
|
|
552
|
-
if (isExcludedVariant) {
|
|
1312
|
+
if (!new URLSearchParams(window.location.search).has(FEATURE_APPLE_PAY_EXPRESS_CHECKOUT))
|
|
553
1313
|
return null;
|
|
554
|
-
}
|
|
555
|
-
if (!new URLSearchParams(window.location.search).has(
|
|
556
|
-
FEATURE_APPLE_PAY_EXPRESS_CHECKOUT
|
|
557
|
-
)) {
|
|
558
|
-
return null;
|
|
559
|
-
}
|
|
560
1314
|
_ApplePayButton.define();
|
|
561
1315
|
const button = document.createElement(_ApplePayButton.tag);
|
|
562
1316
|
button.offer = offer;
|
|
563
1317
|
button.country = country;
|
|
1318
|
+
button.entryPoint = entryPoint;
|
|
564
1319
|
return button;
|
|
565
1320
|
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Fallback session used when the BFF is unreachable at page load.
|
|
1323
|
+
* Treats the user as anonymous (not logged in, new-eligible).
|
|
1324
|
+
* Matches the behaviour of sma-checkout when session fetch fails.
|
|
1325
|
+
*/
|
|
1326
|
+
static #ANONYMOUS_SESSION = {
|
|
1327
|
+
loggedIn: false,
|
|
1328
|
+
userType: "new-eligible-user"
|
|
1329
|
+
};
|
|
1330
|
+
// ─── Private state ──────────────────────────────────────────────────────────
|
|
566
1331
|
#offer = null;
|
|
567
1332
|
#country = "";
|
|
568
1333
|
#rendering = false;
|
|
569
1334
|
#readyPromise = null;
|
|
1335
|
+
#sessionData = null;
|
|
1336
|
+
#paymentOptions = null;
|
|
1337
|
+
#returnUrl = "";
|
|
1338
|
+
#supportedBillingCountries = [];
|
|
1339
|
+
#entryPoint = "";
|
|
1340
|
+
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
570
1341
|
get offer() {
|
|
571
1342
|
return this.#offer;
|
|
572
1343
|
}
|
|
@@ -580,6 +1351,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
580
1351
|
set country(value) {
|
|
581
1352
|
this.#country = value;
|
|
582
1353
|
}
|
|
1354
|
+
get entryPoint() {
|
|
1355
|
+
return this.#entryPoint;
|
|
1356
|
+
}
|
|
1357
|
+
set entryPoint(value) {
|
|
1358
|
+
this.#entryPoint = value;
|
|
1359
|
+
}
|
|
583
1360
|
/**
|
|
584
1361
|
* Awaitable contract for the component's current render lifecycle.
|
|
585
1362
|
*
|
|
@@ -599,60 +1376,130 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
599
1376
|
whenReady() {
|
|
600
1377
|
return this.#readyPromise ?? Promise.resolve();
|
|
601
1378
|
}
|
|
1379
|
+
// ─── Lifecycle ──────────────────────────────────────────────────────────────
|
|
602
1380
|
async connectedCallback() {
|
|
603
|
-
if (this.#rendering || this.querySelector(
|
|
1381
|
+
if (this.#rendering || this.querySelector(SEL_CONTAINER)) {
|
|
604
1382
|
return;
|
|
605
1383
|
}
|
|
606
1384
|
this.#rendering = true;
|
|
607
1385
|
this.#readyPromise = this.#run();
|
|
608
1386
|
await this.#readyPromise;
|
|
609
1387
|
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Resets all render state when the element is removed from the DOM.
|
|
1390
|
+
*
|
|
1391
|
+
* A shared library component cannot assume it won't be moved in the DOM
|
|
1392
|
+
* (e.g. lazy panels, SPA route changes). Resetting on disconnect means
|
|
1393
|
+
* reattach always triggers a fresh `#run()` — new session check, new payment
|
|
1394
|
+
* options fetch, fresh event listeners — preventing stale data from persisting
|
|
1395
|
+
* and eliminating the implicit reliance on the DOM as a render-state flag.
|
|
1396
|
+
*/
|
|
1397
|
+
disconnectedCallback() {
|
|
1398
|
+
this.#rendering = false;
|
|
1399
|
+
this.#readyPromise = null;
|
|
1400
|
+
this.#sessionData = null;
|
|
1401
|
+
this.#paymentOptions = null;
|
|
1402
|
+
this.#returnUrl = "";
|
|
1403
|
+
this.#supportedBillingCountries = [];
|
|
1404
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1405
|
+
}
|
|
1406
|
+
// ─── Render pipeline ────────────────────────────────────────────────────────
|
|
610
1407
|
async #run() {
|
|
611
1408
|
try {
|
|
612
|
-
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
this.style.display = "none";
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
this.style.removeProperty("display");
|
|
623
|
-
ensureApplePayLogoSymbol();
|
|
624
|
-
const template = document.createElement("template");
|
|
625
|
-
template.innerHTML = apple_pay_button_template_default;
|
|
626
|
-
this.appendChild(template.content.cloneNode(true));
|
|
627
|
-
this.#updateDisabledState();
|
|
628
|
-
const button = this.querySelector(".apple-pay-btn");
|
|
629
|
-
button.addEventListener("click", () => {
|
|
630
|
-
if (!this.#offer) {
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
if (document.querySelector(ApplePayModal.tag)) {
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
ApplePayModal.define();
|
|
637
|
-
const modal = document.createElement(ApplePayModal.tag);
|
|
638
|
-
modal.offer = this.#offer;
|
|
639
|
-
modal.country = this.#country;
|
|
640
|
-
if (this.dataset.loggedIn !== void 0) {
|
|
641
|
-
modal.dataset.loggedIn = this.dataset.loggedIn;
|
|
642
|
-
}
|
|
643
|
-
modal.open();
|
|
644
|
-
});
|
|
1409
|
+
if (await this.#checkAvailability()) return;
|
|
1410
|
+
if (await this.#checkSession()) return;
|
|
1411
|
+
if (this.#checkOffer()) return;
|
|
1412
|
+
this.#captureReturnUrl();
|
|
1413
|
+
this.#computeBillingCountries();
|
|
1414
|
+
if (await this.#fetchAndCacheOptions()) return;
|
|
1415
|
+
this.#renderTemplate();
|
|
645
1416
|
} catch {
|
|
646
1417
|
this.style.display = "none";
|
|
647
1418
|
} finally {
|
|
648
1419
|
this.#rendering = false;
|
|
649
1420
|
}
|
|
650
1421
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1422
|
+
/** Returns `true` (hide) when Apple Pay is unavailable on this device/browser. */
|
|
1423
|
+
async #checkAvailability() {
|
|
1424
|
+
const available = await isApplePayAvailable();
|
|
1425
|
+
if (!available) {
|
|
1426
|
+
this.style.display = "none";
|
|
1427
|
+
return true;
|
|
655
1428
|
}
|
|
1429
|
+
return false;
|
|
1430
|
+
}
|
|
1431
|
+
/**
|
|
1432
|
+
* Returns `true` (hide) when the user already has an active subscription or is B2B.
|
|
1433
|
+
* Session fetch errors are swallowed — the component continues as an anonymous user.
|
|
1434
|
+
*/
|
|
1435
|
+
async #checkSession() {
|
|
1436
|
+
this.#sessionData = await this.#fetchSession();
|
|
1437
|
+
if (this.#sessionData?.loggedIn && (this.#sessionData.userType === "active-subscription" || this.#sessionData.userType === "b2b-user")) {
|
|
1438
|
+
this.style.display = "none";
|
|
1439
|
+
return true;
|
|
1440
|
+
}
|
|
1441
|
+
return false;
|
|
1442
|
+
}
|
|
1443
|
+
/** Returns `true` (hide) when no offer has been set before mount. */
|
|
1444
|
+
#checkOffer() {
|
|
1445
|
+
if (!this.#offer) {
|
|
1446
|
+
this.style.display = "none";
|
|
1447
|
+
return true;
|
|
1448
|
+
}
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1451
|
+
/** Captures the current page URL once at mount for reuse across the session lifetime. */
|
|
1452
|
+
#captureReturnUrl() {
|
|
1453
|
+
this.#returnUrl = captureOriginUrl();
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Computes supported billing countries once at mount.
|
|
1457
|
+
* Mirrors sma-checkout `billingCountryOptions()` — stable for the element's
|
|
1458
|
+
* connected lifetime; recalculated on reattach via `disconnectedCallback` reset.
|
|
1459
|
+
*/
|
|
1460
|
+
#computeBillingCountries() {
|
|
1461
|
+
this.#supportedBillingCountries = getSupportedBillingCountries(
|
|
1462
|
+
this.#country,
|
|
1463
|
+
this.#offer.product?.variant ?? ""
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Returns `true` (hide) when the wallet BFF reports Apple Pay is unavailable
|
|
1468
|
+
* for this offer. The response is cached and reused in `#initiateApplePay`.
|
|
1469
|
+
*/
|
|
1470
|
+
async #fetchAndCacheOptions() {
|
|
1471
|
+
this.#paymentOptions = await this.#fetchPaymentOptions();
|
|
1472
|
+
if (!this.#paymentOptions?.apple_pay) {
|
|
1473
|
+
this.style.display = "none";
|
|
1474
|
+
return true;
|
|
1475
|
+
}
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
/** Injects the button template and wires click and confirm event listeners. */
|
|
1479
|
+
#renderTemplate() {
|
|
1480
|
+
this.style.removeProperty("display");
|
|
1481
|
+
ensureApplePayLogoSymbol();
|
|
1482
|
+
const template = document.createElement("template");
|
|
1483
|
+
template.innerHTML = apple_pay_button_template_default;
|
|
1484
|
+
this.appendChild(template.content.cloneNode(true));
|
|
1485
|
+
this.#updateDisabledState();
|
|
1486
|
+
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1487
|
+
entryPoint: this.#entryPoint,
|
|
1488
|
+
country: this.#country
|
|
1489
|
+
});
|
|
1490
|
+
const button = this.querySelector(SEL_BUTTON);
|
|
1491
|
+
button.addEventListener("click", () => {
|
|
1492
|
+
this.#openModal();
|
|
1493
|
+
});
|
|
1494
|
+
this.addEventListener("apple-pay-confirmed", (event) => {
|
|
1495
|
+
const { skuId, country } = event.detail;
|
|
1496
|
+
this.#initiateApplePay(skuId, country);
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
// ─── Private helpers ────────────────────────────────────────────────────────
|
|
1500
|
+
#updateDisabledState() {
|
|
1501
|
+
const button = this.querySelector(SEL_BUTTON);
|
|
1502
|
+
if (!button) return;
|
|
656
1503
|
if (this.#offer) {
|
|
657
1504
|
button.removeAttribute("disabled");
|
|
658
1505
|
button.removeAttribute("aria-disabled");
|
|
@@ -661,6 +1508,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
661
1508
|
button.setAttribute("disabled", "");
|
|
662
1509
|
button.setAttribute("aria-disabled", "true");
|
|
663
1510
|
}
|
|
1511
|
+
#openModal() {
|
|
1512
|
+
if (!this.#offer || document.querySelector(ApplePayModal.tag)) return;
|
|
1513
|
+
trackExpressWalletClick(this.#offer.sku_id);
|
|
1514
|
+
ApplePayModal.define();
|
|
1515
|
+
const modal = document.createElement(ApplePayModal.tag);
|
|
1516
|
+
modal.offer = this.#offer;
|
|
1517
|
+
modal.country = this.#country;
|
|
1518
|
+
if (this.#sessionData?.loggedIn) {
|
|
1519
|
+
modal.dataset.loggedIn = "true";
|
|
1520
|
+
}
|
|
1521
|
+
modal.open();
|
|
1522
|
+
}
|
|
664
1523
|
async #fetchSession() {
|
|
665
1524
|
try {
|
|
666
1525
|
return await getExpressCheckoutSession();
|
|
@@ -668,6 +1527,58 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
668
1527
|
return null;
|
|
669
1528
|
}
|
|
670
1529
|
}
|
|
1530
|
+
async #fetchPaymentOptions() {
|
|
1531
|
+
try {
|
|
1532
|
+
return await getPaymentOptions({
|
|
1533
|
+
sku: this.#offer.sku_id,
|
|
1534
|
+
countryCode: this.#country,
|
|
1535
|
+
currencyCode: this.#offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
1536
|
+
checkoutUrl: this.#returnUrl
|
|
1537
|
+
});
|
|
1538
|
+
} catch {
|
|
1539
|
+
return null;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
#buildSessionParams(skuId, country, sessionData, paymentOptions, recaptchaTokens) {
|
|
1543
|
+
const offer = this.#offer;
|
|
1544
|
+
const totalAmount = offer.price?.purchase ? `${offer.price.purchase}` : DEFAULT_TOTAL;
|
|
1545
|
+
return {
|
|
1546
|
+
countryCode: country,
|
|
1547
|
+
currencyCode: offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
1548
|
+
supportedNetworks: paymentOptions.apple_pay.settings.supported_networks,
|
|
1549
|
+
totalAmount,
|
|
1550
|
+
totalLabel: TOTAL_LABEL,
|
|
1551
|
+
skuId,
|
|
1552
|
+
returnUrl: this.#returnUrl,
|
|
1553
|
+
userLoggedIn: sessionData.loggedIn,
|
|
1554
|
+
onPaymentAuthorized: (event, session) => handlePaymentAuthorized(event, session, sessionData, {
|
|
1555
|
+
skuId,
|
|
1556
|
+
country,
|
|
1557
|
+
returnUrl: this.#returnUrl,
|
|
1558
|
+
queryString: buildQueryString(),
|
|
1559
|
+
supportedBillingCountries: this.#supportedBillingCountries,
|
|
1560
|
+
recaptchaTokens
|
|
1561
|
+
})
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
async #initiateApplePay(skuId, country) {
|
|
1565
|
+
try {
|
|
1566
|
+
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
1567
|
+
const paymentOptions = this.#paymentOptions;
|
|
1568
|
+
const recaptchaTokens = await getPurchaseRecaptchaTokens();
|
|
1569
|
+
startApplePaySession(
|
|
1570
|
+
this.#buildSessionParams(
|
|
1571
|
+
skuId,
|
|
1572
|
+
country,
|
|
1573
|
+
sessionData,
|
|
1574
|
+
paymentOptions,
|
|
1575
|
+
recaptchaTokens
|
|
1576
|
+
)
|
|
1577
|
+
);
|
|
1578
|
+
} catch (error) {
|
|
1579
|
+
console.error(ERROR_INITIATE, error);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
671
1582
|
};
|
|
672
1583
|
|
|
673
1584
|
// src/index.ts
|