@superlogic/spree-pay 0.4.6 → 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-7I6EBCSI.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-AGTOSFMT.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-FKAVWVFL.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-AGTOSFMT.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.6";
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-AGTOSFMT.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-AGTOSFMT.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.6";
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)(
@@ -3137,6 +3145,7 @@ var useCards = () => {
3137
3145
  init_useSlapiBalance();
3138
3146
  init_useSpreePayConfig();
3139
3147
  init_errors();
3148
+ init_payments();
3140
3149
  init_split();
3141
3150
  init_CheckoutButton();
3142
3151
 
@@ -3332,18 +3341,17 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3332
3341
  const [cardError, setCardError] = (0, import_react8.useState)(void 0);
3333
3342
  const [stripeStyles, setStripeStyles] = (0, import_react8.useState)({});
3334
3343
  const [shouldSaveCard, setShouldSaveCard] = (0, import_react8.useState)(true);
3335
- const formRef = (0, import_react8.useRef)(null);
3344
+ const [isSubmitting, setIsSubmitting] = (0, import_react8.useState)(false);
3336
3345
  const elements = (0, import_react_stripe_js.useElements)();
3337
3346
  const stripe = (0, import_react_stripe_js.useStripe)();
3338
3347
  const [id] = (0, import_react8.useState)(() => crypto.randomUUID());
3339
- const computeStripeStyles = (0, import_react8.useCallback)(() => {
3340
- const formRefCurrent = formRef.current;
3341
- if (typeof window === "undefined" || !formRefCurrent) return {};
3342
- const container = formRefCurrent.closest(".sl-spreepay");
3343
- 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;
3344
3352
  const primary = getComputedStyle(container).getPropertyValue("--primary").trim() || "rgba(255, 255, 255, 0.70)";
3345
3353
  const secondary = getComputedStyle(container).getPropertyValue("--secondary").trim() || "rgba(255, 255, 255, 0.5)";
3346
- return {
3354
+ setStripeStyles({
3347
3355
  base: {
3348
3356
  fontSize: "16px",
3349
3357
  fontWeight: "500",
@@ -3355,14 +3363,12 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3355
3363
  invalid: {
3356
3364
  color: primary
3357
3365
  }
3358
- };
3366
+ });
3359
3367
  }, []);
3360
- (0, import_react8.useEffect)(() => {
3361
- setStripeStyles(computeStripeStyles());
3362
- }, [computeStripeStyles]);
3363
3368
  const handleSaveCard = async () => {
3364
- if (!elements || !stripe) return;
3369
+ if (!elements || !stripe || isSubmitting) return;
3365
3370
  setCardError(void 0);
3371
+ setIsSubmitting(true);
3366
3372
  try {
3367
3373
  const { error: submitError } = await elements.submit();
3368
3374
  if (submitError) {
@@ -3393,6 +3399,8 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3393
3399
  } catch (error) {
3394
3400
  cardFormLogger.error("Failed to create Stripe card token", error);
3395
3401
  setCardError("An error occurred while processing your card. Please try again.");
3402
+ } finally {
3403
+ setIsSubmitting(false);
3396
3404
  }
3397
3405
  };
3398
3406
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
@@ -3442,6 +3450,7 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3442
3450
  variant: "outline",
3443
3451
  className: "border-(--b-brand) bg-transparent font-medium text-(--brand-primary) hover:bg-(--s-primary-hover) hover:text-(--brand-primary)",
3444
3452
  onClick: cancel,
3453
+ disabled: isSubmitting,
3445
3454
  children: "Cancel"
3446
3455
  }
3447
3456
  ),
@@ -3450,6 +3459,7 @@ var CreditCardForm = ({ cancel, saveCard }) => {
3450
3459
  {
3451
3460
  className: "bg-(--s-brand) font-medium text-(--inverse) hover:bg-(--s-brand-hover)",
3452
3461
  onClick: handleSaveCard,
3462
+ disabled: isSubmitting,
3453
3463
  children: "Add Card"
3454
3464
  }
3455
3465
  )
@@ -3463,9 +3473,8 @@ var StripeWrapper = ({ onCancel, saveNewCard, publicKey }) => {
3463
3473
  const stripePromise = (0, import_react9.useMemo)(() => (0, import_stripe_js.loadStripe)(publicKey), [publicKey]);
3464
3474
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_stripe_js2.Elements, { stripe: stripePromise, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(CreditCardForm, { cancel: onCancel, saveCard: saveNewCard }) });
3465
3475
  };
3466
- var CreditCard = () => {
3476
+ var CreditCard = ({ newCards, setNewCards }) => {
3467
3477
  const [showForm, setShowForm] = (0, import_react9.useState)(false);
3468
- const [newCards, setNewCards] = (0, import_react9.useState)([]);
3469
3478
  const { selectedPaymentMethod, setSelectedPaymentMethod } = useSpreePaymentMethod();
3470
3479
  const { spreePayConfig } = useSpreePayConfig();
3471
3480
  const setCard = (card) => {
@@ -3481,9 +3490,12 @@ var CreditCard = () => {
3481
3490
  setCard(newCard);
3482
3491
  setShowForm(false);
3483
3492
  };
3484
- const removeNewCard = (0, import_react9.useCallback)((card) => {
3485
- setNewCards((prev) => prev.filter((c) => c.id !== card.id));
3486
- }, []);
3493
+ const removeNewCard = (0, import_react9.useCallback)(
3494
+ (card) => {
3495
+ setNewCards((prev) => prev.filter((c) => c.id !== card.id));
3496
+ },
3497
+ [setNewCards]
3498
+ );
3487
3499
  const handleCancel = () => {
3488
3500
  setShowForm(false);
3489
3501
  };
@@ -4734,8 +4746,9 @@ var CreditCardTab = ({ isLoggedIn }) => {
4734
4746
  const { appProps } = useStaticConfig();
4735
4747
  const { spreePayConfig } = useSpreePayConfig();
4736
4748
  const { register } = useSpreePayRegister();
4737
- const { mutateCards } = useCards();
4749
+ const { cards, mutateCards } = useCards();
4738
4750
  const { mutateBalance } = useSlapiBalance();
4751
+ const [newCards, setNewCards] = (0, import_react15.useState)([]);
4739
4752
  const isWeb3Enabled = Boolean(useWeb3Points);
4740
4753
  const { cardPayment } = useCardPayment();
4741
4754
  const { splitPayment } = useSplitCardPayments(isWeb3Enabled ? "web3" : "web2");
@@ -4760,14 +4773,24 @@ var CreditCardTab = ({ isLoggedIn }) => {
4760
4773
  } catch (_) {
4761
4774
  return Promise.reject(new PaymentError("Payment failed", "FAILED" /* FAILED */));
4762
4775
  } finally {
4763
- if ("token" in (selectedPaymentMethod.method || {})) {
4764
- setSelectedPaymentMethod({ ...selectedPaymentMethod, type: "CREDIT_CARD" /* CREDIT_CARD */, method: null });
4765
- }
4766
- mutateCards();
4767
4776
  mutateBalance();
4777
+ if (selectedPaymentMethod.type === "CREDIT_CARD" /* CREDIT_CARD */ && selectedPaymentMethod.method && isNewCard(selectedPaymentMethod.method)) {
4778
+ const knownIds = new Set(cards.map((c) => c.id));
4779
+ const updated = await mutateCards();
4780
+ const newSavedCard = (updated?.data ?? []).find((c) => c.active && !knownIds.has(c.id));
4781
+ setSelectedPaymentMethod({
4782
+ ...selectedPaymentMethod,
4783
+ type: "CREDIT_CARD" /* CREDIT_CARD */,
4784
+ method: newSavedCard ?? null
4785
+ });
4786
+ setNewCards([]);
4787
+ } else {
4788
+ mutateCards();
4789
+ }
4768
4790
  }
4769
4791
  },
4770
4792
  [
4793
+ cards,
4771
4794
  mutateCards,
4772
4795
  selectedPaymentMethod,
4773
4796
  pointsPayment,
@@ -4783,7 +4806,7 @@ var CreditCardTab = ({ isLoggedIn }) => {
4783
4806
  register(handlePay);
4784
4807
  }, [register, handlePay]);
4785
4808
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { children: [
4786
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "border-b border-b-(--border-component-specific-card) px-5 py-5 md:px-7 md:py-6", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(CreditCard, {}) }),
4809
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "border-b border-b-(--border-component-specific-card) px-5 py-5 md:px-7 md:py-6", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(CreditCard, { newCards, setNewCards }) }),
4787
4810
  !spreePayConfig?.creditCard.hidePoints && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex flex-col gap-4 border-b border-b-(--border-component-specific-card) px-5 pt-5 pb-5 md:px-7 md:pt-6 md:pb-7", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Points, {}) }),
4788
4811
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(CheckoutButton, { isLoggedIn })
4789
4812
  ] });
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-FKAVWVFL.js";
11
+ } from "./chunk-WHTBHEIN.js";
12
12
  import {
13
13
  Iframe3ds
14
- } from "./chunk-7I6EBCSI.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-AGTOSFMT.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 useState11 } 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 } 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
  )
