@doujins/payments-ui 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,24 +1,31 @@
1
1
  import * as React3 from 'react';
2
- import { createContext, useMemo, useEffect, useContext, useState, useCallback, useRef } from 'react';
3
- import { createStore } from 'zustand/vanilla';
2
+ import { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext } from 'react';
3
+ import { QueryClient, useQueryClient, useQuery, useMutation, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query';
4
+ import { useWallet, ConnectionProvider, WalletProvider, useConnection } from '@solana/wallet-adapter-react';
5
+ import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui';
6
+ import '@solana/wallet-adapter-react-ui/styles.css';
7
+ import { Connection, VersionedTransaction, Transaction, clusterApiUrl, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
8
+ import { PhantomWalletAdapter } from '@solana/wallet-adapter-phantom';
9
+ import { SolflareWalletAdapter } from '@solana/wallet-adapter-solflare';
10
+ import { TrustWalletAdapter } from '@solana/wallet-adapter-trust';
11
+ import { CoinbaseWalletAdapter } from '@solana/wallet-adapter-coinbase';
12
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
13
+ import { X, ChevronDown, ChevronUp, Check, User, MapPin, Loader2, CreditCard, WalletCards, Trash2, ArrowLeft, CheckCircle, AlertCircle, Wallet, Ban, TriangleAlert, Star, Copy, ExternalLink, Shield, UserRound, Calendar, KeyRound, Sparkles, XCircle, RotateCcw, RefreshCw } from 'lucide-react';
14
+ import { clsx } from 'clsx';
15
+ import { twMerge } from 'tailwind-merge';
4
16
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { ChevronDown, ChevronUp, Check, X, User, MapPin, Loader2, CreditCard, WalletCards, Trash2, Sparkles, CheckCircle, AlertCircle, Wallet, XCircle, RotateCcw, RefreshCw } from 'lucide-react';
6
17
  import countryList from 'country-list';
7
18
  import { cva } from 'class-variance-authority';
8
- import { clsx } from 'clsx';
9
- import { twMerge } from 'tailwind-merge';
10
19
  import * as LabelPrimitive from '@radix-ui/react-label';
11
20
  import * as SelectPrimitive from '@radix-ui/react-select';
12
- import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
13
- import * as DialogPrimitive from '@radix-ui/react-dialog';
14
21
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
15
- import { useWallet, useConnection } from '@solana/wallet-adapter-react';
22
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
16
23
  import { Buffer } from 'buffer';
17
- import { Connection, VersionedTransaction, Transaction, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
18
24
  import { getAssociatedTokenAddress, getAccount, TOKEN_PROGRAM_ID } from '@solana/spl-token';
19
25
  import QRCode from 'qrcode';
20
- import { useStore } from 'zustand';
21
- import * as TabsPrimitive from '@radix-ui/react-tabs';
26
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
27
+ import bs58 from 'bs58';
28
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
22
29
 
23
30
  // src/context/PaymentContext.tsx
24
31
 
@@ -301,6 +308,38 @@ var SubscriptionService = class {
301
308
  body: { ...payload }
302
309
  });
303
310
  }
311
+ async getPaymentHistory(params) {
312
+ const limit = params?.limit ?? 10;
313
+ const offset = params?.offset ?? 0;
314
+ const query = {
315
+ limit: String(limit),
316
+ offset: String(offset)
317
+ };
318
+ if (params?.type) {
319
+ query.type = params.type;
320
+ }
321
+ const response = await this.api.get("/subscriptions/purchases", {
322
+ query
323
+ });
324
+ const totalItems = response?.total_items ?? 0;
325
+ const pageSize = limit;
326
+ const pageNumber = response?.page ?? (pageSize > 0 ? Math.floor(offset / pageSize) + 1 : 1);
327
+ const totalPages = response?.total_pages ?? (pageSize > 0 ? Math.ceil(totalItems / pageSize) : void 0);
328
+ return {
329
+ data: response?.data ?? [],
330
+ total_items: totalItems,
331
+ limit,
332
+ offset,
333
+ page: pageNumber,
334
+ page_size: pageSize,
335
+ total_pages: totalPages
336
+ };
337
+ }
338
+ async cancelSubscription(feedback) {
339
+ return this.api.post("/subscriptions/cancel", {
340
+ body: feedback ? { feedback } : void 0
341
+ });
342
+ }
304
343
  serializePayload(platform, payload) {
305
344
  if (platform === "nmi") {
306
345
  const data2 = payload;
@@ -345,6 +384,48 @@ var SubscriptionService = class {
345
384
  }
346
385
  };
347
386
 
387
+ // src/services/SolanaWalletService.ts
388
+ var SolanaWalletService = class {
389
+ constructor(api) {
390
+ this.api = api;
391
+ }
392
+ async list() {
393
+ const response = await this.api.get(
394
+ "/wallet/solana"
395
+ );
396
+ if (Array.isArray(response)) {
397
+ return response;
398
+ }
399
+ if ("wallets" in response && Array.isArray(response.wallets)) {
400
+ return response.wallets;
401
+ }
402
+ if ("wallet" in response) {
403
+ return response.wallet ? [response.wallet] : [];
404
+ }
405
+ if ("address" in response) {
406
+ return [response];
407
+ }
408
+ return [];
409
+ }
410
+ async requestChallenge(wallet) {
411
+ return this.api.post("/wallet/solana/challenge", {
412
+ body: { wallet }
413
+ });
414
+ }
415
+ async verify(wallet, signature, nonce) {
416
+ const body = { wallet, signature };
417
+ if (nonce) {
418
+ body.nonce = nonce;
419
+ }
420
+ return this.api.post("/wallet/solana/verify", { body });
421
+ }
422
+ async remove(wallet) {
423
+ await this.api.delete("/wallet/solana", {
424
+ query: { wallet }
425
+ });
426
+ }
427
+ };
428
+
348
429
  // src/core/PaymentApp.ts
349
430
  var PaymentApp = class {
350
431
  constructor(options) {
@@ -394,6 +475,7 @@ var PaymentApp = class {
394
475
  this.resolveAuthToken
395
476
  );
396
477
  const solanaPayments = new SolanaPaymentService(billingApi);
478
+ const solanaWallets = new SolanaWalletService(billingApi);
397
479
  const paymentMethods = new PaymentMethodService(accountApi);
398
480
  const cardPayments = new CardPaymentService(this.config);
399
481
  const walletGateway = new WalletGateway();
@@ -403,6 +485,7 @@ var PaymentApp = class {
403
485
  cardPayments,
404
486
  paymentMethods,
405
487
  solanaPayments,
488
+ solanaWallets,
406
489
  tokenCatalog,
407
490
  walletGateway,
408
491
  subscriptions,
@@ -411,141 +494,98 @@ var PaymentApp = class {
411
494
  };
412
495
  }
413
496
  };
414
- var initialState = {
415
- selectedMethodId: null,
416
- solanaModalOpen: false,
417
- savedPaymentStatus: "idle",
418
- savedPaymentError: null,
419
- newCardStatus: "idle",
420
- newCardError: null,
421
- solanaTab: "wallet",
422
- solanaStatus: "selecting",
423
- solanaError: null,
424
- solanaTransactionId: null,
425
- solanaSelectedToken: null,
426
- solanaTokenAmount: 0
427
- };
428
- var createPaymentStore = (options) => createStore((set, get) => {
429
- const notifyStatus = (status, context) => {
430
- options?.callbacks?.onStatusChange?.({ status, context });
431
- };
432
- const notifySuccess = (payload) => {
433
- if (!options?.callbacks?.onSuccess) return;
434
- options.callbacks.onSuccess(payload ?? {});
435
- };
436
- const notifyError = (error) => {
437
- options?.callbacks?.onError?.(new Error(error));
438
- };
439
- return {
440
- ...initialState,
441
- setSelectedMethod: (methodId) => {
442
- if (get().selectedMethodId === methodId) return;
443
- set({ selectedMethodId: methodId });
444
- },
445
- setSolanaModalOpen: (open) => {
446
- if (get().solanaModalOpen === open) return;
447
- set({ solanaModalOpen: open });
448
- },
449
- setSolanaTab: (tab) => {
450
- if (get().solanaTab === tab) return;
451
- set({ solanaTab: tab });
452
- },
453
- setSolanaSelectedToken: (symbol) => {
454
- if (get().solanaSelectedToken === symbol) return;
455
- set({ solanaSelectedToken: symbol });
456
- },
457
- setSolanaTokenAmount: (amount) => {
458
- if (get().solanaTokenAmount === amount) return;
459
- set({ solanaTokenAmount: amount });
460
- },
461
- setSolanaTransactionId: (txId) => {
462
- if (get().solanaTransactionId === txId) return;
463
- set({ solanaTransactionId: txId });
464
- },
465
- startSavedPayment: () => {
466
- notifyStatus("processing", { source: "saved-payment" });
467
- set({ savedPaymentStatus: "processing", savedPaymentError: null });
468
- },
469
- completeSavedPayment: () => {
470
- notifyStatus("success", { source: "saved-payment" });
471
- set({ savedPaymentStatus: "success", savedPaymentError: null });
472
- },
473
- failSavedPayment: (error) => {
474
- notifyStatus("error", { source: "saved-payment" });
475
- notifyError(error);
476
- set({ savedPaymentStatus: "error", savedPaymentError: error });
477
- },
478
- resetSavedPayment: () => set({ savedPaymentStatus: "idle", savedPaymentError: null }),
479
- startNewCardPayment: () => {
480
- notifyStatus("processing", { source: "new-card" });
481
- set({ newCardStatus: "processing", newCardError: null });
482
- },
483
- completeNewCardPayment: () => {
484
- notifyStatus("success", { source: "new-card" });
485
- set({ newCardStatus: "success", newCardError: null });
486
- },
487
- failNewCardPayment: (error) => {
488
- notifyStatus("error", { source: "new-card" });
489
- notifyError(error);
490
- set({ newCardStatus: "error", newCardError: error });
491
- },
492
- resetNewCardPayment: () => set({ newCardStatus: "idle", newCardError: null }),
493
- startSolanaPayment: () => {
494
- notifyStatus("processing", { source: "solana" });
495
- set({ solanaStatus: "processing", solanaError: null });
496
- },
497
- confirmSolanaPayment: () => set({ solanaStatus: "confirming" }),
498
- completeSolanaPayment: (payload) => {
499
- notifyStatus("success", { source: "solana" });
500
- notifySuccess(payload);
501
- set({ solanaStatus: "success", solanaError: null });
502
- },
503
- failSolanaPayment: (error) => {
504
- notifyStatus("error", { source: "solana" });
505
- notifyError(error);
506
- set({ solanaStatus: "error", solanaError: error });
497
+
498
+ // src/runtime/PaymentsRuntime.ts
499
+ var createQueryClient = () => new QueryClient({
500
+ defaultOptions: {
501
+ queries: {
502
+ staleTime: 3e4,
503
+ gcTime: 5 * 6e4,
504
+ refetchOnWindowFocus: false,
505
+ retry: 1
507
506
  },
508
- resetSolanaPayment: () => set({
509
- solanaStatus: "selecting",
510
- solanaError: null,
511
- solanaTransactionId: null
512
- }),
513
- resetAll: () => set(initialState)
514
- };
507
+ mutations: {
508
+ retry: 1
509
+ }
510
+ }
515
511
  });
516
- var PaymentContext = createContext(void 0);
517
- var PaymentProvider = ({
518
- config,
519
- children
520
- }) => {
521
- const app = useMemo(() => new PaymentApp({ config }), [config]);
522
- const store = useMemo(
523
- () => createPaymentStore({ callbacks: config.callbacks }),
524
- [config.callbacks]
525
- );
526
- const value = useMemo(() => {
527
- return {
528
- config: app.getConfig(),
529
- fetcher: app.getFetcher(),
530
- resolveAuthToken: app.resolveAuthToken,
531
- app,
532
- services: app.getServices(),
533
- store
534
- };
535
- }, [app, store]);
536
- useEffect(() => {
537
- if (!value.config.collectJsKey) return;
538
- loadCollectJs(value.config.collectJsKey);
539
- }, [value.config.collectJsKey]);
540
- return /* @__PURE__ */ jsx(PaymentContext.Provider, { value, children });
541
- };
542
- var usePaymentContext = () => {
543
- const context = useContext(PaymentContext);
544
- if (!context) {
545
- throw new Error("usePaymentContext must be used within a PaymentProvider");
512
+ var PaymentsRuntime = class {
513
+ constructor(config) {
514
+ this.config = config;
515
+ this.app = new PaymentApp({ config });
516
+ this.services = this.app.getServices();
517
+ this.queryClient = createQueryClient();
546
518
  }
547
- return context;
548
519
  };
520
+ var createPaymentsRuntime = (config) => new PaymentsRuntime(config);
521
+
522
+ // node_modules/@solana/wallet-adapter-base/lib/esm/types.js
523
+ var WalletAdapterNetwork;
524
+ (function(WalletAdapterNetwork2) {
525
+ WalletAdapterNetwork2["Mainnet"] = "mainnet-beta";
526
+ WalletAdapterNetwork2["Testnet"] = "testnet";
527
+ WalletAdapterNetwork2["Devnet"] = "devnet";
528
+ })(WalletAdapterNetwork || (WalletAdapterNetwork = {}));
529
+ function cn(...inputs) {
530
+ return twMerge(clsx(inputs));
531
+ }
532
+ var Dialog = DialogPrimitive.Root;
533
+ var DialogPortal = ({ className, ...props }) => /* @__PURE__ */ jsx(DialogPrimitive.Portal, { className: cn(className), ...props });
534
+ DialogPortal.displayName = DialogPrimitive.Portal.displayName;
535
+ var DialogOverlay = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
536
+ DialogPrimitive.Overlay,
537
+ {
538
+ ref,
539
+ className: cn(
540
+ "fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
541
+ className
542
+ ),
543
+ ...props
544
+ }
545
+ ));
546
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
547
+ var DialogContent = React3.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
548
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
549
+ /* @__PURE__ */ jsxs(
550
+ DialogPrimitive.Content,
551
+ {
552
+ ref,
553
+ className: cn(
554
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
555
+ className
556
+ ),
557
+ ...props,
558
+ children: [
559
+ children,
560
+ /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [
561
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
562
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
563
+ ] })
564
+ ]
565
+ }
566
+ )
567
+ ] }));
568
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
569
+ var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
570
+ DialogHeader.displayName = "DialogHeader";
571
+ var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
572
+ DialogPrimitive.Title,
573
+ {
574
+ ref,
575
+ className: cn("text-lg font-semibold leading-none tracking-tight", className),
576
+ ...props
577
+ }
578
+ ));
579
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
580
+ var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
581
+ DialogPrimitive.Description,
582
+ {
583
+ ref,
584
+ className: cn("text-sm text-muted-foreground", className),
585
+ ...props
586
+ }
587
+ ));
588
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
549
589
  var customCountries = [
550
590
  { code: "TW", name: "Taiwan, Province of China" },
551
591
  { code: "KR", name: "Korea" },
@@ -675,9 +715,6 @@ function getElementRef(element) {
675
715
  }
676
716
  return element.props.ref || element.ref;
677
717
  }
