@asteby/storefront-react 0.17.0 → 0.18.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/payments.cjs CHANGED
@@ -621,7 +621,7 @@ function useEmbeddedPayment(options) {
621
621
  autoRetries = 2,
622
622
  retryDelayMs = 600,
623
623
  pollIntervalMs = 2e3,
624
- pollTimeoutMs = 6e4,
624
+ pollTimeoutMs = 3e5,
625
625
  verifyTimeoutMs = 12e4,
626
626
  verifyIntervalMs = 2e3,
627
627
  scope = "default"
@@ -650,6 +650,7 @@ function useEmbeddedPayment(options) {
650
650
  const alive = react.useRef(true);
651
651
  const generation = react.useRef(0);
652
652
  const statusRef = react.useRef("idle");
653
+ const leftChallenge = react.useRef(false);
653
654
  statusRef.current = status;
654
655
  errorRef.current = error;
655
656
  const amountKey = `${amount.amount}|${amount.currency_code}`;
@@ -833,6 +834,7 @@ function useEmbeddedPayment(options) {
833
834
  (result) => {
834
835
  const o = storefront.paymentOutcome(result, live.current.locale, live.current.messages);
835
836
  pendingData.current = null;
837
+ leftChallenge.current = false;
836
838
  setOutcome(o);
837
839
  setError(null);
838
840
  setAttemptsLeft(o.attempts_left);
@@ -993,7 +995,7 @@ function useEmbeddedPayment(options) {
993
995
  }
994
996
  let stop = false;
995
997
  const ctl = new AbortController();
996
- const until = Date.now() + verifyTimeoutMs;
998
+ const until = Date.now() + (leftChallenge.current ? Math.max(verifyTimeoutMs, pollTimeoutMs) : verifyTimeoutMs);
997
999
  setVerifying(true);
998
1000
  (async () => {
999
1001
  let wait = verifyIntervalMs;
@@ -1005,6 +1007,8 @@ function useEmbeddedPayment(options) {
1005
1007
  const r = await transport.refresh({ signal: ctl.signal });
1006
1008
  if (stop || !alive.current) return;
1007
1009
  if (isResolvedAttempt(r)) {
1010
+ const settled = r.payment.status === "paid" || r.payment.status === "failed" || r.payment.status === "expired" || r.payment.status === "cancelled";
1011
+ if (leftChallenge.current && !settled && r.payment.action?.type === "challenge") continue;
1008
1012
  setVerifying(false);
1009
1013
  finish(r);
1010
1014
  return;
@@ -1019,11 +1023,18 @@ function useEmbeddedPayment(options) {
1019
1023
  ctl.abort();
1020
1024
  setVerifying(false);
1021
1025
  };
1022
- }, [unknownOutcome, transport, verifyTimeoutMs, verifyIntervalMs, finish]);
1026
+ }, [unknownOutcome, transport, verifyTimeoutMs, verifyIntervalMs, pollTimeoutMs, finish]);
1023
1027
  const dismiss = react.useCallback(() => {
1024
1028
  setError(null);
1025
1029
  setOutcome((o) => o?.kind === "rejected" ? null : o);
1026
1030
  }, []);
1031
+ const cancelChallenge = react.useCallback(() => {
1032
+ if (statusRef.current !== "challenge") return;
1033
+ leftChallenge.current = true;
1034
+ setOutcome(null);
1035
+ setError({ kind: "payment_in_progress", message: live.current.msgs["challenge.left"], retrySameAttempt: false, canRetry: false });
1036
+ setStatus("blocked");
1037
+ }, []);
1027
1038
  const message = status === "processing" ? msgs["state.processing"] : status === "fallback" ? msgs["state.fallback"] : error?.message ?? outcome?.message ?? (status === "loading" ? msgs["state.loading"] : null);
1028
1039
  return {
1029
1040
  status,
@@ -1039,7 +1050,8 @@ function useEmbeddedPayment(options) {
1039
1050
  containerId,
1040
1051
  submit,
1041
1052
  retry,
1042
- dismiss
1053
+ dismiss,
1054
+ cancelChallenge
1043
1055
  };
1044
1056
  }
1045
1057
  function VoucherCard({ outcome, locale, messages, className }) {
@@ -1076,15 +1088,55 @@ function VoucherCard({ outcome, locale, messages, className }) {
1076
1088
  /* @__PURE__ */ jsxRuntime.jsx("p", { children: m["voucher.email"] })
1077
1089
  ] });
1078
1090
  }
1079
- function PaymentChallenge({ outcome, locale, messages, render, onCancel }) {
1091
+ function PaymentChallenge({ outcome, locale, messages, render, onCancel, escapeAfterMs = 12e4 }) {
1080
1092
  const m = storefront.paymentMessages(locale, messages);
1081
- const { ref } = useModalDialog({ open: true, onClose: () => onCancel?.() });
1093
+ const { ref } = useModalDialog({ open: true, onClose: () => {
1094
+ } });
1095
+ const [canEscape, setCanEscape] = react.useState(false);
1096
+ const [closed, setClosed] = react.useState(false);
1097
+ const cancelRef = react.useRef(onCancel);
1098
+ cancelRef.current = onCancel;
1099
+ const hasCancel = !!onCancel;
1100
+ react.useEffect(() => {
1101
+ if (!hasCancel) return;
1102
+ const t = setTimeout(() => setCanEscape(true), escapeAfterMs);
1103
+ return () => clearTimeout(t);
1104
+ }, [hasCancel, escapeAfterMs]);
1105
+ react.useEffect(() => {
1106
+ const el = ref.current;
1107
+ if (!el) return;
1108
+ const onClosed = () => setClosed(true);
1109
+ el.addEventListener("close", onClosed);
1110
+ return () => el.removeEventListener("close", onClosed);
1111
+ }, [ref]);
1112
+ const reopen = () => {
1113
+ const el = ref.current;
1114
+ if (!el) return;
1115
+ try {
1116
+ if (typeof el.showModal === "function") el.showModal();
1117
+ else el.setAttribute("open", "");
1118
+ } catch {
1119
+ el.setAttribute("open", "");
1120
+ }
1121
+ setClosed(false);
1122
+ el.querySelector("[data-sf-autofocus]")?.focus({ preventScroll: true });
1123
+ };
1082
1124
  const url = outcome.challenge?.url;
1083
- return /* @__PURE__ */ jsxRuntime.jsx("dialog", { ref, "aria-labelledby": "sf-challenge-title", "data-sf-challenge": true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1084
- /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "sf-challenge-title", "data-sf-autofocus": true, tabIndex: -1, children: m["challenge.title"] }),
1085
- render ? render(outcome) : url ? /* @__PURE__ */ jsxRuntime.jsx("iframe", { src: url, title: m["challenge.title"], style: { width: "100%", minHeight: 420, border: 0 } }) : /* @__PURE__ */ jsxRuntime.jsx("p", { children: m["state.challenge"] }),
1086
- onCancel && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onCancel, style: { minWidth: 44, minHeight: 44 }, children: m["action.cancel"] })
1087
- ] }) });
1125
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1126
+ closed && /* @__PURE__ */ jsxRuntime.jsxs("p", { role: "status", "data-sf-challenge-closed": true, children: [
1127
+ m["state.challenge"],
1128
+ " ",
1129
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: reopen, "data-sf-challenge-reopen": true, style: { minWidth: 44, minHeight: 44 }, children: m["challenge.title"] })
1130
+ ] }),
1131
+ /* @__PURE__ */ jsxRuntime.jsx("dialog", { ref, "aria-labelledby": "sf-challenge-title", "data-sf-challenge": true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-sf-challenge-body": true, children: [
1132
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { id: "sf-challenge-title", "data-sf-autofocus": true, tabIndex: -1, children: m["challenge.title"] }),
1133
+ render ? render(outcome) : url ? /* @__PURE__ */ jsxRuntime.jsx("iframe", { src: url, title: m["challenge.title"], "data-sf-challenge-frame": true, style: { width: "100%", minHeight: 420, border: 0 } }) : /* @__PURE__ */ jsxRuntime.jsx("p", { children: m["state.challenge"] }),
1134
+ hasCancel && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-sf-challenge-escape": true, "aria-live": "polite", children: canEscape && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1135
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: m["challenge.escape_hint"] }),
1136
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => cancelRef.current?.(), style: { minWidth: 44, minHeight: 44 }, children: m["challenge.escape"] })
1137
+ ] }) })
1138
+ ] }) })
1139
+ ] });
1088
1140
  }
