@economist/web-apple-pay 0.1.3-beta.42 → 0.1.3-beta.44
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 +90 -52
- package/dist/src/apple-pay-button.js +90 -52
- package/dist/src/apple-pay-button.stories.js +90 -52
- package/dist/src/apple-pay-modal.d.ts +5 -9
- package/dist/src/apple-pay-modal.js +53 -49
- package/dist/src/apple-pay-modal.stories.js +90 -52
- package/dist/src/clients/payment-checkout/apple-session.js +10 -5
- package/dist/src/clients/payment-checkout/payment-handler.js +20 -0
- package/dist/src/index.js +90 -52
- package/dist/src/utils/recaptcha.js +23 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -575,10 +575,11 @@ 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
|
+
const error = new Error("Failed to load existing reCAPTCHA script");
|
|
580
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (existing script)", error);
|
|
581
|
+
reject(error);
|
|
582
|
+
});
|
|
582
583
|
return;
|
|
583
584
|
}
|
|
584
585
|
const script = document.createElement("script");
|
|
@@ -586,7 +587,11 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
586
587
|
script.async = true;
|
|
587
588
|
script.defer = true;
|
|
588
589
|
script.onload = () => resolve();
|
|
589
|
-
script.onerror = () =>
|
|
590
|
+
script.onerror = () => {
|
|
591
|
+
const error = new Error("Failed to load reCAPTCHA script");
|
|
592
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (new script)", error);
|
|
593
|
+
reject(error);
|
|
594
|
+
};
|
|
590
595
|
document.head.appendChild(script);
|
|
591
596
|
});
|
|
592
597
|
};
|
|
@@ -605,12 +610,19 @@ var getRecaptchaToken = async (action) => {
|
|
|
605
610
|
});
|
|
606
611
|
};
|
|
607
612
|
var getPurchaseRecaptchaTokens = async () => {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
613
|
+
console.info("[web-apple-pay] Requesting reCAPTCHA tokens (checkEligibility, newBasket, registerUser)");
|
|
614
|
+
try {
|
|
615
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
616
|
+
getRecaptchaToken("checkEligibility"),
|
|
617
|
+
getRecaptchaToken("newBasket"),
|
|
618
|
+
getRecaptchaToken("registerUser")
|
|
619
|
+
]);
|
|
620
|
+
console.info("[web-apple-pay] All reCAPTCHA tokens acquired successfully");
|
|
621
|
+
return { checkEligibility, newBasket, registerUser };
|
|
622
|
+
} catch (error) {
|
|
623
|
+
console.error("[web-apple-pay] Failed to acquire one or more reCAPTCHA tokens", error);
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
614
626
|
};
|
|
615
627
|
|
|
616
628
|
// src/apple-pay-modal.ts
|
|
@@ -835,17 +847,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
835
847
|
#country = "";
|
|
836
848
|
#triggerElement = null;
|
|
837
849
|
#keyHandler = null;
|
|
838
|
-
#
|
|
839
|
-
#recaptchaTokensPromise = null;
|
|
850
|
+
#isClosed = false;
|
|
840
851
|
/**
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
*
|
|
846
|
-
* reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
|
|
847
|
-
* until the token pre-fetch resolves, so this callback is never reached
|
|
848
|
-
* without valid tokens.
|
|
852
|
+
* Callback invoked from the CTA click handler once the user confirms.
|
|
853
|
+
* The caller (ApplePayButton) must call startApplePaySession inside this
|
|
854
|
+
* callback to preserve the browser's user-gesture chain — Safari requires
|
|
855
|
+
* new ApplePaySession() to execute within the original click stack.
|
|
849
856
|
*/
|
|
850
857
|
onConfirm = null;
|
|
851
858
|
set offer(value) {
|
|
@@ -942,28 +949,36 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
942
949
|
}
|
|
943
950
|
});
|
|
944
951
|
}
|
|
945
|
-
|
|
946
|
-
if (cta)
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
952
|
+
cta?.addEventListener("click", () => {
|
|
953
|
+
if (cta.disabled) return;
|
|
954
|
+
if (isUS && checkbox && !checkbox.checked) {
|
|
955
|
+
errorElement?.classList.add("apm__error--visible");
|
|
956
|
+
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
957
|
+
return;
|
|
950
958
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
cta.removeAttribute("aria-busy");
|
|
959
|
+
errorElement?.classList.remove("apm__error--visible");
|
|
960
|
+
cta.disabled = true;
|
|
961
|
+
cta.setAttribute("aria-busy", "true");
|
|
962
|
+
console.info("[web-apple-pay] CTA clicked \u2014 fetching reCAPTCHA tokens");
|
|
963
|
+
getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
|
|
964
|
+
console.info("[web-apple-pay] reCAPTCHA tokens resolved successfully");
|
|
965
|
+
if (this.#isClosed) {
|
|
966
|
+
console.warn("[web-apple-pay] Modal was closed before reCAPTCHA resolved \u2014 aborting Apple Pay initiation");
|
|
967
|
+
return;
|
|
961
968
|
}
|
|
969
|
+
console.info("[web-apple-pay] Calling onConfirm to initiate Apple Pay session");
|
|
970
|
+
this.onConfirm?.(recaptchaTokens);
|
|
971
|
+
this.close();
|
|
972
|
+
}).catch((error) => {
|
|
973
|
+
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
974
|
+
cta.disabled = false;
|
|
975
|
+
cta.removeAttribute("aria-busy");
|
|
962
976
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
963
977
|
if (existingError) {
|
|
964
978
|
const icon = existingError.querySelector("svg");
|
|
965
979
|
existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
|
|
966
980
|
if (icon) existingError.prepend(icon);
|
|
981
|
+
existingError.setAttribute("role", "alert");
|
|
967
982
|
existingError.classList.add("apm__error--visible");
|
|
968
983
|
} else {
|
|
969
984
|
const errorNotice = document.createElement("div");
|
|
@@ -973,25 +988,13 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
973
988
|
cta?.insertAdjacentElement("beforebegin", errorNotice);
|
|
974
989
|
}
|
|
975
990
|
});
|
|
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
991
|
});
|
|
988
992
|
this.querySelector("#apple-pay-modal-close")?.addEventListener(
|
|
989
993
|
"click",
|
|
990
994
|
() => this.close()
|
|
991
995
|
);
|
|
992
996
|
}
|
|
993
|
-
open(
|
|
994
|
-
this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
|
|
997
|
+
open() {
|
|
995
998
|
this.#triggerElement = document.activeElement;
|
|
996
999
|
document.body.appendChild(this);
|
|
997
1000
|
this.#keyHandler = (event) => {
|
|
@@ -1006,6 +1009,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
1006
1009
|
this.querySelector("#apple-pay-modal")?.focus();
|
|
1007
1010
|
}
|
|
1008
1011
|
close() {
|
|
1012
|
+
this.#isClosed = true;
|
|
1009
1013
|
if (this.#keyHandler) {
|
|
1010
1014
|
document.removeEventListener("keydown", this.#keyHandler, {
|
|
1011
1015
|
capture: true
|
|
@@ -1607,12 +1611,24 @@ function handleStructuredError(ctx) {
|
|
|
1607
1611
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1608
1612
|
const { skuId, returnUrl, country } = config;
|
|
1609
1613
|
let validatedEmail;
|
|
1614
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1615
|
+
skuId,
|
|
1616
|
+
country,
|
|
1617
|
+
loggedIn: sessionData.loggedIn,
|
|
1618
|
+
userType: sessionData.userType,
|
|
1619
|
+
recaptchaTokensPresent: {
|
|
1620
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1621
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1622
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1610
1625
|
try {
|
|
1611
1626
|
validatedEmail = validateContacts(
|
|
1612
1627
|
event,
|
|
1613
1628
|
sessionData,
|
|
1614
1629
|
config.supportedBillingCountries
|
|
1615
1630
|
);
|
|
1631
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1616
1632
|
const result = await performPurchase({
|
|
1617
1633
|
applePayToken: event.payment.token,
|
|
1618
1634
|
skuId: config.skuId,
|
|
@@ -1623,6 +1639,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1623
1639
|
},
|
|
1624
1640
|
recaptchaTokens: config.recaptchaTokens
|
|
1625
1641
|
});
|
|
1642
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1626
1643
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1627
1644
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1628
1645
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1637,10 +1654,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1637
1654
|
});
|
|
1638
1655
|
} catch (err) {
|
|
1639
1656
|
if (err instanceof ContactValidationError) {
|
|
1657
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1640
1658
|
failApplePaySession(session, err.errors);
|
|
1641
1659
|
return;
|
|
1642
1660
|
}
|
|
1643
1661
|
const walletError = parseWalletApiError(err);
|
|
1662
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
1663
|
+
skuId,
|
|
1664
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1665
|
+
message: walletError?.message,
|
|
1666
|
+
err
|
|
1667
|
+
});
|
|
1644
1668
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1645
1669
|
failApplePaySession(session);
|
|
1646
1670
|
if (!walletError) {
|
|
@@ -2172,10 +2196,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2172
2196
|
const skuId = this.#offer.sku_id;
|
|
2173
2197
|
const country = this.#country;
|
|
2174
2198
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2199
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2175
2200
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2176
2201
|
};
|
|
2177
|
-
|
|
2178
|
-
modal.open(
|
|
2202
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2203
|
+
modal.open();
|
|
2179
2204
|
}
|
|
2180
2205
|
async #fetchSession() {
|
|
2181
2206
|
try {
|
|
@@ -2222,6 +2247,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2222
2247
|
try {
|
|
2223
2248
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2224
2249
|
const paymentOptions = this.#paymentOptions;
|
|
2250
|
+
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2251
|
+
skuId,
|
|
2252
|
+
country,
|
|
2253
|
+
loggedIn: sessionData.loggedIn,
|
|
2254
|
+
userType: sessionData.userType,
|
|
2255
|
+
hasPaymentOptions: !!paymentOptions,
|
|
2256
|
+
recaptchaTokensPresent: {
|
|
2257
|
+
checkEligibility: !!recaptchaTokens.checkEligibility,
|
|
2258
|
+
newBasket: !!recaptchaTokens.newBasket,
|
|
2259
|
+
registerUser: !!recaptchaTokens.registerUser
|
|
2260
|
+
}
|
|
2261
|
+
});
|
|
2225
2262
|
startApplePaySession(
|
|
2226
2263
|
this.#buildSessionParams(
|
|
2227
2264
|
skuId,
|
|
@@ -2231,7 +2268,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2231
2268
|
recaptchaTokens
|
|
2232
2269
|
)
|
|
2233
2270
|
);
|
|
2234
|
-
} catch {
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2235
2273
|
}
|
|
2236
2274
|
}
|
|
2237
2275
|
};
|
|
@@ -570,10 +570,11 @@ 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
|
+
const error = new Error("Failed to load existing reCAPTCHA script");
|
|
575
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (existing script)", error);
|
|
576
|
+
reject(error);
|
|
577
|
+
});
|
|
577
578
|
return;
|
|
578
579
|
}
|
|
579
580
|
const script = document.createElement("script");
|
|
@@ -581,7 +582,11 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
581
582
|
script.async = true;
|
|
582
583
|
script.defer = true;
|
|
583
584
|
script.onload = () => resolve();
|
|
584
|
-
script.onerror = () =>
|
|
585
|
+
script.onerror = () => {
|
|
586
|
+
const error = new Error("Failed to load reCAPTCHA script");
|
|
587
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (new script)", error);
|
|
588
|
+
reject(error);
|
|
589
|
+
};
|
|
585
590
|
document.head.appendChild(script);
|
|
586
591
|
});
|
|
587
592
|
};
|
|
@@ -600,12 +605,19 @@ var getRecaptchaToken = async (action) => {
|
|
|
600
605
|
});
|
|
601
606
|
};
|
|
602
607
|
var getPurchaseRecaptchaTokens = async () => {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
608
|
+
console.info("[web-apple-pay] Requesting reCAPTCHA tokens (checkEligibility, newBasket, registerUser)");
|
|
609
|
+
try {
|
|
610
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
611
|
+
getRecaptchaToken("checkEligibility"),
|
|
612
|
+
getRecaptchaToken("newBasket"),
|
|
613
|
+
getRecaptchaToken("registerUser")
|
|
614
|
+
]);
|
|
615
|
+
console.info("[web-apple-pay] All reCAPTCHA tokens acquired successfully");
|
|
616
|
+
return { checkEligibility, newBasket, registerUser };
|
|
617
|
+
} catch (error) {
|
|
618
|
+
console.error("[web-apple-pay] Failed to acquire one or more reCAPTCHA tokens", error);
|
|
619
|
+
throw error;
|
|
620
|
+
}
|
|
609
621
|
};
|
|
610
622
|
|
|
611
623
|
// src/apple-pay-modal.ts
|
|
@@ -830,17 +842,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
830
842
|
#country = "";
|
|
831
843
|
#triggerElement = null;
|
|
832
844
|
#keyHandler = null;
|
|
833
|
-
#
|
|
834
|
-
#recaptchaTokensPromise = null;
|
|
845
|
+
#isClosed = false;
|
|
835
846
|
/**
|
|
836
|
-
*
|
|
837
|
-
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
* reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
|
|
842
|
-
* until the token pre-fetch resolves, so this callback is never reached
|
|
843
|
-
* without valid tokens.
|
|
847
|
+
* Callback invoked from the CTA click handler once the user confirms.
|
|
848
|
+
* The caller (ApplePayButton) must call startApplePaySession inside this
|
|
849
|
+
* callback to preserve the browser's user-gesture chain — Safari requires
|
|
850
|
+
* new ApplePaySession() to execute within the original click stack.
|
|
844
851
|
*/
|
|
845
852
|
onConfirm = null;
|
|
846
853
|
set offer(value) {
|
|
@@ -937,28 +944,36 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
937
944
|
}
|
|
938
945
|
});
|
|
939
946
|
}
|
|
940
|
-
|
|
941
|
-
if (cta)
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
947
|
+
cta?.addEventListener("click", () => {
|
|
948
|
+
if (cta.disabled) return;
|
|
949
|
+
if (isUS && checkbox && !checkbox.checked) {
|
|
950
|
+
errorElement?.classList.add("apm__error--visible");
|
|
951
|
+
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
952
|
+
return;
|
|
945
953
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
cta.removeAttribute("aria-busy");
|
|
954
|
+
errorElement?.classList.remove("apm__error--visible");
|
|
955
|
+
cta.disabled = true;
|
|
956
|
+
cta.setAttribute("aria-busy", "true");
|
|
957
|
+
console.info("[web-apple-pay] CTA clicked \u2014 fetching reCAPTCHA tokens");
|
|
958
|
+
getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
|
|
959
|
+
console.info("[web-apple-pay] reCAPTCHA tokens resolved successfully");
|
|
960
|
+
if (this.#isClosed) {
|
|
961
|
+
console.warn("[web-apple-pay] Modal was closed before reCAPTCHA resolved \u2014 aborting Apple Pay initiation");
|
|
962
|
+
return;
|
|
956
963
|
}
|
|
964
|
+
console.info("[web-apple-pay] Calling onConfirm to initiate Apple Pay session");
|
|
965
|
+
this.onConfirm?.(recaptchaTokens);
|
|
966
|
+
this.close();
|
|
967
|
+
}).catch((error) => {
|
|
968
|
+
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
969
|
+
cta.disabled = false;
|
|
970
|
+
cta.removeAttribute("aria-busy");
|
|
957
971
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
958
972
|
if (existingError) {
|
|
959
973
|
const icon = existingError.querySelector("svg");
|
|
960
974
|
existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
|
|
961
975
|
if (icon) existingError.prepend(icon);
|
|
976
|
+
existingError.setAttribute("role", "alert");
|
|
962
977
|
existingError.classList.add("apm__error--visible");
|
|
963
978
|
} else {
|
|
964
979
|
const errorNotice = document.createElement("div");
|
|
@@ -968,25 +983,13 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
968
983
|
cta?.insertAdjacentElement("beforebegin", errorNotice);
|
|
969
984
|
}
|
|
970
985
|
});
|
|
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
986
|
});
|
|
983
987
|
this.querySelector("#apple-pay-modal-close")?.addEventListener(
|
|
984
988
|
"click",
|
|
985
989
|
() => this.close()
|
|
986
990
|
);
|
|
987
991
|
}
|
|
988
|
-
open(
|
|
989
|
-
this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
|
|
992
|
+
open() {
|
|
990
993
|
this.#triggerElement = document.activeElement;
|
|
991
994
|
document.body.appendChild(this);
|
|
992
995
|
this.#keyHandler = (event) => {
|
|
@@ -1001,6 +1004,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
1001
1004
|
this.querySelector("#apple-pay-modal")?.focus();
|
|
1002
1005
|
}
|
|
1003
1006
|
close() {
|
|
1007
|
+
this.#isClosed = true;
|
|
1004
1008
|
if (this.#keyHandler) {
|
|
1005
1009
|
document.removeEventListener("keydown", this.#keyHandler, {
|
|
1006
1010
|
capture: true
|
|
@@ -1602,12 +1606,24 @@ function handleStructuredError(ctx) {
|
|
|
1602
1606
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1603
1607
|
const { skuId, returnUrl, country } = config;
|
|
1604
1608
|
let validatedEmail;
|
|
1609
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1610
|
+
skuId,
|
|
1611
|
+
country,
|
|
1612
|
+
loggedIn: sessionData.loggedIn,
|
|
1613
|
+
userType: sessionData.userType,
|
|
1614
|
+
recaptchaTokensPresent: {
|
|
1615
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1616
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1617
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1605
1620
|
try {
|
|
1606
1621
|
validatedEmail = validateContacts(
|
|
1607
1622
|
event,
|
|
1608
1623
|
sessionData,
|
|
1609
1624
|
config.supportedBillingCountries
|
|
1610
1625
|
);
|
|
1626
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1611
1627
|
const result = await performPurchase({
|
|
1612
1628
|
applePayToken: event.payment.token,
|
|
1613
1629
|
skuId: config.skuId,
|
|
@@ -1618,6 +1634,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1618
1634
|
},
|
|
1619
1635
|
recaptchaTokens: config.recaptchaTokens
|
|
1620
1636
|
});
|
|
1637
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1621
1638
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1622
1639
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1623
1640
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1632,10 +1649,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1632
1649
|
});
|
|
1633
1650
|
} catch (err) {
|
|
1634
1651
|
if (err instanceof ContactValidationError) {
|
|
1652
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1635
1653
|
failApplePaySession(session, err.errors);
|
|
1636
1654
|
return;
|
|
1637
1655
|
}
|
|
1638
1656
|
const walletError = parseWalletApiError(err);
|
|
1657
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
1658
|
+
skuId,
|
|
1659
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1660
|
+
message: walletError?.message,
|
|
1661
|
+
err
|
|
1662
|
+
});
|
|
1639
1663
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1640
1664
|
failApplePaySession(session);
|
|
1641
1665
|
if (!walletError) {
|
|
@@ -2167,10 +2191,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2167
2191
|
const skuId = this.#offer.sku_id;
|
|
2168
2192
|
const country = this.#country;
|
|
2169
2193
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2194
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2170
2195
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2171
2196
|
};
|
|
2172
|
-
|
|
2173
|
-
modal.open(
|
|
2197
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2198
|
+
modal.open();
|
|
2174
2199
|
}
|
|
2175
2200
|
async #fetchSession() {
|
|
2176
2201
|
try {
|
|
@@ -2217,6 +2242,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2217
2242
|
try {
|
|
2218
2243
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2219
2244
|
const paymentOptions = this.#paymentOptions;
|
|
2245
|
+
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2246
|
+
skuId,
|
|
2247
|
+
country,
|
|
2248
|
+
loggedIn: sessionData.loggedIn,
|
|
2249
|
+
userType: sessionData.userType,
|
|
2250
|
+
hasPaymentOptions: !!paymentOptions,
|
|
2251
|
+
recaptchaTokensPresent: {
|
|
2252
|
+
checkEligibility: !!recaptchaTokens.checkEligibility,
|
|
2253
|
+
newBasket: !!recaptchaTokens.newBasket,
|
|
2254
|
+
registerUser: !!recaptchaTokens.registerUser
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2220
2257
|
startApplePaySession(
|
|
2221
2258
|
this.#buildSessionParams(
|
|
2222
2259
|
skuId,
|
|
@@ -2226,7 +2263,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2226
2263
|
recaptchaTokens
|
|
2227
2264
|
)
|
|
2228
2265
|
);
|
|
2229
|
-
} catch {
|
|
2266
|
+
} catch (error) {
|
|
2267
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2230
2268
|
}
|
|
2231
2269
|
}
|
|
2232
2270
|
};
|