@superlogic/spree-pay 0.4.8 → 0.4.11

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Iframe3ds
3
- } from "./chunk-EXKCBGNL.js";
3
+ } from "./chunk-VDX65MBD.js";
4
4
  import {
5
5
  InfoBanner,
6
6
  Legal,
@@ -12,7 +12,7 @@ import {
12
12
  useSpreePayRegister,
13
13
  useSpreePaymentMethod,
14
14
  useStaticConfig
15
- } from "./chunk-SKM7ZAUY.js";
15
+ } from "./chunk-NAY4SEME.js";
16
16
 
17
17
  // src/components/CryptoComTab/CryptoComTab.tsx
18
18
  import { useCallback, useEffect } from "react";
@@ -78,11 +78,12 @@ var useCryptoComPayment = () => {
78
78
  import { jsx, jsxs } from "react/jsx-runtime";
79
79
  var Checkout = () => {
80
80
  const { appProps } = useStaticConfig();
81
+ const { isInternalProcessing } = useSpreePaymentMethod();
81
82
  return /* @__PURE__ */ jsx(
82
83
  "button",
83
84
  {
84
85
  onClick: appProps.onProcess,
85
- disabled: appProps.isProcessing,
86
+ disabled: !!appProps.isProcessing || isInternalProcessing,
86
87
  className: "flex flex-col items-center rounded-md bg-(--crypto-pay-bg) p-2 text-(--brand-primary) hover:bg-(--crypto-pay-bg-hover) disabled:cursor-not-allowed disabled:bg-(--crypto-pay-bg-hover) disabled:text-(--disabled)",
87
88
  children: /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-7 w-[76px]", fill: "none", viewBox: "0 0 76 28", children: [
88
89
  /* @__PURE__ */ jsx(
@@ -2,7 +2,7 @@ import {
2
2
  CheckoutButton,
3
3
  PointsSwitch,
4
4
  cn as cn2
5
- } from "./chunk-W43VOUYP.js";
5
+ } from "./chunk-WHTBHEIN.js";
6
6
  import {
7
7
  Dialog,
8
8
  DialogContent,
@@ -16,7 +16,7 @@ import {
16
16
  useSpreePayConfig,
17
17
  useSpreePayRegister,
18
18
  useSpreePaymentMethod
19
- } from "./chunk-SKM7ZAUY.js";
19
+ } from "./chunk-NAY4SEME.js";
20
20
 
21
21
  // src/components/CryptoTab/Crypto/CryptoWrapper.tsx
22
22
  import { useMemo as useMemo2 } from "react";
@@ -409,7 +409,7 @@ function Input({ className, type, ...props }) {
409
409
  type,
410
410
  "data-slot": "input",
411
411
  className: cn2(
412
- "file:text-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input px-universal-2xs py-universal-6xs rounded-radius-universal-xs text-body-m file:text-label-s file:font-label-primary flex h-9 w-full min-w-0 border bg-transparent text-(--color-text-icons-primary-default) shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent placeholder:text-(--color-text-icons-tertiary-default) disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
412
+ "file:text-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input px-universal-2xs py-universal-6xs rounded-radius-universal-xs text-body-m file:text-label-s file:font-label-primary flex h-9 w-full min-w-0 border bg-transparent text-(--color-text-icons-primary-default) shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent placeholder:text-(--color-text-icons-tertiary-default) disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 max-md:text-[16px]",
413
413
  "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
414
414
  "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
415
415
  className
@@ -619,95 +619,93 @@ var CryptoSelectModal = NiceModal.create(() => {
619
619
  setSelectedPaymentMethod({ type: "CRYPTO" /* CRYPTO */, method: coin });
620
620
  };
621
621
  const userCoins = [nativeBalance, ...erc20Balances].filter(Boolean);
622
- return /* @__PURE__ */ jsxs4(Dialog, { open: modal.visible, onOpenChange: modal.remove, children: [
623
- /* @__PURE__ */ jsx8(DialogDescription, { className: "hidden", children: "Crypto Select Modal" }),
624
- /* @__PURE__ */ jsxs4(DialogContent, { showCloseButton: false, className: "max-h-[90vh] gap-0 p-0", children: [
625
- /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
626
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-4", children: [
627
- /* @__PURE__ */ jsx8("button", { className: "rounded-md hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ jsx8("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-6", viewBox: "0 0 24 25", children: /* @__PURE__ */ jsx8("path", { stroke: "currentColor", d: "m15 6.5-6 6 6 6" }) }) }),
628
- /* @__PURE__ */ jsx8(DialogTitle, { className: "text-2xl font-medium text-(--brand-primary)", children: "Select a token" }),
629
- /* @__PURE__ */ jsx8("button", { className: "rounded-md p-1 hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ jsx8("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-4", viewBox: "0 0 16 17", children: /* @__PURE__ */ jsx8(
630
- "path",
631
- {
632
- fill: "currentColor",
633
- d: "M12.6 3.9c.2.2.2.52 0 .71L8.7 8.5l3.9 3.89a.5.5 0 1 1-.71.7L8 9.22 4.11 13.1a.5.5 0 1 1-.7-.71L7.28 8.5 3.4 4.61a.5.5 0 1 1 .71-.7L8 7.78l3.89-3.89c.2-.2.51-.2.7 0Z"
634
- }
635
- ) }) })
636
- ] }),
637
- /* @__PURE__ */ jsx8(Input, { onChange: (e) => setSearch(e.target.value), placeholder: "Search by token name", value: search })
622
+ return /* @__PURE__ */ jsx8(Dialog, { open: modal.visible, onOpenChange: modal.remove, children: /* @__PURE__ */ jsxs4(DialogContent, { showCloseButton: false, className: "max-h-[90vh] gap-0 p-0", children: [
623
+ /* @__PURE__ */ jsx8(DialogDescription, { className: "sr-only", children: "Crypto Select Modal" }),
624
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
625
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-4", children: [
626
+ /* @__PURE__ */ jsx8("button", { className: "rounded-md hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ jsx8("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-6", viewBox: "0 0 24 25", children: /* @__PURE__ */ jsx8("path", { stroke: "currentColor", d: "m15 6.5-6 6 6 6" }) }) }),
627
+ /* @__PURE__ */ jsx8(DialogTitle, { className: "text-2xl font-medium text-(--brand-primary)", children: "Select a token" }),
628
+ /* @__PURE__ */ jsx8("button", { className: "rounded-md p-1 hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ jsx8("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-4", viewBox: "0 0 16 17", children: /* @__PURE__ */ jsx8(
629
+ "path",
630
+ {
631
+ fill: "currentColor",
632
+ d: "M12.6 3.9c.2.2.2.52 0 .71L8.7 8.5l3.9 3.89a.5.5 0 1 1-.71.7L8 9.22 4.11 13.1a.5.5 0 1 1-.7-.71L7.28 8.5 3.4 4.61a.5.5 0 1 1 .71-.7L8 7.78l3.89-3.89c.2-.2.51-.2.7 0Z"
633
+ }
634
+ ) }) })
638
635
  ] }),
639
- /* @__PURE__ */ jsx8(Separator2, { className: "hidden md:block" }),
640
- /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
641
- /* @__PURE__ */ jsx8("h3", { className: "text-md font-medium text-(--brand-primary)", children: "Tokens with wallet balance" }),
642
- (error || nativeError) && /* @__PURE__ */ jsx8("p", { className: "text-center text-sm text-(--negative)", children: "Something wrong" }),
643
- /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-1", children: [
644
- isLoadingNative && /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
645
- nativeBalance && /* @__PURE__ */ jsxs4(
636
+ /* @__PURE__ */ jsx8(Input, { onChange: (e) => setSearch(e.target.value), placeholder: "Search by token name", value: search })
637
+ ] }),
638
+ /* @__PURE__ */ jsx8(Separator2, { className: "hidden md:block" }),
639
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
640
+ /* @__PURE__ */ jsx8("h3", { className: "text-md font-medium text-(--brand-primary)", children: "Tokens with wallet balance" }),
641
+ (error || nativeError) && /* @__PURE__ */ jsx8("p", { className: "text-center text-sm text-(--negative)", children: "Something wrong" }),
642
+ /* @__PURE__ */ jsxs4("div", { className: "flex w-full flex-col gap-1", children: [
643
+ isLoadingNative && /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
644
+ nativeBalance && /* @__PURE__ */ jsxs4(
645
+ "button",
646
+ {
647
+ className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover)",
648
+ onClick: () => handleSelect(nativeBalance),
649
+ children: [
650
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
651
+ nativeBalance.logoURI && /* @__PURE__ */ jsx8("img", { className: "size-8 shrink-0", src: nativeBalance.logoURI, alt: `${nativeBalance.symbol} logo` }),
652
+ /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: nativeBalance.symbol })
653
+ ] }),
654
+ /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: nativeBalance.formatted })
655
+ ]
656
+ },
657
+ nativeBalance.symbol
658
+ ),
659
+ isLoading && /* @__PURE__ */ jsxs4(Fragment2, { children: [
660
+ /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
661
+ /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
662
+ /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
663
+ ] }),
664
+ erc20Balances.map((coin) => {
665
+ const Icon = getSymbolLogo(coin.symbol);
666
+ return /* @__PURE__ */ jsxs4(
646
667
  "button",
647
668
  {
648
- className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover)",
649
- onClick: () => handleSelect(nativeBalance),
669
+ className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
670
+ onClick: () => handleSelect(coin),
650
671
  children: [
651
672
  /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
652
- nativeBalance.logoURI && /* @__PURE__ */ jsx8("img", { className: "size-8 shrink-0", src: nativeBalance.logoURI, alt: `${nativeBalance.symbol} logo` }),
653
- /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: nativeBalance.symbol })
673
+ Boolean(Icon) && Icon,
674
+ /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: coin.symbol })
654
675
  ] }),