1089
1141
  var PaymentBrick = react.forwardRef(function PaymentBrick2(props, ref) {
1090
1142
  const { submitLabel, hideAmountInButton, renderVoucher, renderChallenge, minHeight = 360, className, style, ...hookOptions } = props;
@@ -1122,7 +1174,7 @@ var PaymentBrick = react.forwardRef(function PaymentBrick2(props, ref) {
1122
1174
  state.status === "network_error" && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => void state.retry(), style: { minHeight: 44 }, children: m["action.retry"] })
1123
1175
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", "aria-live": "polite", "data-sf-pay-message": true, children: state.message }),
1124
1176
  outcome?.kind === "voucher" && (renderVoucher ? renderVoucher(outcome) : /* @__PURE__ */ jsxRuntime.jsx(VoucherCard, { outcome, locale, ...props.messages ? { messages: props.messages } : {} })),
1125
- outcome?.kind === "challenge" && /* @__PURE__ */ jsxRuntime.jsx(PaymentChallenge, { outcome, locale, ...props.messages ? { messages: props.messages } : {}, ...renderChallenge ? { render: renderChallenge } : {} }),
1177
+ outcome?.kind === "challenge" && /* @__PURE__ */ jsxRuntime.jsx(PaymentChallenge, { outcome, locale, onCancel: state.cancelChallenge, ...props.messages ? { messages: props.messages } : {}, ...renderChallenge ? { render: renderChallenge } : {} }),
1126
1178
  submitLabel !== void 0 && showForm && /* @__PURE__ */ jsxRuntime.jsxs(
1127
1179
  "button",
1128
1180
  {