@economist/web-apple-pay 0.1.1-beta.2 → 0.1.3-beta.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 CHANGED
@@ -411,6 +411,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
411
411
  detail: { skuId: offer.sku_id, country }
412
412
  })
413
413
  );
414
+ console.log("[web-apple-pay] ApplePayModal: dispatching apple-pay-confirmed-final", { skuId: offer.sku_id, country });
414
415
  });
415
416
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
416
417
  "click",
@@ -786,30 +787,45 @@ function startApplePaySession(config) {
786
787
  // Only request email from Apple Pay sheet if user is NOT logged in
787
788
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
788
789
  };
789
- const session = new ApplePaySession(3, paymentRequest);
790
- session.onvalidatemerchant = async (event) => {
791
- console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
792
- try {
793
- const validationHost = new URL(event.validationURL).host;
794
- if (!validationHost.endsWith("apple.com")) {
795
- console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
796
- trackFallbackCheckout(config.skuId);
797
- session.abort();
798
- window.location.href = redirectToCheckoutFallback({
799
- skuId: config.skuId,
800
- returnUrl: config.returnUrl,
801
- inlineError: MSG_PAYMENT_GENERIC_ERROR
790
+ try {
791
+ const session = new ApplePaySession(3, paymentRequest);
792
+ console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
793
+ session.onvalidatemerchant = async (event) => {
794
+ console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
795
+ try {
796
+ const validationHost = new URL(event.validationURL).host;
797
+ if (!validationHost.endsWith("apple.com")) {
798
+ console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
799
+ trackFallbackCheckout(config.skuId);
800
+ session.abort();
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
802
813
  });
803
- return;
804
- }
805
- const recaptchaToken = await getRecaptchaToken("verifyMerchant");
806
- console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
807
- const response = await validateAppleMerchant({
808
- validationURL: event.validationURL,
809
- recaptchaToken
810
- });
811
- if (!response?.merchantSession) {
812
- console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
814
+ if (!response?.merchantSession) {
815
+ console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
816
+ trackFallbackCheckout(config.skuId);
817
+ session.abort();
818
+ window.location.href = redirectToCheckoutFallback({
819
+ skuId: config.skuId,
820
+ returnUrl: config.returnUrl,
821
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
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);
813
829
  trackFallbackCheckout(config.skuId);
814
830
  session.abort();
815
831
  window.location.href = redirectToCheckoutFallback({
@@ -817,33 +833,29 @@ function startApplePaySession(config) {
817
833
  returnUrl: config.returnUrl,
818
834
  inlineError: MSG_PAYMENT_GENERIC_ERROR
819
835
  });
820
- return;
821
836
  }
822
- console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
823
- session.completeMerchantValidation(response.merchantSession);
824
- } catch (err) {
825
- console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
826
- trackFallbackCheckout(config.skuId);
827
- session.abort();
828
- window.location.href = redirectToCheckoutFallback({
829
- skuId: config.skuId,
830
- returnUrl: config.returnUrl,
831
- inlineError: MSG_PAYMENT_GENERIC_ERROR
832
- });
833
- }
834
- };
835
- session.onpaymentauthorized = (event) => {
836
- console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
837
- trackWalletPaymentAuthorised(config.skuId);
838
- config.onPaymentAuthorized(event, session);
839
- };
840
- session.oncancel = () => {
841
- console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
842
- config.onCancel?.();
843
- };
844
- console.log("[web-apple-pay] startApplePaySession: beginning session");
845
- trackWalletPaymentSheetOpen(config.skuId);
846
- session.begin();
837
+ };
838
+ session.onpaymentauthorized = (event) => {
839
+ console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
840
+ trackWalletPaymentAuthorised(config.skuId);
841
+ config.onPaymentAuthorized(event, session);
842
+ };
843
+ session.oncancel = () => {
844
+ console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
845
+ config.onCancel?.();
846
+ };
847
+ console.log("[web-apple-pay] startApplePaySession: beginning session");
848
+ trackWalletPaymentSheetOpen(config.skuId);
849
+ session.begin();
850
+ } catch (err) {
851
+ console.error("[web-apple-pay] startApplePaySession: failed to create or begin ApplePaySession", err);
852
+ trackFallbackCheckout(config.skuId);
853
+ window.location.href = redirectToCheckoutFallback({
854
+ skuId: config.skuId,
855
+ returnUrl: config.returnUrl,
856
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
857
+ });
858
+ }
847
859
  }
848
860
 
849
861
  // src/clients/payment-checkout/contact-validation.ts
@@ -1397,6 +1409,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1397
1409
  #returnUrl = "";
1398
1410
  #supportedBillingCountries = [];
1399
1411
  #entryPoint = "";
1412
+ #recaptchaTokens = null;
1400
1413
  // ─── Public API ─────────────────────────────────────────────────────────────
1401
1414
  get offer() {
1402
1415
  return this.#offer;
@@ -1457,6 +1470,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1457
1470
  * and eliminating the implicit reliance on the DOM as a render-state flag.
1458
1471
  */
1459
1472
  disconnectedCallback() {
1473
+ console.log("[web-apple-pay] disconnectedCallback: element removed from DOM, resetting state");
1460
1474
  this.#rendering = false;
1461
1475
  this.#readyPromise = null;
1462
1476
  this.#sessionData = null;
@@ -1565,10 +1579,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1565
1579
  button.addEventListener("click", () => {
1566
1580
  this.#openModal();
1567
1581
  });
1568
- this.addEventListener("apple-pay-confirmed", (event) => {
1582
+ document.addEventListener("apple-pay-confirmed", (event) => {
1569
1583
  const { skuId, country } = event.detail;
1584
+ console.log("[web-apple-pay] apple-pay-confirmed received on document", { skuId, country });
1570
1585
  this.#initiateApplePay(skuId, country);
1571
- });
1586
+ }, { once: true });
1572
1587
  }
1573
1588
  // ─── Private helpers ────────────────────────────────────────────────────────
1574
1589
  #updateDisabledState() {
@@ -1584,8 +1599,21 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1584
1599
  }
1585
1600
  #openModal() {
1586
1601
  console.log("[web-apple-pay] #openModal: button clicked", { skuId: this.#offer?.sku_id });
1587
- if (!this.#offer || document.querySelector(ApplePayModal.tag)) return;
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
+ }
1588
1610
  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
+ });
1589
1617
  ApplePayModal.define();
1590
1618
  const modal = document.createElement(ApplePayModal.tag);
1591
1619
  modal.offer = this.#offer;
@@ -1648,7 +1676,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1648
1676
  try {
1649
1677
  const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
1650
1678
  const paymentOptions = this.#paymentOptions;
1651
- const recaptchaTokens = await getPurchaseRecaptchaTokens();
1679
+ const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
1680
+ console.log("[web-apple-pay] #initiateApplePay: tokens ready, starting session", { prefetched: !!this.#recaptchaTokens });
1652
1681
  startApplePaySession(
1653
1682
  this.#buildSessionParams(
1654
1683
  skuId,
@@ -411,6 +411,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
411
411
  detail: { skuId: offer.sku_id, country }
412
412
  })
413
413
  );
414
+ console.log("[web-apple-pay] ApplePayModal: dispatching apple-pay-confirmed-final", { skuId: offer.sku_id, country });
414
415
  });
415
416
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
416
417
  "click",
@@ -775,30 +776,45 @@ function startApplePaySession(config) {
775
776
  // Only request email from Apple Pay sheet if user is NOT logged in
776
777
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
777
778
  };
778
- const session = new ApplePaySession(3, paymentRequest);
779
- session.onvalidatemerchant = async (event) => {
780
- console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
781
- try {
782
- const validationHost = new URL(event.validationURL).host;
783
- if (!validationHost.endsWith("apple.com")) {
784
- console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
785
- trackFallbackCheckout(config.skuId);
786
- session.abort();
787
- window.location.href = redirectToCheckoutFallback({
788
- skuId: config.skuId,
789
- returnUrl: config.returnUrl,
790
- inlineError: MSG_PAYMENT_GENERIC_ERROR
779
+ try {
780
+ const session = new ApplePaySession(3, paymentRequest);
781
+ console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
782
+ session.onvalidatemerchant = async (event) => {
783
+ console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
784
+ try {
785
+ const validationHost = new URL(event.validationURL).host;
786
+ if (!validationHost.endsWith("apple.com")) {
787
+ console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
788
+ trackFallbackCheckout(config.skuId);
789
+ session.abort();
790
+ window.location.href = redirectToCheckoutFallback({
791
+ skuId: config.skuId,
792
+ returnUrl: config.returnUrl,
793
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
794
+ });
795
+ return;
796
+ }
797
+ const recaptchaToken = await getRecaptchaToken("verifyMerchant");
798
+ console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
799
+ const response = await validateAppleMerchant({
800
+ validationURL: event.validationURL,
801
+ recaptchaToken
791
802
  });
792
- return;
793
- }
794
- const recaptchaToken = await getRecaptchaToken("verifyMerchant");
795
- console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
796
- const response = await validateAppleMerchant({
797
- validationURL: event.validationURL,
798
- recaptchaToken
799
- });
800
- if (!response?.merchantSession) {
801
- console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
803
+ if (!response?.merchantSession) {
804
+ console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
805
+ trackFallbackCheckout(config.skuId);
806
+ session.abort();
807
+ window.location.href = redirectToCheckoutFallback({
808
+ skuId: config.skuId,
809
+ returnUrl: config.returnUrl,
810
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
811
+ });
812
+ return;
813
+ }
814
+ console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
815
+ session.completeMerchantValidation(response.merchantSession);
816
+ } catch (err) {
817
+ console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
802
818
  trackFallbackCheckout(config.skuId);
803
819
  session.abort();
804
820
  window.location.href = redirectToCheckoutFallback({
@@ -806,33 +822,29 @@ function startApplePaySession(config) {
806
822
  returnUrl: config.returnUrl,
807
823
  inlineError: MSG_PAYMENT_GENERIC_ERROR
808
824
  });
809
- return;
810
825
  }
811
- console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
812
- session.completeMerchantValidation(response.merchantSession);
813
- } catch (err) {
814
- console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
815
- trackFallbackCheckout(config.skuId);
816
- session.abort();
817
- window.location.href = redirectToCheckoutFallback({
818
- skuId: config.skuId,
819
- returnUrl: config.returnUrl,
820
- inlineError: MSG_PAYMENT_GENERIC_ERROR
821
- });
822
- }
823
- };
824
- session.onpaymentauthorized = (event) => {
825
- console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
826
- trackWalletPaymentAuthorised(config.skuId);
827
- config.onPaymentAuthorized(event, session);
828
- };
829
- session.oncancel = () => {
830
- console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
831
- config.onCancel?.();
832
- };
833
- console.log("[web-apple-pay] startApplePaySession: beginning session");
834
- trackWalletPaymentSheetOpen(config.skuId);
835
- session.begin();
826
+ };
827
+ session.onpaymentauthorized = (event) => {
828
+ console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
829
+ trackWalletPaymentAuthorised(config.skuId);
830
+ config.onPaymentAuthorized(event, session);
831
+ };
832
+ session.oncancel = () => {
833
+ console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
834
+ config.onCancel?.();
835
+ };
836
+ console.log("[web-apple-pay] startApplePaySession: beginning session");
837
+ trackWalletPaymentSheetOpen(config.skuId);
838
+ session.begin();
839
+ } catch (err) {
840
+ console.error("[web-apple-pay] startApplePaySession: failed to create or begin ApplePaySession", err);
841
+ trackFallbackCheckout(config.skuId);
842
+ window.location.href = redirectToCheckoutFallback({
843
+ skuId: config.skuId,
844
+ returnUrl: config.returnUrl,
845
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
846
+ });
847
+ }
836
848
  }
837
849
 
838
850
  // src/clients/payment-checkout/contact-validation.ts
@@ -1386,6 +1398,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1386
1398
  #returnUrl = "";
1387
1399
  #supportedBillingCountries = [];
1388
1400
  #entryPoint = "";
1401
+ #recaptchaTokens = null;
1389
1402
  // ─── Public API ─────────────────────────────────────────────────────────────
1390
1403
  get offer() {
1391
1404
  return this.#offer;
@@ -1446,6 +1459,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1446
1459
  * and eliminating the implicit reliance on the DOM as a render-state flag.
1447
1460
  */
1448
1461
  disconnectedCallback() {
1462
+ console.log("[web-apple-pay] disconnectedCallback: element removed from DOM, resetting state");
1449
1463
  this.#rendering = false;
1450
1464
  this.#readyPromise = null;
1451
1465
  this.#sessionData = null;
@@ -1554,10 +1568,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1554
1568
  button.addEventListener("click", () => {
1555
1569
  this.#openModal();
1556
1570
  });
1557
- this.addEventListener("apple-pay-confirmed", (event) => {
1571
+ document.addEventListener("apple-pay-confirmed", (event) => {
1558
1572
  const { skuId, country } = event.detail;
1573
+ console.log("[web-apple-pay] apple-pay-confirmed received on document", { skuId, country });
1559
1574
  this.#initiateApplePay(skuId, country);
1560
- });
1575
+ }, { once: true });
1561
1576
  }
1562
1577
  // ─── Private helpers ────────────────────────────────────────────────────────
1563
1578
  #updateDisabledState() {
@@ -1573,8 +1588,21 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1573
1588
  }
1574
1589
  #openModal() {
1575
1590
  console.log("[web-apple-pay] #openModal: button clicked", { skuId: this.#offer?.sku_id });
1576
- if (!this.#offer || document.querySelector(ApplePayModal.tag)) return;
1591
+ if (!this.#offer) {
1592
+ console.warn("[web-apple-pay] #openModal: no offer set, ignoring click");
1593
+ return;
1594
+ }
1595
+ if (document.querySelector(ApplePayModal.tag)) {
1596
+ console.warn("[web-apple-pay] #openModal: modal already open, ignoring click");
1597
+ return;
1598
+ }
1577
1599
  trackExpressWalletClick(this.#offer.sku_id);
1600
+ getPurchaseRecaptchaTokens().then((tokens) => {
1601
+ console.log("[web-apple-pay] #openModal: reCAPTCHA tokens pre-fetched");
1602
+ this.#recaptchaTokens = tokens;
1603
+ }).catch((err) => {
1604
+ console.warn("[web-apple-pay] #openModal: reCAPTCHA pre-fetch failed", err);
1605
+ });
1578
1606
  ApplePayModal.define();
1579
1607
  const modal = document.createElement(ApplePayModal.tag);
1580
1608
  modal.offer = this.#offer;
@@ -1637,7 +1665,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1637
1665
  try {
1638
1666
  const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
1639
1667
  const paymentOptions = this.#paymentOptions;
1640
- const recaptchaTokens = await getPurchaseRecaptchaTokens();
1668
+ const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
1669
+ console.log("[web-apple-pay] #initiateApplePay: tokens ready, starting session", { prefetched: !!this.#recaptchaTokens });
1641
1670
  startApplePaySession(
1642
1671
  this.#buildSessionParams(
1643
1672
  skuId,
@@ -411,6 +411,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
411
411
  detail: { skuId: offer.sku_id, country }
412
412
  })
413
413
  );
414
+ console.log("[web-apple-pay] ApplePayModal: dispatching apple-pay-confirmed-final", { skuId: offer.sku_id, country });
414
415
  });
415
416
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
416
417
  "click",
@@ -790,30 +791,45 @@ function startApplePaySession(config) {
790
791
  // Only request email from Apple Pay sheet if user is NOT logged in
791
792
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
792
793
  };
793
- const session = new ApplePaySession(3, paymentRequest);
794
- session.onvalidatemerchant = async (event) => {
795
- console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
796
- try {
797
- const validationHost = new URL(event.validationURL).host;
798
- if (!validationHost.endsWith("apple.com")) {
799
- console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
800
- trackFallbackCheckout(config.skuId);
801
- session.abort();
802
- window.location.href = redirectToCheckoutFallback({
803
- skuId: config.skuId,
804
- returnUrl: config.returnUrl,
805
- inlineError: MSG_PAYMENT_GENERIC_ERROR
794
+ try {
795
+ const session = new ApplePaySession(3, paymentRequest);
796
+ console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
797
+ session.onvalidatemerchant = async (event) => {
798
+ console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
799
+ try {
800
+ const validationHost = new URL(event.validationURL).host;
801
+ if (!validationHost.endsWith("apple.com")) {
802
+ console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
803
+ trackFallbackCheckout(config.skuId);
804
+ session.abort();
805
+ window.location.href = redirectToCheckoutFallback({
806
+ skuId: config.skuId,
807
+ returnUrl: config.returnUrl,
808
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
809
+ });
810
+ return;
811
+ }
812
+ const recaptchaToken = await getRecaptchaToken("verifyMerchant");
813
+ console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
814
+ const response = await validateAppleMerchant({
815
+ validationURL: event.validationURL,
816
+ recaptchaToken
806
817
  });
807
- return;
808
- }
809
- const recaptchaToken = await getRecaptchaToken("verifyMerchant");
810
- console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
811
- const response = await validateAppleMerchant({
812
- validationURL: event.validationURL,
813
- recaptchaToken
814
- });
815
- if (!response?.merchantSession) {
816
- console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
818
+ if (!response?.merchantSession) {
819
+ console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
820
+ trackFallbackCheckout(config.skuId);
821
+ session.abort();
822
+ window.location.href = redirectToCheckoutFallback({
823
+ skuId: config.skuId,
824
+ returnUrl: config.returnUrl,
825
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
826
+ });
827
+ return;
828
+ }
829
+ console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
830
+ session.completeMerchantValidation(response.merchantSession);
831
+ } catch (err) {
832
+ console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
817
833
  trackFallbackCheckout(config.skuId);
818
834
  session.abort();
819
835
  window.location.href = redirectToCheckoutFallback({
@@ -821,33 +837,29 @@ function startApplePaySession(config) {
821
837
  returnUrl: config.returnUrl,
822
838
  inlineError: MSG_PAYMENT_GENERIC_ERROR
823
839
  });
824
- return;
825
840
  }
826
- console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
827
- session.completeMerchantValidation(response.merchantSession);
828
- } catch (err) {
829
- console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
830
- trackFallbackCheckout(config.skuId);
831
- session.abort();
832
- window.location.href = redirectToCheckoutFallback({
833
- skuId: config.skuId,
834
- returnUrl: config.returnUrl,
835
- inlineError: MSG_PAYMENT_GENERIC_ERROR
836
- });
837
- }
838
- };
839
- session.onpaymentauthorized = (event) => {
840
- console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
841
- trackWalletPaymentAuthorised(config.skuId);
842
- config.onPaymentAuthorized(event, session);
843
- };
844
- session.oncancel = () => {
845
- console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
846
- config.onCancel?.();
847
- };
848
- console.log("[web-apple-pay] startApplePaySession: beginning session");
849
- trackWalletPaymentSheetOpen(config.skuId);
850
- session.begin();
841
+ };
842
+ session.onpaymentauthorized = (event) => {
843
+ console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
844
+ trackWalletPaymentAuthorised(config.skuId);
845
+ config.onPaymentAuthorized(event, session);
846
+ };
847
+ session.oncancel = () => {
848
+ console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
849
+ config.onCancel?.();
850
+ };
851
+ console.log("[web-apple-pay] startApplePaySession: beginning session");
852
+ trackWalletPaymentSheetOpen(config.skuId);
853
+ session.begin();
854
+ } catch (err) {
855
+ console.error("[web-apple-pay] startApplePaySession: failed to create or begin ApplePaySession", err);
856
+ trackFallbackCheckout(config.skuId);
857
+ window.location.href = redirectToCheckoutFallback({
858
+ skuId: config.skuId,
859
+ returnUrl: config.returnUrl,
860
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
861
+ });
862
+ }
851
863
  }
852
864
 
853
865
  // src/clients/payment-checkout/contact-validation.ts
@@ -1401,6 +1413,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1401
1413
  #returnUrl = "";
1402
1414
  #supportedBillingCountries = [];
1403
1415
  #entryPoint = "";
1416
+ #recaptchaTokens = null;
1404
1417
  // ─── Public API ─────────────────────────────────────────────────────────────
1405
1418
  get offer() {
1406
1419
  return this.#offer;
@@ -1461,6 +1474,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1461
1474
  * and eliminating the implicit reliance on the DOM as a render-state flag.
1462
1475
  */
1463
1476
  disconnectedCallback() {
1477
+ console.log("[web-apple-pay] disconnectedCallback: element removed from DOM, resetting state");
1464
1478
  this.#rendering = false;
1465
1479
  this.#readyPromise = null;
1466
1480
  this.#sessionData = null;
@@ -1569,10 +1583,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1569
1583
  button.addEventListener("click", () => {
1570
1584
  this.#openModal();
1571
1585
  });
1572
- this.addEventListener("apple-pay-confirmed", (event) => {
1586
+ document.addEventListener("apple-pay-confirmed", (event) => {
1573
1587
  const { skuId, country } = event.detail;
1588
+ console.log("[web-apple-pay] apple-pay-confirmed received on document", { skuId, country });
1574
1589
  this.#initiateApplePay(skuId, country);
1575
- });
1590
+ }, { once: true });
1576
1591
  }
1577
1592
  // ─── Private helpers ────────────────────────────────────────────────────────
1578
1593
  #updateDisabledState() {
@@ -1588,8 +1603,21 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1588
1603
  }
1589
1604
  #openModal() {
1590
1605
  console.log("[web-apple-pay] #openModal: button clicked", { skuId: this.#offer?.sku_id });
1591
- if (!this.#offer || document.querySelector(ApplePayModal.tag)) return;
1606
+ if (!this.#offer) {
1607
+ console.warn("[web-apple-pay] #openModal: no offer set, ignoring click");
1608
+ return;
1609
+ }
1610
+ if (document.querySelector(ApplePayModal.tag)) {
1611
+ console.warn("[web-apple-pay] #openModal: modal already open, ignoring click");
1612
+ return;
1613
+ }
1592
1614
  trackExpressWalletClick(this.#offer.sku_id);
1615
+ getPurchaseRecaptchaTokens().then((tokens) => {
1616
+ console.log("[web-apple-pay] #openModal: reCAPTCHA tokens pre-fetched");
1617
+ this.#recaptchaTokens = tokens;
1618
+ }).catch((err) => {
1619
+ console.warn("[web-apple-pay] #openModal: reCAPTCHA pre-fetch failed", err);
1620
+ });
1593
1621
  ApplePayModal.define();
1594
1622
  const modal = document.createElement(ApplePayModal.tag);
1595
1623
  modal.offer = this.#offer;
@@ -1652,7 +1680,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1652
1680
  try {
1653
1681
  const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
1654
1682
  const paymentOptions = this.#paymentOptions;
1655
- const recaptchaTokens = await getPurchaseRecaptchaTokens();
1683
+ const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
1684
+ console.log("[web-apple-pay] #initiateApplePay: tokens ready, starting session", { prefetched: !!this.#recaptchaTokens });
1656
1685
  startApplePaySession(
1657
1686
  this.#buildSessionParams(
1658
1687
  skuId,
@@ -411,6 +411,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
411
411
  detail: { skuId: offer.sku_id, country }
412
412
  })
413
413
  );
414
+ console.log("[web-apple-pay] ApplePayModal: dispatching apple-pay-confirmed-final", { skuId: offer.sku_id, country });
414
415
  });
415
416
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
416
417
  "click",
@@ -411,6 +411,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
411
411
  detail: { skuId: offer.sku_id, country }
412
412
  })
413
413
  );
414
+ console.log("[web-apple-pay] ApplePayModal: dispatching apple-pay-confirmed-final", { skuId: offer.sku_id, country });
414
415
  });
415
416
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
416
417
  "click",
@@ -775,30 +776,45 @@ function startApplePaySession(config) {
775
776
  // Only request email from Apple Pay sheet if user is NOT logged in
776
777
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
777
778
  };
778
- const session = new ApplePaySession(3, paymentRequest);
779
- session.onvalidatemerchant = async (event) => {
780
- console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
781
- try {
782
- const validationHost = new URL(event.validationURL).host;
783
- if (!validationHost.endsWith("apple.com")) {
784
- console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
785
- trackFallbackCheckout(config.skuId);
786
- session.abort();
787
- window.location.href = redirectToCheckoutFallback({
788
- skuId: config.skuId,
789
- returnUrl: config.returnUrl,
790
- inlineError: MSG_PAYMENT_GENERIC_ERROR
779
+ try {
780
+ const session = new ApplePaySession(3, paymentRequest);
781
+ console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
782
+ session.onvalidatemerchant = async (event) => {
783
+ console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
784
+ try {
785
+ const validationHost = new URL(event.validationURL).host;
786
+ if (!validationHost.endsWith("apple.com")) {
787
+ console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
788
+ trackFallbackCheckout(config.skuId);
789
+ session.abort();
790
+ window.location.href = redirectToCheckoutFallback({
791
+ skuId: config.skuId,
792
+ returnUrl: config.returnUrl,
793
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
794
+ });
795
+ return;
796
+ }
797
+ const recaptchaToken = await getRecaptchaToken("verifyMerchant");
798
+ console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
799
+ const response = await validateAppleMerchant({
800
+ validationURL: event.validationURL,
801
+ recaptchaToken
791
802
  });
792
- return;
793
- }
794
- const recaptchaToken = await getRecaptchaToken("verifyMerchant");
795
- console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
796
- const response = await validateAppleMerchant({
797
- validationURL: event.validationURL,
798
- recaptchaToken
799
- });
800
- if (!response?.merchantSession) {
801
- console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
803
+ if (!response?.merchantSession) {
804
+ console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
805
+ trackFallbackCheckout(config.skuId);
806
+ session.abort();
807
+ window.location.href = redirectToCheckoutFallback({
808
+ skuId: config.skuId,
809
+ returnUrl: config.returnUrl,
810
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
811
+ });
812
+ return;
813
+ }
814
+ console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
815
+ session.completeMerchantValidation(response.merchantSession);
816
+ } catch (err) {
817
+ console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
802
818
  trackFallbackCheckout(config.skuId);
803
819
  session.abort();
804
820
  window.location.href = redirectToCheckoutFallback({
@@ -806,33 +822,29 @@ function startApplePaySession(config) {
806
822
  returnUrl: config.returnUrl,
807
823
  inlineError: MSG_PAYMENT_GENERIC_ERROR
808
824
  });
809
- return;
810
825
  }
811
- console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
812
- session.completeMerchantValidation(response.merchantSession);
813
- } catch (err) {
814
- console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
815
- trackFallbackCheckout(config.skuId);
816
- session.abort();
817
- window.location.href = redirectToCheckoutFallback({
818
- skuId: config.skuId,
819
- returnUrl: config.returnUrl,
820
- inlineError: MSG_PAYMENT_GENERIC_ERROR
821
- });
822
- }
823
- };
824
- session.onpaymentauthorized = (event) => {
825
- console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
826
- trackWalletPaymentAuthorised(config.skuId);
827
- config.onPaymentAuthorized(event, session);
828
- };
829
- session.oncancel = () => {
830
- console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
831
- config.onCancel?.();
832
- };
833
- console.log("[web-apple-pay] startApplePaySession: beginning session");
834
- trackWalletPaymentSheetOpen(config.skuId);
835
- session.begin();
826
+ };
827
+ session.onpaymentauthorized = (event) => {
828
+ console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
829
+ trackWalletPaymentAuthorised(config.skuId);
830
+ config.onPaymentAuthorized(event, session);
831
+ };
832
+ session.oncancel = () => {
833
+ console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
834
+ config.onCancel?.();
835
+ };
836
+ console.log("[web-apple-pay] startApplePaySession: beginning session");
837
+ trackWalletPaymentSheetOpen(config.skuId);
838
+ session.begin();
839
+ } catch (err) {
840
+ console.error("[web-apple-pay] startApplePaySession: failed to create or begin ApplePaySession", err);
841
+ trackFallbackCheckout(config.skuId);
842
+ window.location.href = redirectToCheckoutFallback({
843
+ skuId: config.skuId,
844
+ returnUrl: config.returnUrl,
845
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
846
+ });
847
+ }
836
848
  }
837
849
 
838
850
  // src/clients/payment-checkout/contact-validation.ts
@@ -1386,6 +1398,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1386
1398
  #returnUrl = "";
1387
1399
  #supportedBillingCountries = [];
1388
1400
  #entryPoint = "";
1401
+ #recaptchaTokens = null;
1389
1402
  // ─── Public API ─────────────────────────────────────────────────────────────
1390
1403
  get offer() {
1391
1404
  return this.#offer;
@@ -1446,6 +1459,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1446
1459
  * and eliminating the implicit reliance on the DOM as a render-state flag.
1447
1460
  */
1448
1461
  disconnectedCallback() {
1462
+ console.log("[web-apple-pay] disconnectedCallback: element removed from DOM, resetting state");
1449
1463
  this.#rendering = false;
1450
1464
  this.#readyPromise = null;
1451
1465
  this.#sessionData = null;
@@ -1554,10 +1568,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1554
1568
  button.addEventListener("click", () => {
1555
1569
  this.#openModal();
1556
1570
  });
1557
- this.addEventListener("apple-pay-confirmed", (event) => {
1571
+ document.addEventListener("apple-pay-confirmed", (event) => {
1558
1572
  const { skuId, country } = event.detail;
1573
+ console.log("[web-apple-pay] apple-pay-confirmed received on document", { skuId, country });
1559
1574
  this.#initiateApplePay(skuId, country);
1560
- });
1575
+ }, { once: true });
1561
1576
  }
1562
1577
  // ─── Private helpers ────────────────────────────────────────────────────────
1563
1578
  #updateDisabledState() {
@@ -1573,8 +1588,21 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1573
1588
  }
1574
1589
  #openModal() {
1575
1590
  console.log("[web-apple-pay] #openModal: button clicked", { skuId: this.#offer?.sku_id });
1576
- if (!this.#offer || document.querySelector(ApplePayModal.tag)) return;
1591
+ if (!this.#offer) {
1592
+ console.warn("[web-apple-pay] #openModal: no offer set, ignoring click");
1593
+ return;
1594
+ }
1595
+ if (document.querySelector(ApplePayModal.tag)) {
1596
+ console.warn("[web-apple-pay] #openModal: modal already open, ignoring click");
1597
+ return;
1598
+ }
1577
1599
  trackExpressWalletClick(this.#offer.sku_id);
1600
+ getPurchaseRecaptchaTokens().then((tokens) => {
1601
+ console.log("[web-apple-pay] #openModal: reCAPTCHA tokens pre-fetched");
1602
+ this.#recaptchaTokens = tokens;
1603
+ }).catch((err) => {
1604
+ console.warn("[web-apple-pay] #openModal: reCAPTCHA pre-fetch failed", err);
1605
+ });
1578
1606
  ApplePayModal.define();
1579
1607
  const modal = document.createElement(ApplePayModal.tag);
1580
1608
  modal.offer = this.#offer;
@@ -1637,7 +1665,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1637
1665
  try {
1638
1666
  const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
1639
1667
  const paymentOptions = this.#paymentOptions;
1640
- const recaptchaTokens = await getPurchaseRecaptchaTokens();
1668
+ const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
1669
+ console.log("[web-apple-pay] #initiateApplePay: tokens ready, starting session", { prefetched: !!this.#recaptchaTokens });
1641
1670
  startApplePaySession(
1642
1671
  this.#buildSessionParams(
1643
1672
  skuId,
@@ -179,30 +179,45 @@ function startApplePaySession(config) {
179
179
  // Only request email from Apple Pay sheet if user is NOT logged in
180
180
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
181
181
  };
182
- const session = new ApplePaySession(3, paymentRequest);
183
- session.onvalidatemerchant = async (event) => {
184
- console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
185
- try {
186
- const validationHost = new URL(event.validationURL).host;
187
- if (!validationHost.endsWith("apple.com")) {
188
- console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
189
- trackFallbackCheckout(config.skuId);
190
- session.abort();
191
- window.location.href = redirectToCheckoutFallback({
192
- skuId: config.skuId,
193
- returnUrl: config.returnUrl,
194
- inlineError: MSG_PAYMENT_GENERIC_ERROR
182
+ try {
183
+ const session = new ApplePaySession(3, paymentRequest);
184
+ console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
185
+ session.onvalidatemerchant = async (event) => {
186
+ console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
187
+ try {
188
+ const validationHost = new URL(event.validationURL).host;
189
+ if (!validationHost.endsWith("apple.com")) {
190
+ console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
191
+ trackFallbackCheckout(config.skuId);
192
+ session.abort();
193
+ window.location.href = redirectToCheckoutFallback({
194
+ skuId: config.skuId,
195
+ returnUrl: config.returnUrl,
196
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
197
+ });
198
+ return;
199
+ }
200
+ const recaptchaToken = await getRecaptchaToken("verifyMerchant");
201
+ console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
202
+ const response = await validateAppleMerchant({
203
+ validationURL: event.validationURL,
204
+ recaptchaToken
195
205
  });
196
- return;
197
- }
198
- const recaptchaToken = await getRecaptchaToken("verifyMerchant");
199
- console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
200
- const response = await validateAppleMerchant({
201
- validationURL: event.validationURL,
202
- recaptchaToken
203
- });
204
- if (!response?.merchantSession) {
205
- console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
206
+ if (!response?.merchantSession) {
207
+ console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
208
+ trackFallbackCheckout(config.skuId);
209
+ session.abort();
210
+ window.location.href = redirectToCheckoutFallback({
211
+ skuId: config.skuId,
212
+ returnUrl: config.returnUrl,
213
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
214
+ });
215
+ return;
216
+ }
217
+ console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
218
+ session.completeMerchantValidation(response.merchantSession);
219
+ } catch (err) {
220
+ console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
206
221
  trackFallbackCheckout(config.skuId);
207
222
  session.abort();
208
223
  window.location.href = redirectToCheckoutFallback({
@@ -210,33 +225,29 @@ function startApplePaySession(config) {
210
225
  returnUrl: config.returnUrl,
211
226
  inlineError: MSG_PAYMENT_GENERIC_ERROR
212
227
  });
213
- return;
214
228
  }
215
- console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
216
- session.completeMerchantValidation(response.merchantSession);
217
- } catch (err) {
218
- console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
219
- trackFallbackCheckout(config.skuId);
220
- session.abort();
221
- window.location.href = redirectToCheckoutFallback({
222
- skuId: config.skuId,
223
- returnUrl: config.returnUrl,
224
- inlineError: MSG_PAYMENT_GENERIC_ERROR
225
- });
226
- }
227
- };
228
- session.onpaymentauthorized = (event) => {
229
- console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
230
- trackWalletPaymentAuthorised(config.skuId);
231
- config.onPaymentAuthorized(event, session);
232
- };
233
- session.oncancel = () => {
234
- console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
235
- config.onCancel?.();
236
- };
237
- console.log("[web-apple-pay] startApplePaySession: beginning session");
238
- trackWalletPaymentSheetOpen(config.skuId);
239
- session.begin();
229
+ };
230
+ session.onpaymentauthorized = (event) => {
231
+ console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
232
+ trackWalletPaymentAuthorised(config.skuId);
233
+ config.onPaymentAuthorized(event, session);
234
+ };
235
+ session.oncancel = () => {
236
+ console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
237
+ config.onCancel?.();
238
+ };
239
+ console.log("[web-apple-pay] startApplePaySession: beginning session");
240
+ trackWalletPaymentSheetOpen(config.skuId);
241
+ session.begin();
242
+ } catch (err) {
243
+ console.error("[web-apple-pay] startApplePaySession: failed to create or begin ApplePaySession", err);
244
+ trackFallbackCheckout(config.skuId);
245
+ window.location.href = redirectToCheckoutFallback({
246
+ skuId: config.skuId,
247
+ returnUrl: config.returnUrl,
248
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
249
+ });
250
+ }
240
251
  }
241
252
  export {
242
253
  startApplePaySession
package/dist/src/index.js CHANGED
@@ -411,6 +411,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
411
411
  detail: { skuId: offer.sku_id, country }
412
412
  })
413
413
  );
414
+ console.log("[web-apple-pay] ApplePayModal: dispatching apple-pay-confirmed-final", { skuId: offer.sku_id, country });
414
415
  });
415
416
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
416
417
  "click",
@@ -786,30 +787,45 @@ function startApplePaySession(config) {
786
787
  // Only request email from Apple Pay sheet if user is NOT logged in
787
788
  ...!config.userLoggedIn && { requiredShippingContactFields: ["email"] }
788
789
  };
789
- const session = new ApplePaySession(3, paymentRequest);
790
- session.onvalidatemerchant = async (event) => {
791
- console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
792
- try {
793
- const validationHost = new URL(event.validationURL).host;
794
- if (!validationHost.endsWith("apple.com")) {
795
- console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
796
- trackFallbackCheckout(config.skuId);
797
- session.abort();
798
- window.location.href = redirectToCheckoutFallback({
799
- skuId: config.skuId,
800
- returnUrl: config.returnUrl,
801
- inlineError: MSG_PAYMENT_GENERIC_ERROR
790
+ try {
791
+ const session = new ApplePaySession(3, paymentRequest);
792
+ console.log("[web-apple-pay] startApplePaySession: ApplePaySession created");
793
+ session.onvalidatemerchant = async (event) => {
794
+ console.log("[web-apple-pay] onvalidatemerchant: received", { validationURL: event.validationURL });
795
+ try {
796
+ const validationHost = new URL(event.validationURL).host;
797
+ if (!validationHost.endsWith("apple.com")) {
798
+ console.warn("[web-apple-pay] onvalidatemerchant: invalid host, aborting", { validationHost });
799
+ trackFallbackCheckout(config.skuId);
800
+ session.abort();
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
802
813
  });
803
- return;
804
- }
805
- const recaptchaToken = await getRecaptchaToken("verifyMerchant");
806
- console.log("[web-apple-pay] onvalidatemerchant: calling validateAppleMerchant");
807
- const response = await validateAppleMerchant({
808
- validationURL: event.validationURL,
809
- recaptchaToken
810
- });
811
- if (!response?.merchantSession) {
812
- console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
814
+ if (!response?.merchantSession) {
815
+ console.warn("[web-apple-pay] onvalidatemerchant: no merchantSession in response, aborting");
816
+ trackFallbackCheckout(config.skuId);
817
+ session.abort();
818
+ window.location.href = redirectToCheckoutFallback({
819
+ skuId: config.skuId,
820
+ returnUrl: config.returnUrl,
821
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
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);
813
829
  trackFallbackCheckout(config.skuId);
814
830
  session.abort();
815
831
  window.location.href = redirectToCheckoutFallback({
@@ -817,33 +833,29 @@ function startApplePaySession(config) {
817
833
  returnUrl: config.returnUrl,
818
834
  inlineError: MSG_PAYMENT_GENERIC_ERROR
819
835
  });
820
- return;
821
836
  }
822
- console.log("[web-apple-pay] onvalidatemerchant: merchant validated successfully");
823
- session.completeMerchantValidation(response.merchantSession);
824
- } catch (err) {
825
- console.error("[web-apple-pay] onvalidatemerchant: error during validation", err);
826
- trackFallbackCheckout(config.skuId);
827
- session.abort();
828
- window.location.href = redirectToCheckoutFallback({
829
- skuId: config.skuId,
830
- returnUrl: config.returnUrl,
831
- inlineError: MSG_PAYMENT_GENERIC_ERROR
832
- });
833
- }
834
- };
835
- session.onpaymentauthorized = (event) => {
836
- console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
837
- trackWalletPaymentAuthorised(config.skuId);
838
- config.onPaymentAuthorized(event, session);
839
- };
840
- session.oncancel = () => {
841
- console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
842
- config.onCancel?.();
843
- };
844
- console.log("[web-apple-pay] startApplePaySession: beginning session");
845
- trackWalletPaymentSheetOpen(config.skuId);
846
- session.begin();
837
+ };
838
+ session.onpaymentauthorized = (event) => {
839
+ console.log("[web-apple-pay] onpaymentauthorized: payment authorized by user", { skuId: config.skuId });
840
+ trackWalletPaymentAuthorised(config.skuId);
841
+ config.onPaymentAuthorized(event, session);
842
+ };
843
+ session.oncancel = () => {
844
+ console.log("[web-apple-pay] oncancel: payment sheet cancelled by user", { skuId: config.skuId });
845
+ config.onCancel?.();
846
+ };
847
+ console.log("[web-apple-pay] startApplePaySession: beginning session");
848
+ trackWalletPaymentSheetOpen(config.skuId);
849
+ session.begin();
850
+ } catch (err) {
851
+ console.error("[web-apple-pay] startApplePaySession: failed to create or begin ApplePaySession", err);
852
+ trackFallbackCheckout(config.skuId);
853
+ window.location.href = redirectToCheckoutFallback({
854
+ skuId: config.skuId,
855
+ returnUrl: config.returnUrl,
856
+ inlineError: MSG_PAYMENT_GENERIC_ERROR
857
+ });
858
+ }
847
859
  }
848
860
 
849
861
  // src/clients/payment-checkout/contact-validation.ts
@@ -1397,6 +1409,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1397
1409
  #returnUrl = "";
1398
1410
  #supportedBillingCountries = [];
1399
1411
  #entryPoint = "";
1412
+ #recaptchaTokens = null;
1400
1413
  // ─── Public API ─────────────────────────────────────────────────────────────
1401
1414
  get offer() {
1402
1415
  return this.#offer;
@@ -1457,6 +1470,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1457
1470
  * and eliminating the implicit reliance on the DOM as a render-state flag.
1458
1471
  */
1459
1472
  disconnectedCallback() {
1473
+ console.log("[web-apple-pay] disconnectedCallback: element removed from DOM, resetting state");
1460
1474
  this.#rendering = false;
1461
1475
  this.#readyPromise = null;
1462
1476
  this.#sessionData = null;
@@ -1565,10 +1579,11 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1565
1579
  button.addEventListener("click", () => {
1566
1580
  this.#openModal();
1567
1581
  });
1568
- this.addEventListener("apple-pay-confirmed", (event) => {
1582
+ document.addEventListener("apple-pay-confirmed", (event) => {
1569
1583
  const { skuId, country } = event.detail;
1584
+ console.log("[web-apple-pay] apple-pay-confirmed received on document", { skuId, country });
1570
1585
  this.#initiateApplePay(skuId, country);
1571
- });
1586
+ }, { once: true });
1572
1587
  }
1573
1588
  // ─── Private helpers ────────────────────────────────────────────────────────
1574
1589
  #updateDisabledState() {
@@ -1584,8 +1599,21 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1584
1599
  }
1585
1600
  #openModal() {
1586
1601
  console.log("[web-apple-pay] #openModal: button clicked", { skuId: this.#offer?.sku_id });
1587
- if (!this.#offer || document.querySelector(ApplePayModal.tag)) return;
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
+ }
1588
1610
  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
+ });
1589
1617
  ApplePayModal.define();
1590
1618
  const modal = document.createElement(ApplePayModal.tag);
1591
1619
  modal.offer = this.#offer;
@@ -1648,7 +1676,8 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
1648
1676
  try {
1649
1677
  const sessionData = this.#sessionData ?? _ApplePayButton.#ANONYMOUS_SESSION;
1650
1678
  const paymentOptions = this.#paymentOptions;
1651
- const recaptchaTokens = await getPurchaseRecaptchaTokens();
1679
+ const recaptchaTokens = this.#recaptchaTokens ?? await getPurchaseRecaptchaTokens();
1680
+ console.log("[web-apple-pay] #initiateApplePay: tokens ready, starting session", { prefetched: !!this.#recaptchaTokens });
1652
1681
  startApplePaySession(
1653
1682
  this.#buildSessionParams(
1654
1683
  skuId,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@economist/web-apple-pay",
3
3
  "description": "Web component package for Apple Pay checkout UI",
4
- "version": "0.1.1-beta.2",
4
+ "version": "0.1.3-beta.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",