@economist/web-apple-pay 0.1.3-beta.7 → 0.1.3-beta.9
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 +89 -68
- package/dist/src/apple-pay-button.js +84 -63
- package/dist/src/apple-pay-button.stories.js +92 -71
- package/dist/src/apple-pay-modal.js +72 -1
- package/dist/src/apple-pay-modal.stories.js +84 -63
- package/dist/src/index.js +89 -68
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -78,6 +78,62 @@ function ensureApplePayLogoSymbol() {
|
|
|
78
78
|
// src/templates/apple-pay-modal.template.html
|
|
79
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
|
+
// src/clients/payment-checkout/config.ts
|
|
82
|
+
var runtimeConfig = null;
|
|
83
|
+
var configureApplePay = (config) => {
|
|
84
|
+
runtimeConfig = {
|
|
85
|
+
...config
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
var getApplePayConfig = () => runtimeConfig;
|
|
89
|
+
|
|
90
|
+
// src/utils/recaptcha.ts
|
|
91
|
+
var loadRecaptchaScript = (siteKey) => {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
if (typeof grecaptcha !== "undefined") return resolve();
|
|
94
|
+
const existingScript = document.querySelector(
|
|
95
|
+
'script[src*="recaptcha/api.js"]'
|
|
96
|
+
);
|
|
97
|
+
if (existingScript) {
|
|
98
|
+
existingScript.addEventListener("load", () => resolve());
|
|
99
|
+
existingScript.addEventListener(
|
|
100
|
+
"error",
|
|
101
|
+
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
102
|
+
);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const script = document.createElement("script");
|
|
106
|
+
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
107
|
+
script.async = true;
|
|
108
|
+
script.defer = true;
|
|
109
|
+
script.onload = () => resolve();
|
|
110
|
+
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
111
|
+
document.head.appendChild(script);
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
var getRecaptchaToken = async (action) => {
|
|
115
|
+
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
116
|
+
if (!siteKey) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
await loadRecaptchaScript(siteKey);
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
grecaptcha.ready(() => {
|
|
124
|
+
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
var getPurchaseRecaptchaTokens = async () => {
|
|
129
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
130
|
+
getRecaptchaToken("checkEligibility"),
|
|
131
|
+
getRecaptchaToken("newBasket"),
|
|
132
|
+
getRecaptchaToken("registerUser")
|
|
133
|
+
]);
|
|
134
|
+
return { checkEligibility, newBasket, registerUser };
|
|
135
|
+
};
|
|
136
|
+
|
|
81
137
|
// src/apple-pay-modal.ts
|
|
82
138
|
function getRenewalDateText(unit, duration) {
|
|
83
139
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -297,6 +353,8 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
297
353
|
#country = "";
|
|
298
354
|
#triggerElement = null;
|
|
299
355
|
#keyHandler = null;
|
|
356
|
+
#recaptchaTokens = null;
|
|
357
|
+
#recaptchaTokensPromise = null;
|
|
300
358
|
set offer(value) {
|
|
301
359
|
this.#offer = value;
|
|
302
360
|
}
|
|
@@ -391,6 +449,23 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
391
449
|
}
|
|
392
450
|
});
|
|
393
451
|
}
|
|
452
|
+
if (this.#recaptchaTokensPromise) {
|
|
453
|
+
if (cta) {
|
|
454
|
+
cta.setAttribute("disabled", "");
|
|
455
|
+
cta.setAttribute("aria-disabled", "true");
|
|
456
|
+
cta.setAttribute("aria-busy", "true");
|
|
457
|
+
}
|
|
458
|
+
this.#recaptchaTokensPromise.then((tokens) => {
|
|
459
|
+
this.#recaptchaTokens = tokens;
|
|
460
|
+
if (cta) {
|
|
461
|
+
cta.removeAttribute("disabled");
|
|
462
|
+
cta.removeAttribute("aria-disabled");
|
|
463
|
+
cta.removeAttribute("aria-busy");
|
|
464
|
+
}
|
|
465
|
+
}).catch(() => {
|
|
466
|
+
console.error("[web-apple-pay] apple-pay-modal: failed to pre-fetch reCAPTCHA tokens; CTA remains disabled");
|
|
467
|
+
});
|
|
468
|
+
}
|
|
394
469
|
cta?.addEventListener("click", () => {
|
|
395
470
|
if (isUS && checkbox && !checkbox.checked) {
|
|
396
471
|
errorElement?.classList.add("apm__error--visible");
|
|
@@ -401,7 +476,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
401
476
|
new CustomEvent("apple-pay-confirmed", {
|
|
402
477
|
bubbles: true,
|
|
403
478
|
composed: true,
|
|
404
|
-
detail: { skuId: offer.sku_id, country }
|
|
479
|
+
detail: { skuId: offer.sku_id, country, recaptchaTokens: this.#recaptchaTokens }
|
|
405
480
|
})
|
|
406
481
|
);
|
|
407
482
|
this.close();
|
|
@@ -412,6 +487,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
412
487
|
);
|
|
413
488
|
}
|
|
414
489
|
open() {
|
|
490
|
+
this.#recaptchaTokensPromise = getPurchaseRecaptchaTokens();
|
|
415
491
|
this.#triggerElement = document.activeElement;
|
|
416
492
|
document.body.appendChild(this);
|
|
417
493
|
this.#keyHandler = (event) => {
|
|
@@ -524,15 +600,6 @@ var apple_pay_button_template_default = `<style>
|
|
|
524
600
|
</div>
|
|
525
601
|
`;
|
|
526
602
|
|
|
527
|
-
// src/clients/payment-checkout/config.ts
|
|
528
|
-
var runtimeConfig = null;
|
|
529
|
-
var configureApplePay = (config) => {
|
|
530
|
-
runtimeConfig = {
|
|
531
|
-
...config
|
|
532
|
-
};
|
|
533
|
-
};
|
|
534
|
-
var getApplePayConfig = () => runtimeConfig;
|
|
535
|
-
|
|
536
603
|
// src/clients/payment-checkout/detects.ts
|
|
537
604
|
var isAppleDeviceOrSafari = () => {
|
|
538
605
|
const isIOSOrMacTouchDevice = /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1 && /Macintosh/.test(navigator.userAgent);
|
|
@@ -702,53 +769,6 @@ var redirectToCheckoutFallback = (params) => {
|
|
|
702
769
|
return url.pathname + url.search;
|
|
703
770
|
};
|
|
704
771
|
|
|
705
|
-
// src/utils/recaptcha.ts
|
|
706
|
-
var loadRecaptchaScript = (siteKey) => {
|
|
707
|
-
return new Promise((resolve, reject) => {
|
|
708
|
-
if (typeof grecaptcha !== "undefined") return resolve();
|
|
709
|
-
const existingScript = document.querySelector(
|
|
710
|
-
'script[src*="recaptcha/api.js"]'
|
|
711
|
-
);
|
|
712
|
-
if (existingScript) {
|
|
713
|
-
existingScript.addEventListener("load", () => resolve());
|
|
714
|
-
existingScript.addEventListener(
|
|
715
|
-
"error",
|
|
716
|
-
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
717
|
-
);
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
const script = document.createElement("script");
|
|
721
|
-
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
722
|
-
script.async = true;
|
|
723
|
-
script.defer = true;
|
|
724
|
-
script.onload = () => resolve();
|
|
725
|
-
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
726
|
-
document.head.appendChild(script);
|
|
727
|
-
});
|
|
728
|
-
};
|
|
729
|
-
var getRecaptchaToken = async (action) => {
|
|
730
|
-
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
731
|
-
if (!siteKey) {
|
|
732
|
-
throw new Error(
|
|
733
|
-
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
734
|
-
);
|
|
735
|
-
}
|
|
736
|
-
await loadRecaptchaScript(siteKey);
|
|
737
|
-
return new Promise((resolve, reject) => {
|
|
738
|
-
grecaptcha.ready(() => {
|
|
739
|
-
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
740
|
-
});
|
|
741
|
-
});
|
|
742
|
-
};
|
|
743
|
-
var getPurchaseRecaptchaTokens = async () => {
|
|
744
|
-
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
745
|
-
getRecaptchaToken("checkEligibility"),
|
|
746
|
-
getRecaptchaToken("newBasket"),
|
|
747
|
-
getRecaptchaToken("registerUser")
|
|
748
|
-
]);
|
|
749
|
-
return { checkEligibility, newBasket, registerUser };
|
|
750
|
-
};
|
|
751
|
-
|
|
752
772
|
// src/utils/expressCheckoutTracking.ts
|
|
753
773
|
var EXPRESS_CHECKOUT_EVENTS = {
|
|
754
774
|
/** Wallet button displayed */
|
|
@@ -1451,7 +1471,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1451
1471
|
#returnUrl = "";
|
|
1452
1472
|
#supportedBillingCountries = [];
|
|
1453
1473
|
#entryPoint = "";
|
|
1454
|
-
#
|
|
1474
|
+
#applePayConfirmedHandler = null;
|
|
1455
1475
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
1456
1476
|
get offer() {
|
|
1457
1477
|
return this.#offer;
|
|
@@ -1516,6 +1536,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1516
1536
|
this.#paymentOptions = null;
|
|
1517
1537
|
this.#returnUrl = "";
|
|
1518
1538
|
this.#supportedBillingCountries = [];
|
|
1539
|
+
if (this.#applePayConfirmedHandler) {
|
|
1540
|
+
document.removeEventListener("apple-pay-confirmed", this.#applePayConfirmedHandler);
|
|
1541
|
+
this.#applePayConfirmedHandler = null;
|
|
1542
|
+
}
|
|
1519
1543
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1520
1544
|
}
|
|
1521
1545
|
// ─── Render pipeline ────────────────────────────────────────────────────────
|
|
@@ -1623,10 +1647,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1623
1647
|
if (button.classList.contains("wap-loading")) return;
|
|
1624
1648
|
this.#openModal();
|
|
1625
1649
|
});
|
|
1626
|
-
|
|
1627
|
-
const { skuId, country } = event.detail;
|
|
1628
|
-
this.#initiateApplePay(skuId, country);
|
|
1629
|
-
|
|
1650
|
+
this.#applePayConfirmedHandler = (event) => {
|
|
1651
|
+
const { skuId, country, recaptchaTokens } = event.detail;
|
|
1652
|
+
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
1653
|
+
this.#applePayConfirmedHandler = null;
|
|
1654
|
+
};
|
|
1655
|
+
document.addEventListener("apple-pay-confirmed", this.#applePayConfirmedHandler, { once: true });
|
|
1630
1656
|
}
|
|
1631
1657
|
#finishLoading() {
|
|
1632
1658
|
console.debug("[web-apple-pay] #finishLoading: all checks passed, enabling button");
|
|
@@ -1661,10 +1687,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1661
1687
|
return;
|
|
1662
1688
|
}
|
|
1663
1689
|
trackExpressWalletClick(this.#offer.sku_id);
|
|
1664
|
-
getPurchaseRecaptchaTokens().then((tokens) => {
|
|
1665
|
-
this.#recaptchaTokens = tokens;
|
|
1666
|
-
}).catch(() => {
|
|
1667
|
-
});
|
|
1668
1690
|
ApplePayModal.define();
|
|
1669
1691
|
const modal = document.createElement(ApplePayModal.tag);
|
|
1670
1692
|
modal.offer = this.#offer;
|
|
@@ -1716,11 +1738,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1716
1738
|
})
|
|
1717
1739
|
};
|
|
1718
1740
|
}
|
|
1719
|
-
|
|
1741
|
+
#initiateApplePay(skuId, country, recaptchaTokens) {
|
|
1720
1742
|
try {
|
|
1721
1743
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
1722
1744
|
const paymentOptions = this.#paymentOptions;
|
|
1723
|
-
const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
|
|
1724
1745
|
startApplePaySession(
|
|
1725
1746
|
this.#buildSessionParams(
|
|
1726
1747
|
skuId,
|
|
@@ -78,6 +78,57 @@ function ensureApplePayLogoSymbol() {
|
|
|
78
78
|
// src/templates/apple-pay-modal.template.html
|
|
79
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
|
+
// src/clients/payment-checkout/config.ts
|
|
82
|
+
var runtimeConfig = null;
|
|
83
|
+
var getApplePayConfig = () => runtimeConfig;
|
|
84
|
+
|
|
85
|
+
// src/utils/recaptcha.ts
|
|
86
|
+
var loadRecaptchaScript = (siteKey) => {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
if (typeof grecaptcha !== "undefined") return resolve();
|
|
89
|
+
const existingScript = document.querySelector(
|
|
90
|
+
'script[src*="recaptcha/api.js"]'
|
|
91
|
+
);
|
|
92
|
+
if (existingScript) {
|
|
93
|
+
existingScript.addEventListener("load", () => resolve());
|
|
94
|
+
existingScript.addEventListener(
|
|
95
|
+
"error",
|
|
96
|
+
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
97
|
+
);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const script = document.createElement("script");
|
|
101
|
+
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
102
|
+
script.async = true;
|
|
103
|
+
script.defer = true;
|
|
104
|
+
script.onload = () => resolve();
|
|
105
|
+
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
106
|
+
document.head.appendChild(script);
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
var getRecaptchaToken = async (action) => {
|
|
110
|
+
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
111
|
+
if (!siteKey) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
await loadRecaptchaScript(siteKey);
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
grecaptcha.ready(() => {
|
|
119
|
+
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
var getPurchaseRecaptchaTokens = async () => {
|
|
124
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
125
|
+
getRecaptchaToken("checkEligibility"),
|
|
126
|
+
getRecaptchaToken("newBasket"),
|
|
127
|
+
getRecaptchaToken("registerUser")
|
|
128
|
+
]);
|
|
129
|
+
return { checkEligibility, newBasket, registerUser };
|
|
130
|
+
};
|
|
131
|
+
|
|
81
132
|
// src/apple-pay-modal.ts
|
|
82
133
|
function getRenewalDateText(unit, duration) {
|
|
83
134
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -297,6 +348,8 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
297
348
|
#country = "";
|
|
298
349
|
#triggerElement = null;
|
|
299
350
|
#keyHandler = null;
|
|
351
|
+
#recaptchaTokens = null;
|
|
352
|
+
#recaptchaTokensPromise = null;
|
|
300
353
|
set offer(value) {
|
|
301
354
|
this.#offer = value;
|
|
302
355
|
}
|
|
@@ -391,6 +444,23 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
391
444
|
}
|
|
392
445
|
});
|
|
393
446
|
}
|
|
447
|
+
if (this.#recaptchaTokensPromise) {
|
|
448
|
+
if (cta) {
|
|
449
|
+
cta.setAttribute("disabled", "");
|
|
450
|
+
cta.setAttribute("aria-disabled", "true");
|
|
451
|
+
cta.setAttribute("aria-busy", "true");
|
|
452
|
+
}
|
|
453
|
+
this.#recaptchaTokensPromise.then((tokens) => {
|
|
454
|
+
this.#recaptchaTokens = tokens;
|
|
455
|
+
if (cta) {
|
|
456
|
+
cta.removeAttribute("disabled");
|
|
457
|
+
cta.removeAttribute("aria-disabled");
|
|
458
|
+
cta.removeAttribute("aria-busy");
|
|
459
|
+
}
|
|
460
|
+
}).catch(() => {
|
|
461
|
+
console.error("[web-apple-pay] apple-pay-modal: failed to pre-fetch reCAPTCHA tokens; CTA remains disabled");
|
|
462
|
+
});
|
|
463
|
+
}
|
|
394
464
|
cta?.addEventListener("click", () => {
|
|
395
465
|
if (isUS && checkbox && !checkbox.checked) {
|
|
396
466
|
errorElement?.classList.add("apm__error--visible");
|
|
@@ -401,7 +471,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
401
471
|
new CustomEvent("apple-pay-confirmed", {
|
|
402
472
|
bubbles: true,
|
|
403
473
|
composed: true,
|
|
404
|
-
detail: { skuId: offer.sku_id, country }
|
|
474
|
+
detail: { skuId: offer.sku_id, country, recaptchaTokens: this.#recaptchaTokens }
|
|
405
475
|
})
|
|
406
476
|
);
|
|
407
477
|
this.close();
|
|
@@ -412,6 +482,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
412
482
|
);
|
|
413
483
|
}
|
|
414
484
|
open() {
|
|
485
|
+
this.#recaptchaTokensPromise = getPurchaseRecaptchaTokens();
|
|
415
486
|
this.#triggerElement = document.activeElement;
|
|
416
487
|
document.body.appendChild(this);
|
|
417
488
|
this.#keyHandler = (event) => {
|
|
@@ -524,10 +595,6 @@ var apple_pay_button_template_default = `<style>
|
|
|
524
595
|
</div>
|
|
525
596
|
`;
|
|
526
597
|
|
|
527
|
-
// src/clients/payment-checkout/config.ts
|
|
528
|
-
var runtimeConfig = null;
|
|
529
|
-
var getApplePayConfig = () => runtimeConfig;
|
|
530
|
-
|
|
531
598
|
// src/clients/payment-checkout/detects.ts
|
|
532
599
|
var isAppleDeviceOrSafari = () => {
|
|
533
600
|
const isIOSOrMacTouchDevice = /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1 && /Macintosh/.test(navigator.userAgent);
|
|
@@ -697,53 +764,6 @@ var redirectToCheckoutFallback = (params) => {
|
|
|
697
764
|
return url.pathname + url.search;
|
|
698
765
|
};
|
|
699
766
|
|
|
700
|
-
// src/utils/recaptcha.ts
|
|
701
|
-
var loadRecaptchaScript = (siteKey) => {
|
|
702
|
-
return new Promise((resolve, reject) => {
|
|
703
|
-
if (typeof grecaptcha !== "undefined") return resolve();
|
|
704
|
-
const existingScript = document.querySelector(
|
|
705
|
-
'script[src*="recaptcha/api.js"]'
|
|
706
|
-
);
|
|
707
|
-
if (existingScript) {
|
|
708
|
-
existingScript.addEventListener("load", () => resolve());
|
|
709
|
-
existingScript.addEventListener(
|
|
710
|
-
"error",
|
|
711
|
-
() => reject(new Error("Failed to load existing reCAPTCHA script"))
|
|
712
|
-
);
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
const script = document.createElement("script");
|
|
716
|
-
script.src = `https://www.google.com/recaptcha/api.js?render=${siteKey}`;
|
|
717
|
-
script.async = true;
|
|
718
|
-
script.defer = true;
|
|
719
|
-
script.onload = () => resolve();
|
|
720
|
-
script.onerror = () => reject(new Error("Failed to load reCAPTCHA script"));
|
|
721
|
-
document.head.appendChild(script);
|
|
722
|
-
});
|
|
723
|
-
};
|
|
724
|
-
var getRecaptchaToken = async (action) => {
|
|
725
|
-
const siteKey = getApplePayConfig()?.recaptchaSiteKey;
|
|
726
|
-
if (!siteKey) {
|
|
727
|
-
throw new Error(
|
|
728
|
-
"[web-apple-pay] Missing recaptchaSiteKey. Call configureApplePay({ recaptchaSiteKey, ... }) before initiating Apple Pay."
|
|
729
|
-
);
|
|
730
|
-
}
|
|
731
|
-
await loadRecaptchaScript(siteKey);
|
|
732
|
-
return new Promise((resolve, reject) => {
|
|
733
|
-
grecaptcha.ready(() => {
|
|
734
|
-
grecaptcha.execute(siteKey, { action }).then(resolve).catch(reject);
|
|
735
|
-
});
|
|
736
|
-
});
|
|
737
|
-
};
|
|
738
|
-
var getPurchaseRecaptchaTokens = async () => {
|
|
739
|
-
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
740
|
-
getRecaptchaToken("checkEligibility"),
|
|
741
|
-
getRecaptchaToken("newBasket"),
|
|
742
|
-
getRecaptchaToken("registerUser")
|
|
743
|
-
]);
|
|
744
|
-
return { checkEligibility, newBasket, registerUser };
|
|
745
|
-
};
|
|
746
|
-
|
|
747
767
|
// src/utils/expressCheckoutTracking.ts
|
|
748
768
|
var EXPRESS_CHECKOUT_EVENTS = {
|
|
749
769
|
/** Wallet button displayed */
|
|
@@ -1446,7 +1466,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1446
1466
|
#returnUrl = "";
|
|
1447
1467
|
#supportedBillingCountries = [];
|
|
1448
1468
|
#entryPoint = "";
|
|
1449
|
-
#
|
|
1469
|
+
#applePayConfirmedHandler = null;
|
|
1450
1470
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
1451
1471
|
get offer() {
|
|
1452
1472
|
return this.#offer;
|
|
@@ -1511,6 +1531,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1511
1531
|
this.#paymentOptions = null;
|
|
1512
1532
|
this.#returnUrl = "";
|
|
1513
1533
|
this.#supportedBillingCountries = [];
|
|
1534
|
+
if (this.#applePayConfirmedHandler) {
|
|
1535
|
+
document.removeEventListener("apple-pay-confirmed", this.#applePayConfirmedHandler);
|
|
1536
|
+
this.#applePayConfirmedHandler = null;
|
|
1537
|
+
}
|
|
1514
1538
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1515
1539
|
}
|
|
1516
1540
|
// ─── Render pipeline ────────────────────────────────────────────────────────
|
|
@@ -1618,10 +1642,12 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1618
1642
|
if (button.classList.contains("wap-loading")) return;
|
|
1619
1643
|
this.#openModal();
|
|
1620
1644
|
});
|
|
1621
|
-
|
|
1622
|
-
const { skuId, country } = event.detail;
|
|
1623
|
-
this.#initiateApplePay(skuId, country);
|
|
1624
|
-
|
|
1645
|
+
this.#applePayConfirmedHandler = (event) => {
|
|
1646
|
+
const { skuId, country, recaptchaTokens } = event.detail;
|
|
1647
|
+
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
1648
|
+
this.#applePayConfirmedHandler = null;
|
|
1649
|
+
};
|
|
1650
|
+
document.addEventListener("apple-pay-confirmed", this.#applePayConfirmedHandler, { once: true });
|
|
1625
1651
|
}
|
|
1626
1652
|
#finishLoading() {
|
|
1627
1653
|
console.debug("[web-apple-pay] #finishLoading: all checks passed, enabling button");
|
|
@@ -1656,10 +1682,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1656
1682
|
return;
|
|
1657
1683
|
}
|
|
1658
1684
|
trackExpressWalletClick(this.#offer.sku_id);
|
|
1659
|
-
getPurchaseRecaptchaTokens().then((tokens) => {
|
|
1660
|
-
this.#recaptchaTokens = tokens;
|
|
1661
|
-
}).catch(() => {
|
|
1662
|
-
});
|
|
1663
1685
|
ApplePayModal.define();
|
|
1664
1686
|
const modal = document.createElement(ApplePayModal.tag);
|
|
1665
1687
|
modal.offer = this.#offer;
|
|
@@ -1711,11 +1733,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1711
1733
|
})
|
|
1712
1734
|
};
|
|
1713
1735
|
}
|
|
1714
|
-
|
|
1736
|
+
#initiateApplePay(skuId, country, recaptchaTokens) {
|
|
1715
1737
|
try {
|
|
1716
1738
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
1717
1739
|
const paymentOptions = this.#paymentOptions;
|
|
1718
|
-
const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
|
|
1719
1740
|
startApplePaySession(
|
|
1720
1741
|
this.#buildSessionParams(
|
|
1721
1742
|
skuId,
|