@economist/web-apple-pay 0.1.3-beta.0 → 0.1.3-beta.1
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 +163 -175
- package/dist/src/apple-pay-button.js +163 -169
- package/dist/src/apple-pay-button.stories.js +163 -176
- package/dist/src/apple-pay-modal.js +1 -9
- package/dist/src/apple-pay-modal.stories.js +163 -169
- package/dist/src/clients/payment-checkout/apple-session.js +40 -67
- 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/clients/payment-checkout/types.d.ts +4 -6
- package/dist/src/index.js +163 -175
- package/dist/src/stories/utils/story-env.js +0 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -307,9 +307,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
307
307
|
this.#country = value;
|
|
308
308
|
}
|
|
309
309
|
connectedCallback() {
|
|
310
|
-
console.log("[web-apple-pay] ApplePayModal connectedCallback");
|
|
311
310
|
if (this.querySelector(".apm-overlay")) {
|
|
312
|
-
console.log("[web-apple-pay] ApplePayModal connectedCallback: already rendered, skipping");
|
|
313
311
|
return;
|
|
314
312
|
}
|
|
315
313
|
ensureApplePayLogoSymbol();
|
|
@@ -323,7 +321,6 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
323
321
|
this.remove();
|
|
324
322
|
return;
|
|
325
323
|
}
|
|
326
|
-
console.log("[web-apple-pay] ApplePayModal: rendering for offer", { skuId: offer.sku_id, country });
|
|
327
324
|
const offerNameElement = this.querySelector(
|
|
328
325
|
"#apple-pay-modal-offer-name"
|
|
329
326
|
);
|
|
@@ -396,14 +393,11 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
396
393
|
});
|
|
397
394
|
}
|
|
398
395
|
cta?.addEventListener("click", () => {
|
|
399
|
-
console.log("[web-apple-pay] ApplePayModal: CTA clicked", { skuId: offer.sku_id, country, isUS });
|
|
400
396
|
if (isUS && checkbox && !checkbox.checked) {
|
|
401
|
-
console.log("[web-apple-pay] ApplePayModal: T&C checkbox not accepted, showing error");
|
|
402
397
|
errorElement?.classList.add("apm__error--visible");
|
|
403
398
|
checkboxLabel?.classList.add("apm__checkbox-label--error");
|
|
404
399
|
return;
|
|
405
400
|
}
|
|
406
|
-
console.log("[web-apple-pay] ApplePayModal: dispatching apple-pay-confirmed", { skuId: offer.sku_id, country });
|
|
407
401
|
this.dispatchEvent(
|
|
408
402
|
new CustomEvent("apple-pay-confirmed", {
|
|
409
403
|
bubbles: true,
|
|
@@ -411,7 +405,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
411
405
|
detail: { skuId: offer.sku_id, country }
|
|
412
406
|
})
|
|
413
407
|
);
|
|
414
|
-
|
|
408
|
+
this.close();
|
|
415
409
|
});
|
|
416
410
|
this.querySelector("#apple-pay-modal-close")?.addEventListener(
|
|
417
411
|
"click",
|
|
@@ -419,7 +413,6 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
419
413
|
);
|
|
420
414
|
}
|
|
421
415
|
open() {
|
|
422
|
-
console.log("[web-apple-pay] ApplePayModal: open()");
|
|
423
416
|
this.#triggerElement = document.activeElement;
|
|
424
417
|
document.body.appendChild(this);
|
|
425
418
|
this.#keyHandler = (event) => {
|
|
@@ -434,7 +427,6 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
434
427
|
this.querySelector("#apple-pay-modal")?.focus();
|
|
435
428
|
}
|
|
436
429
|
close() {
|
|
437
|
-
console.log("[web-apple-pay] ApplePayModal: close()");
|
|
438
430
|
if (this.#keyHandler) {
|
|
439
431
|
document.removeEventListener("keydown", this.#keyHandler, {
|
|
440
432
|
capture: true
|
|
@@ -447,17 +439,95 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
|
|
|
447
439
|
};
|
|
448
440
|
|
|
449
441
|
// src/templates/apple-pay-button.template.html
|
|
450
|
-
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
|
+
`;
|
|
451
527
|
|
|
452
528
|
// src/clients/payment-checkout/config.ts
|
|
453
529
|
var runtimeConfig = null;
|
|
454
530
|
var configureApplePay = (config) => {
|
|
455
|
-
console.log("[web-apple-pay] configureApplePay called", {
|
|
456
|
-
merchantId: config.merchantId,
|
|
457
|
-
walletApiBaseUrl: config.walletApiBaseUrl,
|
|
458
|
-
debugBypassEnabled: config.debugBypassEnabled,
|
|
459
|
-
recaptchaSiteKey: config.recaptchaSiteKey ? "***" : void 0
|
|
460
|
-
});
|
|
461
531
|
runtimeConfig = {
|
|
462
532
|
...config
|
|
463
533
|
};
|
|
@@ -472,27 +542,21 @@ var isAppleDeviceOrSafari = () => {
|
|
|
472
542
|
};
|
|
473
543
|
var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
|
|
474
544
|
var isApplePayAvailable = async () => {
|
|
475
|
-
console.log("[web-apple-pay] isApplePayAvailable: checking availability");
|
|
476
545
|
if (bypassApplePayChecks()) {
|
|
477
|
-
console.log("[web-apple-pay] isApplePayAvailable: bypass active \u2192 true");
|
|
478
546
|
return true;
|
|
479
547
|
}
|
|
480
548
|
if (!isAppleDeviceOrSafari()) {
|
|
481
|
-
console.log("[web-apple-pay] isApplePayAvailable: not Apple device or Safari \u2192 false");
|
|
482
549
|
return false;
|
|
483
550
|
}
|
|
484
551
|
const applePaySession = window.ApplePaySession;
|
|
485
552
|
if (!applePaySession) {
|
|
486
|
-
console.log("[web-apple-pay] isApplePayAvailable: ApplePaySession not found on window \u2192 false");
|
|
487
553
|
return false;
|
|
488
554
|
}
|
|
489
555
|
try {
|
|
490
556
|
if (typeof applePaySession.canMakePayments !== "function") {
|
|
491
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function \u2192 false");
|
|
492
557
|
return false;
|
|
493
558
|
}
|
|
494
559
|
if (!applePaySession.canMakePayments()) {
|
|
495
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false \u2192 false");
|
|
496
560
|
return false;
|
|
497
561
|
}
|
|
498
562
|
const merchantId = getApplePayConfig()?.merchantId;
|
|
@@ -502,10 +566,7 @@ var isApplePayAvailable = async () => {
|
|
|
502
566
|
);
|
|
503
567
|
return false;
|
|
504
568
|
}
|
|
505
|
-
|
|
506
|
-
const result = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
507
|
-
console.log("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard \u2192", result);
|
|
508
|
-
return result;
|
|
569
|
+
return await applePaySession.canMakePaymentsWithActiveCard(merchantId);
|
|
509
570
|
} catch (error) {
|
|
510
571
|
console.error("Error checking Apple Pay active card status:", error);
|
|
511
572
|
return false;
|
|
@@ -767,13 +828,6 @@ var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
|
|
|
767
828
|
|
|
768
829
|
// src/clients/payment-checkout/apple-session.ts
|
|
769
830
|
function startApplePaySession(config) {
|
|
770
|
-
console.log("[web-apple-pay] startApplePaySession", {
|
|
771
|
-
skuId: config.skuId,
|
|
772
|
-
countryCode: config.countryCode,
|
|
773
|
-
currencyCode: config.currencyCode,
|
|
774
|
-
totalAmount: config.totalAmount,
|
|
775
|
-
userLoggedIn: config.userLoggedIn
|
|
776
|
-
});
|
|
777
831
|
const paymentRequest = {
|
|
778
832
|
countryCode: config.countryCode,
|
|
779
833
|
currencyCode: config.currencyCode,
|
|
@@ -787,45 +841,26 @@ function startApplePaySession(config) {
|
|
|
787
841
|
// Only request email from Apple Pay sheet if user is NOT logged in
|
|
788
842
|
...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
|
|
789
843
|
};
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
window.location.href = redirectToCheckoutFallback({
|
|
802
|
-
skuId: config.skuId,
|
|
803
|
-
returnUrl: config.returnUrl,
|
|
804
|
-
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
805
|
-
});
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
809
|
-
console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
|
|
810
|
-
const response = await validateAppleMerchant({
|
|
811
|
-
validationURL: event.validationURL,
|
|
812
|
-
recaptchaToken
|
|
844
|
+
const session = new ApplePaySession(3, paymentRequest);
|
|
845
|
+
session.onvalidatemerchant = async (event) => {
|
|
846
|
+
try {
|
|
847
|
+
const validationHost = new URL(event.validationURL).host;
|
|
848
|
+
if (!validationHost.endsWith("apple.com")) {
|
|
849
|
+
trackFallbackCheckout(config.skuId);
|
|
850
|
+
session.abort();
|
|
851
|
+
window.location.href = redirectToCheckoutFallback({
|
|
852
|
+
skuId: config.skuId,
|
|
853
|
+
returnUrl: config.returnUrl,
|
|
854
|
+
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
813
855
|
});
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
});
|
|
823
|
-
return;
|
|
824
|
-
}
|
|
825
|
-
console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
|
|
826
|
-
session.completeMerchantValidation(response.merchantSession);
|
|
827
|
-
} catch (err) {
|
|
828
|
-
console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
const recaptchaToken = await getRecaptchaToken("verifyMerchant");
|
|
859
|
+
const response = await validateAppleMerchant({
|
|
860
|
+
validationURL: event.validationURL,
|
|
861
|
+
recaptchaToken
|
|
862
|
+
});
|
|
863
|
+
if (!response?.merchantSession) {
|
|
829
864
|
trackFallbackCheckout(config.skuId);
|
|
830
865
|
session.abort();
|
|
831
866
|
window.location.href = redirectToCheckoutFallback({
|
|
@@ -833,29 +868,28 @@ function startApplePaySession(config) {
|
|
|
833
868
|
returnUrl: config.returnUrl,
|
|
834
869
|
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
835
870
|
});
|
|
871
|
+
return;
|
|
836
872
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
});
|
|
858
|
-
}
|
|
873
|
+
session.completeMerchantValidation(response.merchantSession);
|
|
874
|
+
} catch {
|
|
875
|
+
trackFallbackCheckout(config.skuId);
|
|
876
|
+
session.abort();
|
|
877
|
+
window.location.href = redirectToCheckoutFallback({
|
|
878
|
+
skuId: config.skuId,
|
|
879
|
+
returnUrl: config.returnUrl,
|
|
880
|
+
inlineError: MSG_PAYMENT_GENERIC_ERROR
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
session.onpaymentauthorized = (event) => {
|
|
885
|
+
trackWalletPaymentAuthorised(config.skuId);
|
|
886
|
+
config.onPaymentAuthorized(event, session);
|
|
887
|
+
};
|
|
888
|
+
session.oncancel = () => {
|
|
889
|
+
config.onCancel?.();
|
|
890
|
+
};
|
|
891
|
+
trackWalletPaymentSheetOpen(config.skuId);
|
|
892
|
+
session.begin();
|
|
859
893
|
}
|
|
860
894
|
|
|
861
895
|
// src/clients/payment-checkout/contact-validation.ts
|
|
@@ -1032,16 +1066,12 @@ function handleStructuredError(ctx) {
|
|
|
1032
1066
|
async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
1033
1067
|
const { skuId, returnUrl, queryString } = config;
|
|
1034
1068
|
let validatedEmail;
|
|
1035
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: starting", { skuId, userLoggedIn: sessionData.loggedIn });
|
|
1036
1069
|
try {
|
|
1037
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 1 \u2014 validating contacts");
|
|
1038
1070
|
validatedEmail = validateContacts(
|
|
1039
1071
|
event,
|
|
1040
1072
|
sessionData,
|
|
1041
1073
|
config.supportedBillingCountries
|
|
1042
1074
|
);
|
|
1043
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: contacts valid", { validatedEmail });
|
|
1044
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 2 \u2014 calling performPurchase", { skuId });
|
|
1045
1075
|
const result = await performPurchase({
|
|
1046
1076
|
applePayToken: event.payment.token,
|
|
1047
1077
|
skuId: config.skuId,
|
|
@@ -1051,8 +1081,6 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1051
1081
|
},
|
|
1052
1082
|
recaptchaTokens: config.recaptchaTokens
|
|
1053
1083
|
});
|
|
1054
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: performPurchase succeeded", { basketId: result.basketId });
|
|
1055
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: step 3 \u2014 completing payment and redirecting");
|
|
1056
1084
|
trackWalletPaymentSuccess(skuId, result.basketId);
|
|
1057
1085
|
session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
|
|
1058
1086
|
const userStatus = sessionData.loggedIn ? "lex" : "nex";
|
|
@@ -1065,25 +1093,17 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
|
|
|
1065
1093
|
});
|
|
1066
1094
|
} catch (err) {
|
|
1067
1095
|
if (err instanceof ContactValidationError) {
|
|
1068
|
-
console.warn("[web-apple-pay] handlePaymentAuthorized: contact validation failed", err.errors);
|
|
1069
1096
|
failApplePaySession(session, err.errors);
|
|
1070
1097
|
return;
|
|
1071
1098
|
}
|
|
1072
1099
|
console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
|
|
1073
1100
|
const walletError = parseWalletApiError(err);
|
|
1074
|
-
console.error("[web-apple-pay] handlePaymentAuthorized: BFF/network error", {
|
|
1075
|
-
skuId,
|
|
1076
|
-
errorType: walletError?.errorType ?? "UNKNOWN",
|
|
1077
|
-
message: walletError?.message
|
|
1078
|
-
});
|
|
1079
1101
|
trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
|
|
1080
1102
|
failApplePaySession(session);
|
|
1081
1103
|
if (!walletError) {
|
|
1082
|
-
console.warn("[web-apple-pay] handlePaymentAuthorized: unstructured error, using generic fallback");
|
|
1083
1104
|
redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
|
|
1084
1105
|
return;
|
|
1085
1106
|
}
|
|
1086
|
-
console.log("[web-apple-pay] handlePaymentAuthorized: dispatching structured error handler", { errorType: walletError.errorType });
|
|
1087
1107
|
handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
|
|
1088
1108
|
}
|
|
1089
1109
|
}
|
|
@@ -1370,19 +1390,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1370
1390
|
* Creates and configures an ApplePayButton if the feature flag is present.
|
|
1371
1391
|
*/
|
|
1372
1392
|
static createIfEnabled(offer, country, entryPoint) {
|
|
1373
|
-
if (!offer)
|
|
1374
|
-
|
|
1393
|
+
if (!offer) return null;
|
|
1394
|
+
if (APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant))
|
|
1375
1395
|
return null;
|
|
1376
|
-
|
|
1377
|
-
if (APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant)) {
|
|
1378
|
-
console.log("[web-apple-pay] createIfEnabled: excluded variant \u2192 null", { variant: offer.product?.variant });
|
|
1396
|
+
if (!new URLSearchParams(window.location.search).has(FEATURE_APPLE_PAY_EXPRESS_CHECKOUT))
|
|
1379
1397
|
return null;
|
|
1380
|
-
}
|
|
1381
|
-
if (!new URLSearchParams(window.location.search).has(FEATURE_APPLE_PAY_EXPRESS_CHECKOUT)) {
|
|
1382
|
-
console.log("[web-apple-pay] createIfEnabled: feature flag absent \u2192 null");
|
|
1383
|
-
return null;
|
|
1384
|
-
}
|
|
1385
|
-
console.log("[web-apple-pay] createIfEnabled: creating button", { skuId: offer.sku_id, country, entryPoint });
|
|
1386
1398
|
_ApplePayButton.define();
|
|
1387
1399
|
const button = document.createElement(_ApplePayButton.tag);
|
|
1388
1400
|
button.offer = offer;
|
|
@@ -1409,7 +1421,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1409
1421
|
#returnUrl = "";
|
|
1410
1422
|
#supportedBillingCountries = [];
|
|
1411
1423
|
#entryPoint = "";
|
|
1412
|
-
#recaptchaTokens = null;
|
|
1413
1424
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
1414
1425
|
get offer() {
|
|
1415
1426
|
return this.#offer;
|
|
@@ -1451,9 +1462,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1451
1462
|
}
|
|
1452
1463
|
// ─── Lifecycle ──────────────────────────────────────────────────────────────
|
|
1453
1464
|
async connectedCallback() {
|
|
1454
|
-
console.log("[web-apple-pay] connectedCallback: element connected");
|
|
1455
1465
|
if (this.#rendering || this.querySelector(SEL_CONTAINER)) {
|
|
1456
|
-
console.log("[web-apple-pay] connectedCallback: already rendering or rendered, skipping");
|
|
1457
1466
|
return;
|
|
1458
1467
|
}
|
|
1459
1468
|
this.#rendering = true;
|
|
@@ -1470,7 +1479,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1470
1479
|
* and eliminating the implicit reliance on the DOM as a render-state flag.
|
|
1471
1480
|
*/
|
|
1472
1481
|
disconnectedCallback() {
|
|
1473
|
-
console.log("[web-apple-pay] disconnectedCallback: element removed from DOM, resetting state");
|
|
1474
1482
|
this.#rendering = false;
|
|
1475
1483
|
this.#readyPromise = null;
|
|
1476
1484
|
this.#sessionData = null;
|
|
@@ -1481,17 +1489,18 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1481
1489
|
}
|
|
1482
1490
|
// ─── Render pipeline ────────────────────────────────────────────────────────
|
|
1483
1491
|
async #run() {
|
|
1484
|
-
console.log("[web-apple-pay] #run: starting render pipeline");
|
|
1485
1492
|
try {
|
|
1486
1493
|
if (await this.#checkAvailability()) return;
|
|
1487
|
-
if (await this.#checkSession()) return;
|
|
1488
1494
|
if (this.#checkOffer()) return;
|
|
1495
|
+
this.#renderTemplate();
|
|
1496
|
+
if (await this.#checkSession()) return;
|
|
1489
1497
|
this.#captureReturnUrl();
|
|
1490
1498
|
this.#computeBillingCountries();
|
|
1491
1499
|
if (await this.#fetchAndCacheOptions()) return;
|
|
1492
|
-
this.#
|
|
1500
|
+
this.#finishLoading();
|
|
1493
1501
|
} catch {
|
|
1494
1502
|
this.style.display = "none";
|
|
1503
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1495
1504
|
} finally {
|
|
1496
1505
|
this.#rendering = false;
|
|
1497
1506
|
}
|
|
@@ -1500,11 +1509,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1500
1509
|
async #checkAvailability() {
|
|
1501
1510
|
const available = await isApplePayAvailable();
|
|
1502
1511
|
if (!available) {
|
|
1503
|
-
console.log("[web-apple-pay] #checkAvailability: Apple Pay unavailable \u2192 hiding");
|
|
1504
1512
|
this.style.display = "none";
|
|
1505
1513
|
return true;
|
|
1506
1514
|
}
|
|
1507
|
-
console.log("[web-apple-pay] #checkAvailability: Apple Pay available");
|
|
1508
1515
|
return false;
|
|
1509
1516
|
}
|
|
1510
1517
|
/**
|
|
@@ -1513,10 +1520,9 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1513
1520
|
*/
|
|
1514
1521
|
async #checkSession() {
|
|
1515
1522
|
this.#sessionData = await this.#fetchSession();
|
|
1516
|
-
console.log("[web-apple-pay] #checkSession: session fetched", { loggedIn: this.#sessionData?.loggedIn, userType: this.#sessionData?.userType });
|
|
1517
1523
|
if (this.#sessionData?.loggedIn && (this.#sessionData.userType === "active-subscription" || this.#sessionData.userType === "b2b-user")) {
|
|
1518
|
-
console.log("[web-apple-pay] #checkSession: ineligible user \u2192 hiding", { userType: this.#sessionData.userType });
|
|
1519
1524
|
this.style.display = "none";
|
|
1525
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1520
1526
|
return true;
|
|
1521
1527
|
}
|
|
1522
1528
|
return false;
|
|
@@ -1524,17 +1530,14 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1524
1530
|
/** Returns `true` (hide) when no offer has been set before mount. */
|
|
1525
1531
|
#checkOffer() {
|
|
1526
1532
|
if (!this.#offer) {
|
|
1527
|
-
console.log("[web-apple-pay] #checkOffer: no offer set \u2192 hiding");
|
|
1528
1533
|
this.style.display = "none";
|
|
1529
1534
|
return true;
|
|
1530
1535
|
}
|
|
1531
|
-
console.log("[web-apple-pay] #checkOffer: offer present", { skuId: this.#offer.sku_id });
|
|
1532
1536
|
return false;
|
|
1533
1537
|
}
|
|
1534
1538
|
/** Captures the current page URL once at mount for reuse across the session lifetime. */
|
|
1535
1539
|
#captureReturnUrl() {
|
|
1536
1540
|
this.#returnUrl = captureOriginUrl();
|
|
1537
|
-
console.log("[web-apple-pay] #captureReturnUrl:", this.#returnUrl);
|
|
1538
1541
|
}
|
|
1539
1542
|
/**
|
|
1540
1543
|
* Computes supported billing countries once at mount.
|
|
@@ -1546,7 +1549,6 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1546
1549
|
this.#country,
|
|
1547
1550
|
this.#offer.product?.variant ?? ""
|
|
1548
1551
|
);
|
|
1549
|
-
console.log("[web-apple-pay] #computeBillingCountries:", this.#supportedBillingCountries);
|
|
1550
1552
|
}
|
|
1551
1553
|
/**
|
|
1552
1554
|
* Returns `true` (hide) when the wallet BFF reports Apple Pay is unavailable
|
|
@@ -1554,36 +1556,44 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1554
1556
|
*/
|
|
1555
1557
|
async #fetchAndCacheOptions() {
|
|
1556
1558
|
this.#paymentOptions = await this.#fetchPaymentOptions();
|
|
1557
|
-
|
|
1558
|
-
if (!this.#paymentOptions?.options?.apple_pay) {
|
|
1559
|
-
console.log("[web-apple-pay] #fetchAndCacheOptions: apple_pay not in options \u2192 hiding");
|
|
1559
|
+
if (!this.#paymentOptions?.apple_pay) {
|
|
1560
1560
|
this.style.display = "none";
|
|
1561
|
+
this.querySelector(SEL_CONTAINER)?.remove();
|
|
1561
1562
|
return true;
|
|
1562
1563
|
}
|
|
1563
1564
|
return false;
|
|
1564
1565
|
}
|
|
1565
1566
|
/** Injects the button template and wires click and confirm event listeners. */
|
|
1566
1567
|
#renderTemplate() {
|
|
1567
|
-
console.log("[web-apple-pay] #renderTemplate: rendering Apple Pay button", { skuId: this.#offer?.sku_id, country: this.#country });
|
|
1568
1568
|
this.style.removeProperty("display");
|
|
1569
1569
|
ensureApplePayLogoSymbol();
|
|
1570
1570
|
const template = document.createElement("template");
|
|
1571
1571
|
template.innerHTML = apple_pay_button_template_default;
|
|
1572
1572
|
this.appendChild(template.content.cloneNode(true));
|
|
1573
|
-
this.#updateDisabledState();
|
|
1574
|
-
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1575
|
-
entryPoint: this.#entryPoint,
|
|
1576
|
-
country: this.#country
|
|
1577
|
-
});
|
|
1578
1573
|
const button = this.querySelector(SEL_BUTTON);
|
|
1574
|
+
button.classList.add("wap-loading");
|
|
1575
|
+
button.setAttribute("disabled", "");
|
|
1576
|
+
button.setAttribute("aria-disabled", "true");
|
|
1577
|
+
button.setAttribute("aria-busy", "true");
|
|
1579
1578
|
button.addEventListener("click", () => {
|
|
1579
|
+
if (button.classList.contains("wap-loading")) return;
|
|
1580
1580
|
this.#openModal();
|
|
1581
1581
|
});
|
|
1582
|
-
|
|
1582
|
+
this.addEventListener("apple-pay-confirmed", (event) => {
|
|
1583
1583
|
const { skuId, country } = event.detail;
|
|
1584
|
-
console.log("[web-apple-pay] apple-pay-confirmed received on document", { skuId, country });
|
|
1585
1584
|
this.#initiateApplePay(skuId, country);
|
|
1586
|
-
}
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
#finishLoading() {
|
|
1588
|
+
const button = this.querySelector(SEL_BUTTON);
|
|
1589
|
+
if (button) {
|
|
1590
|
+
button.classList.remove("wap-loading");
|
|
1591
|
+
this.#updateDisabledState();
|
|
1592
|
+
trackExpressWalletImpression(this.#offer.sku_id, {
|
|
1593
|
+
entryPoint: this.#entryPoint,
|
|
1594
|
+
country: this.#country
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1587
1597
|
}
|
|
1588
1598
|
// ─── Private helpers ────────────────────────────────────────────────────────
|
|
1589
1599
|
#updateDisabledState() {
|
|
@@ -1592,28 +1602,15 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1592
1602
|
if (this.#offer) {
|
|
1593
1603
|
button.removeAttribute("disabled");
|
|
1594
1604
|
button.removeAttribute("aria-disabled");
|
|
1605
|
+
button.removeAttribute("aria-busy");
|
|
1595
1606
|
return;
|
|
1596
1607
|
}
|
|
1597
1608
|
button.setAttribute("disabled", "");
|
|
1598
1609
|
button.setAttribute("aria-disabled", "true");
|
|
1599
1610
|
}
|
|
1600
1611
|
#openModal() {
|
|
1601
|
-
|
|
1602
|
-
if (!this.#offer) {
|
|
1603
|
-
console.warn("[web-apple-pay] #openModal: no offer set, ignoring click");
|
|
1604
|
-
return;
|
|
1605
|
-
}
|
|
1606
|
-
if (document.querySelector(ApplePayModal.tag)) {
|
|
1607
|
-
console.warn("[web-apple-pay] #openModal: modal already open, ignoring click");
|
|
1608
|
-
return;
|
|
1609
|
-
}
|
|
1612
|
+
if (!this.#offer || document.querySelector(ApplePayModal.tag)) return;
|
|
1610
1613
|
trackExpressWalletClick(this.#offer.sku_id);
|
|
1611
|
-
getPurchaseRecaptchaTokens().then((tokens) => {
|
|
1612
|
-
console.log("[web-apple-pay] #openModal: reCAPTCHA tokens pre-fetched");
|
|
1613
|
-
this.#recaptchaTokens = tokens;
|
|
1614
|
-
}).catch((err) => {
|
|
1615
|
-
console.warn("[web-apple-pay] #openModal: reCAPTCHA pre-fetch failed", err);
|
|
1616
|
-
});
|
|
1617
1614
|
ApplePayModal.define();
|
|
1618
1615
|
const modal = document.createElement(ApplePayModal.tag);
|
|
1619
1616
|
modal.offer = this.#offer;
|
|
@@ -1625,37 +1622,30 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1625
1622
|
}
|
|
1626
1623
|
async #fetchSession() {
|
|
1627
1624
|
try {
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
return session;
|
|
1631
|
-
} catch (err) {
|
|
1632
|
-
console.warn("[web-apple-pay] #fetchSession: failed, falling back to anonymous session", err);
|
|
1625
|
+
return await getExpressCheckoutSession();
|
|
1626
|
+
} catch {
|
|
1633
1627
|
return null;
|
|
1634
1628
|
}
|
|
1635
1629
|
}
|
|
1636
1630
|
async #fetchPaymentOptions() {
|
|
1637
1631
|
try {
|
|
1638
|
-
|
|
1632
|
+
return await getPaymentOptions({
|
|
1639
1633
|
sku: this.#offer.sku_id,
|
|
1640
1634
|
countryCode: this.#country,
|
|
1641
1635
|
currencyCode: this.#offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
1642
1636
|
checkoutUrl: this.#returnUrl
|
|
1643
1637
|
});
|
|
1644
|
-
|
|
1645
|
-
return options;
|
|
1646
|
-
} catch (err) {
|
|
1647
|
-
console.warn("[web-apple-pay] #fetchPaymentOptions: failed", err);
|
|
1638
|
+
} catch {
|
|
1648
1639
|
return null;
|
|
1649
1640
|
}
|
|
1650
1641
|
}
|
|
1651
1642
|
#buildSessionParams(skuId, country, sessionData, paymentOptions, recaptchaTokens) {
|
|
1652
|
-
console.log("[web-apple-pay] #buildSessionParams", { skuId, country, userLoggedIn: sessionData.loggedIn });
|
|
1653
1643
|
const offer = this.#offer;
|
|
1654
1644
|
const totalAmount = offer.price?.purchase ? `${offer.price.purchase}` : DEFAULT_TOTAL;
|
|
1655
1645
|
return {
|
|
1656
1646
|
countryCode: country,
|
|
1657
1647
|
currencyCode: offer.price?.currency_code ?? DEFAULT_CURRENCY,
|
|
1658
|
-
supportedNetworks: paymentOptions.
|
|
1648
|
+
supportedNetworks: paymentOptions.apple_pay.settings.supported_networks,
|
|
1659
1649
|
totalAmount,
|
|
1660
1650
|
totalLabel: TOTAL_LABEL,
|
|
1661
1651
|
skuId,
|
|
@@ -1672,12 +1662,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
1672
1662
|
};
|
|
1673
1663
|
}
|
|
1674
1664
|
async #initiateApplePay(skuId, country) {
|
|
1675
|
-
console.log("[web-apple-pay] #initiateApplePay: starting", { skuId, country });
|
|
1676
1665
|
try {
|
|
1677
1666
|
const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
|
|
1678
1667
|
const paymentOptions = this.#paymentOptions;
|
|
1679
|
-
const recaptchaTokens =
|
|
1680
|
-
console.log("[web-apple-pay] #initiateApplePay: tokens ready, starting session", { prefetched: !!this.#recaptchaTokens });
|
|
1668
|
+
const recaptchaTokens = await getPurchaseRecaptchaTokens();
|
|
1681
1669
|
startApplePaySession(
|
|
1682
1670
|
this.#buildSessionParams(
|
|
1683
1671
|
skuId,
|