678
- function cn(...inputs) {
679
- return twMerge(clsx(inputs));
680
- }
681
718
  var buttonVariants = cva(
682
719
  "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
683
720
  {
@@ -820,7 +857,6 @@ var defaultBilling = {
820
857
  firstName: "",
821
858
  lastName: "",
822
859
  address1: "",
823
- address2: "",
824
860
  city: "",
825
861
  stateRegion: "",
826
862
  postalCode: "",
@@ -854,7 +890,6 @@ var CardDetailsForm = ({
854
890
  const [firstName, setFirstName] = useState(mergedDefaults.firstName);
855
891
  const [lastName, setLastName] = useState(mergedDefaults.lastName);
856
892
  const [address1, setAddress1] = useState(mergedDefaults.address1);
857
- const [address2, setAddress2] = useState(mergedDefaults.address2 ?? "");
858
893
  const [city, setCity] = useState(mergedDefaults.city);
859
894
  const [stateRegion, setStateRegion] = useState(mergedDefaults.stateRegion ?? "");
860
895
  const [postalCode, setPostalCode] = useState(mergedDefaults.postalCode);
@@ -873,7 +908,6 @@ var CardDetailsForm = ({
873
908
  setFirstName(mergedDefaults.firstName);
874
909
  setLastName(mergedDefaults.lastName);
875
910
  setAddress1(mergedDefaults.address1);
876
- setAddress2(mergedDefaults.address2 ?? "");
877
911
  setCity(mergedDefaults.city);
878
912
  setStateRegion(mergedDefaults.stateRegion ?? "");
879
913
  setPostalCode(mergedDefaults.postalCode);
@@ -886,7 +920,6 @@ var CardDetailsForm = ({
886
920
  firstName,
887
921
  lastName,
888
922
  address1,
889
- address2,
890
923
  city,
891
924
  stateRegion,
892
925
  postalCode,
@@ -898,7 +931,6 @@ var CardDetailsForm = ({
898
931
  firstName,
899
932
  lastName,
900
933
  address1,
901
- address2,
902
934
  city,
903
935
  stateRegion,
904
936
  postalCode,
@@ -922,7 +954,6 @@ var CardDetailsForm = ({
922
954
  firstName,
923
955
  lastName,
924
956
  address1,
925
- address2,
926
957
  city,
927
958
  stateRegion,
928
959
  postalCode,
@@ -953,7 +984,6 @@ var CardDetailsForm = ({
953
984
  firstName,
954
985
  lastName,
955
986
  address1,
956
- address2,
957
987
  city,
958
988
  stateRegion,
959
989
  postalCode,
@@ -982,19 +1012,16 @@ var CardDetailsForm = ({
982
1012
  window.CollectJS.startPaymentRequest();
983
1013
  };
984
1014
  const errorMessage = localError ?? externalError;
985
- const collectFieldClass = "flex h-11 w-full items-center rounded-md border border-dashed border-muted-foreground/40 bg-muted/20 px-3 text-sm text-muted-foreground";
1015
+ const collectFieldClass = "flex h-11 w-full items-center rounded-md border border-border/60 bg-background px-3 text-sm text-muted-foreground";
986
1016
  return /* @__PURE__ */ jsxs(
987
1017
  "form",
988
1018
  {
989
- className: cn(
990
- "space-y-6 rounded-2xl border border-border/60 bg-card/90 p-6 shadow-lg",
991
- className
992
- ),
1019
+ className: cn("space-y-5", className),
993
1020
  onSubmit: handleSubmit,
994
1021
  noValidate: true,
995
1022
  children: [
996
1023
  errorMessage && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-4 py-2 text-sm text-destructive", children: errorMessage }),
997
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
1024
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [
998
1025
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
999
1026
  /* @__PURE__ */ jsxs(Label, { htmlFor: "payments-first", className: "flex items-center gap-2 text-muted-foreground", children: [
1000
1027
  /* @__PURE__ */ jsx(User, { className: "h-4 w-4" }),
@@ -1040,7 +1067,7 @@ var CardDetailsForm = ({
1040
1067
  )
1041
1068
  ] }),
1042
1069
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1043
- /* @__PURE__ */ jsx(Label, { htmlFor: "payments-address1", children: "Address line 1" }),
1070
+ /* @__PURE__ */ jsx(Label, { htmlFor: "payments-address1", children: "Address" }),
1044
1071
  /* @__PURE__ */ jsx(
1045
1072
  Input,
1046
1073
  {
@@ -1051,18 +1078,7 @@ var CardDetailsForm = ({
1051
1078
  }
1052
1079
  )
1053
1080
  ] }),
1054
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1055
- /* @__PURE__ */ jsx(Label, { htmlFor: "payments-address2", children: "Address line 2 (optional)" }),
1056
- /* @__PURE__ */ jsx(
1057
- Input,
1058
- {
1059
- id: "payments-address2",
1060
- value: address2,
1061
- onChange: (e) => setAddress2(e.target.value)
1062
- }
1063
- )
1064
- ] }),
1065
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
1081
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [
1066
1082
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1067
1083
  /* @__PURE__ */ jsx(Label, { htmlFor: "payments-city", children: "City" }),
1068
1084
  /* @__PURE__ */ jsx(
@@ -1087,7 +1103,7 @@ var CardDetailsForm = ({
1087
1103
  )
1088
1104
  ] })
1089
1105
  ] }),
1090
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
1106
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [
1091
1107
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1092
1108
  /* @__PURE__ */ jsxs(Label, { htmlFor: "payments-postal", className: "flex items-center gap-2 text-muted-foreground", children: [
1093
1109
  /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4" }),
@@ -1107,7 +1123,7 @@ var CardDetailsForm = ({
1107
1123
  /* @__PURE__ */ jsx(Label, { children: "Country" }),
1108
1124
  /* @__PURE__ */ jsxs(Select, { value: country, onValueChange: setCountry, children: [
1109
1125
  /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a country" }) }),
1110
- /* @__PURE__ */ jsx(SelectContent, { className: "max-h-64", children: countries.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
1126
+ /* @__PURE__ */ jsx(SelectContent, { className: "max-h-64 w-full", children: countries.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
1111
1127
  ] })
1112
1128
  ] })
1113
1129
  ] }),
@@ -1196,63 +1212,6 @@ var usePaymentMethods = () => {
1196
1212
  deleteMutation
1197
1213
  };
1198
1214
  };
1199
- var Dialog = DialogPrimitive.Root;
1200
- var DialogPortal = ({ className, ...props }) => /* @__PURE__ */ jsx(DialogPrimitive.Portal, { className: cn(className), ...props });
1201
- DialogPortal.displayName = DialogPrimitive.Portal.displayName;
1202
- var DialogOverlay = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1203
- DialogPrimitive.Overlay,
1204
- {
1205
- ref,
1206
- className: cn(
1207
- "fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
1208
- className
1209
- ),
1210
- ...props
1211
- }
1212
- ));
1213
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
1214
- var DialogContent = React3.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
1215
- /* @__PURE__ */ jsx(DialogOverlay, {}),
1216
- /* @__PURE__ */ jsxs(
1217
- DialogPrimitive.Content,
1218
- {
1219
- ref,
1220
- className: cn(
1221
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
1222
- className
1223
- ),
1224
- ...props,
1225
- children: [
1226
- children,
1227
- /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [
1228
- /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
1229
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
1230
- ] })
1231
- ]
1232
- }
1233
- )
1234
- ] }));
1235
- DialogContent.displayName = DialogPrimitive.Content.displayName;
1236
- var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
1237
- DialogHeader.displayName = "DialogHeader";
1238
- var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1239
- DialogPrimitive.Title,
1240
- {
1241
- ref,
1242
- className: cn("text-lg font-semibold leading-none tracking-tight", className),
1243
- ...props
1244
- }
1245
- ));
1246
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
1247
- var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1248
- DialogPrimitive.Description,
1249
- {
1250
- ref,
1251
- className: cn("text-sm text-muted-foreground", className),
1252
- ...props
1253
- }
1254
- ));
1255
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
1256
1215
  var badgeVariants = cva(
1257
1216
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none",
1258
1217
  {
@@ -1302,43 +1261,6 @@ var ScrollBar = React3.forwardRef(({ className, orientation = "vertical", ...pro
1302
1261
  }
1303
1262
  ));
1304
1263
  ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
1305
- var Card = React3.forwardRef(
1306
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1307
- "div",
1308
- {
1309
- ref,
1310
- className: cn("rounded-xl border bg-card text-card-foreground shadow", className),
1311
- ...props
1312
- }
1313
- )
1314
- );
1315
- Card.displayName = "Card";
1316
- var CardHeader = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1317
- "div",
1318
- {
1319
- ref,
1320
- className: cn("flex flex-col space-y-1.5 p-6", className),
1321
- ...props
1322
- }
1323
- ));
1324
- CardHeader.displayName = "CardHeader";
1325
- var CardTitle = React3.forwardRef(
1326
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })
1327
- );
1328
- CardTitle.displayName = "CardTitle";
1329
- var CardDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
1330
- CardDescription.displayName = "CardDescription";
1331
- var CardContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
1332
- CardContent.displayName = "CardContent";
1333
- var CardFooter = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1334
- "div",
1335
- {
1336
- ref,
1337
- className: cn("flex items-center p-6 pt-0", className),
1338
- ...props
1339
- }
1340
- ));
1341
- CardFooter.displayName = "CardFooter";
1342
1264
  var formatCardLabel = (method) => {
1343
1265
  const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
1344
1266
  const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
@@ -1367,22 +1289,22 @@ var StoredPaymentMethods = ({
1367
1289
  }
1368
1290
  );
1369
1291
  };
1370
- return /* @__PURE__ */ jsxs(Card, { className: "border-border/60 bg-card/95", children: [
1371
- /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-start justify-between space-y-0", children: [
1372
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1373
- /* @__PURE__ */ jsx(CardTitle, { className: "text-base font-semibold text-foreground", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium text-muted-foreground", children: [
1292
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1293
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
1294
+ /* @__PURE__ */ jsxs("div", { children: [
1295
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-muted-foreground", children: [
1374
1296
  /* @__PURE__ */ jsx(WalletCards, { className: "h-4 w-4" }),
1375
1297
  " ",
1376
1298
  heading
1377
- ] }) }),
1378
- /* @__PURE__ */ jsx(CardDescription, { children: description })
1299
+ ] }),
1300
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: description })
1379
1301
  ] }),
1380
- showAddButton && /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => setIsModalOpen(true), children: [
1302
+ showAddButton && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setIsModalOpen(true), children: [
1381
1303
  /* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
1382
1304
  " Add card"
1383
1305
  ] })
1384
1306
  ] }),
1385
- /* @__PURE__ */ jsx(CardContent, { children: listQuery.isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center rounded-lg border border-dashed border-border/60 bg-muted/10 py-8 text-sm text-muted-foreground", children: [
1307
+ listQuery.isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center rounded-lg border border-dashed border-border/60 bg-muted/10 py-8 text-sm text-muted-foreground", children: [
1386
1308
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
1387
1309
  " Loading cards\u2026"
1388
1310
  ] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-border/60 bg-muted/10 px-4 py-6 text-sm text-muted-foreground", children: "No saved payment methods yet." }) : /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[320px] pr-2", children: /* @__PURE__ */ jsx("div", { className: "space-y-3", children: payments.map((method) => {
@@ -1440,8 +1362,8 @@ var StoredPaymentMethods = ({
1440
1362
  },
1441
1363
  method.id
1442
1364
  );
1443
- }) }) }) }),
1444
- /* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[90vh] overflow-y-auto", children: [
1365
+ }) }) }),
1366
+ /* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[85vh] overflow-y-auto", children: [
1445
1367
  /* @__PURE__ */ jsxs(DialogHeader, { children: [
1446
1368
  /* @__PURE__ */ jsx(DialogTitle, { children: "Add a new card" }),
1447
1369
  /* @__PURE__ */ jsx(DialogDescription, { children: "Your card details are tokenized securely via our payment provider." })
@@ -1460,6 +1382,71 @@ var StoredPaymentMethods = ({
1460
1382
  ] }) })
1461
1383
  ] });
1462
1384
  };
1385
+ var Tabs = TabsPrimitive.Root;
1386
+ var TabsList = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1387
+ TabsPrimitive.List,
1388
+ {
1389
+ ref,
1390
+ className: cn(
1391
+ "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
1392
+ className
1393
+ ),
1394
+ ...props
1395
+ }
1396
+ ));
1397
+ TabsList.displayName = TabsPrimitive.List.displayName;
1398
+ var TabsTrigger = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1399
+ TabsPrimitive.Trigger,
1400
+ {
1401
+ ref,
1402
+ className: cn(
1403
+ "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
1404
+ className
1405
+ ),
1406
+ ...props
1407
+ }
1408
+ ));
1409
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
1410
+ var TabsContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1411
+ TabsPrimitive.Content,
1412
+ {
1413
+ ref,
1414
+ className: cn(
1415
+ "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
1416
+ className
1417
+ ),
1418
+ ...props
1419
+ }
1420
+ ));
1421
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
1422
+ var usePaymentNotifications = () => {
1423
+ const { config } = usePaymentContext();
1424
+ const notifyStatus = useCallback(
1425
+ (status, context) => {
1426
+ config.callbacks?.onStatusChange?.({ status, context });
1427
+ },
1428
+ [config.callbacks]
1429
+ );
1430
+ const notifySuccess = useCallback(
1431
+ (payload) => {
1432
+ config.callbacks?.onSuccess?.(payload ?? {});
1433
+ },
1434
+ [config.callbacks]
1435
+ );
1436
+ const notifyError = useCallback(
1437
+ (error) => {
1438
+ config.callbacks?.onError?.(
1439
+ typeof error === "string" ? new Error(error) : error
1440
+ );
1441
+ },
1442
+ [config.callbacks]
1443
+ );
1444
+ return {
1445
+ notifyStatus,
1446
+ notifySuccess,
1447
+ notifyError
1448
+ };
1449
+ };
1463
1450
  var useSolanaService = () => {
1464
1451
  const { services } = usePaymentContext();
1465
1452
  return useMemo(() => services.solanaPayments, [services]);
@@ -1736,6 +1723,43 @@ var useSolanaDirectPayment = (options) => {
1736
1723
  pay
1737
1724
  };
1738
1725
  };