@@ -868,9 +869,8 @@ var StripeWrapper = ({ onCancel, saveNewCard, publicKey }) => {
868
869
  const stripePromise = useMemo3(() => loadStripe(publicKey), [publicKey]);
869
870
  return /* @__PURE__ */ jsx5(Elements, { stripe: stripePromise, children: /* @__PURE__ */ jsx5(CreditCardForm, { cancel: onCancel, saveCard: saveNewCard }) });
870
871
  };
871
- var CreditCard = () => {
872
+ var CreditCard = ({ newCards, setNewCards }) => {
872
873
  const [showForm, setShowForm] = useState3(false);
873
- const [newCards, setNewCards] = useState3([]);
874
874
  const { selectedPaymentMethod, setSelectedPaymentMethod } = useSpreePaymentMethod();
875
875
  const { spreePayConfig } = useSpreePayConfig();
876
876
  const setCard = (card) => {
@@ -886,9 +886,12 @@ var CreditCard = () => {
886
886
  setCard(newCard);
887
887
  setShowForm(false);
888
888
  };
889
- const removeNewCard = useCallback2((card) => {
890
- setNewCards((prev) => prev.filter((c) => c.id !== card.id));
891
- }, []);
889
+ const removeNewCard = useCallback2(
890
+ (card) => {
891
+ setNewCards((prev) => prev.filter((c) => c.id !== card.id));
892
+ },
893
+ [setNewCards]
894
+ );
892
895
  const handleCancel = () => {
893
896
  setShowForm(false);
894
897
  };
@@ -923,7 +926,7 @@ var CreditCard = () => {
923
926
  import { useState as useState9 } from "react";
924
927
 
925
928
  // src/components/CreditCardTab/Points/SplitBlock.tsx
926
- 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";
927
930
  import { BUILD_ENV as BUILD_ENV2 } from "@mocanetwork/airkit";
928
931
 
929
932
  // src/components/CreditCardTab/Points/PointsSelector.tsx
@@ -2024,7 +2027,7 @@ var SplitBlock = (props) => {
2024
2027
  const { useWeb3Points, environment } = useSpreePayEnv();
2025
2028
  const hasForeignCurrency = !!(currencyCode && exchangeRate && foreignCurrencyAmount);
2026
2029
  const formatPointsValue = (usd) => hasForeignCurrency ? formatCurrency(usd / exchangeRate, currencyCode) : formatCurrency(usd);
2027
- const prevPointsChainRef = useRef6(spreePayConfig?.pointsChain);
2030
+ const prevPointsChainRef = useRef5(spreePayConfig?.pointsChain);
2028
2031
  const initWallet = useCallback5(
2029
2032
  async (pointsChain) => {
2030
2033
  if (!pointsChain) return;
@@ -2048,7 +2051,7 @@ var SplitBlock = (props) => {
2048
2051
  },
2049
2052
  [onToggle, environment]
2050
2053
  );
2051
- useEffect5(() => {
2054
+ useEffect4(() => {
2052
2055
  if (!useWeb3Points) return;
2053
2056
  const pointsChainChanged = prevPointsChainRef.current !== spreePayConfig?.pointsChain;
2054
2057
  prevPointsChainRef.current = spreePayConfig?.pointsChain;
@@ -2119,8 +2122,9 @@ var CreditCardTab = ({ isLoggedIn }) => {
2119
2122
  const { appProps } = useStaticConfig();
2120
2123
  const { spreePayConfig } = useSpreePayConfig();
2121
2124
  const { register } = useSpreePayRegister();
2122
- const { mutateCards } = useCards();
2125
+ const { cards, mutateCards } = useCards();
2123
2126
  const { mutateBalance } = useSlapiBalance();
2127
+ const [newCards, setNewCards] = useState10([]);
2124
2128
  const isWeb3Enabled = Boolean(useWeb3Points);
2125
2129
  const { cardPayment } = useCardPayment();
2126
2130
  const { splitPayment } = useSplitCardPayments(isWeb3Enabled ? "web3" : "web2");
@@ -2145,14 +2149,24 @@ var CreditCardTab = ({ isLoggedIn }) => {
2145
2149
  } catch (_) {
2146
2150
  return Promise.reject(new PaymentError("Payment failed", "FAILED" /* FAILED */));
2147
2151
  } finally {
2148
- if ("token" in (selectedPaymentMethod.method || {})) {
2149
- setSelectedPaymentMethod({ ...selectedPaymentMethod, type: "CREDIT_CARD" /* CREDIT_CARD */, method: null });
2150
- }
2151
- mutateCards();
2152
2152
  mutateBalance();
2153
+ if (selectedPaymentMethod.type === "CREDIT_CARD" /* CREDIT_CARD */ && selectedPaymentMethod.method && isNewCard(selectedPaymentMethod.method)) {
2154
+ const knownIds = new Set(cards.map((c) => c.id));
2155
+ const updated = await mutateCards();
2156
+ const newSavedCard = (updated?.data ?? []).find((c) => c.active && !knownIds.has(c.id));
2157
+ setSelectedPaymentMethod({
2158
+ ...selectedPaymentMethod,
2159
+ type: "CREDIT_CARD" /* CREDIT_CARD */,
2160
+ method: newSavedCard ?? null
2161
+ });
2162
+ setNewCards([]);
2163
+ } else {
2164
+ mutateCards();
2165
+ }
2153
2166
  }
2154
2167
  },
2155
2168
  [
2169
+ cards,
2156
2170
  mutateCards,
2157
2171
  selectedPaymentMethod,
2158
2172
  pointsPayment,
@@ -2164,11 +2178,11 @@ var CreditCardTab = ({ isLoggedIn }) => {
2164
2178
  mutateBalance
2165
2179
  ]
2166
2180
  );
2167
- useEffect6(() => {
2181
+ useEffect5(() => {
2168
2182
  register(handlePay);
2169
2183
  }, [register, handlePay]);
2170
2184
  return /* @__PURE__ */ jsxs9("div", { children: [
2171
- /* @__PURE__ */ jsx16("div", { className: "border-b border-b-(--border-component-specific-card) px-5 py-5 md:px-7 md:py-6", children: /* @__PURE__ */ jsx16(CreditCard, {}) }),
2185
+ /* @__PURE__ */ jsx16("div", { className: "border-b border-b-(--border-component-specific-card) px-5 py-5 md:px-7 md:py-6", children: /* @__PURE__ */ jsx16(CreditCard, { newCards, setNewCards }) }),
2172
2186
  !spreePayConfig?.creditCard.hidePoints && /* @__PURE__ */ jsx16("div", { className: "flex flex-col gap-4 border-b border-b-(--border-component-specific-card) px-5 pt-5 pb-5 md:px-7 md:pt-6 md:pb-7", children: /* @__PURE__ */ jsx16(Points, {}) }),
2173
2187
  /* @__PURE__ */ jsx16(CheckoutButton, { isLoggedIn })
2174
2188
  ] });
@@ -2246,9 +2260,9 @@ var TabButtons = (props) => {
2246
2260
 
2247
2261
  // src/SpreePayContent.tsx
2248
2262
  import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
2249
- var CryptoTab = lazy(() => import("./CryptoTab-7EG23TQW.js").then((module) => ({ default: module.CryptoTab })));
2263
+ var CryptoTab = lazy(() => import("./CryptoTab-4WXYLS43.js").then((module) => ({ default: module.CryptoTab })));
2250
2264
  var CryptoComTab = lazy(
2251
- () => import("./CryptoComTab-5662XFNI.js").then((module) => ({ default: module.CryptoComTab }))
2265
+ () => import("./CryptoComTab-OCWI7MFM.js").then((module) => ({ default: module.CryptoComTab }))
2252
2266
  );
2253
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: [
2254
2268
  /* @__PURE__ */ jsx18("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-(--border-component-specific-card) border-t-(--brand-primary)" }),
@@ -2318,20 +2332,20 @@ var ErrorBoundary = class extends Component {
2318
2332
  };
2319
2333
 
2320
2334
  // src/hooks/useKeycloakSSO.ts
2321
- import { useCallback as useCallback7, useEffect as useEffect7, useRef as useRef7, useState as useState10 } from "react";
2335
+ import { useCallback as useCallback7, useEffect as useEffect6, useRef as useRef6, useState as useState11 } from "react";
2322
2336
  import Keycloak from "keycloak-js";
2323
2337
  var refreshAheadSeconds = 60;
2324
2338
  var keycloakLogger = logger.child("keycloak");
2325
2339
  function useKeycloakSSO(config) {
2326
2340
  const { url, realm, clientId, ssoPageURI, enabled } = config;
2327
- const initRef = useRef7(false);
2328
- const kcRef = useRef7(null);
2329
- const refreshTimerRef = useRef7(null);
2330
- const scheduleRefreshRef = useRef7(() => {
2341
+ const initRef = useRef6(false);
2342
+ const kcRef = useRef6(null);
2343
+ const refreshTimerRef = useRef6(null);
2344
+ const scheduleRefreshRef = useRef6(() => {
2331
2345
  });
2332
- const [error, setError] = useState10(null);
2333
- const [isChecking, setIsChecking] = useState10(enabled);
2334
- const [accessToken, setAccessToken] = useState10(null);
2346
+ const [error, setError] = useState11(null);
2347
+ const [isChecking, setIsChecking] = useState11(enabled);
2348
+ const [accessToken, setAccessToken] = useState11(null);
2335
2349
  const scheduleRefresh = useCallback7(() => {
2336
2350
  const kc = kcRef.current;
2337
2351
  if (!kc || !kc.tokenParsed || !kc.tokenParsed.exp) {
@@ -2356,10 +2370,10 @@ function useKeycloakSSO(config) {
2356
2370
  });
2357
2371
  }, delayMs);
2358
2372
  }, []);
2359
- useEffect7(() => {
2373
+ useEffect6(() => {
2360
2374
  scheduleRefreshRef.current = scheduleRefresh;
2361
2375
  }, [scheduleRefresh]);
2362
- useEffect7(() => {
2376
+ useEffect6(() => {
2363
2377
  if (initRef.current || !enabled) return;
2364
2378
  initRef.current = true;
2365
2379
  const kc = new Keycloak({ url, realm, clientId });
@@ -2406,14 +2420,14 @@ var isTokenExpired = (token) => {
2406
2420
  // src/SpreePay.tsx
2407
2421
  import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
2408
2422
  var SpreePayInner = () => {
2409
- const [portalEl, setPortalEl] = useState11(null);
2423
+ const [portalEl, setPortalEl] = useState12(null);
2410
2424
  const rootRef = useCallback8((node) => {
2411
2425
  if (!node) return;
2412
2426
  const el = node.querySelector(":scope > .sl-spreepay__portal");
2413
2427
  setPortalEl(el ?? null);
2414
2428
  }, []);
2415
2429
  const { environment, tenantId, keycloakClientId, accessToken: envAccessToken, ssoPageURI } = useSpreePayEnv();
2416
- useEffect8(() => {
2430
+ useEffect7(() => {
2417
2431
  configureLogger({ environment });
2418
2432
  logger.logVersion();
2419
2433
  }, [environment]);
@@ -2468,9 +2482,9 @@ var SpreePay = (props) => {
2468
2482
  };
2469
2483
 
2470
2484
  // src/hooks/useCapture3DS.ts
2471
- import { useEffect as useEffect9 } from "react";
2485
+ import { useEffect as useEffect8 } from "react";
2472
2486
  var useCapture3DS = (searchParams) => {
2473
- useEffect9(() => {
2487
+ useEffect8(() => {
2474
2488
  if (typeof window !== "undefined" && window.parent && searchParams?.paymentIntent) {
2475
2489
  window.parent.SP_EVENT_BUS?.emit("paymentIntent", { paymentIntent: searchParams.paymentIntent });
2476
2490
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superlogic/spree-pay",
3
- "version": "0.4.6",
3
+ "version": "0.4.11",
4
4
  "description": "Spree-pay React component and utilities",
5
5
  "private": false,
6
6
  "type": "module",
@@ -41,37 +41,37 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@ebay/nice-modal-react": "^1.2.13",
44
- "@mocanetwork/airkit": "^1.7.0",
44
+ "@mocanetwork/airkit": "1.7.0",
45
45
  "@radix-ui/react-checkbox": "^1.1.3",
46
46
  "@radix-ui/react-dialog": "^1.1.15",
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": [