@economist/web-apple-pay 0.1.3-beta.43 → 0.1.3-beta.45
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 +79 -16
- package/dist/src/apple-pay-button.js +79 -16
- package/dist/src/apple-pay-button.stories.js +79 -16
- package/dist/src/apple-pay-modal.js +32 -13
- package/dist/src/apple-pay-modal.stories.js +79 -16
- package/dist/src/clients/payment-checkout/apple-session.js +21 -7
- package/dist/src/clients/payment-checkout/payment-handler.js +20 -0
- package/dist/src/index.js +79 -16
- 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");
|
|
@@ -1362,22 +1381,28 @@ async function handleMerchantValidation(event, session, config) {
|
|
|
1362
1381
|
try {
|
|
1363
1382
|
const validationHost = new URL(event.validationURL).host;
|
|
1364
1383
|
if (!validationHost.endsWith("apple.com")) {
|
|
1384
|
+
console.error("[web-apple-pay] Merchant validation URL is not from apple.com \u2014 aborting session", { validationURL: event.validationURL });
|
|
1365
1385
|
session.abort();
|
|
1366
1386
|
redirectToFallbackCheckout(config);
|
|
1367
1387
|
return;
|
|
1368
1388
|
}
|
|
1389
|
+
console.info("[web-apple-pay] Merchant validation started", { validationHost });
|
|
1369
1390
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
1391
|
+
console.info("[web-apple-pay] verifyMerchant reCAPTCHA token acquired \u2014 calling validateAppleMerchant");
|
|
1370
1392
|
const response = await validateAppleMerchant({
|
|
1371
1393
|
validationURL: event.validationURL,
|
|
1372
1394
|
recaptchaToken
|
|
1373
1395
|
});
|
|
1374
1396
|
if (!response?.merchantSession) {
|
|
1397
|
+
console.error("[web-apple-pay] Merchant validation response missing merchantSession \u2014 aborting session", { response });
|
|
1375
1398
|
session.abort();
|
|
1376
1399
|
redirectToFallbackCheckout(config);
|
|
1377
1400
|
return;
|
|
1378
1401
|
}
|
|
1402
|
+
console.info("[web-apple-pay] Merchant validation succeeded \u2014 completing merchant validation");
|
|
1379
1403
|
session.completeMerchantValidation(response.merchantSession);
|
|
1380
|
-
} catch {
|
|
1404
|
+
} catch (error) {
|
|
1405
|
+
console.error("[web-apple-pay] Merchant validation threw an error \u2014 aborting session", error);
|
|
1381
1406
|
session.abort();
|
|
1382
1407
|
redirectToFallbackCheckout(config);
|
|
1383
1408
|
}
|
|
@@ -1398,6 +1423,7 @@ function startApplePaySession(config) {
|
|
|
1398
1423
|
};
|
|
1399
1424
|
try {
|
|
1400
1425
|
const session = new ApplePaySession(3, paymentRequest);
|
|
1426
|
+
console.info("[web-apple-pay] ApplePaySession created successfully");
|
|
1401
1427
|
session.onvalidatemerchant = async (event) => {
|
|
1402
1428
|
await handleMerchantValidation(event, session, config);
|
|
1403
1429
|
};
|
|
@@ -1410,7 +1436,9 @@ function startApplePaySession(config) {
|
|
|
1410
1436
|
};
|
|
1411
1437
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
1412
1438
|
session.begin();
|
|
1413
|
-
|
|
1439
|
+
console.info("[web-apple-pay] ApplePaySession.begin() called \u2014 payment sheet should appear");
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
console.error("[web-apple-pay] ApplePaySession creation or begin() threw \u2014 redirecting to fallback checkout", error);
|
|
1414
1442
|
redirectToFallbackCheckout(config);
|
|
1415
1443
|
}
|
|
1416
1444
|
}
|
|
@@ -1592,12 +1620,24 @@ function handleStructuredError(ctx) {
|
|
|
1592
1620
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1593
1621
|
const { skuId, returnUrl, country } = config;
|
|
1594
1622
|
let validatedEmail;
|
|
1623
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1624
|
+
skuId,
|
|
1625
|
+
country,
|
|
1626
|
+
loggedIn: sessionData.loggedIn,
|
|
1627
|
+
userType: sessionData.userType,
|
|
1628
|
+
recaptchaTokensPresent: {
|
|
1629
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1630
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1631
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1595
1634
|
try {
|
|
1596
1635
|
validatedEmail = validateContacts(
|
|
1597
1636
|
event,
|
|
1598
1637
|
sessionData,
|
|
1599
1638
|
config.supportedBillingCountries
|
|
1600
1639
|
);
|
|
1640
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1601
1641
|
const result = await performPurchase({
|
|
1602
1642
|
applePayToken: event.payment.token,
|
|
1603
1643
|
skuId: config.skuId,
|
|
@@ -1608,6 +1648,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1608
1648
|
},
|
|
1609
1649
|
recaptchaTokens: config.recaptchaTokens
|
|
1610
1650
|
});
|
|
1651
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1611
1652
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1612
1653
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1613
1654
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1622,10 +1663,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1622
1663
|
});
|
|
1623
1664
|
} catch (err) {
|
|
1624
1665
|
if (err instanceof ContactValidationError) {
|
|
1666
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1625
1667
|
failApplePaySession(session, err.errors);
|
|
1626
1668
|
return;
|
|
1627
1669
|
}
|
|
1628
1670
|
const walletError = parseWalletApiError(err);
|
|
1671
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
1672
|
+
skuId,
|
|
1673
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1674
|
+
message: walletError?.message,
|
|
1675
|
+
err
|
|
1676
|
+
});
|
|
1629
1677
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1630
1678
|
failApplePaySession(session);
|
|
1631
1679
|
if (!walletError) {
|
|
@@ -2157,8 +2205,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2157
2205
|
const skuId = this.#offer.sku_id;
|
|
2158
2206
|
const country = this.#country;
|
|
2159
2207
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2208
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2160
2209
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2161
2210
|
};
|
|
2211
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2162
2212
|
modal.open();
|
|
2163
2213
|
}
|
|
2164
2214
|
async #fetchSession() {
|
|
@@ -2206,6 +2256,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2206
2256
|
try {
|
|
2207
2257
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2208
2258
|
const paymentOptions = this.#paymentOptions;
|
|
2259
|
+
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2260
|
+
skuId,
|
|
2261
|
+
country,
|
|
2262
|
+
loggedIn: sessionData.loggedIn,
|
|
2263
|
+
userType: sessionData.userType,
|
|
2264
|
+
hasPaymentOptions: !!paymentOptions,
|
|
2265
|
+
recaptchaTokensPresent: {
|
|
2266
|
+
checkEligibility: !!recaptchaTokens.checkEligibility,
|
|
2267
|
+
newBasket: !!recaptchaTokens.newBasket,
|
|
2268
|
+
registerUser: !!recaptchaTokens.registerUser
|
|
2269
|
+
}
|
|
2270
|
+
});
|
|
2209
2271
|
startApplePaySession(
|
|
2210
2272
|
this.#buildSessionParams(
|
|
2211
2273
|
skuId,
|
|
@@ -2215,7 +2277,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2215
2277
|
recaptchaTokens
|
|
2216
2278
|
)
|
|
2217
2279
|
);
|
|
2218
|
-
} catch {
|
|
2280
|
+
} catch (error) {
|
|
2281
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2219
2282
|
}
|
|
2220
2283
|
}
|
|
2221
2284
|
};
|
|
@@ -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");
|
|
@@ -1357,22 +1376,28 @@ async function handleMerchantValidation(event, session, config) {
|
|
|
1357
1376
|
try {
|
|
1358
1377
|
const validationHost = new URL(event.validationURL).host;
|
|
1359
1378
|
if (!validationHost.endsWith("apple.com")) {
|
|
1379
|
+
console.error("[web-apple-pay] Merchant validation URL is not from apple.com \u2014 aborting session", { validationURL: event.validationURL });
|
|
1360
1380
|
session.abort();
|
|
1361
1381
|
redirectToFallbackCheckout(config);
|
|
1362
1382
|
return;
|
|
1363
1383
|
}
|
|
1384
|
+
console.info("[web-apple-pay] Merchant validation started", { validationHost });
|
|
1364
1385
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
1386
|
+
console.info("[web-apple-pay] verifyMerchant reCAPTCHA token acquired \u2014 calling validateAppleMerchant");
|
|
1365
1387
|
const response = await validateAppleMerchant({
|
|
1366
1388
|
validationURL: event.validationURL,
|
|
1367
1389
|
recaptchaToken
|
|
1368
1390
|
});
|
|
1369
1391
|
if (!response?.merchantSession) {
|
|
1392
|
+
console.error("[web-apple-pay] Merchant validation response missing merchantSession \u2014 aborting session", { response });
|
|
1370
1393
|
session.abort();
|
|
1371
1394
|
redirectToFallbackCheckout(config);
|
|
1372
1395
|
return;
|
|
1373
1396
|
}
|
|
1397
|
+
console.info("[web-apple-pay] Merchant validation succeeded \u2014 completing merchant validation");
|
|
1374
1398
|
session.completeMerchantValidation(response.merchantSession);
|
|
1375
|
-
} catch {
|
|
1399
|
+
} catch (error) {
|
|
1400
|
+
console.error("[web-apple-pay] Merchant validation threw an error \u2014 aborting session", error);
|
|
1376
1401
|
session.abort();
|
|
1377
1402
|
redirectToFallbackCheckout(config);
|
|
1378
1403
|
}
|
|
@@ -1393,6 +1418,7 @@ function startApplePaySession(config) {
|
|
|
1393
1418
|
};
|
|
1394
1419
|
try {
|
|
1395
1420
|
const session = new ApplePaySession(3, paymentRequest);
|
|
1421
|
+
console.info("[web-apple-pay] ApplePaySession created successfully");
|
|
1396
1422
|
session.onvalidatemerchant = async (event) => {
|
|
1397
1423
|
await handleMerchantValidation(event, session, config);
|
|
1398
1424
|
};
|
|
@@ -1405,7 +1431,9 @@ function startApplePaySession(config) {
|
|
|
1405
1431
|
};
|
|
1406
1432
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
1407
1433
|
session.begin();
|
|
1408
|
-
|
|
1434
|
+
console.info("[web-apple-pay] ApplePaySession.begin() called \u2014 payment sheet should appear");
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
console.error("[web-apple-pay] ApplePaySession creation or begin() threw \u2014 redirecting to fallback checkout", error);
|
|
1409
1437
|
redirectToFallbackCheckout(config);
|
|
1410
1438
|
}
|
|
1411
1439
|
}
|
|
@@ -1587,12 +1615,24 @@ function handleStructuredError(ctx) {
|
|
|
1587
1615
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1588
1616
|
const { skuId, returnUrl, country } = config;
|
|
1589
1617
|
let validatedEmail;
|
|
1618
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1619
|
+
skuId,
|
|
1620
|
+
country,
|
|
1621
|
+
loggedIn: sessionData.loggedIn,
|
|
1622
|
+
userType: sessionData.userType,
|
|
1623
|
+
recaptchaTokensPresent: {
|
|
1624
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1625
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1626
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1590
1629
|
try {
|
|
1591
1630
|
validatedEmail = validateContacts(
|
|
1592
1631
|
event,
|
|
1593
1632
|
sessionData,
|
|
1594
1633
|
config.supportedBillingCountries
|
|
1595
1634
|
);
|
|
1635
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1596
1636
|
const result = await performPurchase({
|
|
1597
1637
|
applePayToken: event.payment.token,
|
|
1598
1638
|
skuId: config.skuId,
|
|
@@ -1603,6 +1643,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1603
1643
|
},
|
|
1604
1644
|
recaptchaTokens: config.recaptchaTokens
|
|
1605
1645
|
});
|
|
1646
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1606
1647
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1607
1648
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1608
1649
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1617,10 +1658,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1617
1658
|
});
|
|
1618
1659
|
} catch (err) {
|
|
1619
1660
|
if (err instanceof ContactValidationError) {
|
|
1661
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1620
1662
|
failApplePaySession(session, err.errors);
|
|
1621
1663
|
return;
|
|
1622
1664
|
}
|
|
1623
1665
|
const walletError = parseWalletApiError(err);
|
|
1666
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
1667
|
+
skuId,
|
|
1668
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1669
|
+
message: walletError?.message,
|
|
1670
|
+
err
|
|
1671
|
+
});
|
|
1624
1672
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1625
1673
|
failApplePaySession(session);
|
|
1626
1674
|
if (!walletError) {
|
|
@@ -2152,8 +2200,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2152
2200
|
const skuId = this.#offer.sku_id;
|
|
2153
2201
|
const country = this.#country;
|
|
2154
2202
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2203
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2155
2204
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2156
2205
|
};
|
|
2206
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2157
2207
|
modal.open();
|
|
2158
2208
|
}
|
|
2159
2209
|
async #fetchSession() {
|
|
@@ -2201,6 +2251,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2201
2251
|
try {
|
|
2202
2252
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2203
2253
|
const paymentOptions = this.#paymentOptions;
|
|
2254
|
+
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2255
|
+
skuId,
|
|
2256
|
+
country,
|
|
2257
|
+
loggedIn: sessionData.loggedIn,
|
|
2258
|
+
userType: sessionData.userType,
|
|
2259
|
+
hasPaymentOptions: !!paymentOptions,
|
|
2260
|
+
recaptchaTokensPresent: {
|
|
2261
|
+
checkEligibility: !!recaptchaTokens.checkEligibility,
|
|
2262
|
+
newBasket: !!recaptchaTokens.newBasket,
|
|
2263
|
+
registerUser: !!recaptchaTokens.registerUser
|
|
2264
|
+
}
|
|
2265
|
+
});
|
|
2204
2266
|
startApplePaySession(
|
|
2205
2267
|
this.#buildSessionParams(
|
|
2206
2268
|
skuId,
|
|
@@ -2210,7 +2272,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2210
2272
|
recaptchaTokens
|
|
2211
2273
|
)
|
|
2212
2274
|
);
|
|
2213
|
-
} catch {
|
|
2275
|
+
} catch (error) {
|
|
2276
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2214
2277
|
}
|
|
2215
2278
|
}
|
|
2216
2279
|
};
|
|
@@ -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");
|
|
@@ -1365,22 +1384,28 @@ async function handleMerchantValidation(event, session, config) {
|
|
|
1365
1384
|
try {
|
|
1366
1385
|
const validationHost = new URL(event.validationURL).host;
|
|
1367
1386
|
if (!validationHost.endsWith("apple.com")) {
|
|
1387
|
+
console.error("[web-apple-pay] Merchant validation URL is not from apple.com \u2014 aborting session", { validationURL: event.validationURL });
|
|
1368
1388
|
session.abort();
|
|
1369
1389
|
redirectToFallbackCheckout(config);
|
|
1370
1390
|
return;
|
|
1371
1391
|
}
|
|
1392
|
+
console.info("[web-apple-pay] Merchant validation started", { validationHost });
|
|
1372
1393
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
1394
|
+
console.info("[web-apple-pay] verifyMerchant reCAPTCHA token acquired \u2014 calling validateAppleMerchant");
|
|
1373
1395
|
const response = await validateAppleMerchant({
|
|
1374
1396
|
validationURL: event.validationURL,
|
|
1375
1397
|
recaptchaToken
|
|
1376
1398
|
});
|
|
1377
1399
|
if (!response?.merchantSession) {
|
|
1400
|
+
console.error("[web-apple-pay] Merchant validation response missing merchantSession \u2014 aborting session", { response });
|
|
1378
1401
|
session.abort();
|
|
1379
1402
|
redirectToFallbackCheckout(config);
|
|
1380
1403
|
return;
|
|
1381
1404
|
}
|
|
1405
|
+
console.info("[web-apple-pay] Merchant validation succeeded \u2014 completing merchant validation");
|
|
1382
1406
|
session.completeMerchantValidation(response.merchantSession);
|
|
1383
|
-
} catch {
|
|
1407
|
+
} catch (error) {
|
|
1408
|
+
console.error("[web-apple-pay] Merchant validation threw an error \u2014 aborting session", error);
|
|
1384
1409
|
session.abort();
|
|
1385
1410
|
redirectToFallbackCheckout(config);
|
|
1386
1411
|
}
|
|
@@ -1401,6 +1426,7 @@ function startApplePaySession(config) {
|
|
|
1401
1426
|
};
|
|
1402
1427
|
try {
|
|
1403
1428
|
const session = new ApplePaySession(3, paymentRequest);
|
|
1429
|
+
console.info("[web-apple-pay] ApplePaySession created successfully");
|
|
1404
1430
|
session.onvalidatemerchant = async (event) => {
|
|
1405
1431
|
await handleMerchantValidation(event, session, config);
|
|
1406
1432
|
};
|
|
@@ -1413,7 +1439,9 @@ function startApplePaySession(config) {
|
|
|
1413
1439
|
};
|
|
1414
1440
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
1415
1441
|
session.begin();
|
|
1416
|
-
|
|
1442
|
+
console.info("[web-apple-pay] ApplePaySession.begin() called \u2014 payment sheet should appear");
|
|
1443
|
+
} catch (error) {
|
|
1444
|
+
console.error("[web-apple-pay] ApplePaySession creation or begin() threw \u2014 redirecting to fallback checkout", error);
|
|
1417
1445
|
redirectToFallbackCheckout(config);
|
|
1418
1446
|
}
|
|
1419
1447
|
}
|
|
@@ -1595,12 +1623,24 @@ function handleStructuredError(ctx) {
|
|
|
1595
1623
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1596
1624
|
const { skuId, returnUrl, country } = config;
|
|
1597
1625
|
let validatedEmail;
|
|
1626
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1627
|
+
skuId,
|
|
1628
|
+
country,
|
|
1629
|
+
loggedIn: sessionData.loggedIn,
|
|
1630
|
+
userType: sessionData.userType,
|
|
1631
|
+
recaptchaTokensPresent: {
|
|
1632
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1633
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1634
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1598
1637
|
try {
|
|
1599
1638
|
validatedEmail = validateContacts(
|
|
1600
1639
|
event,
|
|
1601
1640
|
sessionData,
|
|
1602
1641
|
config.supportedBillingCountries
|
|
1603
1642
|
);
|
|
1643
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1604
1644
|
const result = await performPurchase({
|
|
1605
1645
|
applePayToken: event.payment.token,
|
|
1606
1646
|
skuId: config.skuId,
|
|
@@ -1611,6 +1651,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1611
1651
|
},
|
|
1612
1652
|
recaptchaTokens: config.recaptchaTokens
|
|
1613
1653
|
});
|
|
1654
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1614
1655
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1615
1656
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1616
1657
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1625,10 +1666,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1625
1666
|
});
|
|
1626
1667
|
} catch (err) {
|
|
1627
1668
|
if (err instanceof ContactValidationError) {
|
|
1669
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1628
1670
|
failApplePaySession(session, err.errors);
|
|
1629
1671
|
return;
|
|
1630
1672
|
}
|
|
1631
1673
|
const walletError = parseWalletApiError(err);
|
|
1674
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
1675
|
+
skuId,
|
|
1676
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1677
|
+
message: walletError?.message,
|
|
1678
|
+
err
|
|
1679
|
+
});
|
|
1632
1680
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1633
1681
|
failApplePaySession(session);
|
|
1634
1682
|
if (!walletError) {
|
|
@@ -2160,8 +2208,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2160
2208
|
const skuId = this.#offer.sku_id;
|
|
2161
2209
|
const country = this.#country;
|
|
2162
2210
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2211
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2163
2212
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2164
2213
|
};
|
|
2214
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2165
2215
|
modal.open();
|
|
2166
2216
|
}
|
|
2167
2217
|
async #fetchSession() {
|
|
@@ -2209,6 +2259,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2209
2259
|
try {
|
|
2210
2260
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2211
2261
|
const paymentOptions = this.#paymentOptions;
|
|
2262
|
+
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2263
|
+
skuId,
|
|
2264
|
+
country,
|
|
2265
|
+
loggedIn: sessionData.loggedIn,
|
|
2266
|
+
userType: sessionData.userType,
|
|
2267
|
+
hasPaymentOptions: !!paymentOptions,
|
|
2268
|
+
recaptchaTokensPresent: {
|
|
2269
|
+
checkEligibility: !!recaptchaTokens.checkEligibility,
|
|
2270
|
+
newBasket: !!recaptchaTokens.newBasket,
|
|
2271
|
+
registerUser: !!recaptchaTokens.registerUser
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2212
2274
|
startApplePaySession(
|
|
2213
2275
|
this.#buildSessionParams(
|
|
2214
2276
|
skuId,
|
|
@@ -2218,7 +2280,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2218
2280
|
recaptchaTokens
|
|
2219
2281
|
)
|
|
2220
2282
|
);
|
|
2221
|
-
} catch {
|
|
2283
|
+
} catch (error) {
|
|
2284
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2222
2285
|
}
|
|
2223
2286
|
}
|
|
2224
2287
|
};
|
|
@@ -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");
|
|
@@ -1357,22 +1376,28 @@ async function handleMerchantValidation(event, session, config) {
|
|
|
1357
1376
|
try {
|
|
1358
1377
|
const validationHost = new URL(event.validationURL).host;
|
|
1359
1378
|
if (!validationHost.endsWith("apple.com")) {
|
|
1379
|
+
console.error("[web-apple-pay] Merchant validation URL is not from apple.com \u2014 aborting session", { validationURL: event.validationURL });
|
|
1360
1380
|
session.abort();
|
|
1361
1381
|
redirectToFallbackCheckout(config);
|
|
1362
1382
|
return;
|
|
1363
1383
|
}
|
|
1384
|
+
console.info("[web-apple-pay] Merchant validation started", { validationHost });
|
|
1364
1385
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
1386
|
+
console.info("[web-apple-pay] verifyMerchant reCAPTCHA token acquired \u2014 calling validateAppleMerchant");
|
|
1365
1387
|
const response = await validateAppleMerchant({
|
|
1366
1388
|
validationURL: event.validationURL,
|
|
1367
1389
|
recaptchaToken
|
|
1368
1390
|
});
|
|
1369
1391
|
if (!response?.merchantSession) {
|
|
1392
|
+
console.error("[web-apple-pay] Merchant validation response missing merchantSession \u2014 aborting session", { response });
|
|
1370
1393
|
session.abort();
|
|
1371
1394
|
redirectToFallbackCheckout(config);
|
|
1372
1395
|
return;
|
|
1373
1396
|
}
|
|
1397
|
+
console.info("[web-apple-pay] Merchant validation succeeded \u2014 completing merchant validation");
|
|
1374
1398
|
session.completeMerchantValidation(response.merchantSession);
|
|
1375
|
-
} catch {
|
|
1399
|
+
} catch (error) {
|
|
1400
|
+
console.error("[web-apple-pay] Merchant validation threw an error \u2014 aborting session", error);
|
|
1376
1401
|
session.abort();
|
|
1377
1402
|
redirectToFallbackCheckout(config);
|
|
1378
1403
|
}
|
|
@@ -1393,6 +1418,7 @@ function startApplePaySession(config) {
|
|
|
1393
1418
|
};
|
|
1394
1419
|
try {
|
|
1395
1420
|
const session = new ApplePaySession(3, paymentRequest);
|
|
1421
|
+
console.info("[web-apple-pay] ApplePaySession created successfully");
|
|
1396
1422
|
session.onvalidatemerchant = async (event) => {
|
|
1397
1423
|
await handleMerchantValidation(event, session, config);
|
|
1398
1424
|
};
|
|
@@ -1405,7 +1431,9 @@ function startApplePaySession(config) {
|
|
|
1405
1431
|
};
|
|
1406
1432
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
1407
1433
|
session.begin();
|
|
1408
|
-
|
|
1434
|
+
console.info("[web-apple-pay] ApplePaySession.begin() called \u2014 payment sheet should appear");
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
console.error("[web-apple-pay] ApplePaySession creation or begin() threw \u2014 redirecting to fallback checkout", error);
|
|
1409
1437
|
redirectToFallbackCheckout(config);
|
|
1410
1438
|
}
|
|
1411
1439
|
}
|
|
@@ -1587,12 +1615,24 @@ function handleStructuredError(ctx) {
|
|
|
1587
1615
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1588
1616
|
const { skuId, returnUrl, country } = config;
|
|
1589
1617
|
let validatedEmail;
|
|
1618
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1619
|
+
skuId,
|
|
1620
|
+
country,
|
|
1621
|
+
loggedIn: sessionData.loggedIn,
|
|
1622
|
+
userType: sessionData.userType,
|
|
1623
|
+
recaptchaTokensPresent: {
|
|
1624
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1625
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1626
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1590
1629
|
try {
|
|
1591
1630
|
validatedEmail = validateContacts(
|
|
1592
1631
|
event,
|
|
1593
1632
|
sessionData,
|
|
1594
1633
|
config.supportedBillingCountries
|
|
1595
1634
|
);
|
|
1635
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1596
1636
|
const result = await performPurchase({
|
|
1597
1637
|
applePayToken: event.payment.token,
|
|
1598
1638
|
skuId: config.skuId,
|
|
@@ -1603,6 +1643,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1603
1643
|
},
|
|
1604
1644
|
recaptchaTokens: config.recaptchaTokens
|
|
1605
1645
|
});
|
|
1646
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1606
1647
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1607
1648
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1608
1649
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1617,10 +1658,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1617
1658
|
});
|
|
1618
1659
|
} catch (err) {
|
|
1619
1660
|
if (err instanceof ContactValidationError) {
|
|
1661
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1620
1662
|
failApplePaySession(session, err.errors);
|
|
1621
1663
|
return;
|
|
1622
1664
|
}
|
|
1623
1665
|
const walletError = parseWalletApiError(err);
|
|
1666
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
1667
|
+
skuId,
|
|
1668
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1669
|
+
message: walletError?.message,
|
|
1670
|
+
err
|
|
1671
|
+
});
|
|
1624
1672
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1625
1673
|
failApplePaySession(session);
|
|
1626
1674
|
if (!walletError) {
|
|
@@ -2152,8 +2200,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2152
2200
|
const skuId = this.#offer.sku_id;
|
|
2153
2201
|
const country = this.#country;
|
|
2154
2202
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2203
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2155
2204
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2156
2205
|
};
|
|
2206
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2157
2207
|
modal.open();
|
|
2158
2208
|
}
|
|
2159
2209
|
async #fetchSession() {
|
|
@@ -2201,6 +2251,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2201
2251
|
try {
|
|
2202
2252
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2203
2253
|
const paymentOptions = this.#paymentOptions;
|
|
2254
|
+
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2255
|
+
skuId,
|
|
2256
|
+
country,
|
|
2257
|
+
loggedIn: sessionData.loggedIn,
|
|
2258
|
+
userType: sessionData.userType,
|
|
2259
|
+
hasPaymentOptions: !!paymentOptions,
|
|
2260
|
+
recaptchaTokensPresent: {
|
|
2261
|
+
checkEligibility: !!recaptchaTokens.checkEligibility,
|
|
2262
|
+
newBasket: !!recaptchaTokens.newBasket,
|
|
2263
|
+
registerUser: !!recaptchaTokens.registerUser
|
|
2264
|
+
}
|
|
2265
|
+
});
|
|
2204
2266
|
startApplePaySession(
|
|
2205
2267
|
this.#buildSessionParams(
|
|
2206
2268
|
skuId,
|
|
@@ -2210,7 +2272,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2210
2272
|
recaptchaTokens
|
|
2211
2273
|
)
|
|
2212
2274
|
);
|
|
2213
|
-
} catch {
|
|
2275
|
+
} catch (error) {
|
|
2276
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2214
2277
|
}
|
|
2215
2278
|
}
|
|
2216
2279
|
};
|
|
@@ -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
|
};
|
|
@@ -196,22 +201,28 @@ async function handleMerchantValidation(event, session, config) {
|
|
|
196
201
|
try {
|
|
197
202
|
const validationHost = new URL(event.validationURL).host;
|
|
198
203
|
if (!validationHost.endsWith("apple.com")) {
|
|
204
|
+
console.error("[web-apple-pay] Merchant validation URL is not from apple.com \u2014 aborting session", { validationURL: event.validationURL });
|
|
199
205
|
session.abort();
|
|
200
206
|
redirectToFallbackCheckout(config);
|
|
201
207
|
return;
|
|
202
208
|
}
|
|
209
|
+
console.info("[web-apple-pay] Merchant validation started", { validationHost });
|
|
203
210
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
211
|
+
console.info("[web-apple-pay] verifyMerchant reCAPTCHA token acquired \u2014 calling validateAppleMerchant");
|
|
204
212
|
const response = await validateAppleMerchant({
|
|
205
213
|
validationURL: event.validationURL,
|
|
206
214
|
recaptchaToken
|
|
207
215
|
});
|
|
208
216
|
if (!response?.merchantSession) {
|
|
217
|
+
console.error("[web-apple-pay] Merchant validation response missing merchantSession \u2014 aborting session", { response });
|
|
209
218
|
session.abort();
|
|
210
219
|
redirectToFallbackCheckout(config);
|
|
211
220
|
return;
|
|
212
221
|
}
|
|
222
|
+
console.info("[web-apple-pay] Merchant validation succeeded \u2014 completing merchant validation");
|
|
213
223
|
session.completeMerchantValidation(response.merchantSession);
|
|
214
|
-
} catch {
|
|
224
|
+
} catch (error) {
|
|
225
|
+
console.error("[web-apple-pay] Merchant validation threw an error \u2014 aborting session", error);
|
|
215
226
|
session.abort();
|
|
216
227
|
redirectToFallbackCheckout(config);
|
|
217
228
|
}
|
|
@@ -232,6 +243,7 @@ function startApplePaySession(config) {
|
|
|
232
243
|
};
|
|
233
244
|
try {
|
|
234
245
|
const session = new ApplePaySession(3, paymentRequest);
|
|
246
|
+
console.info("[web-apple-pay] ApplePaySession created successfully");
|
|
235
247
|
session.onvalidatemerchant = async (event) => {
|
|
236
248
|
await handleMerchantValidation(event, session, config);
|
|
237
249
|
};
|
|
@@ -244,7 +256,9 @@ function startApplePaySession(config) {
|
|
|
244
256
|
};
|
|
245
257
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
246
258
|
session.begin();
|
|
247
|
-
|
|
259
|
+
console.info("[web-apple-pay] ApplePaySession.begin() called \u2014 payment sheet should appear");
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error("[web-apple-pay] ApplePaySession creation or begin() threw \u2014 redirecting to fallback checkout", error);
|
|
248
262
|
redirectToFallbackCheckout(config);
|
|
249
263
|
}
|
|
250
264
|
}
|
|
@@ -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");
|
|
@@ -1362,22 +1381,28 @@ async function handleMerchantValidation(event, session, config) {
|
|
|
1362
1381
|
try {
|
|
1363
1382
|
const validationHost = new URL(event.validationURL).host;
|
|
1364
1383
|
if (!validationHost.endsWith("apple.com")) {
|
|
1384
|
+
console.error("[web-apple-pay] Merchant validation URL is not from apple.com \u2014 aborting session", { validationURL: event.validationURL });
|
|
1365
1385
|
session.abort();
|
|
1366
1386
|
redirectToFallbackCheckout(config);
|
|
1367
1387
|
return;
|
|
1368
1388
|
}
|
|
1389
|
+
console.info("[web-apple-pay] Merchant validation started", { validationHost });
|
|
1369
1390
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
1391
|
+
console.info("[web-apple-pay] verifyMerchant reCAPTCHA token acquired \u2014 calling validateAppleMerchant");
|
|
1370
1392
|
const response = await validateAppleMerchant({
|
|
1371
1393
|
validationURL: event.validationURL,
|
|
1372
1394
|
recaptchaToken
|
|
1373
1395
|
});
|
|
1374
1396
|
if (!response?.merchantSession) {
|
|
1397
|
+
console.error("[web-apple-pay] Merchant validation response missing merchantSession \u2014 aborting session", { response });
|
|
1375
1398
|
session.abort();
|
|
1376
1399
|
redirectToFallbackCheckout(config);
|
|
1377
1400
|
return;
|
|
1378
1401
|
}
|
|
1402
|
+
console.info("[web-apple-pay] Merchant validation succeeded \u2014 completing merchant validation");
|
|
1379
1403
|
session.completeMerchantValidation(response.merchantSession);
|
|
1380
|
-
} catch {
|
|
1404
|
+
} catch (error) {
|
|
1405
|
+
console.error("[web-apple-pay] Merchant validation threw an error \u2014 aborting session", error);
|
|
1381
1406
|
session.abort();
|
|
1382
1407
|
redirectToFallbackCheckout(config);
|
|
1383
1408
|
}
|
|
@@ -1398,6 +1423,7 @@ function startApplePaySession(config) {
|
|
|
1398
1423
|
};
|
|
1399
1424
|
try {
|
|
1400
1425
|
const session = new ApplePaySession(3, paymentRequest);
|
|
1426
|
+
console.info("[web-apple-pay] ApplePaySession created successfully");
|
|
1401
1427
|
session.onvalidatemerchant = async (event) => {
|
|
1402
1428
|
await handleMerchantValidation(event, session, config);
|
|
1403
1429
|
};
|
|
@@ -1410,7 +1436,9 @@ function startApplePaySession(config) {
|
|
|
1410
1436
|
};
|
|
1411
1437
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
1412
1438
|
session.begin();
|
|
1413
|
-
|
|
1439
|
+
console.info("[web-apple-pay] ApplePaySession.begin() called \u2014 payment sheet should appear");
|
|
1440
|
+
} catch (error) {
|
|
1441
|
+
console.error("[web-apple-pay] ApplePaySession creation or begin() threw \u2014 redirecting to fallback checkout", error);
|
|
1414
1442
|
redirectToFallbackCheckout(config);
|
|
1415
1443
|
}
|
|
1416
1444
|
}
|
|
@@ -1592,12 +1620,24 @@ function handleStructuredError(ctx) {
|
|
|
1592
1620
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1593
1621
|
const { skuId, returnUrl, country } = config;
|
|
1594
1622
|
let validatedEmail;
|
|
1623
|
+
console.info("[web-apple-pay] Payment authorised \u2014 starting purchase flow", {
|
|
1624
|
+
skuId,
|
|
1625
|
+
country,
|
|
1626
|
+
loggedIn: sessionData.loggedIn,
|
|
1627
|
+
userType: sessionData.userType,
|
|
1628
|
+
recaptchaTokensPresent: {
|
|
1629
|
+
checkEligibility: !!config.recaptchaTokens.checkEligibility,
|
|
1630
|
+
newBasket: !!config.recaptchaTokens.newBasket,
|
|
1631
|
+
registerUser: !!config.recaptchaTokens.registerUser
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1595
1634
|
try {
|
|
1596
1635
|
validatedEmail = validateContacts(
|
|
1597
1636
|
event,
|
|
1598
1637
|
sessionData,
|
|
1599
1638
|
config.supportedBillingCountries
|
|
1600
1639
|
);
|
|
1640
|
+
console.info("[web-apple-pay] Contact validation passed \u2014 calling purchase API");
|
|
1601
1641
|
const result = await performPurchase({
|
|
1602
1642
|
applePayToken: event.payment.token,
|
|
1603
1643
|
skuId: config.skuId,
|
|
@@ -1608,6 +1648,7 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1608
1648
|
},
|
|
1609
1649
|
recaptchaTokens: config.recaptchaTokens
|
|
1610
1650
|
});
|
|
1651
|
+
console.info("[web-apple-pay] Purchase API succeeded", { skuId, basketId: result.basketId });
|
|
1611
1652
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1612
1653
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1613
1654
|
const successEmail = sessionData.loggedIn ? sessionData.emailAddress : validatedEmail;
|
|
@@ -1622,10 +1663,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1622
1663
|
});
|
|
1623
1664
|
} catch (err) {
|
|
1624
1665
|
if (err instanceof ContactValidationError) {
|
|
1666
|
+
console.warn("[web-apple-pay] Contact validation failed \u2014 keeping Apple Pay sheet open", err);
|
|
1625
1667
|
failApplePaySession(session, err.errors);
|
|
1626
1668
|
return;
|
|
1627
1669
|
}
|
|
1628
1670
|
const walletError = parseWalletApiError(err);
|
|
1671
|
+
console.error("[web-apple-pay] Purchase API failed", {
|
|
1672
|
+
skuId,
|
|
1673
|
+
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1674
|
+
message: walletError?.message,
|
|
1675
|
+
err
|
|
1676
|
+
});
|
|
1629
1677
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1630
1678
|
failApplePaySession(session);
|
|
1631
1679
|
if (!walletError) {
|
|
@@ -2157,8 +2205,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2157
2205
|
const skuId = this.#offer.sku_id;
|
|
2158
2206
|
const country = this.#country;
|
|
2159
2207
|
modal.onConfirm = (recaptchaTokens) => {
|
|
2208
|
+
console.info("[web-apple-pay] onConfirm received from modal \u2014 initiating Apple Pay session", { skuId, country });
|
|
2160
2209
|
this.#initiateApplePay(skuId, country, recaptchaTokens);
|
|
2161
2210
|
};
|
|
2211
|
+
console.info("[web-apple-pay] Opening Apple Pay modal", { skuId, country });
|
|
2162
2212
|
modal.open();
|
|
2163
2213
|
}
|
|
2164
2214
|
async #fetchSession() {
|
|
@@ -2206,6 +2256,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2206
2256
|
try {
|
|
2207
2257
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
2208
2258
|
const paymentOptions = this.#paymentOptions;
|
|
2259
|
+
console.info("[web-apple-pay] Starting Apple Pay session", {
|
|
2260
|
+
skuId,
|
|
2261
|
+
country,
|
|
2262
|
+
loggedIn: sessionData.loggedIn,
|
|
2263
|
+
userType: sessionData.userType,
|
|
2264
|
+
hasPaymentOptions: !!paymentOptions,
|
|
2265
|
+
recaptchaTokensPresent: {
|
|
2266
|
+
checkEligibility: !!recaptchaTokens.checkEligibility,
|
|
2267
|
+
newBasket: !!recaptchaTokens.newBasket,
|
|
2268
|
+
registerUser: !!recaptchaTokens.registerUser
|
|
2269
|
+
}
|
|
2270
|
+
});
|
|
2209
2271
|
startApplePaySession(
|
|
2210
2272
|
this.#buildSessionParams(
|
|
2211
2273
|
skuId,
|
|
@@ -2215,7 +2277,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
2215
2277
|
recaptchaTokens
|
|
2216
2278
|
)
|
|
2217
2279
|
);
|
|
2218
|
-
} catch {
|
|
2280
|
+
} catch (error) {
|
|
2281
|
+
console.error("[web-apple-pay] Failed to start Apple Pay session", error);
|
|
2219
2282
|
}
|
|
2220
2283
|
}
|
|
2221
2284
|
};
|
|
@@ -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