1726
+ var Card = React3.forwardRef(
1727
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1728
+ "div",
1729
+ {
1730
+ ref,
1731
+ className: cn("rounded-xl border bg-card text-card-foreground shadow", className),
1732
+ ...props
1733
+ }
1734
+ )
1735
+ );
1736
+ Card.displayName = "Card";
1737
+ var CardHeader = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1738
+ "div",
1739
+ {
1740
+ ref,
1741
+ className: cn("flex flex-col space-y-1.5 p-6", className),
1742
+ ...props
1743
+ }
1744
+ ));
1745
+ CardHeader.displayName = "CardHeader";
1746
+ var CardTitle = React3.forwardRef(
1747
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })
1748
+ );
1749
+ CardTitle.displayName = "CardTitle";
1750
+ var CardDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
1751
+ CardDescription.displayName = "CardDescription";
1752
+ var CardContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
1753
+ CardContent.displayName = "CardContent";
1754
+ var CardFooter = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1755
+ "div",
1756
+ {
1757
+ ref,
1758
+ className: cn("flex items-center p-6 pt-0", className),
1759
+ ...props
1760
+ }
1761
+ ));
1762
+ CardFooter.displayName = "CardFooter";
1739
1763
  var DirectPayment = ({
1740
1764
  priceId,
1741
1765
  tokenAmount,
@@ -1756,7 +1780,7 @@ var DirectPayment = ({
1756
1780
  onSuccess: onPaymentSuccess,
1757
1781
  onError: onPaymentError
1758
1782
  });
1759
- return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 border border-border/60 bg-background/80 p-6", children: [
1783
+ return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 rounded-md border border-border/60 bg-background/80 shadow-none p-6", children: [
1760
1784
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1761
1785
  /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: [
1762
1786
  /* @__PURE__ */ jsx(Wallet, { className: "h-4 w-4" }),
@@ -1988,9 +2012,9 @@ var QRCodePayment = ({
1988
2012
  onSuccess: onPaymentSuccess
1989
2013
  });
1990
2014
  if (!selectedToken) {
1991
- return /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-dashed border-border/60 bg-muted/10 px-4 py-6 text-center text-sm text-muted-foreground", children: "Select a token to continue." });
2015
+ return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border/60 bg-muted/10 px-4 py-6 text-center text-sm text-muted-foreground", children: "Select a token to continue." });
1992
2016
  }
1993
- return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 border border-border/60 bg-background/80 p-6", children: [
2017
+ return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 border border-border/60 bg-background/80 p-6 shadow-none rounded-md", children: [
1994
2018
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
1995
2019
  /* @__PURE__ */ jsxs("div", { children: [
1996
2020
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: "Scan with Solana Pay" }),
@@ -2219,109 +2243,21 @@ var useSupportedTokens = () => {
2219
2243
  tokenCount: tokens.length
2220
2244
  };
2221
2245
  };
2222
- var usePaymentStore = (selector) => {
2223
- const { store } = usePaymentContext();
2224
- return useStore(store, selector);
2225
- };
2226
-
2227
- // src/state/selectors.ts
2228
- var selectCheckoutFlow = (state) => ({
2229
- selectedMethodId: state.selectedMethodId,
2230
- savedStatus: state.savedPaymentStatus,
2231
- savedError: state.savedPaymentError,
2232
- newCardStatus: state.newCardStatus,
2233
- newCardError: state.newCardError,
2234
- solanaModalOpen: state.solanaModalOpen,
2235
- setSelectedMethod: state.setSelectedMethod,
2236
- setSolanaModalOpen: state.setSolanaModalOpen,
2237
- startSavedPayment: state.startSavedPayment,
2238
- completeSavedPayment: state.completeSavedPayment,
2239
- failSavedPayment: state.failSavedPayment,
2240
- startNewCardPayment: state.startNewCardPayment,
2241
- completeNewCardPayment: state.completeNewCardPayment,
2242
- failNewCardPayment: state.failNewCardPayment,
2243
- resetSavedPayment: state.resetSavedPayment
2244
- });
2245
- var selectSolanaFlow = (state) => ({
2246
- tab: state.solanaTab,
2247
- status: state.solanaStatus,
2248
- error: state.solanaError,
2249
- transactionId: state.solanaTransactionId,
2250
- tokenAmount: state.solanaTokenAmount,
2251
- selectedTokenSymbol: state.solanaSelectedToken,
2252
- setTab: state.setSolanaTab,
2253
- setTokenAmount: state.setSolanaTokenAmount,
2254
- setTransactionId: state.setSolanaTransactionId,
2255
- setSelectedTokenSymbol: state.setSolanaSelectedToken,
2256
- startSolanaPayment: state.startSolanaPayment,
2257
- confirmSolanaPayment: state.confirmSolanaPayment,
2258
- completeSolanaPayment: state.completeSolanaPayment,
2259
- failSolanaPayment: state.failSolanaPayment,
2260
- resetSolanaPayment: state.resetSolanaPayment
2261
- });
2262
- var Tabs = TabsPrimitive.Root;
2263
- var TabsList = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2264
- TabsPrimitive.List,
2265
- {
2266
- ref,
2267
- className: cn(
2268
- "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
2269
- className
2270
- ),
2271
- ...props
2272
- }
2273
- ));
2274
- TabsList.displayName = TabsPrimitive.List.displayName;
2275
- var TabsTrigger = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2276
- TabsPrimitive.Trigger,
2277
- {
2278
- ref,
2279
- className: cn(
2280
- "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
2281
- className
2282
- ),
2283
- ...props
2284
- }
2285
- ));
2286
- TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
2287
- var TabsContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2288
- TabsPrimitive.Content,
2289
- {
2290
- ref,
2291
- className: cn(
2292
- "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
2293
- className
2294
- ),
2295
- ...props
2296
- }
2297
- ));
2298
- TabsContent.displayName = TabsPrimitive.Content.displayName;
2299
- var SolanaPaymentSelector = ({
2300
- isOpen,
2301
- onClose,
2246
+ var SolanaPaymentView = ({
2302
2247
  priceId,
2303
2248
  usdAmount,
2304
2249
  onSuccess,
2305
- onError
2250
+ onError,
2251
+ onClose
2306
2252
  }) => {
2307
2253
  const { connected } = useWallet();
2308
- const {
2309
- tab: activeTab,
2310
- status: paymentState,
2311
- error: errorMessage,
2312
- transactionId,
2313
- tokenAmount,
2314
- selectedTokenSymbol,
2315
- setTab,
2316
- setTokenAmount,
2317
- setTransactionId,
2318
- setSelectedTokenSymbol,
2319
- startSolanaPayment,
2320
- confirmSolanaPayment,
2321
- completeSolanaPayment,
2322
- failSolanaPayment,
2323
- resetSolanaPayment
2324
- } = usePaymentStore(selectSolanaFlow);
2254
+ const { notifyStatus, notifyError, notifySuccess } = usePaymentNotifications();
2255
+ const [activeTab, setActiveTab] = useState("wallet");
2256
+ const [paymentState, setPaymentState] = useState("selecting");
2257
+ const [errorMessage, setErrorMessage] = useState(null);
2258
+ const [transactionId, setTransactionId] = useState(null);
2259
+ const [tokenAmount, setTokenAmount] = useState(0);
2260
+ const [selectedTokenSymbol, setSelectedTokenSymbol] = useState(null);
2325
2261
  const {
2326
2262
  tokens,
2327
2263
  isLoading: tokensLoading,
@@ -2337,60 +2273,70 @@ var SolanaPaymentSelector = ({
2337
2273
  const defaultToken = tokens.find((token) => token.symbol === "SOL") || tokens[0];
2338
2274
  setSelectedTokenSymbol(defaultToken.symbol);
2339
2275
  }
2340
- }, [tokens, selectedTokenSymbol, setSelectedTokenSymbol]);
2276
+ }, [tokens, selectedTokenSymbol]);
2341
2277
  const handlePaymentStart = useCallback(() => {
2342
- startSolanaPayment();
2343
- }, [startSolanaPayment]);
2278
+ setPaymentState("processing");
2279
+ setErrorMessage(null);
2280
+ notifyStatus("processing", { source: "solana" });
2281
+ }, [notifyStatus]);
2344
2282
  const handlePaymentConfirming = useCallback(() => {
2345
- confirmSolanaPayment();
2346
- }, [confirmSolanaPayment]);
2283
+ setPaymentState("confirming");
2284
+ }, []);
2347
2285
  const handlePaymentSuccess = useCallback(
2348
2286
  (result, txId) => {
2349
2287
  const resolvedTx = txId || (typeof result === "string" ? result : result.transaction_id);
2350
2288
  setTransactionId(resolvedTx);
2351
- completeSolanaPayment(
2352
- typeof result === "string" ? {
2353
- transactionId: resolvedTx,
2354
- processor: "solana",
2355
- metadata: { source: "solana-pay" }
2356
- } : {
2357
- transactionId: result.transaction_id,
2358
- intentId: result.intent_id,
2359
- processor: "solana",
2360
- metadata: {
2361
- purchaseId: result.purchase_id,
2362
- amount: result.amount,
2363
- currency: result.currency
2364
- }
2289
+ setPaymentState("success");
2290
+ setErrorMessage(null);
2291
+ const payload = typeof result === "string" ? {
2292
+ transactionId: resolvedTx,
2293
+ processor: "solana",
2294
+ metadata: { source: "solana-pay" }
2295
+ } : {
2296
+ transactionId: result.transaction_id,
2297
+ intentId: result.intent_id,
2298
+ processor: "solana",
2299
+ metadata: {
2300
+ purchaseId: result.purchase_id,
2301
+ amount: result.amount,
2302
+ currency: result.currency
2365
2303
  }
2366
- );
2304
+ };
2305
+ notifyStatus("success", { source: "solana" });
2306
+ notifySuccess(payload);
2367
2307
  setTimeout(() => {
2368
2308
  onSuccess(result);
2369
2309
  }, 1500);
2370
2310
  },
2371
- [completeSolanaPayment, onSuccess, setTransactionId]
2311
+ [notifyStatus, notifySuccess, onSuccess]
2372
2312
  );
2373
2313
  const handlePaymentError = useCallback(
2374
2314
  (error) => {
2375
- failSolanaPayment(error);
2315
+ setPaymentState("error");
2316
+ setErrorMessage(error);
2317
+ notifyStatus("error", { source: "solana" });
2318
+ notifyError(error);
2376
2319
  onError?.(error);
2377
2320
  },
2378
- [failSolanaPayment, onError]
2321
+ [notifyError, notifyStatus, onError]
2379
2322
  );
2380
- const handleRetry = useCallback(() => {
2381
- resetSolanaPayment();
2323
+ const resetState = useCallback(() => {
2324
+ setPaymentState("selecting");
2325
+ setErrorMessage(null);
2382
2326
  setTransactionId(null);
2383
- }, [resetSolanaPayment, setTransactionId]);
2327
+ }, []);
2328
+ const handleRetry = useCallback(() => {
2329
+ resetState();
2330
+ }, [resetState]);
2384
2331
  const handleClose = useCallback(() => {
2385
2332
  if (paymentState === "processing" || paymentState === "confirming") {
2386
2333
  return;
2387
2334
  }
2388
- resetSolanaPayment();
2389
- setTransactionId(null);
2390
- onClose();
2391
- }, [paymentState, resetSolanaPayment, setTransactionId, onClose]);
2335
+ resetState();
2336
+ onClose?.();
2337
+ }, [paymentState, onClose, resetState]);
2392
2338
  useEffect(() => {
2393
- if (!isOpen || !selectedToken || usdAmount === 0) {
2339
+ if (!selectedToken || usdAmount === 0) {
2394
2340
  setTokenAmount(0);
2395
2341
  return;
2396
2342
  }
@@ -2400,23 +2346,20 @@ var SolanaPaymentSelector = ({
2400
2346
  return;
2401
2347
  }
2402
2348
  setTokenAmount(usdAmount / price);
2403
- }, [isOpen, usdAmount, selectedToken, setTokenAmount]);
2404
- const handleTokenChange = useCallback(
2405
- (value) => {
2406
- setSelectedTokenSymbol(value);
2407
- },
2408
- [setSelectedTokenSymbol]
2409
- );
2349
+ }, [usdAmount, selectedToken]);
2350
+ const handleTokenChange = useCallback((value) => {
2351
+ setSelectedTokenSymbol(value);
2352
+ }, []);
2410
2353
  const wasConnectedRef = useRef(connected);
2411
2354
  useEffect(() => {
2412
2355
  if (connected && !wasConnectedRef.current) {
2413
- setTab("wallet");
2356
+ setActiveTab("wallet");
2414
2357
  }
2415
2358
  if (!connected && wasConnectedRef.current) {
2416
- setTab("qr");
2359
+ setActiveTab("qr");
2417
2360
  }
2418
2361
  wasConnectedRef.current = connected;
2419
- }, [connected, setTab]);
2362
+ }, [connected]);
2420
2363
  const renderBody = () => {
2421
2364
  if (paymentState !== "selecting") {
2422
2365
  return /* @__PURE__ */ jsx(
@@ -2432,63 +2375,79 @@ var SolanaPaymentSelector = ({
2432
2375
  }
2433
2376
  );
2434
2377
  }
2435
- return /* @__PURE__ */ jsx("div", { className: "space-y-6", children: tokensLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center rounded-lg border border-dashed border-border/60 bg-muted/10 py-8 text-sm text-muted-foreground", children: [
2436
- /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
2437
- " Loading supported tokens\u2026"
2438
- ] }) : tokensError ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: tokensError }) : !tokens.length ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-border/60 bg-muted/10 px-4 py-6 text-sm text-muted-foreground", children: "No payment tokens available." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2439
- /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border/60 bg-muted/10 p-4 text-center", children: [
2440
- /* @__PURE__ */ jsxs("div", { className: "text-2xl font-semibold text-foreground", children: [
2378
+ if (tokensLoading) {
2379
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-10 text-sm text-muted-foreground", children: [
2380
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
2381
+ " Loading supported tokens\u2026"
2382
+ ] });
2383
+ }
2384
+ if (tokensError) {
2385
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: tokensError });
2386
+ }
2387
+ if (!tokens.length) {
2388
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No payment tokens available." });
2389
+ }
2390
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2391
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1 text-center", children: [
2392
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Amount due" }),
2393
+ /* @__PURE__ */ jsxs("p", { className: "text-3xl font-semibold text-foreground", children: [
2441
2394
  "$",
2442
2395
  usdAmount.toFixed(2),
2443
2396
  " USD"
2444
2397
  ] }),
2445
- selectedToken && tokenAmount > 0 && /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground", children: [
2398
+ selectedToken && tokenAmount > 0 && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
2446
2399
  "\u2248 ",
2447
2400
  tokenAmount.toFixed(selectedToken.symbol === "SOL" ? 4 : 2),
2448
2401
  " ",
2449
2402
  selectedToken.symbol
2450
2403
  ] })
2451
2404
  ] }),
2452
- /* @__PURE__ */ jsxs(Select, { value: selectedToken?.symbol ?? "", onValueChange: handleTokenChange, children: [
2453
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select token" }) }),
2454
- /* @__PURE__ */ jsx(SelectContent, { className: "max-h-64", children: tokens.map((token) => /* @__PURE__ */ jsxs(SelectItem, { value: token.symbol, children: [
2455
- token.name,
2456
- " (",
2457
- token.symbol,
2458
- ")"
2459
- ] }, token.symbol)) })
2405
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2406
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "Select token" }),
2407
+ /* @__PURE__ */ jsxs(Select, { value: selectedToken?.symbol ?? "", onValueChange: handleTokenChange, children: [
2408
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select token" }) }),
2409
+ /* @__PURE__ */ jsx(SelectContent, { className: "max-h-64", children: tokens.map((token) => /* @__PURE__ */ jsxs(SelectItem, { value: token.symbol, children: [
2410
+ token.name,
2411
+ " (",
2412
+ token.symbol,
2413
+ ")"
2414
+ ] }, token.symbol)) })
2415
+ ] })
2460
2416
  ] }),
2461
- /* @__PURE__ */ jsxs(
2417
+ /* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxs(
2462
2418
  Tabs,
2463
2419
  {
2464
2420
  value: activeTab,
2465
- onValueChange: (value) => setTab(value),
2466
- className: "w-full",
2421
+ onValueChange: (value) => setActiveTab(value),
2422
+ className: "w-full space-y-3",
2467
2423
  children: [
2468
- /* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 bg-muted/20", children: [
2424
+ /* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 bg-muted/10", children: [
2469
2425
  /* @__PURE__ */ jsxs(TabsTrigger, { value: "wallet", disabled: !connected, children: [
2470
2426
  /* @__PURE__ */ jsx(Wallet, { className: "mr-2 h-4 w-4" }),
2471
- " Pay with Wallet"
2427
+ " Wallet"
2472
2428
  ] }),
2473
2429
  /* @__PURE__ */ jsxs(TabsTrigger, { value: "qr", children: [
2474
2430
  /* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
2475
- " Scan QR Code"
2431
+ " QR Code"
2476
2432
  ] })
2477
2433
  ] }),
2478
- /* @__PURE__ */ jsx(TabsContent, { value: "wallet", className: "mt-4", children: activeTab === "wallet" && /* @__PURE__ */ jsx(
2479
- DirectPayment,
2480
- {
2481
- priceId,
2482
- tokenAmount,
2483
- selectedToken,
2484
- supportedTokens: tokens,
2485
- onPaymentStart: handlePaymentStart,
2486
- onPaymentConfirming: handlePaymentConfirming,
2487
- onPaymentSuccess: handlePaymentSuccess,
2488
- onPaymentError: handlePaymentError
2489
- }
2490
- ) }),
2491
- /* @__PURE__ */ jsx(TabsContent, { value: "qr", className: "mt-4", children: activeTab === "qr" && /* @__PURE__ */ jsx(
2434
+ /* @__PURE__ */ jsxs(TabsContent, { value: "wallet", className: "space-y-4", children: [
2435
+ activeTab === "wallet" && /* @__PURE__ */ jsx(
2436
+ DirectPayment,
2437
+ {
2438
+ priceId,
2439
+ tokenAmount,
2440
+ selectedToken,
2441
+ supportedTokens: tokens,
2442
+ onPaymentStart: handlePaymentStart,
2443
+ onPaymentConfirming: handlePaymentConfirming,
2444
+ onPaymentSuccess: handlePaymentSuccess,
2445
+ onPaymentError: handlePaymentError
2446
+ }
2447
+ ),
2448
+ !connected && /* @__PURE__ */ jsx("div", { className: "text-sm text-amber-100", children: "Connect your Solana wallet to continue or switch to QR mode." })
2449
+ ] }),
2450
+ /* @__PURE__ */ jsx(TabsContent, { value: "qr", children: activeTab === "qr" && /* @__PURE__ */ jsx(
2492
2451
  QRCodePayment,
2493
2452
  {
2494
2453
  priceId,
@@ -2499,17 +2458,34 @@ var SolanaPaymentSelector = ({
2499
2458
  ) })
2500
2459
  ]
2501
2460
  }
2502
- ),
2503
- !connected && activeTab === "wallet" && /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-amber-500/40 bg-amber-500/10 p-4 text-sm text-amber-100", children: "Please connect your Solana wallet to complete this payment, or switch to QR mode." })
2504
- ] }) });
2461
+ ) })
2462
+ ] });
2505
2463
  };
2506
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
2507
- /* @__PURE__ */ jsxs(DialogHeader, { className: "space-y-1", children: [
2508
- /* @__PURE__ */ jsx(DialogTitle, { children: "Complete your payment" }),
2509
- /* @__PURE__ */ jsx(DialogDescription, { children: "Select a token and preferred method. We\u2019ll guide you through the rest." })
2464
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2465
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
2466
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2467
+ /* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: "Solana Pay checkout" }),
2468
+ /* @__PURE__ */ jsx("p", { className: "text-2xl font-semibold text-foreground", children: "Pay with Solana" }),
2469
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Choose a supported token and send the payment with your wallet or a QR code." })
2470
+ ] }),
2471
+ onClose && /* @__PURE__ */ jsxs(
2472
+ Button,
2473
+ {
2474
+ type: "button",
2475
+ size: "sm",
2476
+ variant: "ghost",
2477
+ onClick: handleClose,
2478
+ disabled: paymentState === "processing" || paymentState === "confirming",
2479
+ className: "h-8 px-2 text-sm",
2480
+ children: [
2481
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }),
2482
+ " Back"
2483
+ ]
2484
+ }
2485
+ )
2510
2486
  ] }),
2511
2487
  renderBody()
2512
- ] }) });
2488
+ ] });
2513
2489
  };
