@economist/web-apple-pay 1.7.8 → 1.7.10
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 +69 -81
- package/dist/src/apple-pay-button.js +69 -81
- package/dist/src/apple-pay-button.stories.js +69 -81
- package/dist/src/apple-pay-modal.d.ts +9 -14
- package/dist/src/apple-pay-modal.js +36 -44
- package/dist/src/apple-pay-modal.stories.js +69 -81
- package/dist/src/clients/payment-checkout/apple-session.d.ts +1 -0
- package/dist/src/clients/payment-checkout/apple-session.js +11 -6
- package/dist/src/clients/payment-checkout/detects.js +1 -1
- package/dist/src/index.js +69 -81
- package/dist/src/utils/recaptcha.d.ts +9 -3
- package/dist/src/utils/recaptcha.js +6 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -575,10 +575,9 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
575
575
|
);
|
|
576
576
|
if (existingScript) {
|
|
577
577
|
existingScript.addEventListener("load", () => resolve());
|
|
578
|
-
existingScript.addEventListener(
|
|
579
|
-
"
|
|
580
|
-
|
|
581
|
-
);
|
|
578
|
+
existingScript.addEventListener("error", () => {
|
|
579
|
+
reject(new Error("Failed to load existing reCAPTCHA script"));
|
|
580
|
+
});
|
|
582
581
|
return;
|
|
583
582
|
}
|
|
584
583
|
const script = document.createElement("script");
|
|
@@ -586,7 +585,9 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
586
585
|
script.async = true;
|
|
587
586
|
script.defer = true;
|
|
588
587
|
script.onload = () => resolve();
|
|
589
|
-
script.onerror = () =>
|
|
588
|
+
script.onerror = () => {
|
|
589
|
+
reject(new Error("Failed to load reCAPTCHA script"));
|
|
590
|
+
};
|
|
590
591
|
document.head.appendChild(script);
|
|
591
592
|
});
|
|
592
593
|
};
|
|
@@ -835,17 +836,14 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
835
836
|
#country = "";
|
|
836
837
|
#triggerElement = null;
|
|
837
838
|
#keyHandler = null;
|
|
838
|
-
#
|
|
839
|
-
#recaptchaTokensPromise = null;
|
|
839
|
+
#isClosed = false;
|
|
840
840
|
/**
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
* until the token pre-fetch resolves, so this callback is never reached
|
|
848
|
-
* without valid tokens.
|
|
841
|
+
* Callback invoked synchronously from the CTA click handler.
|
|
842
|
+
* Receives a Promise of reCAPTCHA tokens — the promise is already in-flight
|
|
843
|
+
* by the time this is called. The caller (ApplePayButton) must call
|
|
844
|
+
* new ApplePaySession() synchronously inside this callback to stay within
|
|
845
|
+
* Safari's user-gesture window. The token promise resolves before
|
|
846
|
+
* onPaymentAuthorized fires (user still needs to authenticate on the sheet).
|
|
849
847
|
*/
|
|
850
848
|
onConfirm = null;
|
|
851
849
|
set offer(value) {
|
|
@@ -942,28 +940,32 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
942
940
|
}
|
|
943
941
|
});
|
|
944
942
|
}
|
|
945
|
-
|
|
946
|
-
if (cta)
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
943
|
+
cta?.addEventListener("click", () => {
|
|
944
|
+
if (cta.disabled) return;
|
|
945
|
+
if (isUS && checkbox && !checkbox.checked) {
|
|
946
|
+
errorElement?.classList.add("apm__error--visible");
|
|
947
|
+
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
948
|
+
return;
|
|
950
949
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
950
|
+
errorElement?.classList.remove("apm__error--visible");
|
|
951
|
+
this.querySelector('[role="alert"]:not(#apple-pay-modal-error)')?.remove();
|
|
952
|
+
cta.disabled = true;
|
|
953
|
+
cta.setAttribute("aria-busy", "true");
|
|
954
|
+
const recaptchaTokensPromise = getPurchaseRecaptchaTokens();
|
|
955
|
+
if (!this.#isClosed && this.onConfirm) {
|
|
956
|
+
this.onConfirm(recaptchaTokensPromise);
|
|
957
|
+
this.close();
|
|
958
|
+
}
|
|
959
|
+
recaptchaTokensPromise.catch(() => {
|
|
960
|
+
if (this.#isClosed) return;
|
|
961
|
+
cta.disabled = false;
|
|
962
|
+
cta.removeAttribute("aria-busy");
|
|
962
963
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
963
964
|
if (existingError) {
|
|
964
965
|
const icon = existingError.querySelector("svg");
|
|
965
966
|
existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
|
|
966
967
|
if (icon) existingError.prepend(icon);
|
|
968
|
+
existingError.setAttribute("role", "alert");
|
|
967
969
|
existingError.classList.add("apm__error--visible");
|
|
968
970
|
} else {
|
|
969
971
|
const errorNotice = document.createElement("div");
|
|
@@ -973,25 +975,14 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
973
975
|
cta?.insertAdjacentElement("beforebegin", errorNotice);
|
|
974
976
|
}
|
|
975
977
|
});
|
|
976
|
-
}
|
|
977
|
-
cta?.addEventListener("click", () => {
|
|
978
|
-
if (isUS && checkbox && !checkbox.checked) {
|
|
979
|
-
errorElement?.classList.add("apm__error--visible");
|
|
980
|
-
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
if (this.#recaptchaTokens) {
|
|
984
|
-
this.onConfirm?.(this.#recaptchaTokens);
|
|
985
|
-
this.close();
|
|
986
|
-
}
|
|
987
978
|
});
|
|
988
979
|
this.querySelector("#apple-pay-modal-close")?.addEventListener(
|
|
989
980
|
"click",
|
|
990
981
|
() => this.close()
|
|
991
982
|
);
|
|
992
983
|
}
|
|
993
|
-
open(
|
|
994
|
-
this.#
|
|
984
|
+
open() {
|
|
985
|
+
this.#isClosed = false;
|
|
995
986
|
this.#triggerElement = document.activeElement;
|
|
996
987
|
document.body.appendChild(this);
|
|
997
988
|
this.#keyHandler = (event) => {
|
|
@@ -1006,6 +997,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
1006
997
|
this.querySelector("#apple-pay-modal")?.focus();
|
|
1007
998
|
}
|
|
1008
999
|
close() {
|
|
1000
|
+
this.#isClosed = true;
|
|
1009
1001
|
if (this.#keyHandler) {
|
|
1010
1002
|
document.removeEventListener("keydown", this.#keyHandler, {
|
|
1011
1003
|
capture: true
|
|
@@ -1120,7 +1112,7 @@ var isAppleDeviceOrSafari = () => {
|
|
|
1120
1112
|
const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
|
|
1121
1113
|
return isIOSOrMacTouchDevice || isSafariBrowser;
|
|
1122
1114
|
};
|
|
1123
|
-
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null
|
|
1115
|
+
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null;
|
|
1124
1116
|
var isApplePayAvailable = async () => {
|
|
1125
1117
|
if (bypassApplePayChecks()) {
|
|
1126
1118
|
return true;
|
|
@@ -1418,7 +1410,10 @@ function startApplePaySession(config) {
|
|
|
1418
1410
|
};
|
|
1419
1411
|
session.onpaymentauthorized = (event) => {
|
|
1420
1412
|
trackWalletPaymentAuthorised(config.skuId);
|
|
1421
|
-
config.onPaymentAuthorized(event, session)
|
|
1413
|
+
config.onPaymentAuthorized(event, session).catch(() => {
|
|
1414
|
+
session.completePayment({ status: ApplePaySession.STATUS_FAILURE });
|
|
1415
|
+
redirectToFallbackCheckout(config);
|
|
1416
|
+
});
|
|
1422
1417
|
};
|
|
1423
1418
|
session.oncancel = () => {
|
|
1424
1419
|
config.onCancel?.();
|
|
@@ -1969,7 +1964,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1969
1964
|
#returnUrl = "";
|
|
1970
1965
|
#supportedBillingCountries = [];
|
|
1971
1966
|
#entryPoint = "";
|
|
1972
|
-
#recaptchaTokensPromise = null;
|
|
1973
1967
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
1974
1968
|
get offer() {
|
|
1975
1969
|
return this.#offer;
|
|
@@ -2034,15 +2028,14 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2034
2028
|
this.#paymentOptions = null;
|
|
2035
2029
|
this.#returnUrl = "";
|
|
2036
2030
|
this.#supportedBillingCountries = [];
|
|
2037
|
-
this.#recaptchaTokensPromise = null;
|
|
2038
2031
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
2039
2032
|
}
|
|
2040
2033
|
// ─── Render pipeline ────────────────────────────────────────────────────────
|
|
2041
2034
|
async #run() {
|
|
2042
2035
|
try {
|
|
2036
|
+
this.#renderTemplate();
|
|
2043
2037
|
if (await this.#checkAvailability()) return;
|
|
2044
2038
|
if (this.#checkOffer()) return;
|
|
2045
|
-
this.#renderTemplate();
|
|
2046
2039
|
if (await this.#checkSession()) return;
|
|
2047
2040
|
this.#captureReturnUrl();
|
|
2048
2041
|
this.#computeBillingCountries();
|
|
@@ -2060,6 +2053,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2060
2053
|
const available = await isApplePayAvailable();
|
|
2061
2054
|
if (!available) {
|
|
2062
2055
|
this.style.display = "none";
|
|
2056
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
2063
2057
|
return true;
|
|
2064
2058
|
}
|
|
2065
2059
|
return false;
|
|
@@ -2081,6 +2075,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2081
2075
|
#checkOffer() {
|
|
2082
2076
|
if (!this.#offer) {
|
|
2083
2077
|
this.style.display = "none";
|
|
2078
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
2084
2079
|
return true;
|
|
2085
2080
|
}
|
|
2086
2081
|
return false;
|
|
@@ -2135,7 +2130,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2135
2130
|
if (button) {
|
|
2136
2131
|
button.classList.remove("wap-loading");
|
|
2137
2132
|
this.#updateDisabledState();
|
|
2138
|
-
this.#scheduleRecaptchaTokens();
|
|
2139
2133
|
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
2140
2134
|
entryPoint: this.#entryPoint,
|
|
2141
2135
|
country: this.#country
|
|
@@ -2172,27 +2166,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2172
2166
|
}
|
|
2173
2167
|
const skuId = this.#offer.sku_id;
|
|
2174
2168
|
const country = this.#country;
|
|
2175
|
-
modal.onConfirm = (
|
|
2176
|
-
this.#initiateApplePay(skuId, country,
|
|
2177
|
-
this.#scheduleRecaptchaTokens();
|
|
2169
|
+
modal.onConfirm = (recaptchaTokensPromise) => {
|
|
2170
|
+
this.#initiateApplePay(skuId, country, recaptchaTokensPromise);
|
|
2178
2171
|
};
|
|
2179
|
-
modal.open(
|
|
2180
|
-
}
|
|
2181
|
-
/**
|
|
2182
|
-
* Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
|
|
2183
|
-
* If the fetch rejects, the field is cleared so the next `modal.open()` call
|
|
2184
|
-
* falls back to a new attempt via the modal's own null-fallback path — preventing
|
|
2185
|
-
* a single transient failure from permanently disabling the CTA on all retries.
|
|
2186
|
-
* The identity guard prevents a late rejection from clobbering a newer attempt.
|
|
2187
|
-
*/
|
|
2188
|
-
#scheduleRecaptchaTokens() {
|
|
2189
|
-
const attempt = getPurchaseRecaptchaTokens();
|
|
2190
|
-
attempt.catch(() => {
|
|
2191
|
-
if (this.#recaptchaTokensPromise === attempt) {
|
|
2192
|
-
this.#recaptchaTokensPromise = null;
|
|
2193
|
-
}
|
|
2194
|
-
});
|
|
2195
|
-
this.#recaptchaTokensPromise = attempt;
|
|
2172
|
+
modal.open();
|
|
2196
2173
|
}
|
|
2197
2174
|
async #fetchSession() {
|
|
2198
2175
|
try {
|
|
@@ -2213,7 +2190,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2213
2190
|
return null;
|
|
2214
2191
|
}
|
|
2215
2192
|
}
|
|
2216
|
-
#buildSessionParams(skuId, country, sessionData, paymentOptions,
|
|
2193
|
+
#buildSessionParams(skuId, country, sessionData, paymentOptions, recaptchaTokensPromise) {
|
|
2217
2194
|
const offer = this.#offer;
|
|
2218
2195
|
const totalAmount = offer.price?.purchase ? `${offer.price.purchase}` : DEFAULT_TOTAL;
|
|
2219
2196
|
return {
|
|
@@ -2225,30 +2202,41 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2225
2202
|
skuId,
|
|
2226
2203
|
returnUrl: this.#returnUrl,
|
|
2227
2204
|
userLoggedIn: sessionData.loggedIn,
|
|
2228
|
-
onPaymentAuthorized: (event, session) =>
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2205
|
+
onPaymentAuthorized: async (event, session) => {
|
|
2206
|
+
const recaptchaTokens = await recaptchaTokensPromise;
|
|
2207
|
+
return handlePaymentAuthorized(event, session, sessionData, {
|
|
2208
|
+
skuId,
|
|
2209
|
+
country,
|
|
2210
|
+
currency: offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
2211
|
+
returnUrl: this.#returnUrl,
|
|
2212
|
+
supportedBillingCountries: this.#supportedBillingCountries,
|
|
2213
|
+
recaptchaTokens
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2236
2216
|
};
|
|
2237
2217
|
}
|
|
2238
|
-
#initiateApplePay(skuId, country,
|
|
2218
|
+
#initiateApplePay(skuId, country, recaptchaTokensPromise) {
|
|
2239
2219
|
try {
|
|
2240
2220
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2241
2221
|
const paymentOptions = this.#paymentOptions;
|
|
2222
|
+
if (!paymentOptions) {
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2242
2225
|
startApplePaySession(
|
|
2243
2226
|
this.#buildSessionParams(
|
|
2244
2227
|
skuId,
|
|
2245
2228
|
country,
|
|
2246
2229
|
sessionData,
|
|
2247
2230
|
paymentOptions,
|
|
2248
|
-
|
|
2231
|
+
recaptchaTokensPromise
|
|
2249
2232
|
)
|
|
2250
2233
|
);
|
|
2251
2234
|
} catch {
|
|
2235
|
+
redirectToFallbackCheckout({
|
|
2236
|
+
returnUrl: this.#returnUrl,
|
|
2237
|
+
skuId,
|
|
2238
|
+
countryCode: country
|
|
2239
|
+
});
|
|
2252
2240
|
}
|
|
2253
2241
|
}
|
|
2254
2242
|
};
|
|
@@ -570,10 +570,9 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
570
570
|
);
|
|
571
571
|
if (existingScript) {
|
|
572
572
|
existingScript.addEventListener("load", () => resolve());
|
|
573
|
-
existingScript.addEventListener(
|
|
574
|
-
"
|
|
575
|
-
|
|
576
|
-
);
|
|
573
|
+
existingScript.addEventListener("error", () => {
|
|
574
|
+
reject(new Error("Failed to load existing reCAPTCHA script"));
|
|
575
|
+
});
|
|
577
576
|
return;
|
|
578
577
|
}
|
|
579
578
|
const script = document.createElement("script");
|
|
@@ -581,7 +580,9 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
581
580
|
script.async = true;
|
|
582
581
|
script.defer = true;
|
|
583
582
|
script.onload = () => resolve();
|
|
584
|
-
script.onerror = () =>
|
|
583
|
+
script.onerror = () => {
|
|
584
|
+
reject(new Error("Failed to load reCAPTCHA script"));
|
|
585
|
+
};
|
|
585
586
|
document.head.appendChild(script);
|
|
586
587
|
});
|
|
587
588
|
};
|
|
@@ -830,17 +831,14 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
830
831
|
#country = "";
|
|
831
832
|
#triggerElement = null;
|
|
832
833
|
#keyHandler = null;
|
|
833
|
-
#
|
|
834
|
-
#recaptchaTokensPromise = null;
|
|
834
|
+
#isClosed = false;
|
|
835
835
|
/**
|
|
836
|
-
*
|
|
837
|
-
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
* until the token pre-fetch resolves, so this callback is never reached
|
|
843
|
-
* without valid tokens.
|
|
836
|
+
* Callback invoked synchronously from the CTA click handler.
|
|
837
|
+
* Receives a Promise of reCAPTCHA tokens — the promise is already in-flight
|
|
838
|
+
* by the time this is called. The caller (ApplePayButton) must call
|
|
839
|
+
* new ApplePaySession() synchronously inside this callback to stay within
|
|
840
|
+
* Safari's user-gesture window. The token promise resolves before
|
|
841
|
+
* onPaymentAuthorized fires (user still needs to authenticate on the sheet).
|
|
844
842
|
*/
|
|
845
843
|
onConfirm = null;
|
|
846
844
|
set offer(value) {
|
|
@@ -937,28 +935,32 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
937
935
|
}
|
|
938
936
|
});
|
|
939
937
|
}
|
|
940
|
-
|
|
941
|
-
if (cta)
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
938
|
+
cta?.addEventListener("click", () => {
|
|
939
|
+
if (cta.disabled) return;
|
|
940
|
+
if (isUS && checkbox && !checkbox.checked) {
|
|
941
|
+
errorElement?.classList.add("apm__error--visible");
|
|
942
|
+
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
943
|
+
return;
|
|
945
944
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
945
|
+
errorElement?.classList.remove("apm__error--visible");
|
|
946
|
+
this.querySelector('[role="alert"]:not(#apple-pay-modal-error)')?.remove();
|
|
947
|
+
cta.disabled = true;
|
|
948
|
+
cta.setAttribute("aria-busy", "true");
|
|
949
|
+
const recaptchaTokensPromise = getPurchaseRecaptchaTokens();
|
|
950
|
+
if (!this.#isClosed && this.onConfirm) {
|
|
951
|
+
this.onConfirm(recaptchaTokensPromise);
|
|
952
|
+
this.close();
|
|
953
|
+
}
|
|
954
|
+
recaptchaTokensPromise.catch(() => {
|
|
955
|
+
if (this.#isClosed) return;
|
|
956
|
+
cta.disabled = false;
|
|
957
|
+
cta.removeAttribute("aria-busy");
|
|
957
958
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
958
959
|
if (existingError) {
|
|
959
960
|
const icon = existingError.querySelector("svg");
|
|
960
961
|
existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
|
|
961
962
|
if (icon) existingError.prepend(icon);
|
|
963
|
+
existingError.setAttribute("role", "alert");
|
|
962
964
|
existingError.classList.add("apm__error--visible");
|
|
963
965
|
} else {
|
|
964
966
|
const errorNotice = document.createElement("div");
|
|
@@ -968,25 +970,14 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
968
970
|
cta?.insertAdjacentElement("beforebegin", errorNotice);
|
|
969
971
|
}
|
|
970
972
|
});
|
|
971
|
-
}
|
|
972
|
-
cta?.addEventListener("click", () => {
|
|
973
|
-
if (isUS && checkbox && !checkbox.checked) {
|
|
974
|
-
errorElement?.classList.add("apm__error--visible");
|
|
975
|
-
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
|
-
if (this.#recaptchaTokens) {
|
|
979
|
-
this.onConfirm?.(this.#recaptchaTokens);
|
|
980
|
-
this.close();
|
|
981
|
-
}
|
|
982
973
|
});
|
|
983
974
|
this.querySelector("#apple-pay-modal-close")?.addEventListener(
|
|
984
975
|
"click",
|
|
985
976
|
() => this.close()
|
|
986
977
|
);
|
|
987
978
|
}
|
|
988
|
-
open(
|
|
989
|
-
this.#
|
|
979
|
+
open() {
|
|
980
|
+
this.#isClosed = false;
|
|
990
981
|
this.#triggerElement = document.activeElement;
|
|
991
982
|
document.body.appendChild(this);
|
|
992
983
|
this.#keyHandler = (event) => {
|
|
@@ -1001,6 +992,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
1001
992
|
this.querySelector("#apple-pay-modal")?.focus();
|
|
1002
993
|
}
|
|
1003
994
|
close() {
|
|
995
|
+
this.#isClosed = true;
|
|
1004
996
|
if (this.#keyHandler) {
|
|
1005
997
|
document.removeEventListener("keydown", this.#keyHandler, {
|
|
1006
998
|
capture: true
|
|
@@ -1115,7 +1107,7 @@ var isAppleDeviceOrSafari = () => {
|
|
|
1115
1107
|
const isSafariBrowser = navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1;
|
|
1116
1108
|
return isIOSOrMacTouchDevice || isSafariBrowser;
|
|
1117
1109
|
};
|
|
1118
|
-
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null
|
|
1110
|
+
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null;
|
|
1119
1111
|
var isApplePayAvailable = async () => {
|
|
1120
1112
|
if (bypassApplePayChecks()) {
|
|
1121
1113
|
return true;
|
|
@@ -1413,7 +1405,10 @@ function startApplePaySession(config) {
|
|
|
1413
1405
|
};
|
|
1414
1406
|
session.onpaymentauthorized = (event) => {
|
|
1415
1407
|
trackWalletPaymentAuthorised(config.skuId);
|
|
1416
|
-
config.onPaymentAuthorized(event, session)
|
|
1408
|
+
config.onPaymentAuthorized(event, session).catch(() => {
|
|
1409
|
+
session.completePayment({ status: ApplePaySession.STATUS_FAILURE });
|
|
1410
|
+
redirectToFallbackCheckout(config);
|
|
1411
|
+
});
|
|
1417
1412
|
};
|
|
1418
1413
|
session.oncancel = () => {
|
|
1419
1414
|
config.onCancel?.();
|
|
@@ -1964,7 +1959,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1964
1959
|
#returnUrl = "";
|
|
1965
1960
|
#supportedBillingCountries = [];
|
|
1966
1961
|
#entryPoint = "";
|
|
1967
|
-
#recaptchaTokensPromise = null;
|
|
1968
1962
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
1969
1963
|
get offer() {
|
|
1970
1964
|
return this.#offer;
|
|
@@ -2029,15 +2023,14 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2029
2023
|
this.#paymentOptions = null;
|
|
2030
2024
|
this.#returnUrl = "";
|
|
2031
2025
|
this.#supportedBillingCountries = [];
|
|
2032
|
-
this.#recaptchaTokensPromise = null;
|
|
2033
2026
|
this.querySelector(SEL_CONTAINER)?.remove();
|
|
2034
2027
|
}
|
|
2035
2028
|
// ─── Render pipeline ────────────────────────────────────────────────────────
|
|
2036
2029
|
async #run() {
|
|
2037
2030
|
try {
|
|
2031
|
+
this.#renderTemplate();
|
|
2038
2032
|
if (await this.#checkAvailability()) return;
|
|
2039
2033
|
if (this.#checkOffer()) return;
|
|
2040
|
-
this.#renderTemplate();
|
|
2041
2034
|
if (await this.#checkSession()) return;
|
|
2042
2035
|
this.#captureReturnUrl();
|
|
2043
2036
|
this.#computeBillingCountries();
|
|
@@ -2055,6 +2048,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2055
2048
|
const available = await isApplePayAvailable();
|
|
2056
2049
|
if (!available) {
|
|
2057
2050
|
this.style.display = "none";
|
|
2051
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
2058
2052
|
return true;
|
|
2059
2053
|
}
|
|
2060
2054
|
return false;
|
|
@@ -2076,6 +2070,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2076
2070
|
#checkOffer() {
|
|
2077
2071
|
if (!this.#offer) {
|
|
2078
2072
|
this.style.display = "none";
|
|
2073
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
2079
2074
|
return true;
|
|
2080
2075
|
}
|
|
2081
2076
|
return false;
|
|
@@ -2130,7 +2125,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2130
2125
|
if (button) {
|
|
2131
2126
|
button.classList.remove("wap-loading");
|
|
2132
2127
|
this.#updateDisabledState();
|
|
2133
|
-
this.#scheduleRecaptchaTokens();
|
|
2134
2128
|
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
2135
2129
|
entryPoint: this.#entryPoint,
|
|
2136
2130
|
country: this.#country
|
|
@@ -2167,27 +2161,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2167
2161
|
}
|
|
2168
2162
|
const skuId = this.#offer.sku_id;
|
|
2169
2163
|
const country = this.#country;
|
|
2170
|
-
modal.onConfirm = (
|
|
2171
|
-
this.#initiateApplePay(skuId, country,
|
|
2172
|
-
this.#scheduleRecaptchaTokens();
|
|
2164
|
+
modal.onConfirm = (recaptchaTokensPromise) => {
|
|
2165
|
+
this.#initiateApplePay(skuId, country, recaptchaTokensPromise);
|
|
2173
2166
|
};
|
|
2174
|
-
modal.open(
|
|
2175
|
-
}
|
|
2176
|
-
/**
|
|
2177
|
-
* Starts a fresh reCAPTCHA token pre-fetch and stores its promise.
|
|
2178
|
-
* If the fetch rejects, the field is cleared so the next `modal.open()` call
|
|
2179
|
-
* falls back to a new attempt via the modal's own null-fallback path — preventing
|
|
2180
|
-
* a single transient failure from permanently disabling the CTA on all retries.
|
|
2181
|
-
* The identity guard prevents a late rejection from clobbering a newer attempt.
|
|
2182
|
-
*/
|
|
2183
|
-
#scheduleRecaptchaTokens() {
|
|
2184
|
-
const attempt = getPurchaseRecaptchaTokens();
|
|
2185
|
-
attempt.catch(() => {
|
|
2186
|
-
if (this.#recaptchaTokensPromise === attempt) {
|
|
2187
|
-
this.#recaptchaTokensPromise = null;
|
|
2188
|
-
}
|
|
2189
|
-
});
|
|
2190
|
-
this.#recaptchaTokensPromise = attempt;
|
|
2167
|
+
modal.open();
|
|
2191
2168
|
}
|
|
2192
2169
|
async #fetchSession() {
|
|
2193
2170
|
try {
|
|
@@ -2208,7 +2185,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2208
2185
|
return null;
|
|
2209
2186
|
}
|
|
2210
2187
|
}
|
|
2211
|
-
#buildSessionParams(skuId, country, sessionData, paymentOptions,
|
|
2188
|
+
#buildSessionParams(skuId, country, sessionData, paymentOptions, recaptchaTokensPromise) {
|
|
2212
2189
|
const offer = this.#offer;
|
|
2213
2190
|
const totalAmount = offer.price?.purchase ? `${offer.price.purchase}` : DEFAULT_TOTAL;
|
|
2214
2191
|
return {
|
|
@@ -2220,30 +2197,41 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2220
2197
|
skuId,
|
|
2221
2198
|
returnUrl: this.#returnUrl,
|
|
2222
2199
|
userLoggedIn: sessionData.loggedIn,
|
|
2223
|
-
onPaymentAuthorized: (event, session) =>
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2200
|
+
onPaymentAuthorized: async (event, session) => {
|
|
2201
|
+
const recaptchaTokens = await recaptchaTokensPromise;
|
|
2202
|
+
return handlePaymentAuthorized(event, session, sessionData, {
|
|
2203
|
+
skuId,
|
|
2204
|
+
country,
|
|
2205
|
+
currency: offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
2206
|
+
returnUrl: this.#returnUrl,
|
|
2207
|
+
supportedBillingCountries: this.#supportedBillingCountries,
|
|
2208
|
+
recaptchaTokens
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2231
2211
|
};
|
|
2232
2212
|
}
|
|
2233
|
-
#initiateApplePay(skuId, country,
|
|
2213
|
+
#initiateApplePay(skuId, country, recaptchaTokensPromise) {
|
|
2234
2214
|
try {
|
|
2235
2215
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2236
2216
|
const paymentOptions = this.#paymentOptions;
|
|
2217
|
+
if (!paymentOptions) {
|
|
2218
|
+
return;
|
|
2219
|
+
}
|
|
2237
2220
|
startApplePaySession(
|
|
2238
2221
|
this.#buildSessionParams(
|
|
2239
2222
|
skuId,
|
|
2240
2223
|
country,
|
|
2241
2224
|
sessionData,
|
|
2242
2225
|
paymentOptions,
|
|
2243
|
-
|
|
2226
|
+
recaptchaTokensPromise
|
|
2244
2227
|
)
|
|
2245
2228
|
);
|
|
2246
2229
|
} catch {
|
|
2230
|
+
redirectToFallbackCheckout({
|
|
2231
|
+
returnUrl: this.#returnUrl,
|
|
2232
|
+
skuId,
|
|
2233
|
+
countryCode: country
|
|
2234
|
+
});
|
|
2247
2235
|
}
|
|
2248
2236
|
}
|
|
2249
2237
|
};
|