655
- /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: nativeBalance.formatted })
676
+ /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: coin.formatted })
656
677
  ]
657
678
  },
658
- nativeBalance.symbol
659
- ),
660
- isLoading && /* @__PURE__ */ jsxs4(Fragment2, { children: [
661
- /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
662
- /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
663
- /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
664
- ] }),
665
- erc20Balances.map((coin) => {
666
- const Icon = getSymbolLogo(coin.symbol);
667
- return /* @__PURE__ */ jsxs4(
668
- "button",
669
- {
670
- className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
671
- onClick: () => handleSelect(coin),
672
- children: [
673
- /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
674
- Boolean(Icon) && Icon,
675
- /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: coin.symbol })
676
- ] }),
677
- /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: coin.formatted })
678
- ]
679
- },
680
- coin.symbol
681
- );
682
- })
679
+ coin.symbol
680
+ );
681
+ })
682
+ ] }),
683
+ /* @__PURE__ */ jsx8("h3", { className: "text-md font-medium text-(--brand-primary)", children: "All Tokens" }),
684
+ /* @__PURE__ */ jsxs4("div", { className: "flex max-h-[40vh] w-full flex-col gap-1 overflow-y-auto", children: [
685
+ tokensIsLoading && /* @__PURE__ */ jsxs4(Fragment2, { children: [
686
+ /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
687
+ /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
688
+ /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
683
689
  ] }),
684
- /* @__PURE__ */ jsx8("h3", { className: "text-md font-medium text-(--brand-primary)", children: "All Tokens" }),
685
- /* @__PURE__ */ jsxs4("div", { className: "flex max-h-[40vh] w-full flex-col gap-1 overflow-y-auto", children: [
686
- tokensIsLoading && /* @__PURE__ */ jsxs4(Fragment2, { children: [
687
- /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
688
- /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
689
- /* @__PURE__ */ jsx8("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
690
- ] }),
691
- filteredCoins.map((token) => {
692
- const userCoin = userCoins.find((c) => c.symbol === token.symbol);
693
- return /* @__PURE__ */ jsx8(
694
- "button",
695
- {
696
- disabled: !userCoin,
697
- onClick: () => userCoin && handleSelect(userCoin),
698
- className: "flex min-h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
699
- children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
700
- token.logoURI ? /* @__PURE__ */ jsx8("img", { className: "size-8 shrink-0", src: token.logoURI, alt: `${token.name} logo` }) : /* @__PURE__ */ jsx8("div", { className: "size-8 shrink-0 rounded-full bg-(--s-tertiary)" }),
701
- /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: token.symbol })
702
- ] })
703
- },
704
- token.symbol
705
- );
706
- })
707
- ] })
690
+ filteredCoins.map((token) => {
691
+ const userCoin = userCoins.find((c) => c.symbol === token.symbol);
692
+ return /* @__PURE__ */ jsx8(
693
+ "button",
694
+ {
695
+ disabled: !userCoin,
696
+ onClick: () => userCoin && handleSelect(userCoin),
697
+ className: "flex min-h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
698
+ children: /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
699
+ token.logoURI ? /* @__PURE__ */ jsx8("img", { className: "size-8 shrink-0", src: token.logoURI, alt: `${token.name} logo` }) : /* @__PURE__ */ jsx8("div", { className: "size-8 shrink-0 rounded-full bg-(--s-tertiary)" }),
700
+ /* @__PURE__ */ jsx8("p", { className: "text-sm font-medium", children: token.symbol })
701
+ ] })
702
+ },
703
+ token.symbol
704
+ );
705
+ })
708
706
  ] })
709
707
  ] })
710
- ] });
708
+ ] }) });
711
709
  });
712
710
  CryptoSelectModal.displayName = "CryptoSelectModal";
713
711
 
@@ -10,7 +10,7 @@ var PaymentType = /* @__PURE__ */ ((PaymentType2) => {
10
10
  })(PaymentType || {});
11
11
 
12
12
  // package.json
13
- var version = "0.4.8";
13
+ var version = "0.4.11";
14
14
 
15
15
  // src/utils/logger.ts
16
16
  var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
