@economist/web-apple-pay 1.6.2-beta.1 → 1.7.0
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 +110 -55
- package/dist/src/apple-pay-button.js +110 -49
- package/dist/src/apple-pay-button.stories.js +110 -56
- package/dist/src/apple-pay-modal.stories.js +110 -49
- package/dist/src/clients/payment-checkout/apple-session.js +1 -17
- package/dist/src/clients/payment-checkout/config.js +0 -7
- package/dist/src/clients/payment-checkout/detects.js +1 -10
- package/dist/src/clients/payment-checkout/payment-handler.js +0 -14
- package/dist/src/index.js +110 -55
- package/dist/src/stories/utils/story-env.js +0 -7
- package/package.json +1 -1
|
@@ -439,24 +439,101 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
439
439
|
};
|
|
440
440
|
|
|
441
441
|
// src/templates/apple-pay-button.template.html
|
|
442
|
-
var apple_pay_button_template_default =
|
|
442
|
+
var apple_pay_button_template_default = `<style>
|
|
443
|
+
/* Use CSS variables for layout so parent walls can easily override placement and sizing */
|
|
444
|
+
teg-apple-pay-button {
|
|
445
|
+
display: block;
|
|
446
|
+
margin: var(--apple-pay-margin, 0);
|
|
447
|
+
width: 100%;
|
|
448
|
+
height: 100%;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
teg-apple-pay-button .apple-pay-container {
|
|
452
|
+
display: flex;
|
|
453
|
+
flex-direction: column;
|
|
454
|
+
align-items: stretch;
|
|
455
|
+
width: 100%;
|
|
456
|
+
height: 100%;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
teg-apple-pay-button .apple-pay-btn {
|
|
460
|
+
display: flex;
|
|
461
|
+
align-items: center;
|
|
462
|
+
justify-content: center;
|
|
463
|
+
gap: 0.375rem;
|
|
464
|
+
width: 100%;
|
|
465
|
+
min-width: 0;
|
|
466
|
+
height: 100%;
|
|
467
|
+
min-height: var(--apple-pay-height, 2.75rem);
|
|
468
|
+
background-color: #000;
|
|
469
|
+
color: #fff;
|
|
470
|
+
border: none;
|
|
471
|
+
border-radius: var(--apple-pay-border-radius, 1.375rem);
|
|
472
|
+
cursor: pointer;
|
|
473
|
+
font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));
|
|
474
|
+
font-size: 1.0625rem;
|
|
475
|
+
font-weight: 500;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
teg-apple-pay-button .apple-pay-btn svg {
|
|
479
|
+
flex-shrink: 0;
|
|
480
|
+
display: block;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
teg-apple-pay-button .apple-pay-btn.wap-loading {
|
|
484
|
+
position: relative;
|
|
485
|
+
cursor: default;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
teg-apple-pay-button .apple-pay-btn.wap-loading svg {
|
|
489
|
+
visibility: hidden;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
teg-apple-pay-button .apple-pay-btn.wap-loading::after {
|
|
493
|
+
content: '';
|
|
494
|
+
position: absolute;
|
|
495
|
+
left: 50%;
|
|
496
|
+
top: 50%;
|
|
497
|
+
margin-left: -0.625rem;
|
|
498
|
+
margin-top: -0.625rem;
|
|
499
|
+
width: 1.25rem;
|
|
500
|
+
height: 1.25rem;
|
|
501
|
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
502
|
+
border-top-color: #fff;
|
|
503
|
+
border-radius: 50%;
|
|
504
|
+
animation: wap-button-spinner 0.8s linear infinite;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
@keyframes wap-button-spinner {
|
|
508
|
+
to {
|
|
509
|
+
transform: rotate(360deg);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
</style>
|
|
513
|
+
|
|
514
|
+
<div class="apple-pay-container" aria-label="Express checkout with Apple Pay">
|
|
515
|
+
<button
|
|
516
|
+
class="apple-pay-btn"
|
|
517
|
+
type="button"
|
|
518
|
+
aria-label="Pay with Apple Pay"
|
|
519
|
+
data-analytics="paywall:apple-pay-checkout"
|
|
520
|
+
>
|
|
521
|
+
<svg width="51" height="22" aria-label="Apple Pay" role="img">
|
|
522
|
+
<use href="#apple-pay-logo" />
|
|
523
|
+
</svg>
|
|
524
|
+
</button>
|
|
525
|
+
</div>
|
|
526
|
+
`;
|
|
443
527
|
|
|
444
528
|
// src/clients/payment-checkout/config.ts
|
|
445
529
|
var runtimeConfig = null;
|
|
446
530
|
var configureApplePay = (config) => {
|
|
447
|
-
console.log("[web-apple-pay] configureApplePay called", {
|
|
448
|
-
merchantId: config.merchantId,
|
|
449
|
-
walletApiBaseUrl: config.walletApiBaseUrl,
|
|
450
|
-
debugBypassEnabled: config.debugBypassEnabled,
|
|
451
|
-
recaptchaSiteKey: config.recaptchaSiteKey ? "***" : void 0
|
|
452
|
-
});
|
|
453
531
|
runtimeConfig = {
|
|
454
532
|
...config
|
|
455
533
|
};
|
|
456
534
|
};
|
|
457
535
|
var getApplePayConfig = () => runtimeConfig;
|
|
458
536
|
var resetApplePayConfig = () => {
|
|
459
|
-
console.log("[web-apple-pay] resetApplePayConfig called");
|
|
460
537
|
runtimeConfig = null;
|
|
461
538
|
};
|
|
462
539
|
|
|
@@ -468,27 +545,21 @@ var isAppleDeviceOrSafari = () => {
|
|
|
468
545
|
};
|
|
469
546
|
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
|
|
470
547
|
var isApplePayAvailable = async () => {
|
|
471
|
-
console.log("[web-apple-pay] isApplePayAvailable: checking availability");
|
|
472
548
|
if (bypassApplePayChecks()) {
|
|
473
|
-
console.log("[web-apple-pay] isApplePayAvailable: bypass active \u2192 true");
|
|
474
549
|
return true;
|
|
475
550
|
}
|
|
476
551
|
if (!isAppleDeviceOrSafari()) {
|
|
477
|
-
console.log("[web-apple-pay] isApplePayAvailable: not Apple device or Safari \u2192 false");
|
|
478
552
|
return false;
|
|
479
553
|
}
|
|
480
554
|
const applePaySession = window.ApplePaySession;
|
|
481
555
|
if (!applePaySession) {
|
|
482
|
-
console.log("[web-apple-pay] isApplePayAvailable: ApplePaySession not found on window \u2192 false");
|
|
483
556
|
return false;
|
|
484
557
|
}
|
|
485
558
|
try {
|
|
486
559
|
if (typeof applePaySession.canMakePayments !== "function") {
|
|
487
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function \u2192 false");
|
|
488
560
|
return false;
|
|
489
561
|
}
|
|
490
562
|
if (!applePaySession.canMakePayments()) {
|
|
491
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false \u2192 false");
|
|
492
563
|
return false;
|
|
493
564
|
}
|
|
494
565
|
const merchantId = getApplePayConfig()?.merchantId;
|
|
@@ -498,10 +569,7 @@ var isApplePayAvailable = async () => {
|
|
|
498
569
|
);
|
|
499
570
|
return false;
|
|
500
571
|
}
|
|
501
|
-
|
|
502
|
-
const result = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
503
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard \u2192", result);
|
|
504
|
-
return result;
|
|
572
|
+
return await applePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
505
573
|
} catch (error) {
|
|
506
574
|
console.error("Error checking Apple Pay active card status:", error);
|
|
507
575
|
return false;
|
|
@@ -763,13 +831,6 @@ var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
|
|
|
763
831
|
|
|
764
832
|
// src/clients/payment-checkout/apple-session.ts
|
|
765
833
|
function startApplePaySession(config) {
|
|
766
|
-
console.log("[web-apple-pay] startApplePaySession", {
|
|
767
|
-
skuId: config.skuId,
|
|
768
|
-
countryCode: config.countryCode,
|
|
769
|
-
currencyCode: config.currencyCode,
|
|
770
|
-
totalAmount: config.totalAmount,
|
|
771
|
-
userLoggedIn: config.userLoggedIn
|
|
772
|
-
});
|
|
773
834
|
const paymentRequest = {
|
|
774
835
|
countryCode: config.countryCode,
|
|
775
836
|
currencyCode: config.currencyCode,
|
|
@@ -785,11 +846,9 @@ function startApplePaySession(config) {
|
|
|
785
846
|
};
|
|
786
847
|
const session = new ApplePaySession(3, paymentRequest);
|
|
787
848
|
session.onvalidatemerchant = async (event) => {
|
|
788
|
-
console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
|
|
789
849
|
try {
|
|
790
850
|
const validationHost = new URL(event.validationURL).host;
|
|
791
851
|
if (!validationHost.endsWith("apple.com")) {
|
|
792
|
-
console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
|
|
793
852
|
trackFallbackCheckout(config.skuId);
|
|
794
853
|
session.abort();
|
|
795
854
|
window.location.href = redirectToCheckoutFallback({
|
|
@@ -800,13 +859,11 @@ function startApplePaySession(config) {
|
|
|
800
859
|
return;
|
|
801
860
|
}
|
|
802
861
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
803
|
-
console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
|
|
804
862
|
const response = await validateAppleMerchant({
|
|
805
863
|
validationURL: event.validationURL,
|
|
806
864
|
recaptchaToken
|
|
807
865
|
});
|
|
808
866
|
if (!response?.merchantSession) {
|
|
809
|
-
console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
|
|
810
867
|
trackFallbackCheckout(config.skuId);
|
|
811
868
|
session.abort();
|
|
812
869
|
window.location.href = redirectToCheckoutFallback({
|
|
@@ -816,10 +873,8 @@ function startApplePaySession(config) {
|
|
|
816
873
|
});
|
|
817
874
|
return;
|
|
818
875
|
}
|
|
819
|
-
console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
|
|
820
876
|
session.completeMerchantValidation(response.merchantSession);
|
|
821
|
-
} catch
|
|
822
|
-
console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
|
|
877
|
+
} catch {
|
|
823
878
|
trackFallbackCheckout(config.skuId);
|
|
824
879
|
session.abort();
|
|
825
880
|
window.location.href = redirectToCheckoutFallback({
|
|
@@ -830,15 +885,12 @@ function startApplePaySession(config) {
|
|
|
830
885
|
}
|
|
831
886
|
};
|
|
832
887
|
session.onpaymentauthorized = (event) => {
|
|
833
|
-
console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
|
|
834
888
|
trackWalletPaymentAuthorised(config.skuId);
|
|
835
889
|
config.onPaymentAuthorized(event, session);
|
|
836
890
|
};
|
|
837
891
|
session.oncancel = () => {
|
|
838
|
-
console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
|
|
839
892
|
config.onCancel?.();
|
|
840
893
|
};
|
|
841
|
-
console.log("[web-apple-pay] startApplePaySession: beginning session");
|
|
842
894
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
843
895
|
session.begin();
|
|
844
896
|
}
|
|
@@ -1017,16 +1069,12 @@ function handleStructuredError(ctx) {
|
|
|
1017
1069
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1018
1070
|
const { skuId, returnUrl, queryString } = config;
|
|
1019
1071
|
let validatedEmail;
|
|
1020
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: starting", { skuId, userLoggedIn: sessionData.loggedIn });
|
|
1021
1072
|
try {
|
|
1022
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 1 \u2014 validating contacts");
|
|
1023
1073
|
validatedEmail = validateContacts(
|
|
1024
1074
|
event,
|
|
1025
1075
|
sessionData,
|
|
1026
1076
|
config.supportedBillingCountries
|
|
1027
1077
|
);
|
|
1028
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: contacts valid", { validatedEmail });
|
|
1029
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 2 \u2014 calling performPurchase", { skuId });
|
|
1030
1078
|
const result = await performPurchase({
|
|
1031
1079
|
applePayToken: event.payment.token,
|
|
1032
1080
|
skuId: config.skuId,
|
|
@@ -1036,8 +1084,6 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1036
1084
|
},
|
|
1037
1085
|
recaptchaTokens: config.recaptchaTokens
|
|
1038
1086
|
});
|
|
1039
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: performPurchase succeeded", { basketId: result.basketId });
|
|
1040
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 3 \u2014 completing payment and redirecting");
|
|
1041
1087
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1042
1088
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1043
1089
|
const userStatus = sessionData.loggedIn ? "lex" : "nex";
|
|
@@ -1050,25 +1096,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1050
1096
|
});
|
|
1051
1097
|
} catch (err) {
|
|
1052
1098
|
if (err instanceof ContactValidationError) {
|
|
1053
|
-
console.warn("[web-apple-pay] handlePaymentAuthorized: contact validation failed", err.errors);
|
|
1054
1099
|
failApplePaySession(session, err.errors);
|
|
1055
1100
|
return;
|
|
1056
1101
|
}
|
|
1057
1102
|
console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
|
|
1058
1103
|
const walletError = parseWalletApiError(err);
|
|
1059
|
-
console.error("[web-apple-pay] handlePaymentAuthorized: BFF/network error", {
|
|
1060
|
-
skuId,
|
|
1061
|
-
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1062
|
-
message: walletError?.message
|
|
1063
|
-
});
|
|
1064
1104
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1065
1105
|
failApplePaySession(session);
|
|
1066
1106
|
if (!walletError) {
|
|
1067
|
-
console.warn("[web-apple-pay] handlePaymentAuthorized: unstructured error, using generic fallback");
|
|
1068
1107
|
redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
|
|
1069
1108
|
return;
|
|
1070
1109
|
}
|
|
1071
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: dispatching structured error handler", { errorType: walletError.errorType });
|
|
1072
1110
|
handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
|
|
1073
1111
|
}
|
|
1074
1112
|
}
|
|
@@ -1456,14 +1494,16 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1456
1494
|
async #run() {
|
|
1457
1495
|
try {
|
|
1458
1496
|
if (await this.#checkAvailability()) return;
|
|
1459
|
-
if (await this.#checkSession()) return;
|
|
1460
1497
|
if (this.#checkOffer()) return;
|
|
1498
|
+
this.#renderTemplate();
|
|
1499
|
+
if (await this.#checkSession()) return;
|
|
1461
1500
|
this.#captureReturnUrl();
|
|
1462
1501
|
this.#computeBillingCountries();
|
|
1463
1502
|
if (await this.#fetchAndCacheOptions()) return;
|
|
1464
|
-
this.#
|
|
1503
|
+
this.#finishLoading();
|
|
1465
1504
|
} catch {
|
|
1466
1505
|
this.style.display = "none";
|
|
1506
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1467
1507
|
} finally {
|
|
1468
1508
|
this.#rendering = false;
|
|
1469
1509
|
}
|
|
@@ -1485,6 +1525,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1485
1525
|
this.#sessionData = await this.#fetchSession();
|
|
1486
1526
|
if (this.#sessionData?.loggedIn && (this.#sessionData.userType === "active-subscription" || this.#sessionData.userType === "b2b-user")) {
|
|
1487
1527
|
this.style.display = "none";
|
|
1528
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1488
1529
|
return true;
|
|
1489
1530
|
}
|
|
1490
1531
|
return false;
|
|
@@ -1520,6 +1561,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1520
1561
|
this.#paymentOptions = await this.#fetchPaymentOptions();
|
|
1521
1562
|
if (!this.#paymentOptions?.apple_pay) {
|
|
1522
1563
|
this.style.display = "none";
|
|
1564
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1523
1565
|
return true;
|
|
1524
1566
|
}
|
|
1525
1567
|
return false;
|
|
@@ -1531,13 +1573,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1531
1573
|
const template = document.createElement("template");
|
|
1532
1574
|
template.innerHTML = apple_pay_button_template_default;
|
|
1533
1575
|
this.appendChild(template.content.cloneNode(true));
|
|
1534
|
-
this.#updateDisabledState();
|
|
1535
|
-
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1536
|
-
entryPoint: this.#entryPoint,
|
|
1537
|
-
country: this.#country
|
|
1538
|
-
});
|
|
1539
1576
|
const button = this.querySelector(SEL_BUTTON);
|
|
1577
|
+
button.classList.add("wap-loading");
|
|
1578
|
+
button.setAttribute("disabled", "");
|
|
1579
|
+
button.setAttribute("aria-disabled", "true");
|
|
1580
|
+
button.setAttribute("aria-busy", "true");
|
|
1540
1581
|
button.addEventListener("click", () => {
|
|
1582
|
+
if (button.classList.contains("wap-loading")) return;
|
|
1541
1583
|
this.#openModal();
|
|
1542
1584
|
});
|
|
1543
1585
|
this.addEventListener("apple-pay-confirmed", (event) => {
|
|
@@ -1545,6 +1587,17 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1545
1587
|
this.#initiateApplePay(skuId, country);
|
|
1546
1588
|
});
|
|
1547
1589
|
}
|
|
1590
|
+
#finishLoading() {
|
|
1591
|
+
const button = this.querySelector(SEL_BUTTON);
|
|
1592
|
+
if (button) {
|
|
1593
|
+
button.classList.remove("wap-loading");
|
|
1594
|
+
this.#updateDisabledState();
|
|
1595
|
+
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1596
|
+
entryPoint: this.#entryPoint,
|
|
1597
|
+
country: this.#country
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1548
1601
|
// ─── Private helpers ────────────────────────────────────────────────────────
|
|
1549
1602
|
#updateDisabledState() {
|
|
1550
1603
|
const button = this.querySelector(SEL_BUTTON);
|
|
@@ -1552,6 +1605,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1552
1605
|
if (this.#offer) {
|
|
1553
1606
|
button.removeAttribute("disabled");
|
|
1554
1607
|
button.removeAttribute("aria-disabled");
|
|
1608
|
+
button.removeAttribute("aria-busy");
|
|
1555
1609
|
return;
|
|
1556
1610
|
}
|
|
1557
1611
|
button.setAttribute("disabled", "");
|
|
@@ -439,7 +439,91 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
439
439
|
};
|
|
440
440
|
|
|
441
441
|
// src/templates/apple-pay-button.template.html
|
|
442
|
-
var apple_pay_button_template_default =
|
|
442
|
+
var apple_pay_button_template_default = `<style>
|
|
443
|
+
/* Use CSS variables for layout so parent walls can easily override placement and sizing */
|
|
444
|
+
teg-apple-pay-button {
|
|
445
|
+
display: block;
|
|
446
|
+
margin: var(--apple-pay-margin, 0);
|
|
447
|
+
width: 100%;
|
|
448
|
+
height: 100%;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
teg-apple-pay-button .apple-pay-container {
|
|
452
|
+
display: flex;
|
|
453
|
+
flex-direction: column;
|
|
454
|
+
align-items: stretch;
|
|
455
|
+
width: 100%;
|
|
456
|
+
height: 100%;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
teg-apple-pay-button .apple-pay-btn {
|
|
460
|
+
display: flex;
|
|
461
|
+
align-items: center;
|
|
462
|
+
justify-content: center;
|
|
463
|
+
gap: 0.375rem;
|
|
464
|
+
width: 100%;
|
|
465
|
+
min-width: 0;
|
|
466
|
+
height: 100%;
|
|
467
|
+
min-height: var(--apple-pay-height, 2.75rem);
|
|
468
|
+
background-color: #000;
|
|
469
|
+
color: #fff;
|
|
470
|
+
border: none;
|
|
471
|
+
border-radius: var(--apple-pay-border-radius, 1.375rem);
|
|
472
|
+
cursor: pointer;
|
|
473
|
+
font-family: var(--mb-typeface-sans, var(--wall-type-system-sans));
|
|
474
|
+
font-size: 1.0625rem;
|
|
475
|
+
font-weight: 500;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
teg-apple-pay-button .apple-pay-btn svg {
|
|
479
|
+
flex-shrink: 0;
|
|
480
|
+
display: block;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
teg-apple-pay-button .apple-pay-btn.wap-loading {
|
|
484
|
+
position: relative;
|
|
485
|
+
cursor: default;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
teg-apple-pay-button .apple-pay-btn.wap-loading svg {
|
|
489
|
+
visibility: hidden;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
teg-apple-pay-button .apple-pay-btn.wap-loading::after {
|
|
493
|
+
content: '';
|
|
494
|
+
position: absolute;
|
|
495
|
+
left: 50%;
|
|
496
|
+
top: 50%;
|
|
497
|
+
margin-left: -0.625rem;
|
|
498
|
+
margin-top: -0.625rem;
|
|
499
|
+
width: 1.25rem;
|
|
500
|
+
height: 1.25rem;
|
|
501
|
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
502
|
+
border-top-color: #fff;
|
|
503
|
+
border-radius: 50%;
|
|
504
|
+
animation: wap-button-spinner 0.8s linear infinite;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
@keyframes wap-button-spinner {
|
|
508
|
+
to {
|
|
509
|
+
transform: rotate(360deg);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
</style>
|
|
513
|
+
|
|
514
|
+
<div class="apple-pay-container" aria-label="Express checkout with Apple Pay">
|
|
515
|
+
<button
|
|
516
|
+
class="apple-pay-btn"
|
|
517
|
+
type="button"
|
|
518
|
+
aria-label="Pay with Apple Pay"
|
|
519
|
+
data-analytics="paywall:apple-pay-checkout"
|
|
520
|
+
>
|
|
521
|
+
<svg width="51" height="22" aria-label="Apple Pay" role="img">
|
|
522
|
+
<use href="#apple-pay-logo" />
|
|
523
|
+
</svg>
|
|
524
|
+
</button>
|
|
525
|
+
</div>
|
|
526
|
+
`;
|
|
443
527
|
|
|
444
528
|
// src/clients/payment-checkout/config.ts
|
|
445
529
|
var runtimeConfig = null;
|
|
@@ -453,27 +537,21 @@ var isAppleDeviceOrSafari = () => {
|
|
|
453
537
|
};
|
|
454
538
|
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
|
|
455
539
|
var isApplePayAvailable = async () => {
|
|
456
|
-
console.log("[web-apple-pay] isApplePayAvailable: checking availability");
|
|
457
540
|
if (bypassApplePayChecks()) {
|
|
458
|
-
console.log("[web-apple-pay] isApplePayAvailable: bypass active \u2192 true");
|
|
459
541
|
return true;
|
|
460
542
|
}
|
|
461
543
|
if (!isAppleDeviceOrSafari()) {
|
|
462
|
-
console.log("[web-apple-pay] isApplePayAvailable: not Apple device or Safari \u2192 false");
|
|
463
544
|
return false;
|
|
464
545
|
}
|
|
465
546
|
const applePaySession = window.ApplePaySession;
|
|
466
547
|
if (!applePaySession) {
|
|
467
|
-
console.log("[web-apple-pay] isApplePayAvailable: ApplePaySession not found on window \u2192 false");
|
|
468
548
|
return false;
|
|
469
549
|
}
|
|
470
550
|
try {
|
|
471
551
|
if (typeof applePaySession.canMakePayments !== "function") {
|
|
472
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function \u2192 false");
|
|
473
552
|
return false;
|
|
474
553
|
}
|
|
475
554
|
if (!applePaySession.canMakePayments()) {
|
|
476
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false \u2192 false");
|
|
477
555
|
return false;
|
|
478
556
|
}
|
|
479
557
|
const merchantId = getApplePayConfig()?.merchantId;
|
|
@@ -483,10 +561,7 @@ var isApplePayAvailable = async () => {
|
|
|
483
561
|
);
|
|
484
562
|
return false;
|
|
485
563
|
}
|
|
486
|
-
|
|
487
|
-
const result = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
488
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard \u2192", result);
|
|
489
|
-
return result;
|
|
564
|
+
return await applePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
490
565
|
} catch (error) {
|
|
491
566
|
console.error("Error checking Apple Pay active card status:", error);
|
|
492
567
|
return false;
|
|
@@ -748,13 +823,6 @@ var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
|
|
|
748
823
|
|
|
749
824
|
// src/clients/payment-checkout/apple-session.ts
|
|
750
825
|
function startApplePaySession(config) {
|
|
751
|
-
console.log("[web-apple-pay] startApplePaySession", {
|
|
752
|
-
skuId: config.skuId,
|
|
753
|
-
countryCode: config.countryCode,
|
|
754
|
-
currencyCode: config.currencyCode,
|
|
755
|
-
totalAmount: config.totalAmount,
|
|
756
|
-
userLoggedIn: config.userLoggedIn
|
|
757
|
-
});
|
|
758
826
|
const paymentRequest = {
|
|
759
827
|
countryCode: config.countryCode,
|
|
760
828
|
currencyCode: config.currencyCode,
|
|
@@ -770,11 +838,9 @@ function startApplePaySession(config) {
|
|
|
770
838
|
};
|
|
771
839
|
const session = new ApplePaySession(3, paymentRequest);
|
|
772
840
|
session.onvalidatemerchant = async (event) => {
|
|
773
|
-
console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
|
|
774
841
|
try {
|
|
775
842
|
const validationHost = new URL(event.validationURL).host;
|
|
776
843
|
if (!validationHost.endsWith("apple.com")) {
|
|
777
|
-
console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
|
|
778
844
|
trackFallbackCheckout(config.skuId);
|
|
779
845
|
session.abort();
|
|
780
846
|
window.location.href = redirectToCheckoutFallback({
|
|
@@ -785,13 +851,11 @@ function startApplePaySession(config) {
|
|
|
785
851
|
return;
|
|
786
852
|
}
|
|
787
853
|
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
788
|
-
console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
|
|
789
854
|
const response = await validateAppleMerchant({
|
|
790
855
|
validationURL: event.validationURL,
|
|
791
856
|
recaptchaToken
|
|
792
857
|
});
|
|
793
858
|
if (!response?.merchantSession) {
|
|
794
|
-
console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
|
|
795
859
|
trackFallbackCheckout(config.skuId);
|
|
796
860
|
session.abort();
|
|
797
861
|
window.location.href = redirectToCheckoutFallback({
|
|
@@ -801,10 +865,8 @@ function startApplePaySession(config) {
|
|
|
801
865
|
});
|
|
802
866
|
return;
|
|
803
867
|
}
|
|
804
|
-
console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
|
|
805
868
|
session.completeMerchantValidation(response.merchantSession);
|
|
806
|
-
} catch
|
|
807
|
-
console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
|
|
869
|
+
} catch {
|
|
808
870
|
trackFallbackCheckout(config.skuId);
|
|
809
871
|
session.abort();
|
|
810
872
|
window.location.href = redirectToCheckoutFallback({
|
|
@@ -815,15 +877,12 @@ function startApplePaySession(config) {
|
|
|
815
877
|
}
|
|
816
878
|
};
|
|
817
879
|
session.onpaymentauthorized = (event) => {
|
|
818
|
-
console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
|
|
819
880
|
trackWalletPaymentAuthorised(config.skuId);
|
|
820
881
|
config.onPaymentAuthorized(event, session);
|
|
821
882
|
};
|
|
822
883
|
session.oncancel = () => {
|
|
823
|
-
console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
|
|
824
884
|
config.onCancel?.();
|
|
825
885
|
};
|
|
826
|
-
console.log("[web-apple-pay] startApplePaySession: beginning session");
|
|
827
886
|
trackWalletPaymentSheetOpen(config.skuId);
|
|
828
887
|
session.begin();
|
|
829
888
|
}
|
|
@@ -1002,16 +1061,12 @@ function handleStructuredError(ctx) {
|
|
|
1002
1061
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1003
1062
|
const { skuId, returnUrl, queryString } = config;
|
|
1004
1063
|
let validatedEmail;
|
|
1005
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: starting", { skuId, userLoggedIn: sessionData.loggedIn });
|
|
1006
1064
|
try {
|
|
1007
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 1 \u2014 validating contacts");
|
|
1008
1065
|
validatedEmail = validateContacts(
|
|
1009
1066
|
event,
|
|
1010
1067
|
sessionData,
|
|
1011
1068
|
config.supportedBillingCountries
|
|
1012
1069
|
);
|
|
1013
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: contacts valid", { validatedEmail });
|
|
1014
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 2 \u2014 calling performPurchase", { skuId });
|
|
1015
1070
|
const result = await performPurchase({
|
|
1016
1071
|
applePayToken: event.payment.token,
|
|
1017
1072
|
skuId: config.skuId,
|
|
@@ -1021,8 +1076,6 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1021
1076
|
},
|
|
1022
1077
|
recaptchaTokens: config.recaptchaTokens
|
|
1023
1078
|
});
|
|
1024
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: performPurchase succeeded", { basketId: result.basketId });
|
|
1025
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 3 \u2014 completing payment and redirecting");
|
|
1026
1079
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1027
1080
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1028
1081
|
const userStatus = sessionData.loggedIn ? "lex" : "nex";
|
|
@@ -1035,25 +1088,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1035
1088
|
});
|
|
1036
1089
|
} catch (err) {
|
|
1037
1090
|
if (err instanceof ContactValidationError) {
|
|
1038
|
-
console.warn("[web-apple-pay] handlePaymentAuthorized: contact validation failed", err.errors);
|
|
1039
1091
|
failApplePaySession(session, err.errors);
|
|
1040
1092
|
return;
|
|
1041
1093
|
}
|
|
1042
1094
|
console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
|
|
1043
1095
|
const walletError = parseWalletApiError(err);
|
|
1044
|
-
console.error("[web-apple-pay] handlePaymentAuthorized: BFF/network error", {
|
|
1045
|
-
skuId,
|
|
1046
|
-
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1047
|
-
message: walletError?.message
|
|
1048
|
-
});
|
|
1049
1096
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1050
1097
|
failApplePaySession(session);
|
|
1051
1098
|
if (!walletError) {
|
|
1052
|
-
console.warn("[web-apple-pay] handlePaymentAuthorized: unstructured error, using generic fallback");
|
|
1053
1099
|
redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
|
|
1054
1100
|
return;
|
|
1055
1101
|
}
|
|
1056
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: dispatching structured error handler", { errorType: walletError.errorType });
|
|
1057
1102
|
handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
|
|
1058
1103
|
}
|
|
1059
1104
|
}
|
|
@@ -1441,14 +1486,16 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1441
1486
|
async #run() {
|
|
1442
1487
|
try {
|
|
1443
1488
|
if (await this.#checkAvailability()) return;
|
|
1444
|
-
if (await this.#checkSession()) return;
|
|
1445
1489
|
if (this.#checkOffer()) return;
|
|
1490
|
+
this.#renderTemplate();
|
|
1491
|
+
if (await this.#checkSession()) return;
|
|
1446
1492
|
this.#captureReturnUrl();
|
|
1447
1493
|
this.#computeBillingCountries();
|
|
1448
1494
|
if (await this.#fetchAndCacheOptions()) return;
|
|
1449
|
-
this.#
|
|
1495
|
+
this.#finishLoading();
|
|
1450
1496
|
} catch {
|
|
1451
1497
|
this.style.display = "none";
|
|
1498
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1452
1499
|
} finally {
|
|
1453
1500
|
this.#rendering = false;
|
|
1454
1501
|
}
|
|
@@ -1470,6 +1517,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1470
1517
|
this.#sessionData = await this.#fetchSession();
|
|
1471
1518
|
if (this.#sessionData?.loggedIn && (this.#sessionData.userType === "active-subscription" || this.#sessionData.userType === "b2b-user")) {
|
|
1472
1519
|
this.style.display = "none";
|
|
1520
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1473
1521
|
return true;
|
|
1474
1522
|
}
|
|
1475
1523
|
return false;
|
|
@@ -1505,6 +1553,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1505
1553
|
this.#paymentOptions = await this.#fetchPaymentOptions();
|
|
1506
1554
|
if (!this.#paymentOptions?.apple_pay) {
|
|
1507
1555
|
this.style.display = "none";
|
|
1556
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1508
1557
|
return true;
|
|
1509
1558
|
}
|
|
1510
1559
|
return false;
|
|
@@ -1516,13 +1565,13 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1516
1565
|
const template = document.createElement("template");
|
|
1517
1566
|
template.innerHTML = apple_pay_button_template_default;
|
|
1518
1567
|
this.appendChild(template.content.cloneNode(true));
|
|
1519
|
-
this.#updateDisabledState();
|
|
1520
|
-
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1521
|
-
entryPoint: this.#entryPoint,
|
|
1522
|
-
country: this.#country
|
|
1523
|
-
});
|
|
1524
1568
|
const button = this.querySelector(SEL_BUTTON);
|
|
1569
|
+
button.classList.add("wap-loading");
|
|
1570
|
+
button.setAttribute("disabled", "");
|
|
1571
|
+
button.setAttribute("aria-disabled", "true");
|
|
1572
|
+
button.setAttribute("aria-busy", "true");
|
|
1525
1573
|
button.addEventListener("click", () => {
|
|
1574
|
+
if (button.classList.contains("wap-loading")) return;
|
|
1526
1575
|
this.#openModal();
|
|
1527
1576
|
});
|
|
1528
1577
|
this.addEventListener("apple-pay-confirmed", (event) => {
|
|
@@ -1530,6 +1579,17 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1530
1579
|
this.#initiateApplePay(skuId, country);
|
|
1531
1580
|
});
|
|
1532
1581
|
}
|
|
1582
|
+
#finishLoading() {
|
|
1583
|
+
const button = this.querySelector(SEL_BUTTON);
|
|
1584
|
+
if (button) {
|
|
1585
|
+
button.classList.remove("wap-loading");
|
|
1586
|
+
this.#updateDisabledState();
|
|
1587
|
+
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1588
|
+
entryPoint: this.#entryPoint,
|
|
1589
|
+
country: this.#country
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1533
1593
|
// ─── Private helpers ────────────────────────────────────────────────────────
|
|
1534
1594
|
#updateDisabledState() {
|
|
1535
1595
|
const button = this.querySelector(SEL_BUTTON);
|
|
@@ -1537,6 +1597,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1537
1597
|
if (this.#offer) {
|
|
1538
1598
|
button.removeAttribute("disabled");
|
|
1539
1599
|
button.removeAttribute("aria-disabled");
|
|
1600
|
+
button.removeAttribute("aria-busy");
|
|
1540
1601
|
return;
|
|
1541
1602
|
}
|
|
1542
1603
|
button.setAttribute("disabled", "");
|