2514
2490
  var PaymentExperience = ({
2515
2491
  priceId,
@@ -2519,138 +2495,176 @@ var PaymentExperience = ({
2519
2495
  enableNewCard = true,
2520
2496
  enableStoredMethods = true,
2521
2497
  enableSolanaPay = true,
2522
- checkoutSummary,
2523
2498
  onSolanaSuccess,
2524
- onSolanaError
2499
+ onSolanaError,
2500
+ initialMode = "cards"
2525
2501
  }) => {
2526
2502
  const showNewCard = enableNewCard && Boolean(onNewCardPayment);
2527
- const showStored = enableStoredMethods;
2528
- const {
2529
- selectedMethodId,
2530
- savedStatus,
2531
- savedError,
2532
- newCardStatus,
2533
- newCardError,
2534
- solanaModalOpen,
2535
- setSelectedMethod,
2536
- setSolanaModalOpen,
2537
- startSavedPayment,
2538
- completeSavedPayment,
2539
- failSavedPayment,
2540
- startNewCardPayment,
2541
- completeNewCardPayment,
2542
- failNewCardPayment,
2543
- resetSavedPayment
2544
- } = usePaymentStore(selectCheckoutFlow);
2545
- const handleMethodSelect = (method) => {
2546
- setSelectedMethod(method.id);
2547
- resetSavedPayment();
2548
- };
2549
- const handleNewCardTokenize = async (token, billing) => {
2550
- if (!onNewCardPayment) return;
2551
- try {
2552
- startNewCardPayment();
2553
- await onNewCardPayment({ token, billing });
2554
- completeNewCardPayment();
2555
- } catch (error) {
2556
- const message = error instanceof Error ? error.message : "Unable to complete payment";
2557
- failNewCardPayment(message);
2503
+ const showStored = enableStoredMethods && Boolean(onSavedMethodPayment);
2504
+ const defaultTab = showStored ? "saved" : "new";
2505
+ const [activeTab, setActiveTab] = useState(defaultTab);
2506
+ const [mode, setMode] = useState(
2507
+ () => initialMode === "solana" && enableSolanaPay ? "solana" : "cards"
2508
+ );
2509
+ const [selectedMethodId, setSelectedMethodId] = useState(null);
2510
+ const [savedStatus, setSavedStatus] = useState("idle");
2511
+ const [savedError, setSavedError] = useState(null);
2512
+ const [newCardStatus, setNewCardStatus] = useState("idle");
2513
+ const [newCardError, setNewCardError] = useState(null);
2514
+ const { notifyStatus, notifySuccess, notifyError } = usePaymentNotifications();
2515
+ useEffect(() => {
2516
+ setActiveTab(showStored ? "saved" : "new");
2517
+ }, [showStored]);
2518
+ useEffect(() => {
2519
+ if (!enableSolanaPay) {
2520
+ setMode("cards");
2521
+ return;
2558
2522
  }
2559
- };
2560
- const handleSavedPayment = async () => {
2523
+ if (initialMode === "solana") {
2524
+ setMode("solana");
2525
+ } else {
2526
+ setMode("cards");
2527
+ }
2528
+ }, [enableSolanaPay, initialMode]);
2529
+ const handleMethodSelect = useCallback((method) => {
2530
+ setSelectedMethodId(method.id);
2531
+ setSavedStatus("idle");
2532
+ setSavedError(null);
2533
+ }, []);
2534
+ const handleSavedPayment = useCallback(async () => {
2561
2535
  if (!onSavedMethodPayment || !selectedMethodId) return;
2562
2536
  try {
2563
- startSavedPayment();
2537
+ setSavedStatus("processing");
2538
+ setSavedError(null);
2539
+ notifyStatus("processing", { source: "saved-payment" });
2564
2540
  await onSavedMethodPayment({
2565
2541
  paymentMethodId: selectedMethodId,
2566
2542
  amount: usdAmount
2567
2543
  });
2568
- completeSavedPayment();
2544
+ setSavedStatus("success");
2545
+ notifyStatus("success", { source: "saved-payment" });
2569
2546
  } catch (error) {
2570
2547
  const message = error instanceof Error ? error.message : "Unable to complete payment with saved card";
2571
- failSavedPayment(message);
2548
+ setSavedStatus("error");
2549
+ setSavedError(message);
2550
+ notifyStatus("error", { source: "saved-payment" });
2551
+ notifyError(message);
2552
+ }
2553
+ }, [notifyError, notifyStatus, onSavedMethodPayment, selectedMethodId, usdAmount]);
2554
+ const handleNewCardTokenize = useCallback(
2555
+ async (token, billing) => {
2556
+ if (!onNewCardPayment) return;
2557
+ try {
2558
+ setNewCardStatus("processing");
2559
+ setNewCardError(null);
2560
+ notifyStatus("processing", { source: "new-card" });
2561
+ await onNewCardPayment({ token, billing });
2562
+ setNewCardStatus("success");
2563
+ notifyStatus("success", { source: "new-card" });
2564
+ } catch (error) {
2565
+ const message = error instanceof Error ? error.message : "Unable to complete payment";
2566
+ setNewCardStatus("error");
2567
+ setNewCardError(message);
2568
+ notifyStatus("error", { source: "new-card" });
2569
+ notifyError(message);
2570
+ }
2571
+ },
2572
+ [notifyError, notifyStatus, onNewCardPayment]
2573
+ );
2574
+ const showSolanaView = useCallback(() => {
2575
+ if (!enableSolanaPay) return;
2576
+ setMode("solana");
2577
+ }, [enableSolanaPay]);
2578
+ const exitSolanaView = useCallback(() => {
2579
+ setMode("cards");
2580
+ }, []);
2581
+ const handleSolanaSuccess = useCallback(
2582
+ (result) => {
2583
+ onSolanaSuccess?.(result);
2584
+ exitSolanaView();
2585
+ },
2586
+ [exitSolanaView, onSolanaSuccess]
2587
+ );
2588
+ const handleSolanaError = useCallback(
2589
+ (error) => {
2590
+ onSolanaError?.(error);
2591
+ },
2592
+ [onSolanaError]
2593
+ );
2594
+ const renderSavedTab = () => {
2595
+ if (!showStored) {
2596
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Saved payment methods are unavailable right now. Add a new card to get started." });
2572
2597
  }
2598
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2599
+ /* @__PURE__ */ jsx(
2600
+ StoredPaymentMethods,
2601
+ {
2602
+ heading: "Saved cards",
2603
+ selectedMethodId,
2604
+ onMethodSelect: handleMethodSelect,
2605
+ description: "Select one of your stored payment methods.",
2606
+ showAddButton: false
2607
+ }
2608
+ ),
2609
+ /* @__PURE__ */ jsx(
2610
+ Button,
2611
+ {
2612
+ className: "w-full",
2613
+ disabled: !selectedMethodId || savedStatus === "processing",
2614
+ onClick: handleSavedPayment,
2615
+ children: savedStatus === "processing" ? "Processing\u2026" : "Pay with selected card"
2616
+ }
2617
+ ),
2618
+ savedError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: savedError })
2619
+ ] });
2573
2620
  };
2574
- return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
2575
- /* @__PURE__ */ jsxs(Card, { className: "border-border/60 bg-card/95", children: [
2576
- /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
2577
- /* @__PURE__ */ jsxs("div", { children: [
2578
- /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2 text-lg text-foreground", children: [
2579
- /* @__PURE__ */ jsx(CreditCard, { className: "h-5 w-5 text-primary" }),
2580
- " Secure checkout"
2581
- ] }),
2582
- /* @__PURE__ */ jsxs(CardDescription, { children: [
2583
- "Amount due: $",
2584
- usdAmount.toFixed(2)
2585
- ] })
2586
- ] }),
2587
- checkoutSummary && /* @__PURE__ */ jsx("div", { children: checkoutSummary })
2588
- ] }),
2589
- /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "grid gap-8 lg:grid-cols-2", children: [
2590
- showStored && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2591
- /* @__PURE__ */ jsx(
2592
- StoredPaymentMethods,
2593
- {
2594
- selectedMethodId,
2595
- onMethodSelect: handleMethodSelect,
2596
- heading: "Saved cards",
2597
- description: "Use or manage your saved payment methods."
2598
- }
2599
- ),
2600
- onSavedMethodPayment && /* @__PURE__ */ jsx(
2601
- Button,
2602
- {
2603
- className: "w-full",
2604
- disabled: !selectedMethodId || savedStatus === "processing",
2605
- onClick: handleSavedPayment,
2606
- children: savedStatus === "processing" ? "Processing\u2026" : "Pay with selected card"
2607
- }
2608
- ),
2609
- savedError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: savedError })
2610
- ] }),
2611
- showNewCard && /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-border/60 bg-background/80 p-6", children: [
2612
- /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1", children: [
2613
- /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: [
2614
- /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }),
2615
- " Pay with a new card"
2616
- ] }),
2617
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Card details are tokenized via Collect.js and never hit your server." })
2618
- ] }),
2619
- /* @__PURE__ */ jsx(
2620
- CardDetailsForm,
2621
- {
2622
- visible: true,
2623
- submitLabel: "Pay now",
2624
- submitting: newCardStatus === "processing",
2625
- externalError: newCardError,
2626
- onTokenize: handleNewCardTokenize
2627
- }
2628
- )
2629
- ] }) })
2630
- ] }) })
2631
- ] }),
2632
- enableSolanaPay && /* @__PURE__ */ jsx(Card, { className: "border border-primary/40 bg-primary/5", children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col gap-4 text-sm text-primary md:flex-row md:items-center md:justify-between", children: [
2633
- /* @__PURE__ */ jsxs("div", { children: [
2634
- /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-base font-semibold text-primary", children: [
2635
- /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" }),
2636
- " Prefer Solana Pay?"
2637
- ] }),
2638
- /* @__PURE__ */ jsx("p", { className: "text-sm text-primary/80", children: "Use a Solana wallet or QR code for instant settlement." })
2639
- ] }),
2640
- /* @__PURE__ */ jsx(Button, { onClick: () => setSolanaModalOpen(true), children: "Open Solana Pay" })
2641
- ] }) }),
2642
- enableSolanaPay && /* @__PURE__ */ jsx(
2643
- SolanaPaymentSelector,
2621
+ const renderNewTab = () => {
2622
+ if (!showNewCard) {
2623
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Select a subscription plan to add a new card." });
2624
+ }
2625
+ return /* @__PURE__ */ jsx(
2626
+ CardDetailsForm,
2644
2627
  {
2645
- isOpen: solanaModalOpen,
2646
- onClose: () => setSolanaModalOpen(false),
2647
- priceId,
2648
- usdAmount,
2649
- onSuccess: (result) => {
2650
- setSolanaModalOpen(false);
2651
- onSolanaSuccess?.(result);
2652
- },
2653
- onError: onSolanaError
2628
+ visible: true,
2629
+ submitLabel: "Pay now",
2630
+ externalError: newCardError,
2631
+ onTokenize: handleNewCardTokenize,
2632
+ submitting: newCardStatus === "processing"
2633
+ }
2634
+ );
2635
+ };
2636
+ const renderCardExperience = () => /* @__PURE__ */ jsxs(Fragment, { children: [
2637
+ /* @__PURE__ */ jsxs(
2638
+ Tabs,
2639
+ {
2640
+ value: activeTab,
2641
+ onValueChange: (value) => setActiveTab(value),
2642
+ className: "space-y-3",
2643
+ children: [
2644
+ /* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 border border-border/60", children: [
2645
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "saved", disabled: !showStored, children: "Use saved card" }),
2646
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "new", disabled: !showNewCard, children: "Add new card" })
2647
+ ] }),
2648
+ /* @__PURE__ */ jsx(TabsContent, { value: "saved", className: "space-y-4", children: renderSavedTab() }),
2649
+ /* @__PURE__ */ jsx(TabsContent, { value: "new", className: "space-y-4", children: renderNewTab() })
2650
+ ]
2651
+ }
2652
+ ),
2653
+ enableSolanaPay && /* @__PURE__ */ jsxs(Button, { className: "w-full", variant: "secondary", onClick: showSolanaView, children: [
2654
+ /* @__PURE__ */ jsx(Sparkles, { className: "mr-2 h-4 w-4" }),
2655
+ " Pay with Solana"
2656
+ ] })
2657
+ ] });
2658
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-6 pt-4", children: [
2659
+ mode === "cards" && renderCardExperience(),
2660
+ mode === "solana" && enableSolanaPay && /* @__PURE__ */ jsx(
2661
+ SolanaPaymentView,
2662
+ {
2663
+ priceId,
2664
+ usdAmount,
2665
+ onSuccess: handleSolanaSuccess,
2666
+ onError: handleSolanaError,
2667
+ onClose: exitSolanaView
2654
2668
  }
2655
2669
  )
2656
2670
  ] });
@@ -2664,23 +2678,23 @@ var SubscriptionSuccessDialog = ({
2664
2678
  }) => {
2665
2679
  return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (value) => {
2666
2680
  if (!value) onClose();
2667
- }, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-sm text-center", children: [
2668
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2669
- /* @__PURE__ */ jsxs(DialogTitle, { className: "flex flex-col items-center gap-3 text-foreground", children: [
2670
- /* @__PURE__ */ jsx(CheckCircle, { className: "h-10 w-10 text-primary" }),
2671
- "Subscription activated"
2672
- ] }),
2673
- /* @__PURE__ */ jsxs(DialogDescription, { className: "text-base text-muted-foreground", children: [
2674
- "You now have access to ",
2675
- planName,
2676
- ". Billing: ",
2677
- amountLabel,
2678
- " / ",
2679
- billingPeriodLabel,
2680
- "."
2681
+ }, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-full max-w-md overflow-hidden border border-border/70 bg-background/95 p-0 shadow-2xl", children: [
2682
+ /* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-b from-primary/25 via-primary/10 to-background px-6 py-8 text-center", children: [
2683
+ /* @__PURE__ */ jsx("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-background/60", children: /* @__PURE__ */ jsx(CheckCircle, { className: "h-10 w-10 text-primary" }) }),
2684
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
2685
+ /* @__PURE__ */ jsx(DialogTitle, { className: "text-2xl font-semibold text-foreground", children: "Subscription activated" }),
2686
+ /* @__PURE__ */ jsxs(DialogDescription, { className: "text-base text-muted-foreground", children: [
2687
+ "You now have access to ",
2688
+ planName,
2689
+ ". Billing: ",
2690
+ amountLabel,
2691
+ " / ",
2692
+ billingPeriodLabel,
2693
+ "."
2694
+ ] })
2681
2695
  ] })
2682
2696
  ] }),
2683
- /* @__PURE__ */ jsx(Button, { className: "mt-6 w-full", onClick: onClose, children: "Continue" })
2697
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsx(Button, { className: "w-full", onClick: onClose, children: "Continue exploring" }) })
2684
2698
  ] }) });
2685
2699
  };
2686
2700
  var useSubscriptionActions = () => {
@@ -2802,23 +2816,22 @@ var SubscriptionCheckoutModal = ({
2802
2816
  userEmail,
2803
2817
  provider = "mobius",
2804
2818
  onSuccess,
2805
- enableSolanaPay = true
2819
+ enableSolanaPay = true,
2820
+ onSolanaSuccess,
2821
+ onSolanaError,
2822
+ initialMode = "cards"
2806
2823
  }) => {
2807
2824
  const [showSuccess, setShowSuccess] = useState(false);
2808
2825
  const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
2809
2826
  const handleClose = useCallback(
2810
2827
  (nextOpen) => {
2811
2828
  onOpenChange(nextOpen);
2812
- if (!nextOpen) {
2813
- setShowSuccess(false);
2814
- }
2829
+ if (!nextOpen) setShowSuccess(false);
2815
2830
  },
2816
2831
  [onOpenChange]
2817
2832
  );
2818
2833
  const ensurePrice = () => {
2819
- if (!priceId) {
2820
- throw new Error("Select a plan before subscribing.");
2821
- }
2834
+ if (!priceId) throw new Error("Select a plan before subscribing.");
2822
2835
  return priceId;
2823
2836
  };
2824
2837
  const notifySuccess = (result) => {
@@ -2848,40 +2861,34 @@ var SubscriptionCheckoutModal = ({
2848
2861
  };
2849
2862
  const solanaSuccess = (result) => {
2850
2863
  notifySuccess(result);
2864
+ onSolanaSuccess?.(result);
2851
2865
  onOpenChange(false);
2852
2866
  };
2853
- const summary = useMemo(() => {
2854
- if (!planName && !amountLabel) return null;
2855
- return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border/60 bg-muted/10 p-3 text-sm text-muted-foreground", children: [
2856
- /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: planName ?? "Selected plan" }),
2857
- /* @__PURE__ */ jsxs("p", { children: [
2858
- amountLabel ?? `$${usdAmount.toFixed(2)}`,
2859
- " ",
2860
- billingPeriodLabel ? `/ ${billingPeriodLabel}` : ""
2861
- ] })
2862
- ] });
2863
- }, [planName, amountLabel, billingPeriodLabel, usdAmount]);
2867
+ const solanaError = (error) => {
2868
+ onSolanaError?.(error);
2869
+ };
2864
2870
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2865
- /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-3xl", children: [
2866
- !priceId && /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center gap-2 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
2871
+ /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsx(DialogContent, { className: "w-full max-w-3xl max-h-[90vh] overflow-y-auto rounded-md border border-border/60 bg-background p-0 [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-6", children: [
2872
+ !priceId && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
2867
2873
  /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
2868
2874
  " Select a subscription plan to continue."
2869
2875
  ] }),
2870
2876
  /* @__PURE__ */ jsx(
2871
2877
  PaymentExperience,
2872
2878
  {
2873
- priceId: priceId ?? "",
2874
2879
  usdAmount,
2875
- checkoutSummary: summary,
2876
- onNewCardPayment: priceId ? handleNewCardPayment : void 0,
2877
- onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
2880
+ priceId: priceId ?? "",
2881
+ onSolanaSuccess: solanaSuccess,
2882
+ onSolanaError: solanaError,
2878
2883
  enableNewCard: Boolean(priceId),
2879
2884
  enableStoredMethods: Boolean(priceId),
2880
2885
  enableSolanaPay: enableSolanaPay && Boolean(priceId),
2881
- onSolanaSuccess: solanaSuccess
2886
+ onNewCardPayment: priceId ? handleNewCardPayment : void 0,
2887
+ onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
2888
+ initialMode
2882
2889
  }
2883
2890
  )
2884
- ] }) }),
2891
+ ] }) }) }),
2885
2892
  /* @__PURE__ */ jsx(
2886
2893
  SubscriptionSuccessDialog,
2887
2894
  {
@@ -2894,6 +2901,1466 @@ var SubscriptionCheckoutModal = ({
2894
2901
  )
2895
2902
  ] });
2896
2903
  };