@@ -147,6 +147,7 @@ var processLogger = logger.child("process");
147
147
  var SpreePayActionsContext = createContext(void 0);
148
148
  var SpreePayProvider = ({ children, env }) => {
149
149
  const processRef = useRef(null);
150
+ const inFlightRef = useRef(false);
150
151
  const [selectedPaymentMethod, setSelectedPaymentMethod] = useState({
151
152
  type: "CREDIT_CARD" /* CREDIT_CARD */,
152
153
  method: null
@@ -162,12 +163,20 @@ var SpreePayProvider = ({ children, env }) => {
162
163
  processLogger.error("Process function not registered", error);
163
164
  throw error;
164
165
  }
166
+ if (inFlightRef.current) {
167
+ processLogger.warn("Payment process already in flight, ignoring duplicate call", {
168
+ hash: data.hash,
169
+ paymentType: selectedPaymentMethod.type
170
+ });
171
+ throw new PaymentError("Payment already in progress", "FAILED" /* FAILED */);
172
+ }
165
173
  processLogger.info("Payment process started", {
166
174
  hash: data.hash,
167
175
  capture: data.capture,
168
176
  hasMetadata: Boolean(data.metadata),
169
177
  paymentType: selectedPaymentMethod.type
170
178
  });
179
+ inFlightRef.current = true;
171
180
  setInternalProcessing(true);
172
181
  try {
173
182
  const result = await processRef.current(data);
@@ -187,6 +196,7 @@ var SpreePayProvider = ({ children, env }) => {
187
196
  if (e instanceof Error) throw e;
188
197
  throw new PaymentError("Payment failed", "FAILED" /* FAILED */, e);
189
198
  } finally {
199
+ inFlightRef.current = false;
190
200
  setInternalProcessing(false);
191
201
  }
192
202
  },
@@ -235,7 +245,7 @@ var useSpreePayRegister = () => {
235
245
  };
236
246
 
237
247
  // src/context/StaticConfigContext.tsx
238
- import { createContext as createContext2, useContext as useContext2, useEffect, useMemo, useState as useState2 } from "react";
248
+ import { createContext as createContext2, useContext as useContext2, useMemo } from "react";
239
249
 
240
250
  // src/context/config.ts
241
251
  var config = {
@@ -253,8 +263,8 @@ var config = {
253
263
  keycloakUrl: "https://sso.dev.tickets.qiibeefoundation.org"
254
264
  },
255
265
  umhp: {
256
- slapiUrl: "https://slapi.dev.umusicpassport.com",
257
- keycloakUrl: "https://auth.dev.umusicpassport.com"
266
+ slapiUrl: "https://slapi.dev.umpulse.superlogic.com",
267
+ keycloakUrl: "https://auth.dev.umpulse.superlogic.com"
258
268
  },
259
269
  cdc: {
260
270
  slapiUrl: "https://slapi.dev.travel.crypto.superlogic.com",
@@ -279,8 +289,8 @@ var config = {
279
289
  keycloakUrl: "https://sso.stg.tickets.qiibeefoundation.org"
280
290
  },
281
291
  umhp: {
282
- slapiUrl: "https://slapi.stg.umusicpassport.com",
283
- keycloakUrl: "https://auth.stg.umusicpassport.com"
292
+ slapiUrl: "https://slapi.stg.umpulse.superlogic.com",
293
+ keycloakUrl: "https://auth.stg.umpulse.superlogic.com"
284
294
  },
285
295
  cdc: {
286
296
  slapiUrl: "https://slapi.stg.travel.crypto.superlogic.com",
@@ -305,8 +315,8 @@ var config = {
305
315
  keycloakUrl: "https://sso.tickets.qiibeefoundation.org"
306
316
  },
307
317
  umhp: {
308
- slapiUrl: "https://slapi.umusicpassport.com",
309
- keycloakUrl: "https://auth.umusicpassport.com"
318
+ slapiUrl: "https://slapi.umpulse.com",
319
+ keycloakUrl: "https://auth.umpulse.com"
310
320
  },
311
321
  cdc: {
312
322
  slapiUrl: "https://slapi.travel.crypto.com",
@@ -324,10 +334,6 @@ import { jsx as jsx2 } from "react/jsx-runtime";
324
334
  var StaticConfigContext = createContext2(null);
325
335
  var StaticConfigProvider = ({ children, props }) => {
326
336
  const { environment, tenantId } = useSpreePayEnv();
327
- const [appProps, setAppProps] = useState2(props);
328
- useEffect(() => {
329
- setAppProps(props);
330
- }, [props]);
331
337
  const staticConfig = useMemo(() => {
332
338
  const envConfig = config[environment];
333
339
  const isKnownTenant = tenantId in envConfig;
@@ -340,7 +346,8 @@ var StaticConfigProvider = ({ children, props }) => {
340
346
  const appKey = isKnownTenant ? tenantId : "moca";
341
347
  return envConfig[appKey];
342
348
  }, [environment, tenantId]);
343
- return /* @__PURE__ */ jsx2(StaticConfigContext.Provider, { value: { staticConfig, appProps }, children });
349
+ const value = useMemo(() => ({ staticConfig, appProps: props }), [staticConfig, props]);
350
+ return /* @__PURE__ */ jsx2(StaticConfigContext.Provider, { value, children });
344
351
  };
345
352
  var useStaticConfig = () => {
346
353
  const ctx = useContext2(StaticConfigContext);
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  Dialog,
3
3
  DialogContent,
4
+ DialogDescription,
4
5
  DialogTitle
5
- } from "./chunk-SKM7ZAUY.js";
6
+ } from "./chunk-NAY4SEME.js";
6
7
 
7
8
  // src/modals/Iframe3ds.tsx
8
9
  import { useEffect } from "react";
@@ -37,7 +38,8 @@ var Iframe3ds = NiceModal.create(({ url }) => {
37
38
  modal.remove();
38
39
  };
39
40
  return /* @__PURE__ */ jsx(Dialog, { open: modal.visible, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[600px] w-full max-w-[680px] p-0", children: [
40
- /* @__PURE__ */ jsx(DialogTitle, { className: "hidden", children: "Payment Verification" }),
41
+ /* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: "Payment Verification" }),
42
+ /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: "Complete 3D Secure verification to finalize your payment" }),
41
43
  /* @__PURE__ */ jsx(
42
44
  "iframe",
43
45
  {
@@ -6,7 +6,7 @@ import {
6
6
  useSpreePayEnv,
7
7
  useSpreePaymentMethod,
8
8
  useStaticConfig
9
- } from "./chunk-SKM7ZAUY.js";
9
+ } from "./chunk-NAY4SEME.js";
10
10
 
11
11
  // src/components/common/PointsSwitch.tsx
12
12
  import { useId } from "react";
package/build/index.cjs CHANGED
@@ -69,7 +69,7 @@ var init_payments = __esm({
69
69
  var version;
70
70
  var init_package = __esm({
71
71
  "package.json"() {
72
- version = "0.4.8";
72
+ version = "0.4.11";
73
73
  }
74
74
  });
75
75
 
@@ -207,6 +207,7 @@ var init_SpreePayActionsContext = __esm({
207
207
  SpreePayActionsContext = (0, import_react.createContext)(void 0);
208
208
  SpreePayProvider = ({ children, env }) => {
209
209
  const processRef = (0, import_react.useRef)(null);
210
+ const inFlightRef = (0, import_react.useRef)(false);
210
211
  const [selectedPaymentMethod, setSelectedPaymentMethod] = (0, import_react.useState)({
211
212
  type: "CREDIT_CARD" /* CREDIT_CARD */,
212
213
  method: null
@@ -222,12 +223,20 @@ var init_SpreePayActionsContext = __esm({
222
223
  processLogger.error("Process function not registered", error);
223
224
  throw error;
224
225
  }
226
+ if (inFlightRef.current) {
227
+ processLogger.warn("Payment process already in flight, ignoring duplicate call", {
228
+ hash: data.hash,
229
+ paymentType: selectedPaymentMethod.type
230
+ });
231
+ throw new PaymentError("Payment already in progress", "FAILED" /* FAILED */);
232
+ }
225
233
  processLogger.info("Payment process started", {
226
234
  hash: data.hash,
227
235
  capture: data.capture,
228
236
  hasMetadata: Boolean(data.metadata),
229
237
  paymentType: selectedPaymentMethod.type
230
238
  });
239
+ inFlightRef.current = true;
231
240
  setInternalProcessing(true);
232
241
  try {
233
242
  const result = await processRef.current(data);
@@ -247,6 +256,7 @@ var init_SpreePayActionsContext = __esm({
247
256
  if (e instanceof Error) throw e;
248
257
  throw new PaymentError("Payment failed", "FAILED" /* FAILED */, e);
249
258
  } finally {
259
+ inFlightRef.current = false;
250
260
  setInternalProcessing(false);
251
261
  }
252
262
  },
@@ -316,8 +326,8 @@ var init_config = __esm({
316
326
  keycloakUrl: "https://sso.dev.tickets.qiibeefoundation.org"
317
327
  },
318
328
  umhp: {
319
- slapiUrl: "https://slapi.dev.umusicpassport.com",
320
- keycloakUrl: "https://auth.dev.umusicpassport.com"
329
+ slapiUrl: "https://slapi.dev.umpulse.superlogic.com",
330
+ keycloakUrl: "https://auth.dev.umpulse.superlogic.com"
321
331
  },
322
332
  cdc: {
323
333
  slapiUrl: "https://slapi.dev.travel.crypto.superlogic.com",
@@ -342,8 +352,8 @@ var init_config = __esm({
342
352
  keycloakUrl: "https://sso.stg.tickets.qiibeefoundation.org"
343
353
  },
344
354
  umhp: {
345
- slapiUrl: "https://slapi.stg.umusicpassport.com",
346
- keycloakUrl: "https://auth.stg.umusicpassport.com"
355
+ slapiUrl: "https://slapi.stg.umpulse.superlogic.com",
356
+ keycloakUrl: "https://auth.stg.umpulse.superlogic.com"
347
357
  },
348
358
  cdc: {
349
359
  slapiUrl: "https://slapi.stg.travel.crypto.superlogic.com",
@@ -368,8 +378,8 @@ var init_config = __esm({
368
378
  keycloakUrl: "https://sso.tickets.qiibeefoundation.org"
369
379
  },
370
380
  umhp: {
371
- slapiUrl: "https://slapi.umusicpassport.com",
372
- keycloakUrl: "https://auth.umusicpassport.com"
381
+ slapiUrl: "https://slapi.umpulse.com",
382
+ keycloakUrl: "https://auth.umpulse.com"
373
383
  },
374
384
  cdc: {
375
385
  slapiUrl: "https://slapi.travel.crypto.com",
@@ -398,10 +408,6 @@ var init_StaticConfigContext = __esm({
398
408
  StaticConfigContext = (0, import_react2.createContext)(null);
399
409
  StaticConfigProvider = ({ children, props }) => {
400
410
  const { environment, tenantId } = useSpreePayEnv();
401
- const [appProps, setAppProps] = (0, import_react2.useState)(props);
402
- (0, import_react2.useEffect)(() => {
403
- setAppProps(props);
404
- }, [props]);
405
411
  const staticConfig = (0, import_react2.useMemo)(() => {
406
412
  const envConfig = config[environment];
407
413
  const isKnownTenant = tenantId in envConfig;
@@ -414,7 +420,8 @@ var init_StaticConfigContext = __esm({
414
420
  const appKey = isKnownTenant ? tenantId : "moca";
415
421
  return envConfig[appKey];
416
422
  }, [environment, tenantId]);
417
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StaticConfigContext.Provider, { value: { staticConfig, appProps }, children });
423
+ const value = (0, import_react2.useMemo)(() => ({ staticConfig, appProps: props }), [staticConfig, props]);
424
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StaticConfigContext.Provider, { value, children });
418
425
  };
419
426
  useStaticConfig = () => {
420
427
  const ctx = (0, import_react2.useContext)(StaticConfigContext);
@@ -589,7 +596,8 @@ var init_Iframe3ds = __esm({
589
596
  modal.remove();
590
597
  };
591
598
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Dialog, { open: modal.visible, onOpenChange: handleClose, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(DialogContent, { className: "max-h-[600px] w-full max-w-[680px] p-0", children: [
592
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DialogTitle, { className: "hidden", children: "Payment Verification" }),
599
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DialogTitle, { className: "sr-only", children: "Payment Verification" }),
600
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DialogDescription, { className: "sr-only", children: "Complete 3D Secure verification to finalize your payment" }),
593
601
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
594
602
  "iframe",
595
603
  {
@@ -1814,7 +1822,7 @@ function Input2({ className, type, ...props }) {
1814
1822
  type,
1815
1823
  "data-slot": "input",
1816
1824
  className: cn2(
1817
- "file:text-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input px-universal-2xs py-universal-6xs rounded-radius-universal-xs text-body-m file:text-label-s file:font-label-primary flex h-9 w-full min-w-0 border bg-transparent text-(--color-text-icons-primary-default) shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent placeholder:text-(--color-text-icons-tertiary-default) disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
1825
+ "file:text-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input px-universal-2xs py-universal-6xs rounded-radius-universal-xs text-body-m file:text-label-s file:font-label-primary flex h-9 w-full min-w-0 border bg-transparent text-(--color-text-icons-primary-default) shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent placeholder:text-(--color-text-icons-tertiary-default) disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 max-md:text-[16px]",
1818
1826
  "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
1819
1827
  "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
1820
1828
  className
@@ -2086,95 +2094,93 @@ var init_CryptoSelectModal = __esm({
2086
2094
  setSelectedPaymentMethod({ type: "CRYPTO" /* CRYPTO */, method: coin });
2087
2095
  };
2088
2096
  const userCoins = [nativeBalance, ...erc20Balances].filter(Boolean);
2089
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Dialog, { open: modal.visible, onOpenChange: modal.remove, children: [
2090
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(DialogDescription, { className: "hidden", children: "Crypto Select Modal" }),
2091
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(DialogContent, { showCloseButton: false, className: "max-h-[90vh] gap-0 p-0", children: [
2092
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
2093
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center justify-between gap-4", children: [
2094
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { className: "rounded-md hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-6", viewBox: "0 0 24 25", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("path", { stroke: "currentColor", d: "m15 6.5-6 6 6 6" }) }) }),
2095
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(DialogTitle, { className: "text-2xl font-medium text-(--brand-primary)", children: "Select a token" }),
2096
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { className: "rounded-md p-1 hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-4", viewBox: "0 0 16 17", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2097
- "path",
2098
- {
2099
- fill: "currentColor",
2100
- d: "M12.6 3.9c.2.2.2.52 0 .71L8.7 8.5l3.9 3.89a.5.5 0 1 1-.71.7L8 9.22 4.11 13.1a.5.5 0 1 1-.7-.71L7.28 8.5 3.4 4.61a.5.5 0 1 1 .71-.7L8 7.78l3.89-3.89c.2-.2.51-.2.7 0Z"
2101
- }
2102
- ) }) })
2103
- ] }),
2104
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Input2, { onChange: (e) => setSearch(e.target.value), placeholder: "Search by token name", value: search })
2097
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Dialog, { open: modal.visible, onOpenChange: modal.remove, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(DialogContent, { showCloseButton: false, className: "max-h-[90vh] gap-0 p-0", children: [
2098
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(DialogDescription, { className: "sr-only", children: "Crypto Select Modal" }),
2099
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
2100
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center justify-between gap-4", children: [
2101
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { className: "rounded-md hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-6", viewBox: "0 0 24 25", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("path", { stroke: "currentColor", d: "m15 6.5-6 6 6 6" }) }) }),
2102
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(DialogTitle, { className: "text-2xl font-medium text-(--brand-primary)", children: "Select a token" }),
2103
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { className: "rounded-md p-1 hover:bg-(--s-primary-hover)", onClick: modal.remove, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", className: "size-4", viewBox: "0 0 16 17", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2104
+ "path",
2105
+ {
2106
+ fill: "currentColor",
2107
+ d: "M12.6 3.9c.2.2.2.52 0 .71L8.7 8.5l3.9 3.89a.5.5 0 1 1-.71.7L8 9.22 4.11 13.1a.5.5 0 1 1-.7-.71L7.28 8.5 3.4 4.61a.5.5 0 1 1 .71-.7L8 7.78l3.89-3.89c.2-.2.51-.2.7 0Z"
2108
+ }
2109
+ ) }) })
2105
2110
  ] }),
2106
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Separator2, { className: "hidden md:block" }),
2107
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
2108
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h3", { className: "text-md font-medium text-(--brand-primary)", children: "Tokens with wallet balance" }),
2109
- (error || nativeError) && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-center text-sm text-(--negative)", children: "Something wrong" }),
2110
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex w-full flex-col gap-1", children: [
2111
- isLoadingNative && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2112
- nativeBalance && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2111
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Input2, { onChange: (e) => setSearch(e.target.value), placeholder: "Search by token name", value: search })
2112
+ ] }),
2113
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Separator2, { className: "hidden md:block" }),
2114
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-4 px-5 py-5 md:px-7", children: [
2115
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h3", { className: "text-md font-medium text-(--brand-primary)", children: "Tokens with wallet balance" }),
2116
+ (error || nativeError) && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-center text-sm text-(--negative)", children: "Something wrong" }),
2117
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex w-full flex-col gap-1", children: [
2118
+ isLoadingNative && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2119
+ nativeBalance && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2120
+ "button",
2121
+ {
2122
+ className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover)",
2123
+ onClick: () => handleSelect(nativeBalance),
2124
+ children: [
2125
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center gap-2", children: [
2126
+ nativeBalance.logoURI && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("img", { className: "size-8 shrink-0", src: nativeBalance.logoURI, alt: `${nativeBalance.symbol} logo` }),
2127
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: nativeBalance.symbol })
2128
+ ] }),
2129
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: nativeBalance.formatted })
2130
+ ]
2131
+ },
2132
+ nativeBalance.symbol
2133
+ ),
2134
+ isLoading && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
2135
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2136
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2137
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
2138
+ ] }),
2139
+ erc20Balances.map((coin) => {
2140
+ const Icon = getSymbolLogo(coin.symbol);
2141
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2113
2142
  "button",
2114
2143
  {
2115
- className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover)",
2116
- onClick: () => handleSelect(nativeBalance),
2144
+ className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
2145
+ onClick: () => handleSelect(coin),
2117
2146
  children: [
2118
2147
  /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center gap-2", children: [
2119
- nativeBalance.logoURI && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("img", { className: "size-8 shrink-0", src: nativeBalance.logoURI, alt: `${nativeBalance.symbol} logo` }),
2120
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: nativeBalance.symbol })
2148
+ Boolean(Icon) && Icon,
2149
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: coin.symbol })
2121
2150
  ] }),
2122
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: nativeBalance.formatted })
2151
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: coin.formatted })
2123
2152
  ]
