@economist/web-apple-pay 0.1.3-beta.43 → 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 +68 -14
- package/dist/src/apple-pay-button.js +68 -14
- package/dist/src/apple-pay-button.stories.js +68 -14
- package/dist/src/apple-pay-modal.js +32 -13
- package/dist/src/apple-pay-modal.stories.js +68 -14
- 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 +68 -14
- 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
|
|
@@ -947,11 +959,18 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
947
959
|
errorElement?.classList.remove("apm__error--visible");
|
|
948
960
|
cta.disabled = true;
|
|
949
961
|
cta.setAttribute("aria-busy", "true");
|
|
962
|
+
console.info("[web-apple-pay] CTA clicked \u2014 fetching reCAPTCHA tokens");
|
|
950
963
|
getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
|
|
951
|
-
|
|
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;
|
|
968
|
+
}
|
|
969
|
+
console.info("[web-apple-pay] Calling onConfirm to initiate Apple Pay session");
|
|
952
970
|
this.onConfirm?.(recaptchaTokens);
|
|
953
971
|
this.close();
|
|
954
|
-
}).catch(() => {
|
|
972
|
+
}).catch((error) => {
|
|
973
|
+
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
955
974
|
cta.disabled = false;
|
|
956
975
|
cta.removeAttribute("aria-busy");
|
|
957
976
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
@@ -1592,12 +1611,24 @@ function handleStructuredError(ctx) {
|
|
|
1592
1611
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1593
1612
|
const { skuId, returnUrl, country } = config;
|
|
1594
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
|
+
});
|
|
1595
1625
|
try {
|
|
1596
1626
|
validatedEmail = validateContacts(
|
|
1597
1627
|
event,
|
|
1598
1628
|
sessionData,
|
|
1599
1629
|
config.supportedBillingCountries
|
|
1600
1630
|
);
|
|
1631
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1601
1632
|
const result = await performPurchase({
|
|
1602
1633
|
applePayToken: event.payment.token,
|
|
1603
1634
|
skuId: config.skuId,
|
|
@@ -1608,6 +1639,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1608
1639
|
},
|
|
1609
1640
|
recaptchaTokens: config.recaptchaTokens
|
|
1610
1641
|
});
|
|
1642
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1611
1643
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1612
1644
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1613
1645
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1622,10 +1654,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1622
1654
|
});
|
|
1623
1655
|
} catch (err) {
|
|
1624
1656
|
if (err instanceof ContactValidationError) {
|
|
1657
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1625
1658
|
failApplePaySession(session, err.errors);
|
|
1626
1659
|
return;
|
|
1627
1660
|
}
|
|
1628
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
|
+
});
|
|
1629
1668
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1630
1669
|
failApplePaySession(session);
|
|
1631
1670
|
if (!walletError) {
|
|
@@ -2157,8 +2196,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2157
2196
|
const skuId = this.#offer.sku_id;
|
|
2158
2197
|
const country = this.#country;
|
|
2159
2198
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2199
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2160
2200
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2161
2201
|
};
|
|
2202
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2162
2203
|
modal.open();
|
|
2163
2204
|
}
|
|
2164
2205
|
async #fetchSession() {
|
|
@@ -2206,6 +2247,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2206
2247
|
try {
|
|
2207
2248
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2208
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
|
+
});
|
|
2209
2262
|
startApplePaySession(
|
|
2210
2263
|
this.#buildSessionParams(
|
|
2211
2264
|
skuId,
|
|
@@ -2215,7 +2268,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2215
2268
|
recaptchaTokens
|
|
2216
2269
|
)
|
|
2217
2270
|
);
|
|
2218
|
-
} catch {
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2219
2273
|
}
|
|
2220
2274
|
}
|
|
2221
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
|
|
@@ -942,11 +954,18 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
942
954
|
errorElement?.classList.remove("apm__error--visible");
|
|
943
955
|
cta.disabled = true;
|
|
944
956
|
cta.setAttribute("aria-busy", "true");
|
|
957
|
+
console.info("[web-apple-pay] CTA clicked \u2014 fetching reCAPTCHA tokens");
|
|
945
958
|
getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
|
|
946
|
-
|
|
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;
|
|
963
|
+
}
|
|
964
|
+
console.info("[web-apple-pay] Calling onConfirm to initiate Apple Pay session");
|
|
947
965
|
this.onConfirm?.(recaptchaTokens);
|
|
948
966
|
this.close();
|
|
949
|
-
}).catch(() => {
|
|
967
|
+
}).catch((error) => {
|
|
968
|
+
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
950
969
|
cta.disabled = false;
|
|
951
970
|
cta.removeAttribute("aria-busy");
|
|
952
971
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
@@ -1587,12 +1606,24 @@ function handleStructuredError(ctx) {
|
|
|
1587
1606
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1588
1607
|
const { skuId, returnUrl, country } = config;
|
|
1589
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
|
+
});
|
|
1590
1620
|
try {
|
|
1591
1621
|
validatedEmail = validateContacts(
|
|
1592
1622
|
event,
|
|
1593
1623
|
sessionData,
|
|
1594
1624
|
config.supportedBillingCountries
|
|
1595
1625
|
);
|
|
1626
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1596
1627
|
const result = await performPurchase({
|
|
1597
1628
|
applePayToken: event.payment.token,
|
|
1598
1629
|
skuId: config.skuId,
|
|
@@ -1603,6 +1634,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1603
1634
|
},
|
|
1604
1635
|
recaptchaTokens: config.recaptchaTokens
|
|
1605
1636
|
});
|
|
1637
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1606
1638
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1607
1639
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1608
1640
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1617,10 +1649,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1617
1649
|
});
|
|
1618
1650
|
} catch (err) {
|
|
1619
1651
|
if (err instanceof ContactValidationError) {
|
|
1652
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1620
1653
|
failApplePaySession(session, err.errors);
|
|
1621
1654
|
return;
|
|
1622
1655
|
}
|
|
1623
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
|
+
});
|
|
1624
1663
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1625
1664
|
failApplePaySession(session);
|
|
1626
1665
|
if (!walletError) {
|
|
@@ -2152,8 +2191,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2152
2191
|
const skuId = this.#offer.sku_id;
|
|
2153
2192
|
const country = this.#country;
|
|
2154
2193
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2194
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2155
2195
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2156
2196
|
};
|
|
2197
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2157
2198
|
modal.open();
|
|
2158
2199
|
}
|
|
2159
2200
|
async #fetchSession() {
|
|
@@ -2201,6 +2242,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2201
2242
|
try {
|
|
2202
2243
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2203
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
|
+
});
|
|
2204
2257
|
startApplePaySession(
|
|
2205
2258
|
this.#buildSessionParams(
|
|
2206
2259
|
skuId,
|
|
@@ -2210,7 +2263,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2210
2263
|
recaptchaTokens
|
|
2211
2264
|
)
|
|
2212
2265
|
);
|
|
2213
|
-
} catch {
|
|
2266
|
+
} catch (error) {
|
|
2267
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2214
2268
|
}
|
|
2215
2269
|
}
|
|
2216
2270
|
};
|
|
@@ -578,10 +578,11 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
578
578
|
);
|
|
579
579
|
if (existingScript) {
|
|
580
580
|
existingScript.addEventListener("load", () => resolve());
|
|
581
|
-
existingScript.addEventListener(
|
|
582
|
-
|
|
583
|
-
(
|
|
584
|
-
|
|
581
|
+
existingScript.addEventListener("error", () => {
|
|
582
|
+
const error = new Error("Failed to load existing reCAPTCHA script");
|
|
583
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (existing script)", error);
|
|
584
|
+
reject(error);
|
|
585
|
+
});
|
|
585
586
|
return;
|
|
586
587
|
}
|
|
587
588
|
const script = document.createElement("script");
|
|
@@ -589,7 +590,11 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
589
590
|
script.async = true;
|
|
590
591
|
script.defer = true;
|
|
591
592
|
script.onload = () => resolve();
|
|
592
|
-
script.onerror = () =>
|
|
593
|
+
script.onerror = () => {
|
|
594
|
+
const error = new Error("Failed to load reCAPTCHA script");
|
|
595
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (new script)", error);
|
|
596
|
+
reject(error);
|
|
597
|
+
};
|
|
593
598
|
document.head.appendChild(script);
|
|
594
599
|
});
|
|
595
600
|
};
|
|
@@ -608,12 +613,19 @@ var getRecaptchaToken = async (action) => {
|
|
|
608
613
|
});
|
|
609
614
|
};
|
|
610
615
|
var getPurchaseRecaptchaTokens = async () => {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
616
|
+
console.info("[web-apple-pay] Requesting reCAPTCHA tokens (checkEligibility, newBasket, registerUser)");
|
|
617
|
+
try {
|
|
618
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
619
|
+
getRecaptchaToken("checkEligibility"),
|
|
620
|
+
getRecaptchaToken("newBasket"),
|
|
621
|
+
getRecaptchaToken("registerUser")
|
|
622
|
+
]);
|
|
623
|
+
console.info("[web-apple-pay] All reCAPTCHA tokens acquired successfully");
|
|
624
|
+
return { checkEligibility, newBasket, registerUser };
|
|
625
|
+
} catch (error) {
|
|
626
|
+
console.error("[web-apple-pay] Failed to acquire one or more reCAPTCHA tokens", error);
|
|
627
|
+
throw error;
|
|
628
|
+
}
|
|
617
629
|
};
|
|
618
630
|
|
|
619
631
|
// src/apple-pay-modal.ts
|
|
@@ -950,11 +962,18 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
950
962
|
errorElement?.classList.remove("apm__error--visible");
|
|
951
963
|
cta.disabled = true;
|
|
952
964
|
cta.setAttribute("aria-busy", "true");
|
|
965
|
+
console.info("[web-apple-pay] CTA clicked \u2014 fetching reCAPTCHA tokens");
|
|
953
966
|
getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
|
|
954
|
-
|
|
967
|
+
console.info("[web-apple-pay] reCAPTCHA tokens resolved successfully");
|
|
968
|
+
if (this.#isClosed) {
|
|
969
|
+
console.warn("[web-apple-pay] Modal was closed before reCAPTCHA resolved \u2014 aborting Apple Pay initiation");
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
console.info("[web-apple-pay] Calling onConfirm to initiate Apple Pay session");
|
|
955
973
|
this.onConfirm?.(recaptchaTokens);
|
|
956
974
|
this.close();
|
|
957
|
-
}).catch(() => {
|
|
975
|
+
}).catch((error) => {
|
|
976
|
+
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
958
977
|
cta.disabled = false;
|
|
959
978
|
cta.removeAttribute("aria-busy");
|
|
960
979
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
@@ -1595,12 +1614,24 @@ function handleStructuredError(ctx) {
|
|
|
1595
1614
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1596
1615
|
const { skuId, returnUrl, country } = config;
|
|
1597
1616
|
let validatedEmail;
|
|
1617
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1618
|
+
skuId,
|
|
1619
|
+
country,
|
|
1620
|
+
loggedIn: sessionData.loggedIn,
|
|
1621
|
+
userType: sessionData.userType,
|
|
1622
|
+
recaptchaTokensPresent: {
|
|
1623
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1624
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1625
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1598
1628
|
try {
|
|
1599
1629
|
validatedEmail = validateContacts(
|
|
1600
1630
|
event,
|
|
1601
1631
|
sessionData,
|
|
1602
1632
|
config.supportedBillingCountries
|
|
1603
1633
|
);
|
|
1634
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1604
1635
|
const result = await performPurchase({
|
|
1605
1636
|
applePayToken: event.payment.token,
|
|
1606
1637
|
skuId: config.skuId,
|
|
@@ -1611,6 +1642,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1611
1642
|
},
|
|
1612
1643
|
recaptchaTokens: config.recaptchaTokens
|
|
1613
1644
|
});
|
|
1645
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1614
1646
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1615
1647
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1616
1648
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1625,10 +1657,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1625
1657
|
});
|
|
1626
1658
|
} catch (err) {
|
|
1627
1659
|
if (err instanceof ContactValidationError) {
|
|
1660
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1628
1661
|
failApplePaySession(session, err.errors);
|
|
1629
1662
|
return;
|
|
1630
1663
|
}
|
|
1631
1664
|
const walletError = parseWalletApiError(err);
|
|
1665
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
1666
|
+
skuId,
|
|
1667
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1668
|
+
message: walletError?.message,
|
|
1669
|
+
err
|
|
1670
|
+
});
|
|
1632
1671
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1633
1672
|
failApplePaySession(session);
|
|
1634
1673
|
if (!walletError) {
|
|
@@ -2160,8 +2199,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2160
2199
|
const skuId = this.#offer.sku_id;
|
|
2161
2200
|
const country = this.#country;
|
|
2162
2201
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2202
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2163
2203
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2164
2204
|
};
|
|
2205
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2165
2206
|
modal.open();
|
|
2166
2207
|
}
|
|
2167
2208
|
async #fetchSession() {
|
|
@@ -2209,6 +2250,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2209
2250
|
try {
|
|
2210
2251
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2211
2252
|
const paymentOptions = this.#paymentOptions;
|
|
2253
|
+
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2254
|
+
skuId,
|
|
2255
|
+
country,
|
|
2256
|
+
loggedIn: sessionData.loggedIn,
|
|
2257
|
+
userType: sessionData.userType,
|
|
2258
|
+
hasPaymentOptions: !!paymentOptions,
|
|
2259
|
+
recaptchaTokensPresent: {
|
|
2260
|
+
checkEligibility: !!recaptchaTokens.checkEligibility,
|
|
2261
|
+
newBasket: !!recaptchaTokens.newBasket,
|
|
2262
|
+
registerUser: !!recaptchaTokens.registerUser
|
|
2263
|
+
}
|
|
2264
|
+
});
|
|
2212
2265
|
startApplePaySession(
|
|
2213
2266
|
this.#buildSessionParams(
|
|
2214
2267
|
skuId,
|
|
@@ -2218,7 +2271,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2218
2271
|
recaptchaTokens
|
|
2219
2272
|
)
|
|
2220
2273
|
);
|
|
2221
|
-
} catch {
|
|
2274
|
+
} catch (error) {
|
|
2275
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2222
2276
|
}
|
|
2223
2277
|
}
|
|
2224
2278
|
};
|
|
@@ -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
|
|
@@ -942,11 +954,18 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
942
954
|
errorElement?.classList.remove("apm__error--visible");
|
|
943
955
|
cta.disabled = true;
|
|
944
956
|
cta.setAttribute("aria-busy", "true");
|
|
957
|
+
console.info("[web-apple-pay] CTA clicked \u2014 fetching reCAPTCHA tokens");
|
|
945
958
|
getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
|
|
946
|
-
|
|
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;
|
|
963
|
+
}
|
|
964
|
+
console.info("[web-apple-pay] Calling onConfirm to initiate Apple Pay session");
|
|
947
965
|
this.onConfirm?.(recaptchaTokens);
|
|
948
966
|
this.close();
|
|
949
|
-
}).catch(() => {
|
|
967
|
+
}).catch((error) => {
|
|
968
|
+
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
950
969
|
cta.disabled = false;
|
|
951
970
|
cta.removeAttribute("aria-busy");
|
|
952
971
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
@@ -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
|
|
@@ -942,11 +954,18 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
942
954
|
errorElement?.classList.remove("apm__error--visible");
|
|
943
955
|
cta.disabled = true;
|
|
944
956
|
cta.setAttribute("aria-busy", "true");
|
|
957
|
+
console.info("[web-apple-pay] CTA clicked \u2014 fetching reCAPTCHA tokens");
|
|
945
958
|
getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
|
|
946
|
-
|
|
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;
|
|
963
|
+
}
|
|
964
|
+
console.info("[web-apple-pay] Calling onConfirm to initiate Apple Pay session");
|
|
947
965
|
this.onConfirm?.(recaptchaTokens);
|
|
948
966
|
this.close();
|
|
949
|
-
}).catch(() => {
|
|
967
|
+
}).catch((error) => {
|
|
968
|
+
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
950
969
|
cta.disabled = false;
|
|
951
970
|
cta.removeAttribute("aria-busy");
|
|
952
971
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
@@ -1587,12 +1606,24 @@ function handleStructuredError(ctx) {
|
|
|
1587
1606
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1588
1607
|
const { skuId, returnUrl, country } = config;
|
|
1589
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
|
+
});
|
|
1590
1620
|
try {
|
|
1591
1621
|
validatedEmail = validateContacts(
|
|
1592
1622
|
event,
|
|
1593
1623
|
sessionData,
|
|
1594
1624
|
config.supportedBillingCountries
|
|
1595
1625
|
);
|
|
1626
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1596
1627
|
const result = await performPurchase({
|
|
1597
1628
|
applePayToken: event.payment.token,
|
|
1598
1629
|
skuId: config.skuId,
|
|
@@ -1603,6 +1634,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1603
1634
|
},
|
|
1604
1635
|
recaptchaTokens: config.recaptchaTokens
|
|
1605
1636
|
});
|
|
1637
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1606
1638
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1607
1639
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1608
1640
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1617,10 +1649,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1617
1649
|
});
|
|
1618
1650
|
} catch (err) {
|
|
1619
1651
|
if (err instanceof ContactValidationError) {
|
|
1652
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1620
1653
|
failApplePaySession(session, err.errors);
|
|
1621
1654
|
return;
|
|
1622
1655
|
}
|
|
1623
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
|
+
});
|
|
1624
1663
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1625
1664
|
failApplePaySession(session);
|
|
1626
1665
|
if (!walletError) {
|
|
@@ -2152,8 +2191,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2152
2191
|
const skuId = this.#offer.sku_id;
|
|
2153
2192
|
const country = this.#country;
|
|
2154
2193
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2194
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2155
2195
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2156
2196
|
};
|
|
2197
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2157
2198
|
modal.open();
|
|
2158
2199
|
}
|
|
2159
2200
|
async #fetchSession() {
|
|
@@ -2201,6 +2242,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2201
2242
|
try {
|
|
2202
2243
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2203
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
|
+
});
|
|
2204
2257
|
startApplePaySession(
|
|
2205
2258
|
this.#buildSessionParams(
|
|
2206
2259
|
skuId,
|
|
@@ -2210,7 +2263,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2210
2263
|
recaptchaTokens
|
|
2211
2264
|
)
|
|
2212
2265
|
);
|
|
2213
|
-
} catch {
|
|
2266
|
+
} catch (error) {
|
|
2267
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2214
2268
|
}
|
|
2215
2269
|
}
|
|
2216
2270
|
};
|
|
@@ -108,10 +108,11 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
108
108
|
);
|
|
109
109
|
if (existingScript) {
|
|
110
110
|
existingScript.addEventListener("load", () => resolve());
|
|
111
|
-
existingScript.addEventListener(
|
|
112
|
-
|
|
113
|
-
(
|
|
114
|
-
|
|
111
|
+
existingScript.addEventListener("error", () => {
|
|
112
|
+
const error = new Error("Failed to load existing reCAPTCHA script");
|
|
113
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (existing script)", error);
|
|
114
|
+
reject(error);
|
|
115
|
+
});
|
|
115
116
|
return;
|
|
116
117
|
}
|
|
117
118
|
const script = document.createElement("script");
|
|
@@ -119,7 +120,11 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
119
120
|
script.async = true;
|
|
120
121
|
script.defer = true;
|
|
121
122
|
script.onload = () => resolve();
|
|
122
|
-
script.onerror = () =>
|
|
123
|
+
script.onerror = () => {
|
|
124
|
+
const error = new Error("Failed to load reCAPTCHA script");
|
|
125
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (new script)", error);
|
|
126
|
+
reject(error);
|
|
127
|
+
};
|
|
123
128
|
document.head.appendChild(script);
|
|
124
129
|
});
|
|
125
130
|
};
|
|
@@ -345,12 +345,24 @@ function handleStructuredError(ctx) {
|
|
|
345
345
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
346
346
|
const { skuId, returnUrl, country } = config;
|
|
347
347
|
let validatedEmail;
|
|
348
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
349
|
+
skuId,
|
|
350
|
+
country,
|
|
351
|
+
loggedIn: sessionData.loggedIn,
|
|
352
|
+
userType: sessionData.userType,
|
|
353
|
+
recaptchaTokensPresent: {
|
|
354
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
355
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
356
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
357
|
+
}
|
|
358
|
+
});
|
|
348
359
|
try {
|
|
349
360
|
validatedEmail = validateContacts(
|
|
350
361
|
event,
|
|
351
362
|
sessionData,
|
|
352
363
|
config.supportedBillingCountries
|
|
353
364
|
);
|
|
365
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
354
366
|
const result = await performPurchase({
|
|
355
367
|
applePayToken: event.payment.token,
|
|
356
368
|
skuId: config.skuId,
|
|
@@ -361,6 +373,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
361
373
|
},
|
|
362
374
|
recaptchaTokens: config.recaptchaTokens
|
|
363
375
|
});
|
|
376
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
364
377
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
365
378
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
366
379
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -375,10 +388,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
375
388
|
});
|
|
376
389
|
} catch (err) {
|
|
377
390
|
if (err instanceof ContactValidationError) {
|
|
391
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
378
392
|
failApplePaySession(session, err.errors);
|
|
379
393
|
return;
|
|
380
394
|
}
|
|
381
395
|
const walletError = parseWalletApiError(err);
|
|
396
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
397
|
+
skuId,
|
|
398
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
399
|
+
message: walletError?.message,
|
|
400
|
+
err
|
|
401
|
+
});
|
|
382
402
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
383
403
|
failApplePaySession(session);
|
|
384
404
|
if (!walletError) {
|
package/dist/src/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
|
|
@@ -947,11 +959,18 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
947
959
|
errorElement?.classList.remove("apm__error--visible");
|
|
948
960
|
cta.disabled = true;
|
|
949
961
|
cta.setAttribute("aria-busy", "true");
|
|
962
|
+
console.info("[web-apple-pay] CTA clicked \u2014 fetching reCAPTCHA tokens");
|
|
950
963
|
getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
|
|
951
|
-
|
|
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;
|
|
968
|
+
}
|
|
969
|
+
console.info("[web-apple-pay] Calling onConfirm to initiate Apple Pay session");
|
|
952
970
|
this.onConfirm?.(recaptchaTokens);
|
|
953
971
|
this.close();
|
|
954
|
-
}).catch(() => {
|
|
972
|
+
}).catch((error) => {
|
|
973
|
+
console.error("[web-apple-pay] reCAPTCHA token fetch failed", error);
|
|
955
974
|
cta.disabled = false;
|
|
956
975
|
cta.removeAttribute("aria-busy");
|
|
957
976
|
const existingError = this.querySelector("#apple-pay-modal-error");
|
|
@@ -1592,12 +1611,24 @@ function handleStructuredError(ctx) {
|
|
|
1592
1611
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1593
1612
|
const { skuId, returnUrl, country } = config;
|
|
1594
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
|
+
});
|
|
1595
1625
|
try {
|
|
1596
1626
|
validatedEmail = validateContacts(
|
|
1597
1627
|
event,
|
|
1598
1628
|
sessionData,
|
|
1599
1629
|
config.supportedBillingCountries
|
|
1600
1630
|
);
|
|
1631
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1601
1632
|
const result = await performPurchase({
|
|
1602
1633
|
applePayToken: event.payment.token,
|
|
1603
1634
|
skuId: config.skuId,
|
|
@@ -1608,6 +1639,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1608
1639
|
},
|
|
1609
1640
|
recaptchaTokens: config.recaptchaTokens
|
|
1610
1641
|
});
|
|
1642
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1611
1643
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1612
1644
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1613
1645
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1622,10 +1654,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1622
1654
|
});
|
|
1623
1655
|
} catch (err) {
|
|
1624
1656
|
if (err instanceof ContactValidationError) {
|
|
1657
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1625
1658
|
failApplePaySession(session, err.errors);
|
|
1626
1659
|
return;
|
|
1627
1660
|
}
|
|
1628
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
|
+
});
|
|
1629
1668
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1630
1669
|
failApplePaySession(session);
|
|
1631
1670
|
if (!walletError) {
|
|
@@ -2157,8 +2196,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2157
2196
|
const skuId = this.#offer.sku_id;
|
|
2158
2197
|
const country = this.#country;
|
|
2159
2198
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2199
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2160
2200
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2161
2201
|
};
|
|
2202
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2162
2203
|
modal.open();
|
|
2163
2204
|
}
|
|
2164
2205
|
async #fetchSession() {
|
|
@@ -2206,6 +2247,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2206
2247
|
try {
|
|
2207
2248
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2208
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
|
+
});
|
|
2209
2262
|
startApplePaySession(
|
|
2210
2263
|
this.#buildSessionParams(
|
|
2211
2264
|
skuId,
|
|
@@ -2215,7 +2268,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2215
2268
|
recaptchaTokens
|
|
2216
2269
|
)
|
|
2217
2270
|
);
|
|
2218
|
-
} catch {
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2219
2273
|
}
|
|
2220
2274
|
}
|
|
2221
2275
|
};
|
|
@@ -11,10 +11,11 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
11
11
|
);
|
|
12
12
|
if (existingScript) {
|
|
13
13
|
existingScript.addEventListener("load", () => resolve());
|
|
14
|
-
existingScript.addEventListener(
|
|
15
|
-
|
|
16
|
-
(
|
|
17
|
-
|
|
14
|
+
existingScript.addEventListener("error", () => {
|
|
15
|
+
const error = new Error("Failed to load existing reCAPTCHA script");
|
|
16
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (existing script)", error);
|
|
17
|
+
reject(error);
|
|
18
|
+
});
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
20
21
|
const script = document.createElement("script");
|
|
@@ -22,7 +23,11 @@ var loadRecaptchaScript = (siteKey) => {
|
|
|
22
23
|
script.async = true;
|
|
23
24
|
script.defer = true;
|
|
24
25
|
script.onload = () => resolve();
|
|
25
|
-
script.onerror = () =>
|
|
26
|
+
script.onerror = () => {
|
|
27
|
+
const error = new Error("Failed to load reCAPTCHA script");
|
|
28
|
+
console.error("[web-apple-pay] reCAPTCHA script load failed (new script)", error);
|
|
29
|
+
reject(error);
|
|
30
|
+
};
|
|
26
31
|
document.head.appendChild(script);
|
|
27
32
|
});
|
|
28
33
|
};
|
|
@@ -41,12 +46,19 @@ var getRecaptchaToken = async (action) => {
|
|
|
41
46
|
});
|
|
42
47
|
};
|
|
43
48
|
var getPurchaseRecaptchaTokens = async () => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
console.info("[web-apple-pay] Requesting reCAPTCHA tokens (checkEligibility, newBasket, registerUser)");
|
|
50
|
+
try {
|
|
51
|
+
const [checkEligibility, newBasket, registerUser] = await Promise.all([
|
|
52
|
+
getRecaptchaToken("checkEligibility"),
|
|
53
|
+
getRecaptchaToken("newBasket"),
|
|
54
|
+
getRecaptchaToken("registerUser")
|
|
55
|
+
]);
|
|
56
|
+
console.info("[web-apple-pay] All reCAPTCHA tokens acquired successfully");
|
|
57
|
+
return { checkEligibility, newBasket, registerUser };
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("[web-apple-pay] Failed to acquire one or more reCAPTCHA tokens", error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
50
62
|
};
|
|
51
63
|
export {
|
|
52
64
|
getPurchaseRecaptchaTokens,
|
package/package.json
CHANGED