@economist/web-apple-pay 0.1.3-beta.1 → 0.1.3-beta.2

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 CHANGED
@@ -317,7 +317,6 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
317
317
  const offer = this.#offer;
318
318
  const country = this.#country;
319
319
  if (!offer) {
320
- console.error("ApplePayModal: Missing or invalid offer data");
321
320
  this.remove();
322
321
  return;
323
322
  }
@@ -528,6 +527,12 @@ var apple_pay_button_template_default = `<style>
528
527
  // src/clients/payment-checkout/config.ts
529
528
  var runtimeConfig = null;
530
529
  var configureApplePay = (config) => {
530
+ console.log("[web-apple-pay] configureApplePay called", {
531
+ merchantId: config.merchantId,
532
+ walletApiBaseUrl: config.walletApiBaseUrl,
533
+ debugBypassEnabled: config.debugBypassEnabled,
534
+ recaptchaSiteKey: config.recaptchaSiteKey ? "***" : void 0
535
+ });
531
536
  runtimeConfig = {
532
537
  ...config
533
538
  };
@@ -542,21 +547,27 @@ var isAppleDeviceOrSafari = () => {
542
547
  };
543
548
  var bypassApplePayChecks = () => new URLSearchParams(window.location.search).get("BYPASS_APPLE_PAY_CHECKS") !== null && getApplePayConfig()?.debugBypassEnabled === true;
544
549
  var isApplePayAvailable = async () => {
550
+ console.log("[web-apple-pay] isApplePayAvailable: checking availability");
545
551
  if (bypassApplePayChecks()) {
552
+ console.log("[web-apple-pay] isApplePayAvailable: bypass active \u2192 true");
546
553
  return true;
547
554
  }
548
555
  if (!isAppleDeviceOrSafari()) {
556
+ console.log("[web-apple-pay] isApplePayAvailable: not Apple device or Safari \u2192 false");
549
557
  return false;
550
558
  }
551
559
  const applePaySession = window.ApplePaySession;
552
560
  if (!applePaySession) {
561
+ console.log("[web-apple-pay] isApplePayAvailable: ApplePaySession not found on window \u2192 false");
553
562
  return false;
554
563
  }
555
564
  try {
556
565
  if (typeof applePaySession.canMakePayments !== "function") {
566
+ console.log("[web-apple-pay] isApplePayAvailable: canMakePayments is not a function \u2192 false");
557
567
  return false;
558
568
  }
559
569
  if (!applePaySession.canMakePayments()) {
570
+ console.log("[web-apple-pay] isApplePayAvailable: canMakePayments() returned false \u2192 false");
560
571
  return false;
561
572
  }
562
573
  const merchantId = getApplePayConfig()?.merchantId;
@@ -566,7 +577,10 @@ var isApplePayAvailable = async () => {
566
577
  );
567
578
  return false;
568
579
  }
569
- return await applePaySession.canMakePaymentsWithActiveCard(merchantId);
580
+ console.log("[web-apple-pay] isApplePayAvailable : calling canMakePaymentsWithActiveCard", { merchantId });
581
+ const result = await applePaySession.canMakePaymentsWithActiveCard(merchantId);
582
+ console.log("[web-apple-pay] isApplePayAvailable: canMakePaymentsWithActiveCard \u2192", result);
583
+ return result;
570
584
  } catch (error) {
571
585
  console.error("Error checking Apple Pay active card status:", error);
572
586
  return false;
@@ -827,7 +841,73 @@ var MSG_EMAIL_COLLISION = "This email address is already linked to an account. P
827
841
  var MSG_CONTACT_VALIDATION_INTERNAL = "Could not validate Apple Pay contact";
828
842
 
829
843
  // src/clients/payment-checkout/apple-session.ts
844
+ function redirectToFallbackCheckout(config) {
845
+ console.warn("[web-apple-pay] redirectToFallbackCheckout", {
846
+ skuId: config.skuId,
847
+ returnUrl: config.returnUrl,
848
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
849
+ });
850
+ trackFallbackCheckout(config.skuId);
851
+ window.location.href = redirectToCheckoutFallback({
852
+ skuId: config.skuId,
853
+ returnUrl: config.returnUrl,
854
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
855
+ });
856
+ }
857
+ async function handleMerchantValidation(event, session, config) {
858
+ console.log("[web-apple-pay] handleMerchantValidation: start", {
859
+ skuId: config.skuId,
860
+ validationURL: event.validationURL
861
+ });
862
+ try {
863
+ const validationHost = new URL(event.validationURL).host;
864
+ console.log("[web-apple-pay] handleMerchantValidation: host parsed", {
865
+ validationHost
866
+ });
867
+ if (!validationHost.endsWith("apple.com")) {
868
+ console.warn(
869
+ "[web-apple-pay] handleMerchantValidation: invalid validation host",
870
+ { validationHost }
871
+ );
872
+ session.abort();
873
+ redirectToFallbackCheckout(config);
874
+ return;
875
+ }
876
+ console.log("[web-apple-pay] handleMerchantValidation: requesting recaptcha token");
877
+ const recaptchaToken = await getRecaptchaToken("verifyMerchant");
878
+ console.log("[web-apple-pay] handleMerchantValidation: recaptcha token received");
879
+ const response = await validateAppleMerchant({
880
+ validationURL: event.validationURL,
881
+ recaptchaToken
882
+ });
883
+ if (!response?.merchantSession) {
884
+ console.warn(
885
+ "[web-apple-pay] handleMerchantValidation: missing merchantSession in response"
886
+ );
887
+ session.abort();
888
+ redirectToFallbackCheckout(config);
889
+ return;
890
+ }
891
+ console.log("[web-apple-pay] handleMerchantValidation: completing merchant validation");
892
+ session.completeMerchantValidation(response.merchantSession);
893
+ console.log("[web-apple-pay] handleMerchantValidation: success");
894
+ } catch (err) {
895
+ console.error("[web-apple-pay] handleMerchantValidation: failed", {
896
+ skuId: config.skuId,
897
+ err
898
+ });
899
+ session.abort();
900
+ redirectToFallbackCheckout(config);
901
+ }
902
+ }
830
903
  function startApplePaySession(config) {
904
+ console.log("[web-apple-pay] startApplePaySession", {
905
+ skuId: config.skuId,
906
+ countryCode: config.countryCode,
907
+ currencyCode: config.currencyCode,
908
+ totalAmount: config.totalAmount,
909
+ userLoggedIn: config.userLoggedIn
910
+ });
831
911
  const paymentRequest = {
832
912
  countryCode: config.countryCode,
833
913
  currencyCode: config.currencyCode,
@@ -841,55 +921,45 @@ function startApplePaySession(config) {
841
921
  // Only request email from Apple Pay sheet if user is NOT logged in
842
922
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
843
923
  };
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
855
- });
856
- return;
857
- }
858
- const recaptchaToken = await getRecaptchaToken("verifyMerchant");
859
- const response = await validateAppleMerchant({
860
- validationURL: event.validationURL,
861
- recaptchaToken
924
+ console.log("[web-apple-pay] startApplePaySession: payment request created", {
925
+ skuId: config.skuId,
926
+ requiredShippingContactFields: paymentRequest.requiredShippingContactFields
927
+ });
928
+ try {
929
+ const session = new ApplePaySession(3, paymentRequest);
930
+ console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
931
+ session.onvalidatemerchant = async (event) => {
932
+ console.log("[web-apple-pay] onvalidatemerchant: invoked", {
933
+ skuId: config.skuId
862
934
  });
863
- if (!response?.merchantSession) {
864
- trackFallbackCheckout(config.skuId);
865
- session.abort();
866
- window.location.href = redirectToCheckoutFallback({
867
- skuId: config.skuId,
868
- returnUrl: config.returnUrl,
869
- inlineError: MSG_PAYMENT_GENERIC_ERROR
870
- });
871
- return;
872
- }
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
935
+ await handleMerchantValidation(event, session, config);
936
+ };
937
+ session.onpaymentauthorized = (event) => {
938
+ console.log("[web-apple-pay] onpaymentauthorized: invoked", {
939
+ skuId: config.skuId
881
940
  });
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();
941
+ trackWalletPaymentAuthorised(config.skuId);
942
+ config.onPaymentAuthorized(event, session);
943
+ };
944
+ session.oncancel = () => {
945
+ console.log("[web-apple-pay] oncancel: invoked", {
946
+ skuId: config.skuId
947
+ });
948
+ config.onCancel?.();
949
+ };
950
+ console.log("[web-apple-pay] startApplePaySession: opening payment sheet", {
951
+ skuId: config.skuId
952
+ });
953
+ trackWalletPaymentSheetOpen(config.skuId);
954
+ session.begin();
955
+ console.log("[web-apple-pay] startApplePaySession: session begin called");
956
+ } catch (err) {
957
+ console.error("[web-apple-pay] startApplePaySession: failed to start session", {
958
+ skuId: config.skuId,
959
+ err
960
+ });
961
+ redirectToFallbackCheckout(config);
962
+ }
893
963
  }
894
964
 
895
965
  // src/clients/payment-checkout/contact-validation.ts
@@ -978,13 +1048,25 @@ var billingContactDetails = (contact, supportedBillingCountries) => {
978
1048
  // src/clients/payment-checkout/shipping-contact-details.ts
979
1049
  var EMAIL_FORMAT_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
980
1050
  var shippingContactDetails = (shippingContact) => {
1051
+ console.log("[web-apple-pay] shippingContactDetails: start", {
1052
+ hasShippingContact: Boolean(shippingContact),
1053
+ hasEmailAddress: shippingContact?.emailAddress !== void 0
1054
+ });
981
1055
  const errors = [];
982
1056
  let emailAddress = "";
983
1057
  if (shippingContact?.emailAddress === void 0 || shippingContact?.emailAddress === "") {
1058
+ console.log(
1059
+ "[web-apple-pay] shippingContactDetails: email missing or empty"
1060
+ );
984
1061
  pushRequiredFieldError(errors, "shipping", "emailAddress");
985
1062
  } else {
1063
+ console.log("[web-apple-pay] shippingContactDetails: email provided");
986
1064
  emailAddress = shippingContact.emailAddress.trim();
1065
+ console.log("[web-apple-pay] shippingContactDetails: email trimmed", {
1066
+ emailLength: emailAddress.length
1067
+ });
987
1068
  if (emailAddress.length > 80) {
1069
+ console.log("[web-apple-pay] shippingContactDetails: email too long");
988
1070
  pushFieldError(
989
1071
  errors,
990
1072
  "shipping",
@@ -993,6 +1075,9 @@ var shippingContactDetails = (shippingContact) => {
993
1075
  MSG_EMAIL_TOO_LONG
994
1076
  );
995
1077
  } else if (!EMAIL_FORMAT_REGEX.test(emailAddress)) {
1078
+ console.log(
1079
+ "[web-apple-pay] shippingContactDetails: email format invalid"
1080
+ );
996
1081
  pushFieldError(
997
1082
  errors,
998
1083
  "shipping",
@@ -1000,11 +1085,17 @@ var shippingContactDetails = (shippingContact) => {
1000
1085
  "format",
1001
1086
  MSG_EMAIL_WRONG_FORMAT
1002
1087
  );
1088
+ } else {
1089
+ console.log("[web-apple-pay] shippingContactDetails: email valid");
1003
1090
  }
1004
1091
  }
1005
1092
  if (errors.length > 0) {
1093
+ console.log("[web-apple-pay] shippingContactDetails: throwing validation error", {
1094
+ errorCount: errors.length
1095
+ });
1006
1096
  throw new ContactValidationError(errors);
1007
1097
  }
1098
+ console.log("[web-apple-pay] shippingContactDetails: success");
1008
1099
  return { emailAddress };
1009
1100
  };
1010
1101
 
@@ -1066,12 +1157,16 @@ function handleStructuredError(ctx) {
1066
1157
  async function handlePaymentAuthorized(event, session, sessionData, config) {
1067
1158
  const { skuId, returnUrl, queryString } = config;
1068
1159
  let validatedEmail;
1160
+ console.log("[web-apple-pay] handlePaymentAuthorized: starting", { skuId, userLoggedIn: sessionData.loggedIn });
1069
1161
  try {
1162
+ console.log("[web-apple-pay] handlePaymentAuthorized: step 1 \u2014 validating contacts");
1070
1163
  validatedEmail = validateContacts(
1071
1164
  event,
1072
1165
  sessionData,
1073
1166
  config.supportedBillingCountries
1074
1167
  );
1168
+ console.log("[web-apple-pay] handlePaymentAuthorized: contacts valid", { validatedEmail });
1169
+ console.log("[web-apple-pay] handlePaymentAuthorized: step 2 \u2014 calling performPurchase", { skuId });
1075
1170
  const result = await performPurchase({
1076
1171
  applePayToken: event.payment.token,
1077
1172
  skuId: config.skuId,
@@ -1081,6 +1176,8 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1081
1176
  },
1082
1177
  recaptchaTokens: config.recaptchaTokens
1083
1178
  });
1179
+ console.log("[web-apple-pay] handlePaymentAuthorized: performPurchase succeeded", { basketId: result.basketId });
1180
+ console.log("[web-apple-pay] handlePaymentAuthorized: step 3 \u2014 completing payment and redirecting");
1084
1181
  trackWalletPaymentSuccess(skuId, result.basketId);
1085
1182
  session.completePayment({ status: ApplePaySession.STATUS_SUCCESS });
1086
1183
  const userStatus = sessionData.loggedIn ? "lex" : "nex";
@@ -1093,17 +1190,25 @@ async function handlePaymentAuthorized(event, session, sessionData, config) {
1093
1190
  });
1094
1191
  } catch (err) {
1095
1192
  if (err instanceof ContactValidationError) {
1193
+ console.warn("[web-apple-pay] handlePaymentAuthorized: contact validation failed", err.errors);
1096
1194
  failApplePaySession(session, err.errors);
1097
1195
  return;
1098
1196
  }
1099
1197
  console.error("[web-apple-pay] onpaymentauthorized error", { skuId, err });
1100
1198
  const walletError = parseWalletApiError(err);
1199
+ console.error("[web-apple-pay] handlePaymentAuthorized: BFF/network error", {
1200
+ skuId,
1201
+ errorType: walletError?.errorType ?? "UNKNOWN",
1202
+ message: walletError?.message
1203
+ });
1101
1204
  trackWalletPaymentFailure(skuId, walletError?.errorType ?? "UNKNOWN");
1102
1205
  failApplePaySession(session);
1103
1206
  if (!walletError) {
1207
+ console.warn("[web-apple-pay] handlePaymentAuthorized: unstructured error, using generic fallback");
1104
1208
  redirectFallback(skuId, returnUrl, { inlineError: MSG_PAYMENT_GENERIC_ERROR });
1105
1209
  return;
1106
1210
  }
1211
+ console.log("[web-apple-pay] handlePaymentAuthorized: dispatching structured error handler", { errorType: walletError.errorType });
1107
1212
  handleStructuredError({ skuId, returnUrl, validatedEmail, walletError });
1108
1213
  }
1109
1214
  }
@@ -1390,11 +1495,15 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1390
1495
  * Creates and configures an ApplePayButton if the feature flag is present.
1391
1496
  */
1392
1497
  static createIfEnabled(offer, country, entryPoint) {
1393
- if (!offer) return null;
1394
- if (APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant))
1498
+ if (!offer) {
1499
+ return null;
1500
+ }
1501
+ if (APPLE_PAY_EXCLUDED_VARIANTS.includes(offer.product?.variant)) {
1395
1502
  return null;
1396
- if (!new URLSearchParams(window.location.search).has(FEATURE_APPLE_PAY_EXPRESS_CHECKOUT))
1503
+ }
1504
+ if (!new URLSearchParams(window.location.search).has(FEATURE_APPLE_PAY_EXPRESS_CHECKOUT)) {
1397
1505
  return null;
1506
+ }
1398
1507
  _ApplePayButton.define();
1399
1508
  const button = document.createElement(_ApplePayButton.tag);
1400
1509
  button.offer = offer;
@@ -1421,6 +1530,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1421
1530
  #returnUrl = "";
1422
1531
  #supportedBillingCountries = [];
1423
1532
  #entryPoint = "";
1533
+ #recaptchaTokens = null;
1424
1534
  // ─── Public API ─────────────────────────────────────────────────────────────
1425
1535
  get offer() {
1426
1536
  return this.#offer;
@@ -1556,7 +1666,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1556
1666
  */
1557
1667
  async #fetchAndCacheOptions() {
1558
1668
  this.#paymentOptions = await this.#fetchPaymentOptions();
1559
- if (!this.#paymentOptions?.apple_pay) {
1669
+ if (!this.#paymentOptions?.options?.apple_pay) {
1560
1670
  this.style.display = "none";
1561
1671
  this.querySelector(SEL_CONTAINER)?.remove();
1562
1672
  return true;
@@ -1579,10 +1689,10 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1579
1689
  if (button.classList.contains("wap-loading")) return;
1580
1690
  this.#openModal();
1581
1691
  });
1582
- this.addEventListener("apple-pay-confirmed", (event) => {
1692
+ document.addEventListener("apple-pay-confirmed", (event) => {
1583
1693
  const { skuId, country } = event.detail;
1584
1694
  this.#initiateApplePay(skuId, country);
1585
- });
1695
+ }, { once: true });
1586
1696
  }
1587
1697
  #finishLoading() {
1588
1698
  const button = this.querySelector(SEL_BUTTON);
@@ -1609,8 +1719,17 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1609
1719
  button.setAttribute("aria-disabled", "true");
1610
1720
  }
1611
1721
  #openModal() {
1612
- if (!this.#offer || document.querySelector(ApplePayModal.tag)) return;
1722
+ if (!this.#offer) {
1723
+ return;
1724
+ }
1725
+ if (document.querySelector(ApplePayModal.tag)) {
1726
+ return;
1727
+ }
1613
1728
  trackExpressWalletClick(this.#offer.sku_id);
1729
+ getPurchaseRecaptchaTokens().then((tokens) => {
1730
+ this.#recaptchaTokens = tokens;
1731
+ }).catch(() => {
1732
+ });
1614
1733
  ApplePayModal.define();
1615
1734
  const modal = document.createElement(ApplePayModal.tag);
1616
1735
  modal.offer = this.#offer;
@@ -1645,7 +1764,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1645
1764
  return {
1646
1765
  countryCode: country,
1647
1766
  currencyCode: offer.price?.currency_code ?? DEFAULT_CURRENCY,
1648
- supportedNetworks: paymentOptions.apple_pay.settings.supported_networks,
1767
+ supportedNetworks: paymentOptions.options.apple_pay.settings.supported_networks,
1649
1768
  totalAmount,
1650
1769
  totalLabel: TOTAL_LABEL,
1651
1770
  skuId,
@@ -1665,7 +1784,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1665
1784
  try {
1666
1785
  const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
1667
1786
  const paymentOptions = this.#paymentOptions;
1668
- const recaptchaTokens = await getPurchaseRecaptchaTokens();
1787
+ const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
1669
1788
  startApplePaySession(
1670
1789
  this.#buildSessionParams(
1671
1790
  skuId,