2124
2153
  },
2125
- nativeBalance.symbol
2126
- ),
2127
- isLoading && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
2128
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2129
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2130
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
2131
- ] }),
2132
- erc20Balances.map((coin) => {
2133
- const Icon = getSymbolLogo(coin.symbol);
2134
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2135
- "button",
2136
- {
2137
- className: "flex h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
2138
- onClick: () => handleSelect(coin),
2139
- children: [
2140
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center gap-2", children: [
2141
- Boolean(Icon) && Icon,
2142
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: coin.symbol })
2143
- ] }),
2144
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: coin.formatted })
2145
- ]
2146
- },
2147
- coin.symbol
2148
- );
2149
- })
2154
+ coin.symbol
2155
+ );
2156
+ })
2157
+ ] }),
2158
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h3", { className: "text-md font-medium text-(--brand-primary)", children: "All Tokens" }),
2159
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex max-h-[40vh] w-full flex-col gap-1 overflow-y-auto", children: [
2160
+ tokensIsLoading && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
2161
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2162
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2163
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
2150
2164
  ] }),
2151
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h3", { className: "text-md font-medium text-(--brand-primary)", children: "All Tokens" }),
2152
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex max-h-[40vh] w-full flex-col gap-1 overflow-y-auto", children: [
2153
- tokensIsLoading && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
2154
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2155
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" }),
2156
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "h-11 animate-pulse rounded-md bg-(--s-primary)" })
2157
- ] }),
2158
- filteredCoins.map((token) => {
2159
- const userCoin = userCoins.find((c) => c.symbol === token.symbol);
2160
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2161
- "button",
2162
- {
2163
- disabled: !userCoin,
2164
- onClick: () => userCoin && handleSelect(userCoin),
2165
- className: "flex min-h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
2166
- children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center gap-2", children: [
2167
- token.logoURI ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("img", { className: "size-8 shrink-0", src: token.logoURI, alt: `${token.name} logo` }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "size-8 shrink-0 rounded-full bg-(--s-tertiary)" }),
2168
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: token.symbol })
2169
- ] })
2170
- },
2171
- token.symbol
2172
- );
2173
- })
2174
- ] })
2165
+ filteredCoins.map((token) => {
2166
+ const userCoin = userCoins.find((c) => c.symbol === token.symbol);
2167
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2168
+ "button",
2169
+ {
2170
+ disabled: !userCoin,
2171
+ onClick: () => userCoin && handleSelect(userCoin),
2172
+ className: "flex min-h-11 w-full items-center justify-between gap-4 rounded-sm px-1.5 text-(--brand-primary) hover:bg-(--s-primary-hover) disabled:cursor-not-allowed disabled:opacity-50",
2173
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex items-center gap-2", children: [
2174
+ token.logoURI ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("img", { className: "size-8 shrink-0", src: token.logoURI, alt: `${token.name} logo` }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "size-8 shrink-0 rounded-full bg-(--s-tertiary)" }),
2175
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-sm font-medium", children: token.symbol })
2176
+ ] })
2177
+ },
2178
+ token.symbol
2179
+ );
2180
+ })
2175
2181
  ] })
