@economist/web-apple-pay 0.1.3-beta.42 → 0.1.3-beta.43

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
@@ -835,17 +835,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
835
835
  #country = "";
836
836
  #triggerElement = null;
837
837
  #keyHandler = null;
838
- #recaptchaTokens = null;
839
- #recaptchaTokensPromise = null;
838
+ #isClosed = false;
840
839
  /**
841
- * Synchronous callback invoked directly from the CTA click handler once the
842
- * user confirms. The caller (ApplePayButton) must call startApplePaySession
843
- * inside this callback to preserve the browser's user-gesture chain — Safari
844
- * requires new ApplePaySession() to execute within the original click stack.
845
- *
846
- * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
847
- * until the token pre-fetch resolves, so this callback is never reached
848
- * without valid tokens.
840
+ * Callback invoked from the CTA click handler once the user confirms.
841
+ * The caller (ApplePayButton) must call startApplePaySession inside this
842
+ * callback to preserve the browser's user-gesture chain — Safari requires
843
+ * new ApplePaySession() to execute within the original click stack.
849
844
  */
850
845
  onConfirm = null;
851
846
  set offer(value) {
@@ -942,28 +937,29 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
942
937
  }
943
938
  });
944
939
  }
945
- if (this.#recaptchaTokensPromise) {
946
- if (cta) {
947
- cta.setAttribute("disabled", "");
948
- cta.setAttribute("aria-disabled", "true");
949
- cta.setAttribute("aria-busy", "true");
940
+ cta?.addEventListener("click", () => {
941
+ if (cta.disabled) return;
942
+ if (isUS && checkbox && !checkbox.checked) {
943
+ errorElement?.classList.add("apm__error--visible");
944
+ checkboxLabel?.classList.add("apm__checkbox-label--error");
945
+ return;
950
946
  }
951
- this.#recaptchaTokensPromise.then((tokens) => {
952
- this.#recaptchaTokens = tokens;
953
- if (cta) {
954
- cta.removeAttribute("disabled");
955
- cta.removeAttribute("aria-disabled");
956
- cta.removeAttribute("aria-busy");
957
- }
947
+ errorElement?.classList.remove("apm__error--visible");
948
+ cta.disabled = true;
949
+ cta.setAttribute("aria-busy", "true");
950
+ getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
951
+ if (this.#isClosed) return;
952
+ this.onConfirm?.(recaptchaTokens);
953
+ this.close();
958
954
  }).catch(() => {
959
- if (cta) {
960
- cta.removeAttribute("aria-busy");
961
- }
955
+ cta.disabled = false;
956
+ cta.removeAttribute("aria-busy");
962
957
  const existingError = this.querySelector("#apple-pay-modal-error");
963
958
  if (existingError) {
964
959
  const icon = existingError.querySelector("svg");
965
960
  existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
966
961
  if (icon) existingError.prepend(icon);
962
+ existingError.setAttribute("role", "alert");
967
963
  existingError.classList.add("apm__error--visible");
968
964
  } else {
969
965
  const errorNotice = document.createElement("div");
@@ -973,25 +969,13 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
973
969
  cta?.insertAdjacentElement("beforebegin", errorNotice);
974
970
  }
975
971
  });
976
- }
977
- cta?.addEventListener("click", () => {
978
- if (isUS && checkbox && !checkbox.checked) {
979
- errorElement?.classList.add("apm__error--visible");
980
- checkboxLabel?.classList.add("apm__checkbox-label--error");
981
- return;
982
- }
983
- if (this.#recaptchaTokens) {
984
- this.onConfirm?.(this.#recaptchaTokens);
985
- this.close();
986
- }
987
972
  });
988
973
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
989
974
  "click",
990
975
  () => this.close()
991
976
  );
992
977
  }
993
- open(recaptchaTokensPromise) {
994
- this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
978
+ open() {
995
979
  this.#triggerElement = document.activeElement;
996
980
  document.body.appendChild(this);
997
981
  this.#keyHandler = (event) => {
@@ -1006,6 +990,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
1006
990
  this.querySelector("#apple-pay-modal")?.focus();
1007
991
  }
1008
992
  close() {
993
+ this.#isClosed = true;
1009
994
  if (this.#keyHandler) {
1010
995
  document.removeEventListener("keydown", this.#keyHandler, {
1011
996
  capture: true
@@ -2174,8 +2159,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2174
2159
  modal.onConfirm = (recaptchaTokens) => {
2175
2160
  this.#initiateApplePay(skuId, country, recaptchaTokens);
2176
2161
  };
2177
- const recaptchaTokensPromise = getPurchaseRecaptchaTokens();
2178
- modal.open(recaptchaTokensPromise);
2162
+ modal.open();
2179
2163
  }
2180
2164
  async #fetchSession() {
2181
2165
  try {
@@ -830,17 +830,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
830
830
  #country = "";
831
831
  #triggerElement = null;
832
832
  #keyHandler = null;
833
- #recaptchaTokens = null;
834
- #recaptchaTokensPromise = null;
833
+ #isClosed = false;
835
834
  /**
836
- * Synchronous callback invoked directly from the CTA click handler once the
837
- * user confirms. The caller (ApplePayButton) must call startApplePaySession
838
- * inside this callback to preserve the browser's user-gesture chain — Safari
839
- * requires new ApplePaySession() to execute within the original click stack.
840
- *
841
- * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
842
- * until the token pre-fetch resolves, so this callback is never reached
843
- * without valid tokens.
835
+ * Callback invoked from the CTA click handler once the user confirms.
836
+ * The caller (ApplePayButton) must call startApplePaySession inside this
837
+ * callback to preserve the browser's user-gesture chain — Safari requires
838
+ * new ApplePaySession() to execute within the original click stack.
844
839
  */
845
840
  onConfirm = null;
846
841
  set offer(value) {
@@ -937,28 +932,29 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
937
932
  }
938
933
  });
939
934
  }
940
- if (this.#recaptchaTokensPromise) {
941
- if (cta) {
942
- cta.setAttribute("disabled", "");
943
- cta.setAttribute("aria-disabled", "true");
944
- cta.setAttribute("aria-busy", "true");
935
+ cta?.addEventListener("click", () => {
936
+ if (cta.disabled) return;
937
+ if (isUS && checkbox && !checkbox.checked) {
938
+ errorElement?.classList.add("apm__error--visible");
939
+ checkboxLabel?.classList.add("apm__checkbox-label--error");
940
+ return;
945
941
  }
946
- this.#recaptchaTokensPromise.then((tokens) => {
947
- this.#recaptchaTokens = tokens;
948
- if (cta) {
949
- cta.removeAttribute("disabled");
950
- cta.removeAttribute("aria-disabled");
951
- cta.removeAttribute("aria-busy");
952
- }
942
+ errorElement?.classList.remove("apm__error--visible");
943
+ cta.disabled = true;
944
+ cta.setAttribute("aria-busy", "true");
945
+ getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
946
+ if (this.#isClosed) return;
947
+ this.onConfirm?.(recaptchaTokens);
948
+ this.close();
953
949
  }).catch(() => {
954
- if (cta) {
955
- cta.removeAttribute("aria-busy");
956
- }
950
+ cta.disabled = false;
951
+ cta.removeAttribute("aria-busy");
957
952
  const existingError = this.querySelector("#apple-pay-modal-error");
958
953
  if (existingError) {
959
954
  const icon = existingError.querySelector("svg");
960
955
  existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
961
956
  if (icon) existingError.prepend(icon);
957
+ existingError.setAttribute("role", "alert");
962
958
  existingError.classList.add("apm__error--visible");
963
959
  } else {
964
960
  const errorNotice = document.createElement("div");
@@ -968,25 +964,13 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
968
964
  cta?.insertAdjacentElement("beforebegin", errorNotice);
969
965
  }
970
966
  });
971
- }
972
- cta?.addEventListener("click", () => {
973
- if (isUS && checkbox && !checkbox.checked) {
974
- errorElement?.classList.add("apm__error--visible");
975
- checkboxLabel?.classList.add("apm__checkbox-label--error");
976
- return;
977
- }
978
- if (this.#recaptchaTokens) {
979
- this.onConfirm?.(this.#recaptchaTokens);
980
- this.close();
981
- }
982
967
  });
983
968
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
984
969
  "click",
985
970
  () => this.close()
986
971
  );
987
972
  }
988
- open(recaptchaTokensPromise) {
989
- this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
973
+ open() {
990
974
  this.#triggerElement = document.activeElement;
991
975
  document.body.appendChild(this);
992
976
  this.#keyHandler = (event) => {
@@ -1001,6 +985,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
1001
985
  this.querySelector("#apple-pay-modal")?.focus();
1002
986
  }
1003
987
  close() {
988
+ this.#isClosed = true;
1004
989
  if (this.#keyHandler) {
1005
990
  document.removeEventListener("keydown", this.#keyHandler, {
1006
991
  capture: true
@@ -2169,8 +2154,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2169
2154
  modal.onConfirm = (recaptchaTokens) => {
2170
2155
  this.#initiateApplePay(skuId, country, recaptchaTokens);
2171
2156
  };
2172
- const recaptchaTokensPromise = getPurchaseRecaptchaTokens();
2173
- modal.open(recaptchaTokensPromise);
2157
+ modal.open();
2174
2158
  }
2175
2159
  async #fetchSession() {
2176
2160
  try {
@@ -838,17 +838,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
838
838
  #country = "";
839
839
  #triggerElement = null;
840
840
  #keyHandler = null;
841
- #recaptchaTokens = null;
842
- #recaptchaTokensPromise = null;
841
+ #isClosed = false;
843
842
  /**
844
- * Synchronous callback invoked directly from the CTA click handler once the
845
- * user confirms. The caller (ApplePayButton) must call startApplePaySession
846
- * inside this callback to preserve the browser's user-gesture chain — Safari
847
- * requires new ApplePaySession() to execute within the original click stack.
848
- *
849
- * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
850
- * until the token pre-fetch resolves, so this callback is never reached
851
- * without valid tokens.
843
+ * Callback invoked from the CTA click handler once the user confirms.
844
+ * The caller (ApplePayButton) must call startApplePaySession inside this
845
+ * callback to preserve the browser's user-gesture chain — Safari requires
846
+ * new ApplePaySession() to execute within the original click stack.
852
847
  */
853
848
  onConfirm = null;
854
849
  set offer(value) {
@@ -945,28 +940,29 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
945
940
  }
946
941
  });
947
942
  }
948
- if (this.#recaptchaTokensPromise) {
949
- if (cta) {
950
- cta.setAttribute("disabled", "");
951
- cta.setAttribute("aria-disabled", "true");
952
- cta.setAttribute("aria-busy", "true");
943
+ cta?.addEventListener("click", () => {
944
+ if (cta.disabled) return;
945
+ if (isUS && checkbox && !checkbox.checked) {
946
+ errorElement?.classList.add("apm__error--visible");
947
+ checkboxLabel?.classList.add("apm__checkbox-label--error");
948
+ return;
953
949
  }
954
- this.#recaptchaTokensPromise.then((tokens) => {
955
- this.#recaptchaTokens = tokens;
956
- if (cta) {
957
- cta.removeAttribute("disabled");
958
- cta.removeAttribute("aria-disabled");
959
- cta.removeAttribute("aria-busy");
960
- }
950
+ errorElement?.classList.remove("apm__error--visible");
951
+ cta.disabled = true;
952
+ cta.setAttribute("aria-busy", "true");
953
+ getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
954
+ if (this.#isClosed) return;
955
+ this.onConfirm?.(recaptchaTokens);
956
+ this.close();
961
957
  }).catch(() => {
962
- if (cta) {
963
- cta.removeAttribute("aria-busy");
964
- }
958
+ cta.disabled = false;
959
+ cta.removeAttribute("aria-busy");
965
960
  const existingError = this.querySelector("#apple-pay-modal-error");
966
961
  if (existingError) {
967
962
  const icon = existingError.querySelector("svg");
968
963
  existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
969
964
  if (icon) existingError.prepend(icon);
965
+ existingError.setAttribute("role", "alert");
970
966
  existingError.classList.add("apm__error--visible");
971
967
  } else {
972
968
  const errorNotice = document.createElement("div");
@@ -976,25 +972,13 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
976
972
  cta?.insertAdjacentElement("beforebegin", errorNotice);
977
973
  }
978
974
  });
979
- }
980
- cta?.addEventListener("click", () => {
981
- if (isUS && checkbox && !checkbox.checked) {
982
- errorElement?.classList.add("apm__error--visible");
983
- checkboxLabel?.classList.add("apm__checkbox-label--error");
984
- return;
985
- }
986
- if (this.#recaptchaTokens) {
987
- this.onConfirm?.(this.#recaptchaTokens);
988
- this.close();
989
- }
990
975
  });
991
976
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
992
977
  "click",
993
978
  () => this.close()
994
979
  );
995
980
  }
996
- open(recaptchaTokensPromise) {
997
- this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
981
+ open() {
998
982
  this.#triggerElement = document.activeElement;
999
983
  document.body.appendChild(this);
1000
984
  this.#keyHandler = (event) => {
@@ -1009,6 +993,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
1009
993
  this.querySelector("#apple-pay-modal")?.focus();
1010
994
  }
1011
995
  close() {
996
+ this.#isClosed = true;
1012
997
  if (this.#keyHandler) {
1013
998
  document.removeEventListener("keydown", this.#keyHandler, {
1014
999
  capture: true
@@ -2177,8 +2162,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2177
2162
  modal.onConfirm = (recaptchaTokens) => {
2178
2163
  this.#initiateApplePay(skuId, country, recaptchaTokens);
2179
2164
  };
2180
- const recaptchaTokensPromise = getPurchaseRecaptchaTokens();
2181
- modal.open(recaptchaTokensPromise);
2165
+ modal.open();
2182
2166
  }
2183
2167
  async #fetchSession() {
2184
2168
  try {
@@ -7,21 +7,17 @@ export declare class ApplePayModal extends HTMLElement {
7
7
  static readonly tag = "teg-apple-pay-modal";
8
8
  static define(): void;
9
9
  /**
10
- * Synchronous callback invoked directly from the CTA click handler once the
11
- * user confirms. The caller (ApplePayButton) must call startApplePaySession
12
- * inside this callback to preserve the browser's user-gesture chain — Safari
13
- * requires new ApplePaySession() to execute within the original click stack.
14
- *
15
- * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
16
- * until the token pre-fetch resolves, so this callback is never reached
17
- * without valid tokens.
10
+ * Callback invoked from the CTA click handler once the user confirms.
11
+ * The caller (ApplePayButton) must call startApplePaySession inside this
12
+ * callback to preserve the browser's user-gesture chain — Safari requires
13
+ * new ApplePaySession() to execute within the original click stack.
18
14
  */
19
15
  onConfirm: ((recaptchaTokens: RecaptchaTokens) => void) | null;
20
16
  set offer(value: Offer | null);
21
17
  get country(): string;
22
18
  set country(value: string);
23
19
  connectedCallback(): void;
24
- open(recaptchaTokensPromise?: Promise<RecaptchaTokens> | null): void;
20
+ open(): void;
25
21
  close(): void;
26
22
  }
27
23
  export {};
@@ -830,17 +830,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
830
830
  #country = "";
831
831
  #triggerElement = null;
832
832
  #keyHandler = null;
833
- #recaptchaTokens = null;
834
- #recaptchaTokensPromise = null;
833
+ #isClosed = false;
835
834
  /**
836
- * Synchronous callback invoked directly from the CTA click handler once the
837
- * user confirms. The caller (ApplePayButton) must call startApplePaySession
838
- * inside this callback to preserve the browser's user-gesture chain — Safari
839
- * requires new ApplePaySession() to execute within the original click stack.
840
- *
841
- * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
842
- * until the token pre-fetch resolves, so this callback is never reached
843
- * without valid tokens.
835
+ * Callback invoked from the CTA click handler once the user confirms.
836
+ * The caller (ApplePayButton) must call startApplePaySession inside this
837
+ * callback to preserve the browser's user-gesture chain — Safari requires
838
+ * new ApplePaySession() to execute within the original click stack.
844
839
  */
845
840
  onConfirm = null;
846
841
  set offer(value) {
@@ -937,28 +932,29 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
937
932
  }
938
933
  });
939
934
  }
940
- if (this.#recaptchaTokensPromise) {
941
- if (cta) {
942
- cta.setAttribute("disabled", "");
943
- cta.setAttribute("aria-disabled", "true");
944
- cta.setAttribute("aria-busy", "true");
935
+ cta?.addEventListener("click", () => {
936
+ if (cta.disabled) return;
937
+ if (isUS && checkbox && !checkbox.checked) {
938
+ errorElement?.classList.add("apm__error--visible");
939
+ checkboxLabel?.classList.add("apm__checkbox-label--error");
940
+ return;
945
941
  }
946
- this.#recaptchaTokensPromise.then((tokens) => {
947
- this.#recaptchaTokens = tokens;
948
- if (cta) {
949
- cta.removeAttribute("disabled");
950
- cta.removeAttribute("aria-disabled");
951
- cta.removeAttribute("aria-busy");
952
- }
942
+ errorElement?.classList.remove("apm__error--visible");
943
+ cta.disabled = true;
944
+ cta.setAttribute("aria-busy", "true");
945
+ getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
946
+ if (this.#isClosed) return;
947
+ this.onConfirm?.(recaptchaTokens);
948
+ this.close();
953
949
  }).catch(() => {
954
- if (cta) {
955
- cta.removeAttribute("aria-busy");
956
- }
950
+ cta.disabled = false;
951
+ cta.removeAttribute("aria-busy");
957
952
  const existingError = this.querySelector("#apple-pay-modal-error");
958
953
  if (existingError) {
959
954
  const icon = existingError.querySelector("svg");
960
955
  existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
961
956
  if (icon) existingError.prepend(icon);
957
+ existingError.setAttribute("role", "alert");
962
958
  existingError.classList.add("apm__error--visible");
963
959
  } else {
964
960
  const errorNotice = document.createElement("div");
@@ -968,25 +964,13 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
968
964
  cta?.insertAdjacentElement("beforebegin", errorNotice);
969
965
  }
970
966
  });
971
- }
972
- cta?.addEventListener("click", () => {
973
- if (isUS && checkbox && !checkbox.checked) {
974
- errorElement?.classList.add("apm__error--visible");
975
- checkboxLabel?.classList.add("apm__checkbox-label--error");
976
- return;
977
- }
978
- if (this.#recaptchaTokens) {
979
- this.onConfirm?.(this.#recaptchaTokens);
980
- this.close();
981
- }
982
967
  });
983
968
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
984
969
  "click",
985
970
  () => this.close()
986
971
  );
987
972
  }
988
- open(recaptchaTokensPromise) {
989
- this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
973
+ open() {
990
974
  this.#triggerElement = document.activeElement;
991
975
  document.body.appendChild(this);
992
976
  this.#keyHandler = (event) => {
@@ -1001,6 +985,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
1001
985
  this.querySelector("#apple-pay-modal")?.focus();
1002
986
  }
1003
987
  close() {
988
+ this.#isClosed = true;
1004
989
  if (this.#keyHandler) {
1005
990
  document.removeEventListener("keydown", this.#keyHandler, {
1006
991
  capture: true
@@ -830,17 +830,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
830
830
  #country = "";
831
831
  #triggerElement = null;
832
832
  #keyHandler = null;
833
- #recaptchaTokens = null;
834
- #recaptchaTokensPromise = null;
833
+ #isClosed = false;
835
834
  /**
836
- * Synchronous callback invoked directly from the CTA click handler once the
837
- * user confirms. The caller (ApplePayButton) must call startApplePaySession
838
- * inside this callback to preserve the browser's user-gesture chain — Safari
839
- * requires new ApplePaySession() to execute within the original click stack.
840
- *
841
- * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
842
- * until the token pre-fetch resolves, so this callback is never reached
843
- * without valid tokens.
835
+ * Callback invoked from the CTA click handler once the user confirms.
836
+ * The caller (ApplePayButton) must call startApplePaySession inside this
837
+ * callback to preserve the browser's user-gesture chain — Safari requires
838
+ * new ApplePaySession() to execute within the original click stack.
844
839
  */
845
840
  onConfirm = null;
846
841
  set offer(value) {
@@ -937,28 +932,29 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
937
932
  }
938
933
  });
939
934
  }
940
- if (this.#recaptchaTokensPromise) {
941
- if (cta) {
942
- cta.setAttribute("disabled", "");
943
- cta.setAttribute("aria-disabled", "true");
944
- cta.setAttribute("aria-busy", "true");
935
+ cta?.addEventListener("click", () => {
936
+ if (cta.disabled) return;
937
+ if (isUS && checkbox && !checkbox.checked) {
938
+ errorElement?.classList.add("apm__error--visible");
939
+ checkboxLabel?.classList.add("apm__checkbox-label--error");
940
+ return;
945
941
  }
946
- this.#recaptchaTokensPromise.then((tokens) => {
947
- this.#recaptchaTokens = tokens;
948
- if (cta) {
949
- cta.removeAttribute("disabled");
950
- cta.removeAttribute("aria-disabled");
951
- cta.removeAttribute("aria-busy");
952
- }
942
+ errorElement?.classList.remove("apm__error--visible");
943
+ cta.disabled = true;
944
+ cta.setAttribute("aria-busy", "true");
945
+ getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
946
+ if (this.#isClosed) return;
947
+ this.onConfirm?.(recaptchaTokens);
948
+ this.close();
953
949
  }).catch(() => {
954
- if (cta) {
955
- cta.removeAttribute("aria-busy");
956
- }
950
+ cta.disabled = false;
951
+ cta.removeAttribute("aria-busy");
957
952
  const existingError = this.querySelector("#apple-pay-modal-error");
958
953
  if (existingError) {
959
954
  const icon = existingError.querySelector("svg");
960
955
  existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
961
956
  if (icon) existingError.prepend(icon);
957
+ existingError.setAttribute("role", "alert");
962
958
  existingError.classList.add("apm__error--visible");
963
959
  } else {
964
960
  const errorNotice = document.createElement("div");
@@ -968,25 +964,13 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
968
964
  cta?.insertAdjacentElement("beforebegin", errorNotice);
969
965
  }
970
966
  });
971
- }
972
- cta?.addEventListener("click", () => {
973
- if (isUS && checkbox && !checkbox.checked) {
974
- errorElement?.classList.add("apm__error--visible");
975
- checkboxLabel?.classList.add("apm__checkbox-label--error");
976
- return;
977
- }
978
- if (this.#recaptchaTokens) {
979
- this.onConfirm?.(this.#recaptchaTokens);
980
- this.close();
981
- }
982
967
  });
983
968
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
984
969
  "click",
985
970
  () => this.close()
986
971
  );
987
972
  }
988
- open(recaptchaTokensPromise) {
989
- this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
973
+ open() {
990
974
  this.#triggerElement = document.activeElement;
991
975
  document.body.appendChild(this);
992
976
  this.#keyHandler = (event) => {
@@ -1001,6 +985,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
1001
985
  this.querySelector("#apple-pay-modal")?.focus();
1002
986
  }
1003
987
  close() {
988
+ this.#isClosed = true;
1004
989
  if (this.#keyHandler) {
1005
990
  document.removeEventListener("keydown", this.#keyHandler, {
1006
991
  capture: true
@@ -2169,8 +2154,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2169
2154
  modal.onConfirm = (recaptchaTokens) => {
2170
2155
  this.#initiateApplePay(skuId, country, recaptchaTokens);
2171
2156
  };
2172
- const recaptchaTokensPromise = getPurchaseRecaptchaTokens();
2173
- modal.open(recaptchaTokensPromise);
2157
+ modal.open();
2174
2158
  }
2175
2159
  async #fetchSession() {
2176
2160
  try {
package/dist/src/index.js CHANGED
@@ -835,17 +835,12 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
835
835
  #country = "";
836
836
  #triggerElement = null;
837
837
  #keyHandler = null;
838
- #recaptchaTokens = null;
839
- #recaptchaTokensPromise = null;
838
+ #isClosed = false;
840
839
  /**
841
- * Synchronous callback invoked directly from the CTA click handler once the
842
- * user confirms. The caller (ApplePayButton) must call startApplePaySession
843
- * inside this callback to preserve the browser's user-gesture chain — Safari
844
- * requires new ApplePaySession() to execute within the original click stack.
845
- *
846
- * reCAPTCHA tokens are guaranteed non-null here: the CTA remains disabled
847
- * until the token pre-fetch resolves, so this callback is never reached
848
- * without valid tokens.
840
+ * Callback invoked from the CTA click handler once the user confirms.
841
+ * The caller (ApplePayButton) must call startApplePaySession inside this
842
+ * callback to preserve the browser's user-gesture chain — Safari requires
843
+ * new ApplePaySession() to execute within the original click stack.
849
844
  */
850
845
  onConfirm = null;
851
846
  set offer(value) {
@@ -942,28 +937,29 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
942
937
  }
943
938
  });
944
939
  }
945
- if (this.#recaptchaTokensPromise) {
946
- if (cta) {
947
- cta.setAttribute("disabled", "");
948
- cta.setAttribute("aria-disabled", "true");
949
- cta.setAttribute("aria-busy", "true");
940
+ cta?.addEventListener("click", () => {
941
+ if (cta.disabled) return;
942
+ if (isUS && checkbox && !checkbox.checked) {
943
+ errorElement?.classList.add("apm__error--visible");
944
+ checkboxLabel?.classList.add("apm__checkbox-label--error");
945
+ return;
950
946
  }
951
- this.#recaptchaTokensPromise.then((tokens) => {
952
- this.#recaptchaTokens = tokens;
953
- if (cta) {
954
- cta.removeAttribute("disabled");
955
- cta.removeAttribute("aria-disabled");
956
- cta.removeAttribute("aria-busy");
957
- }
947
+ errorElement?.classList.remove("apm__error--visible");
948
+ cta.disabled = true;
949
+ cta.setAttribute("aria-busy", "true");
950
+ getPurchaseRecaptchaTokens().then((recaptchaTokens) => {
951
+ if (this.#isClosed) return;
952
+ this.onConfirm?.(recaptchaTokens);
953
+ this.close();
958
954
  }).catch(() => {
959
- if (cta) {
960
- cta.removeAttribute("aria-busy");
961
- }
955
+ cta.disabled = false;
956
+ cta.removeAttribute("aria-busy");
962
957
  const existingError = this.querySelector("#apple-pay-modal-error");
963
958
  if (existingError) {
964
959
  const icon = existingError.querySelector("svg");
965
960
  existingError.textContent = MSG_RECAPTCHA_LOAD_ERROR;
966
961
  if (icon) existingError.prepend(icon);
962
+ existingError.setAttribute("role", "alert");
967
963
  existingError.classList.add("apm__error--visible");
968
964
  } else {
969
965
  const errorNotice = document.createElement("div");
@@ -973,25 +969,13 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
973
969
  cta?.insertAdjacentElement("beforebegin", errorNotice);
974
970
  }
975
971
  });
976
- }
977
- cta?.addEventListener("click", () => {
978
- if (isUS && checkbox && !checkbox.checked) {
979
- errorElement?.classList.add("apm__error--visible");
980
- checkboxLabel?.classList.add("apm__checkbox-label--error");
981
- return;
982
- }
983
- if (this.#recaptchaTokens) {
984
- this.onConfirm?.(this.#recaptchaTokens);
985
- this.close();
986
- }
987
972
  });
988
973
  this.querySelector("#apple-pay-modal-close")?.addEventListener(
989
974
  "click",
990
975
  () => this.close()
991
976
  );
992
977
  }
993
- open(recaptchaTokensPromise) {
994
- this.#recaptchaTokensPromise = recaptchaTokensPromise ?? getPurchaseRecaptchaTokens();
978
+ open() {
995
979
  this.#triggerElement = document.activeElement;
996
980
  document.body.appendChild(this);
997
981
  this.#keyHandler = (event) => {
@@ -1006,6 +990,7 @@ var ApplePayModal = class _ApplePayModal extends HTMLElement {
1006
990
  this.querySelector("#apple-pay-modal")?.focus();
1007
991
  }
1008
992
  close() {
993
+ this.#isClosed = true;
1009
994
  if (this.#keyHandler) {
1010
995
  document.removeEventListener("keydown", this.#keyHandler, {
1011
996
  capture: true
@@ -2174,8 +2159,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
2174
2159
  modal.onConfirm = (recaptchaTokens) => {
2175
2160
  this.#initiateApplePay(skuId, country, recaptchaTokens);
2176
2161
  };
2177
- const recaptchaTokensPromise = getPurchaseRecaptchaTokens();
2178
- modal.open(recaptchaTokensPromise);
2162
+ modal.open();
2179
2163
  }
2180
2164
  async #fetchSession() {
2181
2165
  try {
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.3-beta.42",
4
+ "version": "0.1.3-beta.43",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",