2904
+ var wallets = [
2905
+ {
2906
+ id: "phantom",
2907
+ name: "Phantom",
2908
+ icon: "https://phantom.app/img/logo.png"
2909
+ },
2910
+ {
2911
+ id: "solflare",
2912
+ name: "Solflare",
2913
+ icon: "https://solflare.com/favicon.ico"
2914
+ }
2915
+ ];
2916
+ var WalletModal = ({ open, onOpenChange }) => {
2917
+ const [expandedWallet, setExpandedWallet] = useState(null);
2918
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-full max-w-lg max-h-[90vh] overflow-y-auto rounded-md border border-border/70 bg-background/95 p-0 shadow-2xl [&::-webkit-scrollbar]:hidden", children: [
2919
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "border-b border-border/40 bg-gradient-to-r from-primary/10 via-background to-background px-6 py-5 text-left", children: [
2920
+ /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2 text-foreground", children: [
2921
+ /* @__PURE__ */ jsx(Wallet, { className: "h-5 w-5 text-primary" }),
2922
+ " Connect a Solana wallet"
2923
+ ] }),
2924
+ /* @__PURE__ */ jsx(DialogDescription, { className: "text-sm text-muted-foreground", children: "Pick a supported wallet to link with Doujins. Verified wallets unlock Solana payments and withdrawals." })
2925
+ ] }),
2926
+ /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-4", children: [
2927
+ wallets.map((wallet) => /* @__PURE__ */ jsxs(
2928
+ "div",
2929
+ {
2930
+ className: "rounded-2xl border border-border/60 bg-background/80 p-4 shadow-sm",
2931
+ children: [
2932
+ /* @__PURE__ */ jsxs(
2933
+ "button",
2934
+ {
2935
+ className: "flex w-full items-center justify-between",
2936
+ onClick: () => setExpandedWallet((prev) => prev === wallet.id ? null : wallet.id),
2937
+ children: [
2938
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-left", children: [
2939
+ /* @__PURE__ */ jsx("img", { src: wallet.icon, alt: wallet.name, className: "h-10 w-10 rounded-full" }),
2940
+ /* @__PURE__ */ jsxs("div", { children: [
2941
+ /* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-foreground", children: wallet.name }),
2942
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Browser extension or mobile app" })
2943
+ ] })
2944
+ ] }),
2945
+ expandedWallet === wallet.id ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
2946
+ ]
2947
+ }
2948
+ ),
2949
+ expandedWallet === wallet.id && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-3 text-sm text-muted-foreground", children: [
2950
+ /* @__PURE__ */ jsxs("p", { children: [
2951
+ "Open the ",
2952
+ wallet.name,
2953
+ " wallet, approve the connection request, and confirm the signature prompt to finish linking."
2954
+ ] }),
2955
+ /* @__PURE__ */ jsxs(Button, { className: "w-full", variant: "outline", disabled: true, children: [
2956
+ "Connect with ",
2957
+ wallet.name,
2958
+ " (coming soon)"
2959
+ ] })
2960
+ ] })
2961
+ ]
2962
+ },
2963
+ wallet.id
2964
+ )),
2965
+ /* @__PURE__ */ jsx("div", { className: "rounded-2xl border border-border/60 bg-muted/10 p-4 text-xs text-muted-foreground", children: "Don\u2019t see your wallet? Additional providers will be added soon. Contact support if you need manual verification." })
2966
+ ] })
2967
+ ] }) });
2968
+ };
2969
+ var createDialogState = () => ({
2970
+ isOpen: false,
2971
+ props: null
2972
+ });
2973
+ var PaymentsDialogContext = createContext(void 0);
2974
+ var PaymentsDialogProvider = ({
2975
+ children
2976
+ }) => {
2977
+ const [checkoutState, setCheckoutState] = useState(
2978
+ () => createDialogState()
2979
+ );
2980
+ const [walletState, setWalletState] = useState(
2981
+ () => createDialogState()
2982
+ );
2983
+ const contextValue = useMemo(() => {
2984
+ const openCheckout = (options) => setCheckoutState({
2985
+ isOpen: true,
2986
+ props: options
2987
+ });
2988
+ return {
2989
+ checkout: {
2990
+ isOpen: checkoutState.isOpen,
2991
+ open: openCheckout,
2992
+ close: () => setCheckoutState(createDialogState())
2993
+ },
2994
+ solana: {
2995
+ isOpen: checkoutState.isOpen && checkoutState.props?.initialMode === "solana",
2996
+ open: (options) => openCheckout({
2997
+ priceId: options.priceId,
2998
+ usdAmount: options.usdAmount,
2999
+ enableSolanaPay: true,
3000
+ initialMode: "solana",
3001
+ onSolanaSuccess: options.onSuccess,
3002
+ onSolanaError: options.onError
3003
+ }),
3004
+ close: () => setCheckoutState(createDialogState())
3005
+ },
3006
+ wallet: {
3007
+ isOpen: walletState.isOpen,
3008
+ open: (options) => setWalletState({
3009
+ isOpen: true,
3010
+ props: options ?? null
3011
+ }),
3012
+ close: () => setWalletState(createDialogState())
3013
+ }
3014
+ };
3015
+ }, [checkoutState, walletState.isOpen]);
3016
+ return /* @__PURE__ */ jsxs(PaymentsDialogContext.Provider, { value: contextValue, children: [
3017
+ children,
3018
+ checkoutState.props && /* @__PURE__ */ jsx(
3019
+ SubscriptionCheckoutModal,
3020
+ {
3021
+ open: checkoutState.isOpen,
3022
+ onOpenChange: (open) => open ? setCheckoutState((prev) => ({ ...prev, isOpen: true })) : setCheckoutState(createDialogState()),
3023
+ ...checkoutState.props
3024
+ }
3025
+ ),
3026
+ /* @__PURE__ */ jsx(
3027
+ WalletModal,
3028
+ {
3029
+ open: walletState.isOpen,
3030
+ onOpenChange: (open) => open ? setWalletState((prev) => ({ ...prev, isOpen: true })) : setWalletState(createDialogState()),
3031
+ ...walletState.props ?? {}
3032
+ }
3033
+ )
3034
+ ] });
3035
+ };
3036
+ var usePaymentDialogs = () => {
3037
+ const context = useContext(PaymentsDialogContext);
3038
+ if (!context) {
3039
+ throw new Error("usePaymentDialogs must be used within PaymentProvider");
3040
+ }
3041
+ return context;
3042
+ };
3043
+ var PaymentContext = createContext(void 0);
3044
+ var PaymentProvider = ({
3045
+ config,
3046
+ runtime: runtimeProp,
3047
+ children
3048
+ }) => {
3049
+ const runtime = useMemo(
3050
+ () => runtimeProp ?? createPaymentsRuntime(config),
3051
+ [runtimeProp, config]
3052
+ );
3053
+ const solanaEndpoint = useMemo(() => {
3054
+ if (config.solana?.endpoint) return config.solana.endpoint;
3055
+ const network = config.solana?.network ?? WalletAdapterNetwork.Mainnet;
3056
+ return clusterApiUrl(network);
3057
+ }, [config.solana?.endpoint, config.solana?.network]);
3058
+ const walletAdapters = useMemo(() => {
3059
+ if (config.solana?.wallets?.length) {
3060
+ return config.solana.wallets;
3061
+ }
3062
+ return [
3063
+ new PhantomWalletAdapter(),
3064
+ new SolflareWalletAdapter(),
3065
+ new TrustWalletAdapter(),
3066
+ new CoinbaseWalletAdapter()
3067
+ ];
3068
+ }, [config.solana?.wallets]);
3069
+ const autoConnect = config.solana?.autoConnect ?? true;
3070
+ const value = useMemo(() => {
3071
+ return {
3072
+ config: runtime.config,
3073
+ fetcher: runtime.app.getFetcher(),
3074
+ resolveAuthToken: runtime.app.resolveAuthToken,
3075
+ app: runtime.app,
3076
+ services: runtime.services,
3077
+ queryClient: runtime.queryClient
3078
+ };
3079
+ }, [runtime]);
3080
+ useEffect(() => {
3081
+ if (!config.collectJsKey) return;
3082
+ loadCollectJs(config.collectJsKey);
3083
+ }, [config.collectJsKey]);
3084
+ return /* @__PURE__ */ jsx(PaymentContext.Provider, { value, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: runtime.queryClient, children: /* @__PURE__ */ jsx(ConnectionProvider, { endpoint: solanaEndpoint, config: { commitment: "confirmed" }, children: /* @__PURE__ */ jsx(WalletProvider, { wallets: walletAdapters, autoConnect, children: /* @__PURE__ */ jsx(WalletModalProvider, { children: /* @__PURE__ */ jsx(PaymentsDialogProvider, { children }) }) }) }) }) });
3085
+ };
3086
+ var usePaymentContext = () => {
3087
+ const context = useContext(PaymentContext);
3088
+ if (!context) {
3089
+ throw new Error("usePaymentContext must be used within a PaymentProvider");
3090
+ }
3091
+ return context;
3092
+ };
3093
+ var SolanaPaymentSelector = ({
3094
+ isOpen,
3095
+ onClose,
3096
+ ...props
3097
+ }) => {
3098
+ return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (value) => value ? void 0 : onClose(), children: /* @__PURE__ */ jsx(DialogContent, { className: "w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-md border border-border/70 bg-background/95 p-0 shadow-2xl [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ jsx(SolanaPaymentView, { ...props, onClose }) }) });
3099
+ };
3100
+ var Table = React3.forwardRef(
3101
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3102
+ "table",
3103
+ {
3104
+ ref,
3105
+ className: cn("w-full caption-bottom text-sm", className),
3106
+ ...props
3107
+ }
3108
+ )
3109
+ );
3110
+ Table.displayName = "Table";
3111
+ var TableHeader = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
3112
+ TableHeader.displayName = "TableHeader";
3113
+ var TableBody = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
3114
+ TableBody.displayName = "TableBody";
3115
+ var TableFooter = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tfoot", { ref, className: cn("bg-muted/50 font-medium text-muted-foreground", className), ...props }));
3116
+ TableFooter.displayName = "TableFooter";
3117
+ var TableRow = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tr", { ref, className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className), ...props }));
3118
+ TableRow.displayName = "TableRow";
3119
+ var TableHead = React3.forwardRef(
3120
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3121
+ "th",
3122
+ {
3123
+ ref,
3124
+ className: cn(
3125
+ "h-10 px-2 text-left align-middle text-xs font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
3126
+ className
3127
+ ),
3128
+ ...props
3129
+ }
3130
+ )
3131
+ );
3132
+ TableHead.displayName = "TableHead";
3133
+ var TableCell = React3.forwardRef(
3134
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3135
+ "td",
3136
+ {
3137
+ ref,
3138
+ className: cn("p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className),
3139
+ ...props
3140
+ }
3141
+ )
3142
+ );
3143
+ TableCell.displayName = "TableCell";
3144
+ var TableCaption = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
3145
+ TableCaption.displayName = "TableCaption";
3146
+ var AlertDialog = AlertDialogPrimitive.Root;
3147
+ var AlertDialogTrigger = AlertDialogPrimitive.Trigger;
3148
+ var AlertDialogPortal = AlertDialogPrimitive.Portal;
3149
+ var AlertDialogOverlay = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3150
+ AlertDialogPrimitive.Overlay,
3151
+ {
3152
+ ref,
3153
+ className: cn(
3154
+ "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0",
3155
+ className
3156
+ ),
3157
+ ...props
3158
+ }
3159
+ ));
3160
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
3161
+ var AlertDialogContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
3162
+ /* @__PURE__ */ jsx(AlertDialogOverlay, {}),
3163
+ /* @__PURE__ */ jsx(
3164
+ AlertDialogPrimitive.Content,
3165
+ {
3166
+ ref,
3167
+ className: cn(
3168
+ "fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-popover p-6 text-popover-foreground shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
3169
+ className
3170
+ ),
3171
+ ...props
3172
+ }
3173
+ )
3174
+ ] }));
3175
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
3176
+ var AlertDialogHeader = ({
3177
+ className,
3178
+ ...props
3179
+ }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-2 text-center sm:text-left", className), ...props });
3180
+ AlertDialogHeader.displayName = "AlertDialogHeader";
3181
+ var AlertDialogFooter = ({
3182
+ className,
3183
+ ...props
3184
+ }) => /* @__PURE__ */ jsx(
3185
+ "div",
3186
+ {
3187
+ className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:gap-2", className),
3188
+ ...props
3189
+ }
3190
+ );
3191
+ AlertDialogFooter.displayName = "AlertDialogFooter";
3192
+ var AlertDialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
3193
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
3194
+ var AlertDialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
3195
+ AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
3196
+ var AlertDialogAction = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Action, { ref, className: cn(buttonVariants(), className), ...props }));
3197
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
3198
+ var AlertDialogCancel = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3199
+ AlertDialogPrimitive.Cancel,
3200
+ {
3201
+ ref,
3202
+ className: cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className),
3203
+ ...props
3204
+ }
3205
+ ));
3206
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
3207
+ var Textarea = React3.forwardRef(
3208
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3209
+ "textarea",
3210
+ {
3211
+ ref,
3212
+ className: cn(
3213
+ "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
3214
+ className
3215
+ ),
3216
+ ...props
3217
+ }
3218
+ )
3219
+ );
3220
+ Textarea.displayName = "Textarea";
3221
+ var notifyDefault = (payload) => {
3222
+ const level = payload.status === "destructive" ? "error" : "info";
3223
+ console[level === "error" ? "error" : "log"]("[payments-ui] cancellation", payload);
3224
+ };
3225
+ var CancelMembershipDialog = ({
3226
+ minReasonLength = 15,
3227
+ onCancelled,
3228
+ onNotify
3229
+ }) => {
3230
+ const { services } = usePaymentContext();
3231
+ const notify = onNotify ?? notifyDefault;
3232
+ const [cancelReason, setCancelReason] = useState("");
3233
+ const [isOpen, setIsOpen] = useState(false);
3234
+ const [isReasonValid, setIsReasonValid] = useState(false);
3235
+ const [hasInteracted, setHasInteracted] = useState(false);
3236
+ const [isSubmitting, setIsSubmitting] = useState(false);
3237
+ useEffect(() => {
3238
+ const trimmed = cancelReason.trim();
3239
+ setIsReasonValid(trimmed.length >= minReasonLength);
3240
+ }, [cancelReason, minReasonLength]);
3241
+ const handleOpenChange = (open) => {
3242
+ setIsOpen(open);
3243
+ if (!open) {
3244
+ setCancelReason("");
3245
+ setIsReasonValid(false);
3246
+ setHasInteracted(false);
3247
+ setIsSubmitting(false);
3248
+ }
3249
+ };
3250
+ const handleReasonChange = (e) => {
3251
+ setCancelReason(e.target.value);
3252
+ if (!hasInteracted) {
3253
+ setHasInteracted(true);
3254
+ }
3255
+ };
3256
+ const handleConfirm = async () => {
3257
+ if (!isReasonValid) {
3258
+ setHasInteracted(true);
3259
+ return;
3260
+ }
3261
+ setIsSubmitting(true);
3262
+ try {
3263
+ await services.subscriptions.cancelSubscription(cancelReason.trim());
3264
+ notify({
3265
+ title: "Membership cancelled",
3266
+ description: "Your subscription has been cancelled successfully.",
3267
+ status: "success"
3268
+ });
3269
+ onCancelled?.();
3270
+ handleOpenChange(false);
3271
+ } catch (error) {
3272
+ const message = error instanceof Error ? error.message : "Unable to cancel membership";
3273
+ notify({ title: "Cancellation failed", description: message, status: "destructive" });
3274
+ } finally {
3275
+ setIsSubmitting(false);
3276
+ }
3277
+ };
3278
+ const showError = hasInteracted && !isReasonValid;
3279
+ return /* @__PURE__ */ jsxs(AlertDialog, { open: isOpen, onOpenChange: handleOpenChange, children: [
3280
+ /* @__PURE__ */ jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "border-destructive/50 text-destructive", children: [
3281
+ /* @__PURE__ */ jsx(Ban, { className: "mr-2 h-4 w-4" }),
3282
+ " Cancel Membership"
3283
+ ] }) }),
3284
+ /* @__PURE__ */ jsxs(AlertDialogContent, { className: "max-h-[90vh] overflow-y-auto rounded-md border border-border bg-background", children: [
3285
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
3286
+ /* @__PURE__ */ jsxs(AlertDialogTitle, { className: "flex items-center gap-2 text-lg font-semibold", children: [
3287
+ /* @__PURE__ */ jsx(TriangleAlert, { className: "h-5 w-5 text-destructive" }),
3288
+ " Confirm Membership Cancellation"
3289
+ ] }),
3290
+ /* @__PURE__ */ jsxs(AlertDialogDescription, { className: "mt-3 space-y-3 text-muted-foreground", children: [
3291
+ /* @__PURE__ */ jsx("p", { children: "You are about to cancel your membership. Please review the consequences:" }),
3292
+ /* @__PURE__ */ jsxs("ul", { className: "list-disc space-y-1 pl-5 text-sm", children: [
3293
+ /* @__PURE__ */ jsx("li", { children: "You will immediately lose access to premium features upon confirmation." }),
3294
+ /* @__PURE__ */ jsx("li", { children: "Your benefits remain active until the end of the billing cycle." }),
3295
+ /* @__PURE__ */ jsx("li", { children: "Your account will revert to the free plan afterwards." })
3296
+ ] })
3297
+ ] })
3298
+ ] }),
3299
+ /* @__PURE__ */ jsxs("div", { className: "my-4 space-y-2 py-2", children: [
3300
+ /* @__PURE__ */ jsx(Label, { htmlFor: "cancelReason", className: "text-sm font-medium", children: "Please provide a reason for cancellation (required):" }),
3301
+ /* @__PURE__ */ jsx(
3302
+ Textarea,
3303
+ {
3304
+ id: "cancelReason",
3305
+ value: cancelReason,
3306
+ onChange: handleReasonChange,
3307
+ placeholder: "Your feedback helps us improve...",
3308
+ className: cn(
3309
+ "w-full resize-none border-border bg-background",
3310
+ showError && "border-destructive"
3311
+ ),
3312
+ rows: 4,
3313
+ "aria-describedby": "reason-hint",
3314
+ "aria-invalid": showError
3315
+ }
3316
+ ),
3317
+ /* @__PURE__ */ jsx(
3318
+ "p",
3319
+ {
3320
+ id: "reason-hint",
3321
+ className: `text-xs ${showError ? "text-destructive" : "text-muted-foreground"}`,
3322
+ children: showError ? `Reason must be at least ${minReasonLength} characters long.` : `Minimum ${minReasonLength} characters required.`
3323
+ }
3324
+ )
3325
+ ] }),
3326
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { className: "mt-6 gap-2", children: [
3327
+ /* @__PURE__ */ jsx(AlertDialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "border-border text-muted-foreground", children: "Keep Membership" }) }),
3328
+ /* @__PURE__ */ jsx(AlertDialogAction, { asChild: true, children: /* @__PURE__ */ jsx(
3329
+ Button,
3330
+ {
3331
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
3332
+ onClick: handleConfirm,
3333
+ disabled: !isReasonValid || isSubmitting,
3334
+ children: isSubmitting ? "Cancelling..." : "Confirm Cancellation"
3335
+ }
3336
+ ) })
3337
+ ] })
3338
+ ] })
3339
+ ] });
3340
+ };
3341
+ var notifyDefault2 = (payload) => {
3342
+ const level = payload.status === "destructive" ? "error" : "info";
3343
+ console[level === "error" ? "error" : "log"]("[payments-ui] billing", payload);
3344
+ };
3345
+ var BillingHistory = ({
3346
+ pageSize = 10,
3347
+ initialPage = 1,
3348
+ enableCancel = true,
3349
+ onNotify
3350
+ }) => {
3351
+ const { services } = usePaymentContext();
3352
+ const notify = onNotify ?? notifyDefault2;
3353
+ const [isExpanded, setIsExpanded] = useState(false);
3354
+ const observerRef = useRef(null);
3355
+ const loadMoreRef = useRef(null);
3356
+ const historyQuery = useInfiniteQuery({
3357
+ queryKey: ["payments-ui", "billing-history", pageSize],
3358
+ queryFn: async ({ pageParam = initialPage }) => {
3359
+ const offset = (pageParam - 1) * pageSize;
3360
+ return services.subscriptions.getPaymentHistory({ limit: pageSize, offset });
3361
+ },
3362
+ initialPageParam: initialPage,
3363
+ getNextPageParam: (lastPage) => {
3364
+ const nextOffset = (lastPage.offset ?? 0) + lastPage.data.length;
3365
+ if (lastPage.total_items <= nextOffset) {
3366
+ return void 0;
3367
+ }
3368
+ return lastPage.page ? lastPage.page + 1 : initialPage + 1;
3369
+ },
3370
+ staleTime: 5 * 60 * 1e3
3371
+ });
3372
+ useEffect(() => {
3373
+ if (!loadMoreRef.current || !isExpanded) return;
3374
+ observerRef.current = new IntersectionObserver((entries) => {
3375
+ const [entry] = entries;
3376
+ if (entry?.isIntersecting && historyQuery.hasNextPage && !historyQuery.isFetchingNextPage) {
3377
+ historyQuery.fetchNextPage().catch(() => {
3378
+ notify({ title: "Failed to load more history", status: "destructive" });
3379
+ });
3380
+ }
3381
+ });
3382
+ observerRef.current.observe(loadMoreRef.current);
3383
+ return () => {
3384
+ observerRef.current?.disconnect();
3385
+ };
3386
+ }, [historyQuery, isExpanded, notify]);
3387
+ const payments = useMemo(() => {
3388
+ const data = historyQuery.data;
3389
+ return data?.pages ?? [];
3390
+ }, [historyQuery.data]);
3391
+ const formatDate = (value) => new Date(value).toLocaleDateString("en-US", {
3392
+ year: "numeric",
3393
+ month: "short",
3394
+ day: "numeric"
3395
+ });
3396
+ const formatAmount = (amount, currency) => {
3397
+ try {
3398
+ return new Intl.NumberFormat("en-US", {
3399
+ style: "currency",
3400
+ currency
3401
+ }).format(amount);
3402
+ } catch {
3403
+ return `${amount.toFixed(2)} ${currency}`;
3404
+ }
3405
+ };
3406
+ return /* @__PURE__ */ jsx(Card, { className: "border-0 bg-background/5 shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "p-4 sm:p-6", children: [
3407
+ /* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center justify-between", onClick: () => setIsExpanded((prev) => !prev), children: [
3408
+ /* @__PURE__ */ jsxs("div", { children: [
3409
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-xl font-semibold", children: "Transaction History" }),
3410
+ /* @__PURE__ */ jsx(CardDescription, { children: "Record of billing history" })
3411
+ ] }),
3412
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-5 w-5 text-muted-foreground transition-transform", isExpanded && "rotate-180") })
3413
+ ] }),
3414
+ /* @__PURE__ */ jsx(
3415
+ "div",
3416
+ {
3417
+ className: cn(
3418
+ "overflow-hidden transition-all duration-300",
3419
+ isExpanded ? "mt-4 max-h-[1000px] opacity-100" : "max-h-0 opacity-0"
3420
+ ),
3421
+ children: /* @__PURE__ */ jsx(CardContent, { className: "p-0 pt-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3422
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between", children: [
3423
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Review your account activity below" }),
3424
+ enableCancel && /* @__PURE__ */ jsx(CancelMembershipDialog, { onNotify: notify })
3425
+ ] }),
3426
+ /* @__PURE__ */ jsx("div", { className: "max-h-[300px] overflow-y-auto rounded-lg border border-border/70", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: historyQuery.isLoading ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-sm text-muted-foreground", children: "Loading..." }) : historyQuery.isError ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-sm text-destructive", children: "Error loading billing history." }) : /* @__PURE__ */ jsxs(Table, { children: [
3427
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { className: "border-border/60", children: [
3428
+ /* @__PURE__ */ jsx(TableHead, { children: "Reference" }),
3429
+ /* @__PURE__ */ jsx(TableHead, { children: "Date" }),
3430
+ /* @__PURE__ */ jsx(TableHead, { children: "Amount" }),
3431
+ /* @__PURE__ */ jsx(TableHead, { children: "Processor" }),
3432
+ /* @__PURE__ */ jsx(TableHead, { children: "Status" })
3433
+ ] }) }),
3434
+ /* @__PURE__ */ jsx(TableBody, { children: payments.map(
3435
+ (page) => page.data.map((payment) => /* @__PURE__ */ jsxs(TableRow, { className: "border-border/40", children: [
3436
+ /* @__PURE__ */ jsx(TableCell, { className: "font-mono text-sm", children: payment.id.slice(0, 7).toUpperCase() }),
3437
+ /* @__PURE__ */ jsx(TableCell, { children: formatDate(payment.purchased_at) }),
3438
+ /* @__PURE__ */ jsx(TableCell, { children: formatAmount(payment.amount, payment.currency) }),
3439
+ /* @__PURE__ */ jsx(TableCell, { className: "capitalize", children: payment.processor }),
3440
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Badge, { className: "bg-emerald-500/10 text-emerald-400", children: (payment.status || "completed").toLowerCase() }) })
3441
+ ] }, payment.id))
3442
+ ) })
3443
+ ] }) }) }),
3444
+ /* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "h-10 w-full", children: historyQuery.isFetchingNextPage && /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-muted-foreground", children: "Loading more..." }) })
3445
+ ] }) })
3446
+ }
3447
+ )
3448
+ ] }) });
3449
+ };
3450
+ var formatCardLabel2 = (method) => {
3451
+ const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
3452
+ const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
3453
+ return `${brand} ${lastFour}`.trim();
3454
+ };
3455
+ var notifyDefault3 = (payload) => {
3456
+ const level = payload.status === "destructive" ? "error" : "info";
3457
+ console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
3458
+ };
3459
+ var PaymentMethodsSection = ({
3460
+ isAuthenticated = true,
3461
+ userEmail,
3462
+ provider = "mobius",
3463
+ defaultCountry = "US",
3464
+ collectPrefix = "account-card",
3465
+ onNotify
3466
+ }) => {
3467
+ const paymentMethods = usePaymentMethodService();
3468
+ const queryClient = useQueryClient();
3469
+ const [isModalOpen, setIsModalOpen] = useState(false);
3470
+ const [deletingId, setDeletingId] = useState(null);
3471
+ const notify = onNotify ?? notifyDefault3;
3472
+ const queryKey = ["payments-ui", "payment-methods"];
3473
+ const paymentQuery = useQuery({
3474
+ queryKey,
3475
+ queryFn: () => paymentMethods.list({ pageSize: 50 }),
3476
+ enabled: isAuthenticated,
3477
+ staleTime: 3e4
3478
+ });
3479
+ const createMutation = useMutation({
3480
+ mutationFn: (payload) => paymentMethods.create(payload),
3481
+ onSuccess: () => {
3482
+ notify({ title: "Card added successfully", status: "success" });
3483
+ setIsModalOpen(false);
3484
+ void queryClient.invalidateQueries({ queryKey });
3485
+ },
3486
+ onError: (error) => {
3487
+ notify({
3488
+ title: "Unable to add card",
3489
+ description: error.message,
3490
+ status: "destructive"
3491
+ });
3492
+ }
3493
+ });
3494
+ const deleteMutation = useMutation({
3495
+ mutationFn: (id) => paymentMethods.remove(id),
3496
+ onMutate: (id) => setDeletingId(id),
3497
+ onSuccess: () => {
3498
+ notify({ title: "Card removed", status: "success" });
3499
+ void queryClient.invalidateQueries({ queryKey });
3500
+ },
3501
+ onError: (error) => {
3502
+ notify({
3503
+ title: "Unable to remove card",
3504
+ description: error.message,
3505
+ status: "destructive"
3506
+ });
3507
+ },
3508
+ onSettled: () => setDeletingId(null)
3509
+ });
3510
+ useEffect(() => {
3511
+ if (!isModalOpen) {
3512
+ createMutation.reset();
3513
+ }
3514
+ }, [createMutation, isModalOpen]);
3515
+ const payments = useMemo(() => paymentQuery.data?.data ?? [], [paymentQuery.data]);
3516
+ const loading = paymentQuery.isLoading || paymentQuery.isFetching;
3517
+ const handleCardTokenize = (token, billing) => {
3518
+ const payload = {
3519
+ payment_token: token,
3520
+ first_name: billing.firstName,
3521
+ last_name: billing.lastName,
3522
+ address1: billing.address1,
3523
+ address2: billing.address2,
3524
+ city: billing.city,
3525
+ state: billing.stateRegion,
3526
+ zip: billing.postalCode,
3527
+ country: billing.country,
3528
+ email: billing.email,
3529
+ provider: billing.provider
3530
+ };
3531
+ createMutation.mutate(payload);
3532
+ };
3533
+ return /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-background/5 shadow-lg", children: [
3534
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
3535
+ /* @__PURE__ */ jsxs("div", { children: [
3536
+ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2 text-xl", children: [
3537
+ /* @__PURE__ */ jsx(WalletCards, { className: "h-5 w-5 text-primary" }),
3538
+ " Payment Methods"
3539
+ ] }),
3540
+ /* @__PURE__ */ jsx(CardDescription, { children: "Manage your saved billing cards" })
3541
+ ] }),
3542
+ /* @__PURE__ */ jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
3543
+ /* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
3544
+ " Add card"
3545
+ ] })
3546
+ ] }),
3547
+ /* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: loading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-10 text-muted-foreground", children: [
3548
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
3549
+ " Loading cards..."
3550
+ ] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-border/60 bg-muted/10 p-6 text-sm text-muted-foreground", children: "No saved payment methods yet." }) : /* @__PURE__ */ jsx("div", { className: "space-y-3", children: payments.map((method) => /* @__PURE__ */ jsxs(
3551
+ "div",
3552
+ {
3553
+ className: "rounded-lg border border-border/80 bg-background/40 p-4 shadow-sm",
3554
+ children: [
3555
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:flex-row md:items-center md:justify-between", children: [
3556
+ /* @__PURE__ */ jsxs("div", { children: [
3557
+ /* @__PURE__ */ jsx("h4", { className: "text-base font-medium text-foreground", children: formatCardLabel2(method) }),
3558
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
3559
+ "Added on",
3560
+ " ",
3561
+ method.created_at ? new Date(method.created_at).toLocaleDateString() : "unknown date"
3562
+ ] })
3563
+ ] }),
3564
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
3565
+ /* @__PURE__ */ jsx(
3566
+ Badge,
3567
+ {
3568
+ variant: method.is_active ? "default" : "secondary",
3569
+ className: method.is_active ? "bg-emerald-500/20 text-emerald-400" : "",
3570
+ children: method.is_active ? "Active" : "Inactive"
3571
+ }
3572
+ ),
3573
+ method.failure_reason && /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: method.failure_reason })
3574
+ ] })
3575
+ ] }),
3576
+ /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: /* @__PURE__ */ jsxs(
3577
+ Button,
3578
+ {
3579
+ variant: "ghost",
3580
+ className: "text-destructive hover:text-destructive",
3581
+ disabled: deletingId === method.id && deleteMutation.isPending,
3582
+ onClick: () => deleteMutation.mutate(method.id),
3583
+ children: [
3584
+ deletingId === method.id && deleteMutation.isPending ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
3585
+ "Remove"
3586
+ ]
3587
+ }
3588
+ ) })
3589
+ ]
3590
+ },
3591
+ method.id
3592
+ )) }) }),
3593
+ /* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[95vh] overflow-y-auto border border-border bg-background", children: [
3594
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
3595
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Add a new card" }),
3596
+ /* @__PURE__ */ jsx(DialogDescription, { children: "Your card details are tokenized securely via our payment provider." })
3597
+ ] }),
3598
+ /* @__PURE__ */ jsx(
3599
+ CardDetailsForm,
3600
+ {
3601
+ visible: isModalOpen,
3602
+ collectPrefix,
3603
+ submitting: createMutation.isPending,
3604
+ submitLabel: "Save card",
3605
+ defaultValues: {
3606
+ email: userEmail ?? "",
3607
+ country: defaultCountry,
3608
+ provider
3609
+ },
3610
+ externalError: createMutation.error?.message ?? null,
3611
+ onTokenize: handleCardTokenize,
3612
+ className: "rounded-2xl border border-border bg-muted/20 p-6"
3613
+ }
3614
+ )
3615
+ ] }) })
3616
+ ] });
3617
+ };
3618
+ var useWalletList = (options = {}) => {
3619
+ const { services } = usePaymentContext();
3620
+ const [state, setState] = useState({
3621
+ wallets: [],
3622
+ isLoading: false,
3623
+ error: null
3624
+ });
3625
+ const fetchWallets = useCallback(async () => {
3626
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
3627
+ try {
3628
+ const wallets2 = await services.solanaWallets.list();
3629
+ setState({ wallets: wallets2, isLoading: false, error: null });
3630
+ return wallets2;
3631
+ } catch (error) {
3632
+ const message = error instanceof Error ? error.message : "Failed to load wallets";
3633
+ console.error("payments-ui: wallet list fetch failed", error);
3634
+ setState((prev) => ({ ...prev, isLoading: false, error: message }));
3635
+ throw error;
3636
+ }
3637
+ }, [services.solanaWallets]);
3638
+ const deleteWallet = useCallback(
3639
+ async (walletIdOrAddress) => {
3640
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
3641
+ try {
3642
+ await services.solanaWallets.remove(walletIdOrAddress);
3643
+ setState((prev) => ({
3644
+ ...prev,
3645
+ wallets: prev.wallets.filter(
3646
+ (wallet) => wallet.id !== walletIdOrAddress && wallet.address !== walletIdOrAddress
3647
+ ),
3648
+ isLoading: false
3649
+ }));
3650
+ } catch (error) {
3651
+ const message = error instanceof Error ? error.message : "Failed to remove wallet";
3652
+ console.error("payments-ui: wallet removal failed", error);
3653
+ setState((prev) => ({ ...prev, isLoading: false, error: message }));
3654
+ throw error;
3655
+ }
3656
+ },
3657
+ [services.solanaWallets]
3658
+ );
3659
+ const addWallet = useCallback((wallet) => {
3660
+ setState((prev) => ({ ...prev, wallets: [...prev.wallets, wallet] }));
3661
+ }, []);
3662
+ const updateWallet = useCallback((walletId, updates) => {
3663
+ setState((prev) => ({
3664
+ ...prev,
3665
+ wallets: prev.wallets.map(
3666
+ (wallet) => wallet.id === walletId ? { ...wallet, ...updates } : wallet
3667
+ )
3668
+ }));
3669
+ }, []);
3670
+ const clearError = useCallback(() => {
3671
+ setState((prev) => ({ ...prev, error: null }));
3672
+ }, []);
3673
+ const findWalletByAddress = useCallback(
3674
+ (address) => state.wallets.find((wallet) => wallet.address === address),
3675
+ [state.wallets]
3676
+ );
3677
+ const getVerifiedWallets = useCallback(
3678
+ () => state.wallets.filter((wallet) => wallet.is_verified),
3679
+ [state.wallets]
3680
+ );
3681
+ useEffect(() => {
3682
+ if (options.autoFetch !== false) {
3683
+ fetchWallets().catch(() => {
3684
+ });
3685
+ }
3686
+ }, [fetchWallets, options.autoFetch]);
3687
+ return {
3688
+ wallets: state.wallets,
3689
+ isLoading: state.isLoading,
3690
+ error: state.error,
3691
+ fetchWallets,
3692
+ deleteWallet,
3693
+ addWallet,
3694
+ updateWallet,
3695
+ clearError,
3696
+ findWalletByAddress,
3697
+ getVerifiedWallets,
3698
+ hasVerifiedWallets: state.wallets.some((w) => w.is_verified),
3699
+ totalWallets: state.wallets.length
3700
+ };
3701
+ };
3702
+ var useWalletVerification = () => {
3703
+ const { services } = usePaymentContext();
3704
+ const { publicKey, signMessage } = useWallet();
3705
+ const [state, setState] = useState({
3706
+ isVerifying: false,
3707
+ isVerified: false,
3708
+ error: null
3709
+ });
3710
+ const signAndVerifyWallet = useCallback(
3711
+ async (walletAddress, message, nonce) => {
3712
+ if (!publicKey || !signMessage) {
3713
+ throw new Error("Wallet not connected or signing unavailable");
3714
+ }
3715
+ if (publicKey.toBase58() !== walletAddress) {
3716
+ throw new Error("Connected wallet does not match target wallet");
3717
+ }
3718
+ setState((prev) => ({ ...prev, isVerifying: true, error: null }));
3719
+ try {
3720
+ const encodedMessage = new TextEncoder().encode(message);
3721
+ const signature = await signMessage(encodedMessage);
3722
+ const signatureBase58 = bs58.encode(signature);
3723
+ const response = await services.solanaWallets.verify(
3724
+ walletAddress,
3725
+ signatureBase58,
3726
+ nonce
3727
+ );
3728
+ setState({ isVerifying: false, isVerified: response.verified, error: null });
3729
+ return response;
3730
+ } catch (error) {
3731
+ const message2 = error instanceof Error ? error.message : "Wallet verification failed";
3732
+ console.error("payments-ui: wallet verification failed", error);
3733
+ setState({ isVerifying: false, isVerified: false, error: message2 });
3734
+ throw error;
3735
+ }
3736
+ },
3737
+ [publicKey, signMessage, services.solanaWallets]
3738
+ );
3739
+ const clearError = useCallback(() => {
3740
+ setState((prev) => ({ ...prev, error: null }));
3741
+ }, []);
3742
+ const resetVerification = useCallback(() => {
3743
+ setState({ isVerifying: false, isVerified: false, error: null });
3744
+ }, []);
3745
+ const autoVerifyWallet = useCallback(
3746
+ async (walletAddress, message, nonce) => {
3747
+ try {
3748
+ return await signAndVerifyWallet(walletAddress, message, nonce);
3749
+ } catch (error) {
3750
+ console.warn("payments-ui: auto-verification skipped", error);
3751
+ return null;
3752
+ }
3753
+ },
3754
+ [signAndVerifyWallet]
3755
+ );
3756
+ return {
3757
+ ...state,
3758
+ signAndVerifyWallet,
3759
+ autoVerifyWallet,
3760
+ clearError,
3761
+ resetVerification
3762
+ };
3763
+ };
3764
+ var useWalletConnection = () => {
3765
+ const { services } = usePaymentContext();
3766
+ const { publicKey, connected, connecting, disconnect, wallet } = useWallet();
3767
+ const [state, setState] = useState({
3768
+ isConnected: false,
3769
+ isConnecting: false,
3770
+ publicKey: null,
3771
+ wallets: [],
3772
+ isLoading: false,
3773
+ error: null
3774
+ });
3775
+ useEffect(() => {
3776
+ setState((prev) => ({
3777
+ ...prev,
3778
+ isConnected: connected,
3779
+ isConnecting: connecting,
3780
+ publicKey: publicKey?.toBase58() ?? null
3781
+ }));
3782
+ }, [connected, connecting, publicKey]);
3783
+ const connectWalletToBackend = useCallback(
3784
+ async (address) => {
3785
+ if (!address) {
3786
+ throw new Error("Wallet address is required");
3787
+ }
3788
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
3789
+ try {
3790
+ const challenge = await services.solanaWallets.requestChallenge(address);
3791
+ setState((prev) => ({ ...prev, isLoading: false }));
3792
+ return challenge;
3793
+ } catch (error) {
3794
+ const message = error instanceof Error ? error.message : "Wallet challenge failed";
3795
+ console.error("payments-ui: wallet challenge failed", error);
3796
+ setState((prev) => ({ ...prev, isLoading: false, error: message }));
3797
+ throw error;
3798
+ }
3799
+ },
3800
+ [services.solanaWallets]
3801
+ );
3802
+ const connectWallet = useCallback(async () => {
3803
+ if (!wallet) {
3804
+ throw new Error("No wallet adapter selected");
3805
+ }
3806
+ setState((prev) => ({ ...prev, isConnecting: true, error: null }));
3807
+ try {
3808
+ await wallet.adapter.connect();
3809
+ if (publicKey) {
3810
+ await connectWalletToBackend(publicKey.toBase58());
3811
+ }
3812
+ } catch (error) {
3813
+ const message = error instanceof Error ? error.message : "Failed to connect wallet";
3814
+ console.error("payments-ui: wallet connection failed", error);
3815
+ setState((prev) => ({ ...prev, isConnecting: false, error: message }));
3816
+ throw error;
3817
+ } finally {
3818
+ setState((prev) => ({ ...prev, isConnecting: false }));
3819
+ }
3820
+ }, [wallet, publicKey, connectWalletToBackend]);
3821
+ const disconnectWallet = useCallback(async () => {
3822
+ try {
3823
+ await disconnect();
3824
+ setState((prev) => ({
3825
+ ...prev,
3826
+ isConnected: false,
3827
+ publicKey: null,
3828
+ wallets: [],
3829
+ error: null
3830
+ }));
3831
+ } catch (error) {
3832
+ const message = error instanceof Error ? error.message : "Failed to disconnect wallet";
3833
+ console.error("payments-ui: wallet disconnect failed", error);
3834
+ setState((prev) => ({ ...prev, error: message }));
3835
+ throw error;
3836
+ }
3837
+ }, [disconnect]);
3838
+ const clearError = useCallback(() => {
3839
+ setState((prev) => ({ ...prev, error: null }));
3840
+ }, []);
3841
+ return {
3842
+ ...state,
3843
+ walletName: wallet?.adapter.name,
3844
+ walletIcon: wallet?.adapter.icon,
3845
+ connectWallet,
3846
+ disconnectWallet,
3847
+ connectWalletToBackend,
3848
+ clearError
3849
+ };
3850
+ };
3851
+ var notifyDefault4 = (payload) => {
3852
+ console.log("[payments-ui] wallet-card", payload);
3853
+ };
3854
+ var WalletCard = ({
3855
+ wallet,
3856
+ isPrimary = false,
3857
+ isConnected = false,
3858
+ balance,
3859
+ onSetPrimary,
3860
+ onVerify,
3861
+ onDelete,
3862
+ isVerifying = false,
3863
+ isDeleting = false,
3864
+ notify = notifyDefault4
3865
+ }) => {
3866
+ const [isCopying, setIsCopying] = useState(false);
3867
+ const formatAddress = (address) => address.length <= 12 ? address : `${address.slice(0, 4)}...${address.slice(-4)}`;
3868
+ const copyToClipboard = async (text) => {
3869
+ setIsCopying(true);
3870
+ try {
3871
+ await navigator.clipboard.writeText(text);
3872
+ notify({ title: "Copied", description: "Wallet address copied to clipboard" });
3873
+ } catch (error) {
3874
+ notify({ title: "Failed to copy", description: error.message, status: "destructive" });
3875
+ } finally {
3876
+ setTimeout(() => setIsCopying(false), 500);
3877
+ }
3878
+ };
3879
+ const openExplorer = () => {
3880
+ window.open(`https://solscan.io/account/${wallet.address}`, "_blank", "noopener,noreferrer");
3881
+ };
3882
+ return /* @__PURE__ */ jsxs(
3883
+ "div",
3884
+ {
3885
+ className: cn(
3886
+ "rounded-lg border bg-background/40 p-4 transition-shadow",
3887
+ isPrimary && "border-primary/50 shadow-lg",
3888
+ isConnected && "ring-1 ring-primary"
3889
+ ),
3890
+ children: [
3891
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
3892
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3893
+ /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-primary/20", children: /* @__PURE__ */ jsx("div", { className: "h-5 w-5 rounded-full bg-gradient-to-br from-primary to-primary/60" }) }),
3894
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
3895
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3896
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-sm font-medium", children: formatAddress(wallet.address) }),
3897
+ isPrimary && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 rounded-full bg-yellow-500/20 px-2 py-0.5 text-xs text-yellow-500", children: [
3898
+ /* @__PURE__ */ jsx(Star, { className: "h-3 w-3 fill-yellow-500 text-yellow-500" }),
3899
+ " Primary"
3900
+ ] }),
3901
+ wallet.is_verified ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-emerald-400", children: [
3902
+ /* @__PURE__ */ jsx(CheckCircle, { className: "h-3.5 w-3.5" }),
3903
+ " Verified"
3904
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-amber-400", children: [
3905
+ /* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5" }),
3906
+ " Unverified"
3907
+ ] }),
3908
+ isConnected && /* @__PURE__ */ jsx("span", { className: "rounded bg-blue-500/20 px-2 py-0.5 text-xs text-blue-400", children: "Connected" })
3909
+ ] }),
3910
+ balance !== null && typeof balance !== "undefined" && /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [
3911
+ "Balance: ",
3912
+ balance.toFixed(4),
3913
+ " SOL"
3914
+ ] }),
3915
+ /* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
3916
+ "Added ",
3917
+ new Date(wallet.created_at).toLocaleDateString()
3918
+ ] })
3919
+ ] })
3920
+ ] }) }),
3921
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
3922
+ /* @__PURE__ */ jsx(
3923
+ Button,
3924
+ {
3925
+ variant: "ghost",
3926
+ size: "icon",
3927
+ className: "h-8 w-8",
3928
+ onClick: () => copyToClipboard(wallet.address),
3929
+ disabled: isCopying,
3930
+ children: isCopying ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" })
3931
+ }
3932
+ ),
3933
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", onClick: openExplorer, children: /* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4" }) })
3934
+ ] })
3935
+ ] }),
3936
+ /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [
3937
+ !isPrimary && onSetPrimary && wallet.is_verified && /* @__PURE__ */ jsxs(
3938
+ Button,
3939
+ {
3940
+ variant: "outline",
3941
+ size: "sm",
3942
+ className: "border-primary/40 text-primary",
3943
+ onClick: () => onSetPrimary(wallet.id),
3944
+ children: [
3945
+ /* @__PURE__ */ jsx(Star, { className: "mr-1 h-3 w-3" }),
3946
+ " Set Primary"
3947
+ ]
3948
+ }
3949
+ ),
3950
+ !wallet.is_verified && onVerify && isConnected && /* @__PURE__ */ jsx(
3951
+ Button,
3952
+ {
3953
+ variant: "outline",
3954
+ size: "sm",
3955
+ className: "border-emerald-500/40 text-emerald-400",
3956
+ onClick: () => onVerify(wallet),
3957
+ disabled: isVerifying,
3958
+ children: isVerifying ? /* @__PURE__ */ jsxs(Fragment, { children: [
3959
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" }),
3960
+ " Verifying..."
3961
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3962
+ /* @__PURE__ */ jsx(Shield, { className: "mr-1 h-3 w-3" }),
3963
+ " Verify"
3964
+ ] })
3965
+ }
3966
+ ),
3967
+ onDelete && /* @__PURE__ */ jsx(
3968
+ Button,
3969
+ {
3970
+ variant: "outline",
3971
+ size: "sm",
3972
+ className: "ml-auto border-destructive/40 text-destructive",
3973
+ onClick: () => onDelete(wallet.id),
3974
+ disabled: isDeleting,
3975
+ children: isDeleting ? /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
3976
+ }
3977
+ )
3978
+ ] })
3979
+ ]
3980
+ }
3981
+ );
3982
+ };
3983
+ var EmptyWalletState = () => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [
3984
+ /* @__PURE__ */ jsx(Wallet, { className: "mb-4 h-12 w-12 text-muted-foreground" }),
3985
+ /* @__PURE__ */ jsx("h3", { className: "mb-2 text-lg font-medium", children: "No wallets connected" }),
3986
+ /* @__PURE__ */ jsx("p", { className: "mb-6 text-sm text-muted-foreground", children: "Connect your Solana wallet to get started" }),
3987
+ /* @__PURE__ */ jsx(WalletMultiButton, { className: "!bg-primary text-primary-foreground hover:!bg-primary/90" })
3988
+ ] });
3989
+ var notifyDefault5 = (payload) => {
3990
+ console.log("[payments-ui] solana-wallets", payload);
3991
+ };
3992
+ var SolanaWalletSection = ({
3993
+ onNotify,
3994
+ rpcUrl
3995
+ }) => {
3996
+ const notify = onNotify ?? notifyDefault5;
3997
+ const { config } = usePaymentContext();
3998
+ const { connected, publicKey, disconnect } = useWallet();
3999
+ const { wallets: wallets2, isLoading, deleteWallet, fetchWallets } = useWalletList();
4000
+ const { signAndVerifyWallet } = useWalletVerification();
4001
+ const walletConnection = useWalletConnection();
4002
+ const [primaryWalletId, setPrimaryWalletId] = useState(null);
4003
+ const [showOtherWallets, setShowOtherWallets] = useState(false);
4004
+ const [balances, setBalances] = useState({});
4005
+ const [verifyingWalletId, setVerifyingWalletId] = useState(null);
4006
+ const [deletingWalletId, setDeletingWalletId] = useState(null);
4007
+ const [walletToDelete, setWalletToDelete] = useState(null);
4008
+ const [isRegistering, setIsRegistering] = useState(false);
4009
+ const rpcEndpoint = rpcUrl || config.solanaRpcUrl || "https://api.mainnet-beta.solana.com";
4010
+ const connection = useMemo(() => new Connection(rpcEndpoint), [rpcEndpoint]);
4011
+ const primaryWallet = wallets2.find((w) => w.id === primaryWalletId) ?? wallets2.find((w) => w.is_verified) ?? null;
4012
+ const otherWallets = wallets2.filter((w) => w.id !== primaryWallet?.id);
4013
+ const fetchBalance = useCallback(
4014
+ async (address) => {
4015
+ try {
4016
+ const pubkey = new PublicKey(address);
4017
+ const balance = await connection.getBalance(pubkey);
4018
+ return balance / LAMPORTS_PER_SOL;
4019
+ } catch (error) {
4020
+ console.error("payments-ui: failed to fetch balance", error);
4021
+ return null;
4022
+ }
4023
+ },
4024
+ [connection]
4025
+ );
4026
+ const fetchAllBalances = useCallback(async () => {
4027
+ const results = await Promise.all(
4028
+ wallets2.map(async (wallet) => ({ address: wallet.address, balance: await fetchBalance(wallet.address) }))
4029
+ );
4030
+ const next = {};
4031
+ results.forEach(({ address, balance }) => {
4032
+ if (balance !== null && typeof balance !== "undefined") {
4033
+ next[address] = balance;
4034
+ }
4035
+ });
4036
+ setBalances(next);
4037
+ }, [wallets2, fetchBalance]);
4038
+ useEffect(() => {
4039
+ if (wallets2.length > 0) {
4040
+ fetchAllBalances().catch(() => void 0);
4041
+ }
4042
+ }, [wallets2, fetchAllBalances]);
4043
+ useEffect(() => {
4044
+ const verifiedWallet = wallets2.find((w) => w.is_verified);
4045
+ if (verifiedWallet && !primaryWalletId) {
4046
+ setPrimaryWalletId(verifiedWallet.id);
4047
+ }
4048
+ }, [wallets2, primaryWalletId]);
4049
+ const registerWallet = useCallback(async () => {
4050
+ if (!connected || !publicKey || isRegistering) return;
4051
+ setIsRegistering(true);
4052
+ try {
4053
+ const challenge = await walletConnection.connectWalletToBackend(publicKey.toBase58());
4054
+ if (!challenge?.message) {
4055
+ throw new Error("Failed to retrieve wallet verification challenge");
4056
+ }
4057
+ await signAndVerifyWallet(challenge.wallet, challenge.message, challenge.nonce);
4058
+ await fetchWallets();
4059
+ notify({
4060
+ title: "Wallet verified",
4061
+ description: "Your wallet has been linked successfully.",
4062
+ status: "success"
4063
+ });
4064
+ } catch (error) {
4065
+ notify({
4066
+ title: "Wallet verification failed",
4067
+ description: error instanceof Error ? error.message : "Failed to verify wallet",
4068
+ status: "destructive"
4069
+ });
4070
+ } finally {
4071
+ setIsRegistering(false);
4072
+ }
4073
+ }, [connected, publicKey, isRegistering, walletConnection, signAndVerifyWallet, fetchWallets, notify]);
4074
+ useEffect(() => {
4075
+ if (connected && publicKey && !walletConnection.isConnecting && wallets2.length === 0) {
4076
+ registerWallet().catch(() => void 0);
4077
+ }
4078
+ }, [connected, publicKey, walletConnection.isConnecting, wallets2.length, registerWallet]);
4079
+ const handleVerifyWallet = useCallback(
4080
+ async (wallet) => {
4081
+ if (!connected || publicKey?.toBase58() !== wallet.address) {
4082
+ notify({
4083
+ title: "Connect wallet first",
4084
+ description: "Please connect the wallet you want to verify.",
4085
+ status: "destructive"
4086
+ });
4087
+ return;
4088
+ }
4089
+ setVerifyingWalletId(wallet.id);
4090
+ try {
4091
+ const challenge = await walletConnection.connectWalletToBackend(wallet.address);
4092
+ if (!challenge?.message) {
4093
+ throw new Error("Failed to retrieve verification challenge");
4094
+ }
4095
+ await signAndVerifyWallet(challenge.wallet, challenge.message, challenge.nonce);
4096
+ await fetchWallets();
4097
+ notify({ title: "Wallet verified", status: "success" });
4098
+ } catch (error) {
4099
+ notify({
4100
+ title: "Verification failed",
4101
+ description: error instanceof Error ? error.message : "Failed to verify wallet",
4102
+ status: "destructive"
4103
+ });
4104
+ } finally {
4105
+ setVerifyingWalletId(null);
4106
+ }
4107
+ },
4108
+ [connected, publicKey, walletConnection, signAndVerifyWallet, fetchWallets, notify]
4109
+ );
4110
+ const handleDeleteWallet = useCallback(
4111
+ async (walletId) => {
4112
+ setDeletingWalletId(walletId);
4113
+ try {
4114
+ await deleteWallet(walletId);
4115
+ if (primaryWalletId === walletId) {
4116
+ setPrimaryWalletId(null);
4117
+ }
4118
+ if (connected && publicKey) {
4119
+ const deletedWallet = wallets2.find((w) => w.id === walletId);
4120
+ if (deletedWallet?.address === publicKey.toBase58()) {
4121
+ await disconnect();
4122
+ }
4123
+ }
4124
+ notify({ title: "Wallet removed", status: "success" });
4125
+ } catch (error) {
4126
+ notify({
4127
+ title: "Failed to remove wallet",
4128
+ description: error instanceof Error ? error.message : "Please try again",
4129
+ status: "destructive"
4130
+ });
4131
+ } finally {
4132
+ setDeletingWalletId(null);
4133
+ setWalletToDelete(null);
4134
+ }
4135
+ },
4136
+ [deleteWallet, primaryWalletId, connected, publicKey, wallets2, disconnect, notify]
4137
+ );
4138
+ const handleSetPrimary = (walletId) => {
4139
+ setPrimaryWalletId(walletId);
4140
+ notify({ title: "Primary wallet updated", status: "success" });
4141
+ };
4142
+ const isWalletRegistered = connected && publicKey && wallets2.some((w) => w.address === publicKey.toBase58());
4143
+ if (isLoading && wallets2.length === 0) {
4144
+ return /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-background/5", children: [
4145
+ /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-xl", children: "Solana Wallets" }) }),
4146
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-8", children: [
4147
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-6 w-6 animate-spin text-muted-foreground" }),
4148
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Loading wallets..." })
4149
+ ] }) })
4150
+ ] });
4151
+ }
4152
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4153
+ /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-background/5", children: [
4154
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
4155
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-xl", children: "Solana Wallets" }),
4156
+ /* @__PURE__ */ jsx(CardDescription, { children: "Connect and manage your Solana wallets for payments" })
4157
+ ] }),
4158
+ /* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: wallets2.length === 0 && !connected ? /* @__PURE__ */ jsx(EmptyWalletState, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
4159
+ primaryWallet && /* @__PURE__ */ jsxs("div", { children: [
4160
+ /* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-medium uppercase tracking-wide text-muted-foreground", children: "Primary Wallet" }),
4161
+ /* @__PURE__ */ jsx(
4162
+ WalletCard,
4163
+ {
4164
+ wallet: primaryWallet,
4165
+ isPrimary: true,
4166
+ isConnected: connected && publicKey?.toBase58() === primaryWallet.address,
4167
+ balance: balances[primaryWallet.address],
4168
+ onVerify: handleVerifyWallet,
4169
+ onDelete: (id) => {
4170
+ const target = wallets2.find((w) => w.id === id);
4171
+ if (target) setWalletToDelete(target);
4172
+ },
4173
+ isVerifying: verifyingWalletId === primaryWallet.id,
4174
+ isDeleting: deletingWalletId === primaryWallet.id,
4175
+ notify
4176
+ }
4177
+ )
4178
+ ] }),
4179
+ otherWallets.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
4180
+ /* @__PURE__ */ jsxs(
4181
+ "button",
4182
+ {
4183
+ className: "mb-3 flex w-full items-center justify-between text-left",
4184
+ onClick: () => setShowOtherWallets((prev) => !prev),
4185
+ children: [
4186
+ /* @__PURE__ */ jsxs("h3", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: [
4187
+ "Other Wallets (",
4188
+ otherWallets.length,
4189
+ ")"
4190
+ ] }),
4191
+ showOtherWallets ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
4192
+ ]
4193
+ }
4194
+ ),
4195
+ showOtherWallets && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: otherWallets.map((wallet) => /* @__PURE__ */ jsx(
4196
+ WalletCard,
4197
+ {
4198
+ wallet,
4199
+ isConnected: connected && publicKey?.toBase58() === wallet.address,
4200
+ balance: balances[wallet.address],
4201
+ onSetPrimary: handleSetPrimary,
4202
+ onVerify: handleVerifyWallet,
4203
+ onDelete: (id) => {
4204
+ const target = wallets2.find((w) => w.id === id);
4205
+ if (target) setWalletToDelete(target);
4206
+ },
4207
+ isVerifying: verifyingWalletId === wallet.id,
4208
+ isDeleting: deletingWalletId === wallet.id,
4209
+ notify
4210
+ },
4211
+ wallet.id
4212
+ )) })
4213
+ ] }),
4214
+ /* @__PURE__ */ jsx("div", { className: "pt-4", children: connected && publicKey ? isWalletRegistered ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
4215
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-emerald-500/40 bg-emerald-500/10 p-3 text-sm text-emerald-200", children: [
4216
+ /* @__PURE__ */ jsx(CheckCircle, { className: "h-4 w-4" }),
4217
+ " Connected wallet is verified and linked to your account."
4218
+ ] }),
4219
+ /* @__PURE__ */ jsx(WalletMultiButton, { className: "w-full !bg-primary text-primary-foreground" })
4220
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
4221
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-amber-500/40 bg-amber-500/10 p-3 text-sm text-amber-200", children: "Your connected wallet is not registered with your account." }),
4222
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4223
+ /* @__PURE__ */ jsx(Button, { onClick: registerWallet, disabled: isRegistering || walletConnection.isConnecting, className: "flex-1 bg-primary text-primary-foreground", children: isRegistering ? /* @__PURE__ */ jsxs(Fragment, { children: [
4224
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
4225
+ " Registering..."
4226
+ ] }) : "Register This Wallet" }),
4227
+ /* @__PURE__ */ jsx(Button, { onClick: () => disconnect(), variant: "outline", className: "border-border", children: "Disconnect" })
4228
+ ] })
4229
+ ] }) : /* @__PURE__ */ jsx(WalletMultiButton, { className: "w-full !bg-primary text-primary-foreground" }) })
4230
+ ] }) })
4231
+ ] }),
4232
+ /* @__PURE__ */ jsx(AlertDialog, { open: !!walletToDelete, onOpenChange: () => setWalletToDelete(null), children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
4233
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
4234
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: "Remove Wallet" }),
4235
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: "Are you sure you want to remove this wallet from your account? This action cannot be undone." })
4236
+ ] }),
4237
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
4238
+ /* @__PURE__ */ jsx(AlertDialogCancel, { children: "Cancel" }),
4239
+ /* @__PURE__ */ jsx(AlertDialogAction, { onClick: () => walletToDelete && handleDeleteWallet(walletToDelete.id), className: "bg-destructive text-destructive-foreground", children: "Remove" })
4240
+ ] })
4241
+ ] }) })
4242
+ ] });
4243
+ };
4244
+ var WalletManagement = (props) => /* @__PURE__ */ jsx(SolanaWalletSection, { ...props });
4245
+ var Checkbox = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4246
+ CheckboxPrimitive.Root,
4247
+ {
4248
+ ref,
4249
+ className: cn(
4250
+ "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
4251
+ className
4252
+ ),
4253
+ ...props,
4254
+ children: /* @__PURE__ */ jsx(
4255
+ CheckboxPrimitive.Indicator,
4256
+ {
4257
+ className: cn("flex items-center justify-center text-current"),
4258
+ children: /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" })
4259
+ }
4260
+ )
4261
+ }
4262
+ ));
4263
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
4264
+ var initialState = {
4265
+ nameOnCard: "",
4266
+ cardNumber: "",
4267
+ expiration: "",
4268
+ cvv: "",
4269
+ termsAccepted: false
4270
+ };
4271
+ var WalletDialog = ({ open, onOpenChange }) => {
4272
+ const [form, setForm] = useState(initialState);
4273
+ const [errors, setErrors] = useState({});
4274
+ const validators = useMemo(
4275
+ () => ({
4276
+ nameOnCard: (value) => !value ? "Name is required" : void 0,
4277
+ cardNumber: (value) => /^\d{16}$/.test(value) ? void 0 : "Card number must be 16 digits",
4278
+ expiration: (value) => /^(0[1-9]|1[0-2])\/([2-9]\d)$/.test(value) ? void 0 : "Must be in MM/YY format",
4279
+ cvv: (value) => /^\d{3,4}$/.test(value) ? void 0 : "CVV must be 3 or 4 digits",
4280
+ termsAccepted: (value) => value ? void 0 : "You must accept the terms"
4281
+ }),
4282
+ []
4283
+ );
4284
+ const updateField = (field, value) => {
4285
+ setForm((prev) => ({ ...prev, [field]: value }));
4286
+ setErrors((prev) => ({ ...prev, [field]: void 0 }));
4287
+ };
4288
+ const validate = () => {
4289
+ const next = {};
4290
+ Object.keys(validators).forEach((key) => {
4291
+ const validator = validators[key];
4292
+ const message = validator?.(form[key]);
4293
+ if (message) {
4294
+ next[key] = message;
4295
+ }
4296
+ });
4297
+ setErrors(next);
4298
+ return Object.keys(next).length === 0;
4299
+ };
4300
+ const handleSubmit = (event) => {
4301
+ event.preventDefault();
4302
+ if (!validate()) return;
4303
+ console.log("[payments-ui] wallet dialog submit", form);
4304
+ onOpenChange(false);
4305
+ setForm(initialState);
4306
+ };
4307
+ return /* @__PURE__ */ jsx(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(AlertDialogContent, { className: "max-h-[95vh] max-w-lg overflow-y-auto rounded-2xl border border-border bg-background", children: [
4308
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { className: "border-b border-border/60 pb-4", children: [
4309
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
4310
+ /* @__PURE__ */ jsx(Shield, { className: "h-5 w-5 text-primary" }),
4311
+ /* @__PURE__ */ jsx(AlertDialogTitle, { className: "text-center text-base font-semibold uppercase tracking-wide", children: "Secure Payment via Mobius Pay" })
4312
+ ] }),
4313
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-center text-sm text-muted-foreground", children: "$23 USD per month, cancel at any time." })
4314
+ ] }),
4315
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-5 px-2 py-4 sm:px-4", children: [
4316
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
4317
+ /* @__PURE__ */ jsxs("div", { children: [
4318
+ /* @__PURE__ */ jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Name on Card" }),
4319
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
4320
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(UserRound, { className: "h-4 w-4" }) }),
4321
+ /* @__PURE__ */ jsx(Input, { value: form.nameOnCard, onChange: (e) => updateField("nameOnCard", e.target.value), className: "pl-10" })
4322
+ ] }),
4323
+ errors.nameOnCard && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.nameOnCard })
4324
+ ] }),
4325
+ /* @__PURE__ */ jsxs("div", { children: [
4326
+ /* @__PURE__ */ jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Credit Card Number" }),
4327
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
4328
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }) }),
4329
+ /* @__PURE__ */ jsx(Input, { value: form.cardNumber, onChange: (e) => updateField("cardNumber", e.target.value), className: "pl-10" })
4330
+ ] }),
4331
+ errors.cardNumber && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.cardNumber })
4332
+ ] }),
4333
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
4334
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
4335
+ /* @__PURE__ */ jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Expiration" }),
4336
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
4337
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4" }) }),
4338
+ /* @__PURE__ */ jsx(Input, { value: form.expiration, onChange: (e) => updateField("expiration", e.target.value), className: "pl-10", placeholder: "MM/YY" })
4339
+ ] }),
4340
+ errors.expiration && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.expiration })
4341
+ ] }),
4342
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
4343
+ /* @__PURE__ */ jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "CVV" }),
4344
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
4345
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(KeyRound, { className: "h-4 w-4" }) }),
4346
+ /* @__PURE__ */ jsx(Input, { value: form.cvv, onChange: (e) => updateField("cvv", e.target.value), className: "pl-10" })
4347
+ ] }),
4348
+ errors.cvv && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.cvv })
4349
+ ] })
4350
+ ] })
4351
+ ] }),
4352
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-md border border-border/70 bg-muted/10 p-4", children: [
4353
+ /* @__PURE__ */ jsx(Checkbox, { id: "terms-agree", checked: form.termsAccepted, onCheckedChange: (checked) => updateField("termsAccepted", Boolean(checked)) }),
4354
+ /* @__PURE__ */ jsx(Label, { htmlFor: "terms-agree", className: "text-sm text-muted-foreground", children: "By completing this order, I confirm that I am 18 years or older and agree to your privacy policy and terms." })
4355
+ ] }),
4356
+ errors.termsAccepted && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: errors.termsAccepted }),
4357
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { className: "flex gap-2", children: [
4358
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "flex-1", children: "Subscribe" }),
4359
+ /* @__PURE__ */ jsx(AlertDialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "flex-1", children: "Close" }) })
4360
+ ] })
4361
+ ] })
4362
+ ] }) });
4363
+ };
2897
4364
  var useTokenBalance = (tokens) => {
2898
4365
  const { publicKey } = useWallet();
2899
4366
  const { connection } = useConnection();
@@ -3413,6 +4880,6 @@ var useAlternativePaymentProvider = () => {
3413
4880
  return { openFlexForm, isLoading, error };
3414
4881
  };
3415
4882
 
3416
- export { CardDetailsForm, CardPaymentService, PaymentApp, PaymentExperience, PaymentMethodService, PaymentProvider, SolanaPaymentSelector, SolanaPaymentService, StoredPaymentMethods, SubscriptionCheckoutModal, SubscriptionService, SubscriptionSuccessDialog, TokenCatalog, WalletGateway, createPaymentStore, useAlternativePaymentProvider, useDirectWalletPayment, usePaymentContext, usePaymentMethodService, usePaymentMethods, usePaymentStatus, usePaymentStore, useSolanaDirectPayment, useSolanaQrPayment, useSolanaService, useSubscriptionActions, useSupportedTokens, useTokenBalance };
4883
+ export { BillingHistory, CancelMembershipDialog, CardDetailsForm, CardPaymentService, EmptyWalletState, PaymentApp, PaymentExperience, PaymentMethodService, PaymentMethodsSection, PaymentProvider, PaymentsDialogProvider, PaymentsRuntime, SolanaPaymentSelector, SolanaPaymentService, SolanaPaymentView, SolanaWalletSection, SolanaWalletService, StoredPaymentMethods, SubscriptionCheckoutModal, SubscriptionService, SubscriptionSuccessDialog, TokenCatalog, WalletCard, WalletDialog, WalletGateway, WalletManagement, WalletModal, createPaymentsRuntime, useAlternativePaymentProvider, useDirectWalletPayment, usePaymentContext, usePaymentDialogs, usePaymentMethodService, usePaymentMethods, usePaymentNotifications, usePaymentStatus, useSolanaDirectPayment, useSolanaQrPayment, useSolanaService, useSubscriptionActions, useSupportedTokens, useTokenBalance, useWalletConnection, useWalletList, useWalletVerification };
3417
4884
  //# sourceMappingURL=index.js.map
3418
4885
  //# sourceMappingURL=index.js.map