2176
2182
  ] })
2177
- ] });
2183
+ ] }) });
2178
2184
  });
2179
2185
  CryptoSelectModal.displayName = "CryptoSelectModal";
2180
2186
  }
@@ -2452,15 +2458,17 @@ var import_jsx_runtime43, Checkout;
2452
2458
  var init_Checkout = __esm({
2453
2459
  "src/components/CryptoComTab/Checkout.tsx"() {
2454
2460
  "use strict";
2461
+ init_SpreePayActionsContext();
2455
2462
  init_StaticConfigContext();
2456
2463
  import_jsx_runtime43 = require("react/jsx-runtime");
2457
2464
  Checkout = () => {
2458
2465
  const { appProps } = useStaticConfig();
2466
+ const { isInternalProcessing } = useSpreePaymentMethod();
2459
2467
  return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2460
2468
  "button",
2461
2469
  {
2462
2470
  onClick: appProps.onProcess,
2463
- disabled: appProps.isProcessing,
2471
+ disabled: !!appProps.isProcessing || isInternalProcessing,
2464
2472
  className: "flex flex-col items-center rounded-md bg-(--crypto-pay-bg) p-2 text-(--brand-primary) hover:bg-(--crypto-pay-bg-hover) disabled:cursor-not-allowed disabled:bg-(--crypto-pay-bg-hover) disabled:text-(--disabled)",
2465
2473
  children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-7 w-[76px]", fill: "none", viewBox: "0 0 76 28", children: [
2466
2474
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
@@ -3333,18 +3341,17 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3333
3341
  const [cardError, setCardError] = (0, import_react8.useState)(void 0);
3334
3342
  const [stripeStyles, setStripeStyles] = (0, import_react8.useState)({});
3335
3343
  const [shouldSaveCard, setShouldSaveCard] = (0, import_react8.useState)(true);
3336
- const formRef = (0, import_react8.useRef)(null);
3344
+ const [isSubmitting, setIsSubmitting] = (0, import_react8.useState)(false);
3337
3345
  const elements = (0, import_react_stripe_js.useElements)();
3338
3346
  const stripe = (0, import_react_stripe_js.useStripe)();
3339
3347
  const [id] = (0, import_react8.useState)(() => crypto.randomUUID());
3340
- const computeStripeStyles = (0, import_react8.useCallback)(() => {
3341
- const formRefCurrent = formRef.current;
3342
- if (typeof window === "undefined" || !formRefCurrent) return {};
3343
- const container = formRefCurrent.closest(".sl-spreepay");
3344
- if (!container) return {};
3348
+ const formRef = (0, import_react8.useCallback)((node) => {
3349
+ if (!node) return;
3350
+ const container = node.closest(".sl-spreepay");
3351
+ if (!container) return;
3345
3352
  const primary = getComputedStyle(container).getPropertyValue("--primary").trim() || "rgba(255, 255, 255, 0.70)";
3346
3353
  const secondary = getComputedStyle(container).getPropertyValue("--secondary").trim() || "rgba(255, 255, 255, 0.5)";
3347
- return {
3354
+ setStripeStyles({
3348
3355
  base: {
3349
3356
  fontSize: "16px",
3350
3357
  fontWeight: "500",
@@ -3356,14 +3363,12 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3356
3363
  invalid: {
3357
3364
  color: primary
3358
3365
  }
3359
- };
3366
+ });
3360
3367
  }, []);
3361
- (0, import_react8.useEffect)(() => {
3362
- setStripeStyles(computeStripeStyles());
3363
- }, [computeStripeStyles]);
3364
3368
  const handleSaveCard = async () => {
3365
- if (!elements || !stripe) return;
3369
+ if (!elements || !stripe || isSubmitting) return;
3366
3370
  setCardError(void 0);
3371
+ setIsSubmitting(true);
3367
3372
  try {
3368
3373
  const { error: submitError } = await elements.submit();
3369
3374
  if (submitError) {
@@ -3394,6 +3399,8 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3394
3399
  } catch (error) {
3395
3400
  cardFormLogger.error("Failed to create Stripe card token", error);
3396
3401
  setCardError("An error occurred while processing your card. Please try again.");
3402
+ } finally {
3403
+ setIsSubmitting(false);
3397
3404
  }
3398
3405
  };
3399
3406
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
@@ -3443,6 +3450,7 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3443
3450
  variant: "outline",
3444
3451
  className: "border-(--b-brand) bg-transparent font-medium text-(--brand-primary) hover:bg-(--s-primary-hover) hover:text-(--brand-primary)",
3445
3452
  onClick: cancel,
3453
+ disabled: isSubmitting,
3446
3454
  children: "Cancel"
3447
3455
  }
3448
3456
  ),
@@ -3451,6 +3459,7 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3451
3459
  {
3452
3460
  className: "bg-(--s-brand) font-medium text-(--inverse) hover:bg-(--s-brand-hover)",
3453
3461
  onClick: handleSaveCard,
3462
+ disabled: isSubmitting,
3454
3463
  children: "Add Card"
3455
3464
  }
3456
3465
  )
package/build/index.css CHANGED
@@ -1784,4 +1784,4 @@
1784
1784
  }
1785
1785
  }
1786
1786
  }
1787
- /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
1787
+ /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
package/build/index.js CHANGED
@@ -8,10 +8,10 @@ import {
8
8
  getSplitAmount,
9
9
  getTransactionFee,
10
10
  useSlapiBalance
11
- } from "./chunk-W43VOUYP.js";
11
+ } from "./chunk-WHTBHEIN.js";
12
12
  import {
13
13
  Iframe3ds
14
- } from "./chunk-EXKCBGNL.js";
14
+ } from "./chunk-VDX65MBD.js";
15
15
  import {
16
16
  InfoBanner,
17
17
  LogLevel,
@@ -32,10 +32,10 @@ import {
32
32
  useSpreePayRegister,
33
33
  useSpreePaymentMethod,
34
34
  useStaticConfig
35
- } from "./chunk-SKM7ZAUY.js";
35
+ } from "./chunk-NAY4SEME.js";
36
36
 
37
37
  // src/SpreePay.tsx
38
- import { useCallback as useCallback8, useEffect as useEffect8, useMemo as useMemo9, useState as useState12 } from "react";
38
+ import { useCallback as useCallback8, useEffect as useEffect7, useMemo as useMemo9, useState as useState12 } from "react";
39
39
  import NiceModal4 from "@ebay/nice-modal-react";
40
40
  import { SWRConfig } from "swr";
41
41
 
@@ -43,7 +43,7 @@ import { SWRConfig } from "swr";
43
43
  import { Suspense, lazy } from "react";
44
44
 
45
45
  // src/components/CreditCardTab/CreditCardTab.tsx
46
- import { useCallback as useCallback6, useEffect as useEffect6, useState as useState10 } from "react";
46
+ import { useCallback as useCallback6, useEffect as useEffect5, useState as useState10 } from "react";
47
47
 
48
48
  // src/hooks/payments/useCardPayment.ts
49
49
  import NiceModal from "@ebay/nice-modal-react";
@@ -655,7 +655,7 @@ var CardsList = ({ selectedCard, setCard, newCards, onRemoveNewCard }) => {
655
655
  };
656
656
 
657
657
  // src/components/CreditCardTab/CreditCard/CreditCardForm.tsx
658
- import { useCallback, useEffect, useRef, useState as useState2 } from "react";
658
+ import { useCallback, useState as useState2 } from "react";
659
659
  import { CardCvcElement, CardExpiryElement, CardNumberElement, useElements, useStripe } from "@stripe/react-stripe-js";
660
660
 
661
661
  // src/ui/button.tsx
@@ -737,18 +737,17 @@ var CreditCardForm = ({ cancel, saveCard }) => {
737
737
  const [cardError, setCardError] = useState2(void 0);
738
738
  const [stripeStyles, setStripeStyles] = useState2({});
739
739
  const [shouldSaveCard, setShouldSaveCard] = useState2(true);
740
- const formRef = useRef(null);
740
+ const [isSubmitting, setIsSubmitting] = useState2(false);
741
741
  const elements = useElements();
742
742
  const stripe = useStripe();
743
743
  const [id] = useState2(() => crypto.randomUUID());
744
- const computeStripeStyles = useCallback(() => {
745
- const formRefCurrent = formRef.current;
746
- if (typeof window === "undefined" || !formRefCurrent) return {};
747
- const container = formRefCurrent.closest(".sl-spreepay");
748
- if (!container) return {};
744
+ const formRef = useCallback((node) => {
745
+ if (!node) return;
746
+ const container = node.closest(".sl-spreepay");
747
+ if (!container) return;
749
748
  const primary = getComputedStyle(container).getPropertyValue("--primary").trim() || "rgba(255, 255, 255, 0.70)";
750
749
  const secondary = getComputedStyle(container).getPropertyValue("--secondary").trim() || "rgba(255, 255, 255, 0.5)";
751
- return {
750
+ setStripeStyles({
752
751
  base: {
753
752
  fontSize: "16px",
754
753
  fontWeight: "500",
@@ -760,14 +759,12 @@ var CreditCardForm = ({ cancel, saveCard }) => {
760
759
  invalid: {
761
760
  color: primary
762
761
  }
763
- };
762
+ });
764
763
  }, []);
765
- useEffect(() => {
766
- setStripeStyles(computeStripeStyles());
767
- }, [computeStripeStyles]);
768
764
  const handleSaveCard = async () => {
769
- if (!elements || !stripe) return;
765
+ if (!elements || !stripe || isSubmitting) return;
770
766
  setCardError(void 0);
767
+ setIsSubmitting(true);
771
768
  try {
772
769
  const { error: submitError } = await elements.submit();
773
770
  if (submitError) {
@@ -798,6 +795,8 @@ var CreditCardForm = ({ cancel, saveCard }) => {
798
795
  } catch (error) {
799
796
  cardFormLogger.error("Failed to create Stripe card token", error);
800
797
  setCardError("An error occurred while processing your card. Please try again.");
798
+ } finally {
799
+ setIsSubmitting(false);
801
800
  }
802
801
  };
803
802
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
@@ -847,6 +846,7 @@ var CreditCardForm = ({ cancel, saveCard }) => {
847
846
  variant: "outline",
848
847
  className: "border-(--b-brand) bg-transparent font-medium text-(--brand-primary) hover:bg-(--s-primary-hover) hover:text-(--brand-primary)",
849
848
  onClick: cancel,
849
+ disabled: isSubmitting,
850
850
  children: "Cancel"
851
851
  }
852
852
  ),
@@ -855,6 +855,7 @@ var CreditCardForm = ({ cancel, saveCard }) => {
855
855
  {
856
856
  className: "bg-(--s-brand) font-medium text-(--inverse) hover:bg-(--s-brand-hover)",
857
857
  onClick: handleSaveCard,
858
+ disabled: isSubmitting,
858
859
  children: "Add Card"
859
860
  }
860
861
  )
@@ -925,7 +926,7 @@ var CreditCard = ({ newCards, setNewCards }) => {
925
926
  import { useState as useState9 } from "react";
926
927
 
927
928
  // src/components/CreditCardTab/Points/SplitBlock.tsx
928
- import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef6, useState as useState8 } from "react";
929
+ import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef5, useState as useState8 } from "react";
929
930
  import { BUILD_ENV as BUILD_ENV2 } from "@mocanetwork/airkit";
930
931
 
931
932
  // src/components/CreditCardTab/Points/PointsSelector.tsx
@@ -2026,7 +2027,7 @@ var SplitBlock = (props) => {
2026
2027
  const { useWeb3Points, environment } = useSpreePayEnv();
2027
2028
  const hasForeignCurrency = !!(currencyCode && exchangeRate && foreignCurrencyAmount);
2028
2029
  const formatPointsValue = (usd) => hasForeignCurrency ? formatCurrency(usd / exchangeRate, currencyCode) : formatCurrency(usd);
2029
- const prevPointsChainRef = useRef6(spreePayConfig?.pointsChain);
2030
+ const prevPointsChainRef = useRef5(spreePayConfig?.pointsChain);
2030
2031
  const initWallet = useCallback5(
2031
2032
  async (pointsChain) => {
2032
2033
  if (!pointsChain) return;
@@ -2050,7 +2051,7 @@ var SplitBlock = (props) => {
2050
2051
  },
2051
2052
  [onToggle, environment]
2052
2053
  );
2053
- useEffect5(() => {
2054
+ useEffect4(() => {
2054
2055
  if (!useWeb3Points) return;
2055
2056
  const pointsChainChanged = prevPointsChainRef.current !== spreePayConfig?.pointsChain;
2056
2057
  prevPointsChainRef.current = spreePayConfig?.pointsChain;
@@ -2177,7 +2178,7 @@ var CreditCardTab = ({ isLoggedIn }) => {
2177
2178
  mutateBalance
2178
2179
  ]
2179
2180
  );
2180
- useEffect6(() => {
2181
+ useEffect5(() => {
2181
2182
  register(handlePay);
2182
2183
  }, [register, handlePay]);
2183
2184
  return /* @__PURE__ */ jsxs9("div", { children: [
@@ -2259,9 +2260,9 @@ var TabButtons = (props) => {
2259
2260
 
2260
2261
  // src/SpreePayContent.tsx
2261
2262
  import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
2262
- var CryptoTab = lazy(() => import("./CryptoTab-DQCQF6BG.js").then((module) => ({ default: module.CryptoTab })));
2263
+ var CryptoTab = lazy(() => import("./CryptoTab-4WXYLS43.js").then((module) => ({ default: module.CryptoTab })));
2263
2264
  var CryptoComTab = lazy(
2264
- () => import("./CryptoComTab-3SEGMLPB.js").then((module) => ({ default: module.CryptoComTab }))
2265
+ () => import("./CryptoComTab-OCWI7MFM.js").then((module) => ({ default: module.CryptoComTab }))
2265
2266
  );
2266
2267
  var TabLoadingFallback = () => /* @__PURE__ */ jsx18("div", { className: "flex items-center justify-center px-5 py-8 md:px-7", children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-3", children: [
2267
2268
  /* @__PURE__ */ jsx18("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-(--border-component-specific-card) border-t-(--brand-primary)" }),
@@ -2331,16 +2332,16 @@ var ErrorBoundary = class extends Component {
2331
2332
  };
2332
2333
 
2333
2334
  // src/hooks/useKeycloakSSO.ts
2334
- import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef7, useState as useState11 } from "react";
2335
+ import { useCallback as useCallback7, useEffect as useEffect6, useRef as useRef6, useState as useState11 } from "react";
2335
2336
  import Keycloak from "keycloak-js";
2336
2337
  var refreshAheadSeconds = 60;
2337
2338
  var keycloakLogger = logger.child("keycloak");
2338
2339
  function useKeycloakSSO(config) {
2339
2340
  const { url, realm, clientId, ssoPageURI, enabled } = config;
2340
- const initRef = useRef7(false);
2341
- const kcRef = useRef7(null);
2342
- const refreshTimerRef = useRef7(null);
2343
- const scheduleRefreshRef = useRef7(() => {
2341
+ const initRef = useRef6(false);
2342
+ const kcRef = useRef6(null);
2343
+ const refreshTimerRef = useRef6(null);
2344
+ const scheduleRefreshRef = useRef6(() => {
2344
2345
  });
2345
2346
  const [error, setError] = useState11(null);
2346
2347
  const [isChecking, setIsChecking] = useState11(enabled);
@@ -2369,10 +2370,10 @@ function useKeycloakSSO(config) {
2369
2370
  });
2370
2371
  }, delayMs);
2371
2372
  }, []);
2372
- useEffect7(() => {
2373
+ useEffect6(() => {
2373
2374
  scheduleRefreshRef.current = scheduleRefresh;
2374
2375
  }, [scheduleRefresh]);
2375
- useEffect7(() => {
2376
+ useEffect6(() => {
2376
2377
  if (initRef.current || !enabled) return;
2377
2378
  initRef.current = true;
2378
2379
  const kc = new Keycloak({ url, realm, clientId });
@@ -2426,7 +2427,7 @@ var SpreePayInner = () => {
2426
2427
  setPortalEl(el ?? null);
2427
2428
  }, []);
2428
2429
  const { environment, tenantId, keycloakClientId, accessToken: envAccessToken, ssoPageURI } = useSpreePayEnv();
2429
- useEffect8(() => {
2430
+ useEffect7(() => {
2430
2431
  configureLogger({ environment });
2431
2432
  logger.logVersion();
2432
2433
  }, [environment]);
@@ -2481,9 +2482,9 @@ var SpreePay = (props) => {
2481
2482
  };
2482
2483
 
2483
2484
  // src/hooks/useCapture3DS.ts
2484
- import { useEffect as useEffect9 } from "react";
2485
+ import { useEffect as useEffect8 } from "react";
2485
2486
  var useCapture3DS = (searchParams) => {
2486
- useEffect9(() => {
2487
+ useEffect8(() => {
2487
2488
  if (typeof window !== "undefined" && window.parent && searchParams?.paymentIntent) {
2488
2489
  window.parent.SP_EVENT_BUS?.emit("paymentIntent", { paymentIntent: searchParams.paymentIntent });
2489
2490
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superlogic/spree-pay",
3
- "version": "0.4.8",
3
+ "version": "0.4.11",
4
4
  "description": "Spree-pay React component and utilities",
5
5
  "private": false,
6
6
  "type": "module",
@@ -47,31 +47,31 @@
47
47
  "@radix-ui/react-label": "^2.1.8",
48
48
  "@radix-ui/react-slot": "^1.2.4",
49
49
  "@radix-ui/react-switch": "^1.1.2",
50
- "@rainbow-me/rainbowkit": "^2.2.9",
50
+ "@rainbow-me/rainbowkit": "^2.2.11",
51
51
  "@stripe/react-stripe-js": "^3.10.0",
52
52
  "@stripe/stripe-js": "^7.9.0",
53
- "@tanstack/react-query": "^5.97.0",
53
+ "@tanstack/react-query": "^5.100.10",
54
54
  "class-variance-authority": "^0.7.0",
55
55
  "clsx": "^2.1.1",
56
56
  "jwt-decode": "^4.0.0",
57
- "keycloak-js": "^26.2.3",
58
- "lucide-react": "^1.8.0",
57
+ "keycloak-js": "^26.2.4",
58
+ "lucide-react": "^1.16.0",
59
59
  "swr": "^2.4.1",
60
- "tailwind-merge": "^3.5.0",
61
- "viem": "^2.47.11",
60
+ "tailwind-merge": "^3.6.0",
61
+ "viem": "^2.49.3",
62
62
  "wagmi": "^2.19.5",
63
63
  "xss": "^1.0.15"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@repo/eslint-config": "^1.0.0",
67
- "@tailwindcss/postcss": "^4.2.2",
67
+ "@tailwindcss/postcss": "^4.3.0",
68
68
  "@types/react": "^19",
69
69
  "@types/react-dom": "^19",
70
70
  "postcss-prefix-selector": "^2.1.1",
71
- "tailwindcss": "^4.2.2",
71
+ "tailwindcss": "^4.3.0",
72
72
  "tsup": "^8.5.1",
73
73
  "tw-animate-css": "^1.4.0",
74
- "typescript": "^6.0.2"
74
+ "typescript": "^6.0.3"
75
75
  },
76
76
  "tsup": {
77
77
  "entry": [