@doujins/payments-ui 0.0.12 → 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,26 +1,29 @@
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, Ban, TriangleAlert, Star, CheckCircle, AlertCircle, Copy, ExternalLink, Shield, Wallet, UserRound, Calendar, KeyRound, 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, useInfiniteQuery } 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';
22
26
  import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
23
- import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';
24
27
  import bs58 from 'bs58';
25
28
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
26
29
 
@@ -491,141 +494,98 @@ var PaymentApp = class {
491
494
  };
492
495
  }
493
496
  };
494
- var initialState = {
495
- selectedMethodId: null,
496
- solanaModalOpen: false,
497
- savedPaymentStatus: "idle",
498
- savedPaymentError: null,
499
- newCardStatus: "idle",
500
- newCardError: null,
501
- solanaTab: "wallet",
502
- solanaStatus: "selecting",
503
- solanaError: null,
504
- solanaTransactionId: null,
505
- solanaSelectedToken: null,
506
- solanaTokenAmount: 0
507
- };
508
- var createPaymentStore = (options) => createStore((set, get) => {
509
- const notifyStatus = (status, context) => {
510
- options?.callbacks?.onStatusChange?.({ status, context });
511
- };
512
- const notifySuccess = (payload) => {
513
- if (!options?.callbacks?.onSuccess) return;
514
- options.callbacks.onSuccess(payload ?? {});
515
- };
516
- const notifyError = (error) => {
517
- options?.callbacks?.onError?.(new Error(error));
518
- };
519
- return {
520
- ...initialState,
521
- setSelectedMethod: (methodId) => {
522
- if (get().selectedMethodId === methodId) return;
523
- set({ selectedMethodId: methodId });
524
- },
525
- setSolanaModalOpen: (open) => {
526
- if (get().solanaModalOpen === open) return;
527
- set({ solanaModalOpen: open });
528
- },
529
- setSolanaTab: (tab) => {
530
- if (get().solanaTab === tab) return;
531
- set({ solanaTab: tab });
532
- },
533
- setSolanaSelectedToken: (symbol) => {
534
- if (get().solanaSelectedToken === symbol) return;
535
- set({ solanaSelectedToken: symbol });
536
- },
537
- setSolanaTokenAmount: (amount) => {
538
- if (get().solanaTokenAmount === amount) return;
539
- set({ solanaTokenAmount: amount });
540
- },
541
- setSolanaTransactionId: (txId) => {
542
- if (get().solanaTransactionId === txId) return;
543
- set({ solanaTransactionId: txId });
544
- },
545
- startSavedPayment: () => {
546
- notifyStatus("processing", { source: "saved-payment" });
547
- set({ savedPaymentStatus: "processing", savedPaymentError: null });
548
- },
549
- completeSavedPayment: () => {
550
- notifyStatus("success", { source: "saved-payment" });
551
- set({ savedPaymentStatus: "success", savedPaymentError: null });
552
- },
553
- failSavedPayment: (error) => {
554
- notifyStatus("error", { source: "saved-payment" });
555
- notifyError(error);
556
- set({ savedPaymentStatus: "error", savedPaymentError: error });
557
- },
558
- resetSavedPayment: () => set({ savedPaymentStatus: "idle", savedPaymentError: null }),
559
- startNewCardPayment: () => {
560
- notifyStatus("processing", { source: "new-card" });
561
- set({ newCardStatus: "processing", newCardError: null });
562
- },
563
- completeNewCardPayment: () => {
564
- notifyStatus("success", { source: "new-card" });
565
- set({ newCardStatus: "success", newCardError: null });
566
- },
567
- failNewCardPayment: (error) => {
568
- notifyStatus("error", { source: "new-card" });
569
- notifyError(error);
570
- set({ newCardStatus: "error", newCardError: error });
571
- },
572
- resetNewCardPayment: () => set({ newCardStatus: "idle", newCardError: null }),
573
- startSolanaPayment: () => {
574
- notifyStatus("processing", { source: "solana" });
575
- set({ solanaStatus: "processing", solanaError: null });
576
- },
577
- confirmSolanaPayment: () => set({ solanaStatus: "confirming" }),
578
- completeSolanaPayment: (payload) => {
579
- notifyStatus("success", { source: "solana" });
580
- notifySuccess(payload);
581
- set({ solanaStatus: "success", solanaError: null });
582
- },
583
- failSolanaPayment: (error) => {
584
- notifyStatus("error", { source: "solana" });
585
- notifyError(error);
586
- 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
587
506
  },
588
- resetSolanaPayment: () => set({
589
- solanaStatus: "selecting",
590
- solanaError: null,
591
- solanaTransactionId: null
592
- }),
593
- resetAll: () => set(initialState)
594
- };
507
+ mutations: {
508
+ retry: 1
509
+ }
510
+ }
595
511
  });
596
- var PaymentContext = createContext(void 0);
597
- var PaymentProvider = ({
598
- config,
599
- children
600
- }) => {
601
- const app = useMemo(() => new PaymentApp({ config }), [config]);
602
- const store = useMemo(
603
- () => createPaymentStore({ callbacks: config.callbacks }),
604
- [config.callbacks]
605
- );
606
- const value = useMemo(() => {
607
- return {
608
- config: app.getConfig(),
609
- fetcher: app.getFetcher(),
610
- resolveAuthToken: app.resolveAuthToken,
611
- app,
612
- services: app.getServices(),
613
- store
614
- };
615
- }, [app, store]);
616
- useEffect(() => {
617
- if (!value.config.collectJsKey) return;
618
- loadCollectJs(value.config.collectJsKey);
619
- }, [value.config.collectJsKey]);
620
- return /* @__PURE__ */ jsx(PaymentContext.Provider, { value, children });
621
- };
622
- var usePaymentContext = () => {
623
- const context = useContext(PaymentContext);
624
- if (!context) {
625
- 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();
626
518
  }
627
- return context;
628
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;
629
589
  var customCountries = [
630
590
  { code: "TW", name: "Taiwan, Province of China" },
631
591
  { code: "KR", name: "Korea" },
@@ -755,9 +715,6 @@ function getElementRef(element) {
755
715
  }
756
716
  return element.props.ref || element.ref;
757
717
  }
758
- function cn(...inputs) {
759
- return twMerge(clsx(inputs));
760
- }
761
718
  var buttonVariants = cva(
762
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",
763
720
  {
@@ -900,7 +857,6 @@ var defaultBilling = {
900
857
  firstName: "",
901
858
  lastName: "",
902
859
  address1: "",
903
- address2: "",
904
860
  city: "",
905
861
  stateRegion: "",
906
862
  postalCode: "",
@@ -934,7 +890,6 @@ var CardDetailsForm = ({
934
890
  const [firstName, setFirstName] = useState(mergedDefaults.firstName);
935
891
  const [lastName, setLastName] = useState(mergedDefaults.lastName);
936
892
  const [address1, setAddress1] = useState(mergedDefaults.address1);
937
- const [address2, setAddress2] = useState(mergedDefaults.address2 ?? "");
938
893
  const [city, setCity] = useState(mergedDefaults.city);
939
894
  const [stateRegion, setStateRegion] = useState(mergedDefaults.stateRegion ?? "");
940
895
  const [postalCode, setPostalCode] = useState(mergedDefaults.postalCode);
@@ -953,7 +908,6 @@ var CardDetailsForm = ({
953
908
  setFirstName(mergedDefaults.firstName);
954
909
  setLastName(mergedDefaults.lastName);
955
910
  setAddress1(mergedDefaults.address1);
956
- setAddress2(mergedDefaults.address2 ?? "");
957
911
  setCity(mergedDefaults.city);
958
912
  setStateRegion(mergedDefaults.stateRegion ?? "");
959
913
  setPostalCode(mergedDefaults.postalCode);
@@ -966,7 +920,6 @@ var CardDetailsForm = ({
966
920
  firstName,
967
921
  lastName,
968
922
  address1,
969
- address2,
970
923
  city,
971
924
  stateRegion,
972
925
  postalCode,
@@ -978,7 +931,6 @@ var CardDetailsForm = ({
978
931
  firstName,
979
932
  lastName,
980
933
  address1,
981
- address2,
982
934
  city,
983
935
  stateRegion,
984
936
  postalCode,
@@ -1002,7 +954,6 @@ var CardDetailsForm = ({
1002
954
  firstName,
1003
955
  lastName,
1004
956
  address1,
1005
- address2,
1006
957
  city,
1007
958
  stateRegion,
1008
959
  postalCode,
@@ -1033,7 +984,6 @@ var CardDetailsForm = ({
1033
984
  firstName,
1034
985
  lastName,
1035
986
  address1,
1036
- address2,
1037
987
  city,
1038
988
  stateRegion,
1039
989
  postalCode,
@@ -1062,19 +1012,16 @@ var CardDetailsForm = ({
1062
1012
  window.CollectJS.startPaymentRequest();
1063
1013
  };
1064
1014
  const errorMessage = localError ?? externalError;
1065
- 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";
1066
1016
  return /* @__PURE__ */ jsxs(
1067
1017
  "form",
1068
1018
  {
1069
- className: cn(
1070
- "space-y-6 rounded-2xl border border-border/60 bg-card/90 p-6 shadow-lg",
1071
- className
1072
- ),
1019
+ className: cn("space-y-5", className),
1073
1020
  onSubmit: handleSubmit,
1074
1021
  noValidate: true,
1075
1022
  children: [
1076
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 }),
1077
- /* @__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: [
1078
1025
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1079
1026
  /* @__PURE__ */ jsxs(Label, { htmlFor: "payments-first", className: "flex items-center gap-2 text-muted-foreground", children: [
1080
1027
  /* @__PURE__ */ jsx(User, { className: "h-4 w-4" }),
@@ -1120,7 +1067,7 @@ var CardDetailsForm = ({
1120
1067
  )
1121
1068
  ] }),
1122
1069
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1123
- /* @__PURE__ */ jsx(Label, { htmlFor: "payments-address1", children: "Address line 1" }),
1070
+ /* @__PURE__ */ jsx(Label, { htmlFor: "payments-address1", children: "Address" }),
1124
1071
  /* @__PURE__ */ jsx(
1125
1072
  Input,
1126
1073
  {
@@ -1131,18 +1078,7 @@ var CardDetailsForm = ({
1131
1078
  }
1132
1079
  )
1133
1080
  ] }),
1134
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1135
- /* @__PURE__ */ jsx(Label, { htmlFor: "payments-address2", children: "Address line 2 (optional)" }),
1136
- /* @__PURE__ */ jsx(
1137
- Input,
1138
- {
1139
- id: "payments-address2",
1140
- value: address2,
1141
- onChange: (e) => setAddress2(e.target.value)
1142
- }
1143
- )
1144
- ] }),
1145
- /* @__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: [
1146
1082
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1147
1083
  /* @__PURE__ */ jsx(Label, { htmlFor: "payments-city", children: "City" }),
1148
1084
  /* @__PURE__ */ jsx(
@@ -1167,7 +1103,7 @@ var CardDetailsForm = ({
1167
1103
  )
1168
1104
  ] })
1169
1105
  ] }),
1170
- /* @__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: [
1171
1107
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1172
1108
  /* @__PURE__ */ jsxs(Label, { htmlFor: "payments-postal", className: "flex items-center gap-2 text-muted-foreground", children: [
1173
1109
  /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4" }),
@@ -1187,7 +1123,7 @@ var CardDetailsForm = ({
1187
1123
  /* @__PURE__ */ jsx(Label, { children: "Country" }),
1188
1124
  /* @__PURE__ */ jsxs(Select, { value: country, onValueChange: setCountry, children: [
1189
1125
  /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a country" }) }),
1190
- /* @__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)) })
1191
1127
  ] })
1192
1128
  ] })
1193
1129
  ] }),
@@ -1276,63 +1212,6 @@ var usePaymentMethods = () => {
1276
1212
  deleteMutation
1277
1213
  };
1278
1214
  };
1279
- var Dialog = DialogPrimitive.Root;
1280
- var DialogPortal = ({ className, ...props }) => /* @__PURE__ */ jsx(DialogPrimitive.Portal, { className: cn(className), ...props });
1281
- DialogPortal.displayName = DialogPrimitive.Portal.displayName;
1282
- var DialogOverlay = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1283
- DialogPrimitive.Overlay,
1284
- {
1285
- ref,
1286
- className: cn(
1287
- "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",
1288
- className
1289
- ),
1290
- ...props
1291
- }
1292
- ));
1293
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
1294
- var DialogContent = React3.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
1295
- /* @__PURE__ */ jsx(DialogOverlay, {}),
1296
- /* @__PURE__ */ jsxs(
1297
- DialogPrimitive.Content,
1298
- {
1299
- ref,
1300
- className: cn(
1301
- "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%]",
1302
- className
1303
- ),
1304
- ...props,
1305
- children: [
1306
- children,
1307
- /* @__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: [
1308
- /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
1309
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
1310
- ] })
1311
- ]
1312
- }
1313
- )
1314
- ] }));
1315
- DialogContent.displayName = DialogPrimitive.Content.displayName;
1316
- var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
1317
- DialogHeader.displayName = "DialogHeader";
1318
- var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1319
- DialogPrimitive.Title,
1320
- {
1321
- ref,
1322
- className: cn("text-lg font-semibold leading-none tracking-tight", className),
1323
- ...props
1324
- }
1325
- ));
1326
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
1327
- var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1328
- DialogPrimitive.Description,
1329
- {
1330
- ref,
1331
- className: cn("text-sm text-muted-foreground", className),
1332
- ...props
1333
- }
1334
- ));
1335
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
1336
1215
  var badgeVariants = cva(
1337
1216
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none",
1338
1217
  {
@@ -1382,43 +1261,6 @@ var ScrollBar = React3.forwardRef(({ className, orientation = "vertical", ...pro
1382
1261
  }
1383
1262
  ));
1384
1263
  ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
1385
- var Card = React3.forwardRef(
1386
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1387
- "div",
1388
- {
1389
- ref,
1390
- className: cn("rounded-xl border bg-card text-card-foreground shadow", className),
1391
- ...props
1392
- }
1393
- )
1394
- );
1395
- Card.displayName = "Card";
1396
- var CardHeader = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1397
- "div",
1398
- {
1399
- ref,
1400
- className: cn("flex flex-col space-y-1.5 p-6", className),
1401
- ...props
1402
- }
1403
- ));
1404
- CardHeader.displayName = "CardHeader";
1405
- var CardTitle = React3.forwardRef(
1406
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })
1407
- );
1408
- CardTitle.displayName = "CardTitle";
1409
- var CardDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
1410
- CardDescription.displayName = "CardDescription";
1411
- var CardContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
1412
- CardContent.displayName = "CardContent";
1413
- var CardFooter = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1414
- "div",
1415
- {
1416
- ref,
1417
- className: cn("flex items-center p-6 pt-0", className),
1418
- ...props
1419
- }
1420
- ));
1421
- CardFooter.displayName = "CardFooter";
1422
1264
  var formatCardLabel = (method) => {
1423
1265
  const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
1424
1266
  const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
@@ -1447,22 +1289,22 @@ var StoredPaymentMethods = ({
1447
1289
  }
1448
1290
  );
1449
1291
  };
1450
- return /* @__PURE__ */ jsxs(Card, { className: "border-border/60 bg-card/95", children: [
1451
- /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-start justify-between space-y-0", children: [
1452
- /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1453
- /* @__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: [
1454
1296
  /* @__PURE__ */ jsx(WalletCards, { className: "h-4 w-4" }),
1455
1297
  " ",
1456
1298
  heading
1457
- ] }) }),
1458
- /* @__PURE__ */ jsx(CardDescription, { children: description })
1299
+ ] }),
1300
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: description })
1459
1301
  ] }),
1460
- showAddButton && /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => setIsModalOpen(true), children: [
1302
+ showAddButton && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setIsModalOpen(true), children: [
1461
1303
  /* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
1462
1304
  " Add card"
1463
1305
  ] })
1464
1306
  ] }),
1465
- /* @__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: [
1466
1308
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
1467
1309
  " Loading cards\u2026"
1468
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) => {
@@ -1520,8 +1362,8 @@ var StoredPaymentMethods = ({
1520
1362
  },
1521
1363
  method.id
1522
1364
  );
1523
- }) }) }) }),
1524
- /* @__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: [
1525
1367
  /* @__PURE__ */ jsxs(DialogHeader, { children: [
1526
1368
  /* @__PURE__ */ jsx(DialogTitle, { children: "Add a new card" }),
1527
1369
  /* @__PURE__ */ jsx(DialogDescription, { children: "Your card details are tokenized securely via our payment provider." })
@@ -1540,26 +1382,91 @@ var StoredPaymentMethods = ({
1540
1382
  ] }) })
1541
1383
  ] });
1542
1384
  };
1543
- var useSolanaService = () => {
1544
- const { services } = usePaymentContext();
1545
- return useMemo(() => services.solanaPayments, [services]);
1546
- };
1547
- var getSolBalance = async (connection, publicKey) => {
1548
- try {
1549
- const lamports = await connection.getBalance(publicKey);
1550
- return lamports / LAMPORTS_PER_SOL;
1551
- } catch (error) {
1552
- console.error("Failed to fetch SOL balance:", error);
1553
- return 0;
1554
- }
1555
- };
1556
- var fetchAllTokenBalances = async (connection, publicKey) => {
1557
- const balances = /* @__PURE__ */ new Map();
1558
- try {
1559
- const tokenAccounts = await connection.getParsedProgramAccounts(
1560
- TOKEN_PROGRAM_ID,
1561
- {
1562
- filters: [
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
+ };
1450
+ var useSolanaService = () => {
1451
+ const { services } = usePaymentContext();
1452
+ return useMemo(() => services.solanaPayments, [services]);
1453
+ };
1454
+ var getSolBalance = async (connection, publicKey) => {
1455
+ try {
1456
+ const lamports = await connection.getBalance(publicKey);
1457
+ return lamports / LAMPORTS_PER_SOL;
1458
+ } catch (error) {
1459
+ console.error("Failed to fetch SOL balance:", error);
1460
+ return 0;
1461
+ }
1462
+ };
1463
+ var fetchAllTokenBalances = async (connection, publicKey) => {
1464
+ const balances = /* @__PURE__ */ new Map();
1465
+ try {
1466
+ const tokenAccounts = await connection.getParsedProgramAccounts(
1467
+ TOKEN_PROGRAM_ID,
1468
+ {
1469
+ filters: [
1563
1470
  {
1564
1471
  dataSize: 165
1565
1472
  // Size of token account
@@ -1816,6 +1723,43 @@ var useSolanaDirectPayment = (options) => {
1816
1723
  pay
1817
1724
  };
1818
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";
1819
1763
  var DirectPayment = ({
1820
1764
  priceId,
1821
1765
  tokenAmount,
@@ -1836,7 +1780,7 @@ var DirectPayment = ({
1836
1780
  onSuccess: onPaymentSuccess,
1837
1781
  onError: onPaymentError
1838
1782
  });
1839
- 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: [
1840
1784
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1841
1785
  /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: [
1842
1786
  /* @__PURE__ */ jsx(Wallet, { className: "h-4 w-4" }),
@@ -2068,9 +2012,9 @@ var QRCodePayment = ({
2068
2012
  onSuccess: onPaymentSuccess
2069
2013
  });
2070
2014
  if (!selectedToken) {
2071
- 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." });
2072
2016
  }
2073
- 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: [
2074
2018
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
2075
2019
  /* @__PURE__ */ jsxs("div", { children: [
2076
2020
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: "Scan with Solana Pay" }),
@@ -2299,109 +2243,21 @@ var useSupportedTokens = () => {
2299
2243
  tokenCount: tokens.length
2300
2244
  };
2301
2245
  };
2302
- var usePaymentStore = (selector) => {
2303
- const { store } = usePaymentContext();
2304
- return useStore(store, selector);
2305
- };
2306
-
2307
- // src/state/selectors.ts
2308
- var selectCheckoutFlow = (state) => ({
2309
- selectedMethodId: state.selectedMethodId,
2310
- savedStatus: state.savedPaymentStatus,
2311
- savedError: state.savedPaymentError,
2312
- newCardStatus: state.newCardStatus,
2313
- newCardError: state.newCardError,
2314
- solanaModalOpen: state.solanaModalOpen,
2315
- setSelectedMethod: state.setSelectedMethod,
2316
- setSolanaModalOpen: state.setSolanaModalOpen,
2317
- startSavedPayment: state.startSavedPayment,
2318
- completeSavedPayment: state.completeSavedPayment,
2319
- failSavedPayment: state.failSavedPayment,
2320
- startNewCardPayment: state.startNewCardPayment,
2321
- completeNewCardPayment: state.completeNewCardPayment,
2322
- failNewCardPayment: state.failNewCardPayment,
2323
- resetSavedPayment: state.resetSavedPayment
2324
- });
2325
- var selectSolanaFlow = (state) => ({
2326
- tab: state.solanaTab,
2327
- status: state.solanaStatus,
2328
- error: state.solanaError,
2329
- transactionId: state.solanaTransactionId,
2330
- tokenAmount: state.solanaTokenAmount,
2331
- selectedTokenSymbol: state.solanaSelectedToken,
2332
- setTab: state.setSolanaTab,
2333
- setTokenAmount: state.setSolanaTokenAmount,
2334
- setTransactionId: state.setSolanaTransactionId,
2335
- setSelectedTokenSymbol: state.setSolanaSelectedToken,
2336
- startSolanaPayment: state.startSolanaPayment,
2337
- confirmSolanaPayment: state.confirmSolanaPayment,
2338
- completeSolanaPayment: state.completeSolanaPayment,
2339
- failSolanaPayment: state.failSolanaPayment,
2340
- resetSolanaPayment: state.resetSolanaPayment
2341
- });
2342
- var Tabs = TabsPrimitive.Root;
2343
- var TabsList = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2344
- TabsPrimitive.List,
2345
- {
2346
- ref,
2347
- className: cn(
2348
- "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
2349
- className
2350
- ),
2351
- ...props
2352
- }
2353
- ));
2354
- TabsList.displayName = TabsPrimitive.List.displayName;
2355
- var TabsTrigger = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2356
- TabsPrimitive.Trigger,
2357
- {
2358
- ref,
2359
- className: cn(
2360
- "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",
2361
- className
2362
- ),
2363
- ...props
2364
- }
2365
- ));
2366
- TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
2367
- var TabsContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2368
- TabsPrimitive.Content,
2369
- {
2370
- ref,
2371
- className: cn(
2372
- "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
2373
- className
2374
- ),
2375
- ...props
2376
- }
2377
- ));
2378
- TabsContent.displayName = TabsPrimitive.Content.displayName;
2379
- var SolanaPaymentSelector = ({
2380
- isOpen,
2381
- onClose,
2246
+ var SolanaPaymentView = ({
2382
2247
  priceId,
2383
2248
  usdAmount,
2384
2249
  onSuccess,
2385
- onError
2250
+ onError,
2251
+ onClose
2386
2252
  }) => {
2387
2253
  const { connected } = useWallet();
2388
- const {
2389
- tab: activeTab,
2390
- status: paymentState,
2391
- error: errorMessage,
2392
- transactionId,
2393
- tokenAmount,
2394
- selectedTokenSymbol,
2395
- setTab,
2396
- setTokenAmount,
2397
- setTransactionId,
2398
- setSelectedTokenSymbol,
2399
- startSolanaPayment,
2400
- confirmSolanaPayment,
2401
- completeSolanaPayment,
2402
- failSolanaPayment,
2403
- resetSolanaPayment
2404
- } = 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);
2405
2261
  const {
2406
2262
  tokens,
2407
2263
  isLoading: tokensLoading,
@@ -2417,60 +2273,70 @@ var SolanaPaymentSelector = ({
2417
2273
  const defaultToken = tokens.find((token) => token.symbol === "SOL") || tokens[0];
2418
2274
  setSelectedTokenSymbol(defaultToken.symbol);
2419
2275
  }
2420
- }, [tokens, selectedTokenSymbol, setSelectedTokenSymbol]);
2276
+ }, [tokens, selectedTokenSymbol]);
2421
2277
  const handlePaymentStart = useCallback(() => {
2422
- startSolanaPayment();
2423
- }, [startSolanaPayment]);
2278
+ setPaymentState("processing");
2279
+ setErrorMessage(null);
2280
+ notifyStatus("processing", { source: "solana" });
2281
+ }, [notifyStatus]);
2424
2282
  const handlePaymentConfirming = useCallback(() => {
2425
- confirmSolanaPayment();
2426
- }, [confirmSolanaPayment]);
2283
+ setPaymentState("confirming");
2284
+ }, []);
2427
2285
  const handlePaymentSuccess = useCallback(
2428
2286
  (result, txId) => {
2429
2287
  const resolvedTx = txId || (typeof result === "string" ? result : result.transaction_id);
2430
2288
  setTransactionId(resolvedTx);
2431
- completeSolanaPayment(
2432
- typeof result === "string" ? {
2433
- transactionId: resolvedTx,
2434
- processor: "solana",
2435
- metadata: { source: "solana-pay" }
2436
- } : {
2437
- transactionId: result.transaction_id,
2438
- intentId: result.intent_id,
2439
- processor: "solana",
2440
- metadata: {
2441
- purchaseId: result.purchase_id,
2442
- amount: result.amount,
2443
- currency: result.currency
2444
- }
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
2445
2303
  }
2446
- );
2304
+ };
2305
+ notifyStatus("success", { source: "solana" });
2306
+ notifySuccess(payload);
2447
2307
  setTimeout(() => {
2448
2308
  onSuccess(result);
2449
2309
  }, 1500);
2450
2310
  },
2451
- [completeSolanaPayment, onSuccess, setTransactionId]
2311
+ [notifyStatus, notifySuccess, onSuccess]
2452
2312
  );
2453
2313
  const handlePaymentError = useCallback(
2454
2314
  (error) => {
2455
- failSolanaPayment(error);
2315
+ setPaymentState("error");
2316
+ setErrorMessage(error);
2317
+ notifyStatus("error", { source: "solana" });
2318
+ notifyError(error);
2456
2319
  onError?.(error);
2457
2320
  },
2458
- [failSolanaPayment, onError]
2321
+ [notifyError, notifyStatus, onError]
2459
2322
  );
2460
- const handleRetry = useCallback(() => {
2461
- resetSolanaPayment();
2323
+ const resetState = useCallback(() => {
2324
+ setPaymentState("selecting");
2325
+ setErrorMessage(null);
2462
2326
  setTransactionId(null);
2463
- }, [resetSolanaPayment, setTransactionId]);
2327
+ }, []);
2328
+ const handleRetry = useCallback(() => {
2329
+ resetState();
2330
+ }, [resetState]);
2464
2331
  const handleClose = useCallback(() => {
2465
2332
  if (paymentState === "processing" || paymentState === "confirming") {
2466
2333
  return;
2467
2334
  }
2468
- resetSolanaPayment();
2469
- setTransactionId(null);
2470
- onClose();
2471
- }, [paymentState, resetSolanaPayment, setTransactionId, onClose]);
2335
+ resetState();
2336
+ onClose?.();
2337
+ }, [paymentState, onClose, resetState]);
2472
2338
  useEffect(() => {
2473
- if (!isOpen || !selectedToken || usdAmount === 0) {
2339
+ if (!selectedToken || usdAmount === 0) {
2474
2340
  setTokenAmount(0);
2475
2341
  return;
2476
2342
  }
@@ -2480,23 +2346,20 @@ var SolanaPaymentSelector = ({
2480
2346
  return;
2481
2347
  }
2482
2348
  setTokenAmount(usdAmount / price);
2483
- }, [isOpen, usdAmount, selectedToken, setTokenAmount]);
2484
- const handleTokenChange = useCallback(
2485
- (value) => {
2486
- setSelectedTokenSymbol(value);
2487
- },
2488
- [setSelectedTokenSymbol]
2489
- );
2349
+ }, [usdAmount, selectedToken]);
2350
+ const handleTokenChange = useCallback((value) => {
2351
+ setSelectedTokenSymbol(value);
2352
+ }, []);
2490
2353
  const wasConnectedRef = useRef(connected);
2491
2354
  useEffect(() => {
2492
2355
  if (connected && !wasConnectedRef.current) {
2493
- setTab("wallet");
2356
+ setActiveTab("wallet");
2494
2357
  }
2495
2358
  if (!connected && wasConnectedRef.current) {
2496
- setTab("qr");
2359
+ setActiveTab("qr");
2497
2360
  }
2498
2361
  wasConnectedRef.current = connected;
2499
- }, [connected, setTab]);
2362
+ }, [connected]);
2500
2363
  const renderBody = () => {
2501
2364
  if (paymentState !== "selecting") {
2502
2365
  return /* @__PURE__ */ jsx(
@@ -2512,63 +2375,79 @@ var SolanaPaymentSelector = ({
2512
2375
  }
2513
2376
  );
2514
2377
  }
2515
- 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: [
2516
- /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
2517
- " Loading supported tokens\u2026"
2518
- ] }) : 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: [
2519
- /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border/60 bg-muted/10 p-4 text-center", children: [
2520
- /* @__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: [
2521
2394
  "$",
2522
2395
  usdAmount.toFixed(2),
2523
2396
  " USD"
2524
2397
  ] }),
2525
- 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: [
2526
2399
  "\u2248 ",
2527
2400
  tokenAmount.toFixed(selectedToken.symbol === "SOL" ? 4 : 2),
2528
2401
  " ",
2529
2402
  selectedToken.symbol
2530
2403
  ] })
2531
2404
  ] }),
2532
- /* @__PURE__ */ jsxs(Select, { value: selectedToken?.symbol ?? "", onValueChange: handleTokenChange, children: [
2533
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select token" }) }),
2534
- /* @__PURE__ */ jsx(SelectContent, { className: "max-h-64", children: tokens.map((token) => /* @__PURE__ */ jsxs(SelectItem, { value: token.symbol, children: [
2535
- token.name,
2536
- " (",
2537
- token.symbol,
2538
- ")"
2539
- ] }, 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
+ ] })
2540
2416
  ] }),
2541
- /* @__PURE__ */ jsxs(
2417
+ /* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxs(
2542
2418
  Tabs,
2543
2419
  {
2544
2420
  value: activeTab,
2545
- onValueChange: (value) => setTab(value),
2546
- className: "w-full",
2421
+ onValueChange: (value) => setActiveTab(value),
2422
+ className: "w-full space-y-3",
2547
2423
  children: [
2548
- /* @__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: [
2549
2425
  /* @__PURE__ */ jsxs(TabsTrigger, { value: "wallet", disabled: !connected, children: [
2550
2426
  /* @__PURE__ */ jsx(Wallet, { className: "mr-2 h-4 w-4" }),
2551
- " Pay with Wallet"
2427
+ " Wallet"
2552
2428
  ] }),
2553
2429
  /* @__PURE__ */ jsxs(TabsTrigger, { value: "qr", children: [
2554
2430
  /* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
2555
- " Scan QR Code"
2431
+ " QR Code"
2556
2432
  ] })
2557
2433
  ] }),
2558
- /* @__PURE__ */ jsx(TabsContent, { value: "wallet", className: "mt-4", children: activeTab === "wallet" && /* @__PURE__ */ jsx(
2559
- DirectPayment,
2560
- {
2561
- priceId,
2562
- tokenAmount,
2563
- selectedToken,
2564
- supportedTokens: tokens,
2565
- onPaymentStart: handlePaymentStart,
2566
- onPaymentConfirming: handlePaymentConfirming,
2567
- onPaymentSuccess: handlePaymentSuccess,
2568
- onPaymentError: handlePaymentError
2569
- }
2570
- ) }),
2571
- /* @__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(
2572
2451
  QRCodePayment,
2573
2452
  {
2574
2453
  priceId,
@@ -2579,17 +2458,34 @@ var SolanaPaymentSelector = ({
2579
2458
  ) })
2580
2459
  ]
2581
2460
  }
2582
- ),
2583
- !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." })
2584
- ] }) });
2461
+ ) })
2462
+ ] });
2585
2463
  };
2586
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-lg", children: [
2587
- /* @__PURE__ */ jsxs(DialogHeader, { className: "space-y-1", children: [
2588
- /* @__PURE__ */ jsx(DialogTitle, { children: "Complete your payment" }),
2589
- /* @__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
+ )
2590
2486
  ] }),
2591
2487
  renderBody()
2592
- ] }) });
2488
+ ] });
2593
2489
  };
2594
2490
  var PaymentExperience = ({
2595
2491
  priceId,
@@ -2599,142 +2495,608 @@ var PaymentExperience = ({
2599
2495
  enableNewCard = true,
2600
2496
  enableStoredMethods = true,
2601
2497
  enableSolanaPay = true,
2602
- checkoutSummary,
2603
2498
  onSolanaSuccess,
2604
- onSolanaError
2499
+ onSolanaError,
2500
+ initialMode = "cards"
2605
2501
  }) => {
2606
2502
  const showNewCard = enableNewCard && Boolean(onNewCardPayment);
2607
- const showStored = enableStoredMethods;
2608
- const {
2609
- selectedMethodId,
2610
- savedStatus,
2611
- savedError,
2612
- newCardStatus,
2613
- newCardError,
2614
- solanaModalOpen,
2615
- setSelectedMethod,
2616
- setSolanaModalOpen,
2617
- startSavedPayment,
2618
- completeSavedPayment,
2619
- failSavedPayment,
2620
- startNewCardPayment,
2621
- completeNewCardPayment,
2622
- failNewCardPayment,
2623
- resetSavedPayment
2624
- } = usePaymentStore(selectCheckoutFlow);
2625
- const handleMethodSelect = (method) => {
2626
- setSelectedMethod(method.id);
2627
- resetSavedPayment();
2628
- };
2629
- const handleNewCardTokenize = async (token, billing) => {
2630
- if (!onNewCardPayment) return;
2631
- try {
2632
- startNewCardPayment();
2633
- await onNewCardPayment({ token, billing });
2634
- completeNewCardPayment();
2635
- } catch (error) {
2636
- const message = error instanceof Error ? error.message : "Unable to complete payment";
2637
- 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;
2638
2522
  }
2639
- };
2640
- 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 () => {
2641
2535
  if (!onSavedMethodPayment || !selectedMethodId) return;
2642
2536
  try {
2643
- startSavedPayment();
2537
+ setSavedStatus("processing");
2538
+ setSavedError(null);
2539
+ notifyStatus("processing", { source: "saved-payment" });
2644
2540
  await onSavedMethodPayment({
2645
2541
  paymentMethodId: selectedMethodId,
2646
2542
  amount: usdAmount
2647
2543
  });
2648
- completeSavedPayment();
2544
+ setSavedStatus("success");
2545
+ notifyStatus("success", { source: "saved-payment" });
2649
2546
  } catch (error) {
2650
2547
  const message = error instanceof Error ? error.message : "Unable to complete payment with saved card";
2651
- 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." });
2652
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
+ ] });
2653
2620
  };
2654
- return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
2655
- /* @__PURE__ */ jsxs(Card, { className: "border-border/60 bg-card/95", children: [
2656
- /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
2657
- /* @__PURE__ */ jsxs("div", { children: [
2658
- /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2 text-lg text-foreground", children: [
2659
- /* @__PURE__ */ jsx(CreditCard, { className: "h-5 w-5 text-primary" }),
2660
- " Secure checkout"
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,
2627
+ {
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" })
2661
2647
  ] }),
2662
- /* @__PURE__ */ jsxs(CardDescription, { children: [
2663
- "Amount due: $",
2664
- usdAmount.toFixed(2)
2665
- ] })
2666
- ] }),
2667
- checkoutSummary && /* @__PURE__ */ jsx("div", { children: checkoutSummary })
2668
- ] }),
2669
- /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "grid gap-8 lg:grid-cols-2", children: [
2670
- showStored && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2671
- /* @__PURE__ */ jsx(
2672
- StoredPaymentMethods,
2673
- {
2674
- selectedMethodId,
2675
- onMethodSelect: handleMethodSelect,
2676
- heading: "Saved cards",
2677
- description: "Use or manage your saved payment methods."
2678
- }
2679
- ),
2680
- onSavedMethodPayment && /* @__PURE__ */ jsx(
2681
- Button,
2682
- {
2683
- className: "w-full",
2684
- disabled: !selectedMethodId || savedStatus === "processing",
2685
- onClick: handleSavedPayment,
2686
- children: savedStatus === "processing" ? "Processing\u2026" : "Pay with selected card"
2687
- }
2688
- ),
2689
- savedError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: savedError })
2690
- ] }),
2691
- 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: [
2692
- /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1", children: [
2693
- /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: [
2694
- /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }),
2695
- " Pay with a new card"
2696
- ] }),
2697
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Card details are tokenized via Collect.js and never hit your server." })
2698
- ] }),
2699
- /* @__PURE__ */ jsx(
2700
- CardDetailsForm,
2701
- {
2702
- visible: true,
2703
- submitLabel: "Pay now",
2704
- submitting: newCardStatus === "processing",
2705
- externalError: newCardError,
2706
- onTokenize: handleNewCardTokenize
2707
- }
2708
- )
2709
- ] }) })
2710
- ] }) })
2711
- ] }),
2712
- 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: [
2713
- /* @__PURE__ */ jsxs("div", { children: [
2714
- /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-base font-semibold text-primary", children: [
2715
- /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" }),
2716
- " Prefer Solana Pay?"
2717
- ] }),
2718
- /* @__PURE__ */ jsx("p", { className: "text-sm text-primary/80", children: "Use a Solana wallet or QR code for instant settlement." })
2719
- ] }),
2720
- /* @__PURE__ */ jsx(Button, { onClick: () => setSolanaModalOpen(true), children: "Open Solana Pay" })
2721
- ] }) }),
2722
- enableSolanaPay && /* @__PURE__ */ jsx(
2723
- SolanaPaymentSelector,
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,
2724
2662
  {
2725
- isOpen: solanaModalOpen,
2726
- onClose: () => setSolanaModalOpen(false),
2727
2663
  priceId,
2728
2664
  usdAmount,
2729
- onSuccess: (result) => {
2730
- setSolanaModalOpen(false);
2731
- onSolanaSuccess?.(result);
2665
+ onSuccess: handleSolanaSuccess,
2666
+ onError: handleSolanaError,
2667
+ onClose: exitSolanaView
2668
+ }
2669
+ )
2670
+ ] });
2671
+ };
2672
+ var SubscriptionSuccessDialog = ({
2673
+ open,
2674
+ planName = "Premium Plan",
2675
+ amountLabel = "$0.00",
2676
+ billingPeriodLabel = "billing period",
2677
+ onClose
2678
+ }) => {
2679
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (value) => {
2680
+ if (!value) onClose();
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
+ ] })
2695
+ ] })
2696
+ ] }),
2697
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsx(Button, { className: "w-full", onClick: onClose, children: "Continue exploring" }) })
2698
+ ] }) });
2699
+ };
2700
+ var useSubscriptionActions = () => {
2701
+ const { services } = usePaymentContext();
2702
+ const ensurePrice = (priceId) => {
2703
+ if (!priceId) {
2704
+ throw new Error("payments-ui: priceId is required for subscription actions");
2705
+ }
2706
+ return priceId;
2707
+ };
2708
+ const subscribeWithCard = useCallback(
2709
+ async ({
2710
+ priceId,
2711
+ processor = "nmi",
2712
+ provider,
2713
+ paymentToken,
2714
+ billing
2715
+ }) => {
2716
+ const payload = {
2717
+ priceId: ensurePrice(priceId),
2718
+ paymentToken,
2719
+ processor,
2720
+ provider,
2721
+ email: billing.email,
2722
+ firstName: billing.firstName,
2723
+ lastName: billing.lastName,
2724
+ address1: billing.address1,
2725
+ city: billing.city,
2726
+ state: billing.stateRegion,
2727
+ zipCode: billing.postalCode,
2728
+ country: billing.country
2729
+ };
2730
+ return services.subscriptions.subscribe("nmi", payload);
2731
+ },
2732
+ [services]
2733
+ );
2734
+ const subscribeWithSavedMethod = useCallback(
2735
+ async ({
2736
+ priceId,
2737
+ processor = "nmi",
2738
+ provider,
2739
+ paymentMethodId,
2740
+ email
2741
+ }) => {
2742
+ const payload = {
2743
+ priceId: ensurePrice(priceId),
2744
+ paymentMethodId,
2745
+ processor,
2746
+ provider,
2747
+ email
2748
+ };
2749
+ return services.subscriptions.subscribe("nmi", payload);
2750
+ },
2751
+ [services]
2752
+ );
2753
+ const subscribeWithCCBill = useCallback(
2754
+ async ({
2755
+ priceId,
2756
+ email,
2757
+ firstName,
2758
+ lastName,
2759
+ zipCode,
2760
+ country,
2761
+ processor = "ccbill"
2762
+ }) => {
2763
+ const payload = {
2764
+ priceId: ensurePrice(priceId),
2765
+ email,
2766
+ firstName,
2767
+ lastName,
2768
+ zipCode,
2769
+ country,
2770
+ processor
2771
+ };
2772
+ return services.subscriptions.subscribe("ccbill", payload);
2773
+ },
2774
+ [services]
2775
+ );
2776
+ const generateFlexFormUrl = useCallback(
2777
+ async ({
2778
+ priceId,
2779
+ firstName,
2780
+ lastName,
2781
+ address1,
2782
+ city,
2783
+ state,
2784
+ zipCode,
2785
+ country
2786
+ }) => {
2787
+ const payload = {
2788
+ price_id: ensurePrice(priceId),
2789
+ first_name: firstName,
2790
+ last_name: lastName,
2791
+ address1,
2792
+ city,
2793
+ state,
2794
+ zip_code: zipCode,
2795
+ country
2796
+ };
2797
+ return services.subscriptions.generateFlexFormUrl(payload);
2798
+ },
2799
+ [services]
2800
+ );
2801
+ return {
2802
+ subscribeWithCard,
2803
+ subscribeWithSavedMethod,
2804
+ subscribeWithCCBill,
2805
+ generateFlexFormUrl
2806
+ };
2807
+ };
2808
+ var SubscriptionCheckoutModal = ({
2809
+ open,
2810
+ onOpenChange,
2811
+ priceId,
2812
+ usdAmount = 0,
2813
+ planName,
2814
+ amountLabel,
2815
+ billingPeriodLabel,
2816
+ userEmail,
2817
+ provider = "mobius",
2818
+ onSuccess,
2819
+ enableSolanaPay = true,
2820
+ onSolanaSuccess,
2821
+ onSolanaError,
2822
+ initialMode = "cards"
2823
+ }) => {
2824
+ const [showSuccess, setShowSuccess] = useState(false);
2825
+ const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
2826
+ const handleClose = useCallback(
2827
+ (nextOpen) => {
2828
+ onOpenChange(nextOpen);
2829
+ if (!nextOpen) setShowSuccess(false);
2830
+ },
2831
+ [onOpenChange]
2832
+ );
2833
+ const ensurePrice = () => {
2834
+ if (!priceId) throw new Error("Select a plan before subscribing.");
2835
+ return priceId;
2836
+ };
2837
+ const notifySuccess = (result) => {
2838
+ setShowSuccess(true);
2839
+ onSuccess?.();
2840
+ if (result && typeof window !== "undefined") {
2841
+ console.debug("[payments-ui] subscription success", result);
2842
+ }
2843
+ };
2844
+ const handleNewCardPayment = async ({ token, billing }) => {
2845
+ await subscribeWithCard({
2846
+ priceId: ensurePrice(),
2847
+ provider,
2848
+ paymentToken: token,
2849
+ billing
2850
+ });
2851
+ notifySuccess();
2852
+ };
2853
+ const handleSavedMethodPayment = async ({ paymentMethodId }) => {
2854
+ await subscribeWithSavedMethod({
2855
+ priceId: ensurePrice(),
2856
+ provider,
2857
+ paymentMethodId,
2858
+ email: userEmail ?? ""
2859
+ });
2860
+ notifySuccess();
2861
+ };
2862
+ const solanaSuccess = (result) => {
2863
+ notifySuccess(result);
2864
+ onSolanaSuccess?.(result);
2865
+ onOpenChange(false);
2866
+ };
2867
+ const solanaError = (error) => {
2868
+ onSolanaError?.(error);
2869
+ };
2870
+ return /* @__PURE__ */ jsxs(Fragment, { 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: [
2873
+ /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
2874
+ " Select a subscription plan to continue."
2875
+ ] }),
2876
+ /* @__PURE__ */ jsx(
2877
+ PaymentExperience,
2878
+ {
2879
+ usdAmount,
2880
+ priceId: priceId ?? "",
2881
+ onSolanaSuccess: solanaSuccess,
2882
+ onSolanaError: solanaError,
2883
+ enableNewCard: Boolean(priceId),
2884
+ enableStoredMethods: Boolean(priceId),
2885
+ enableSolanaPay: enableSolanaPay && Boolean(priceId),
2886
+ onNewCardPayment: priceId ? handleNewCardPayment : void 0,
2887
+ onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
2888
+ initialMode
2889
+ }
2890
+ )
2891
+ ] }) }) }),
2892
+ /* @__PURE__ */ jsx(
2893
+ SubscriptionSuccessDialog,
2894
+ {
2895
+ open: showSuccess,
2896
+ onClose: () => setShowSuccess(false),
2897
+ planName,
2898
+ amountLabel: amountLabel ?? `$${usdAmount.toFixed(2)}`,
2899
+ billingPeriodLabel
2900
+ }
2901
+ )
2902
+ ] });
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
+ ]
2732
2962
  },
2733
- onError: onSolanaError
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 ?? {}
2734
3032
  }
2735
3033
  )
2736
3034
  ] });
2737
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
+ };
2738
3100
  var Table = React3.forwardRef(
2739
3101
  ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2740
3102
  "table",
@@ -2919,7 +3281,7 @@ var CancelMembershipDialog = ({
2919
3281
  /* @__PURE__ */ jsx(Ban, { className: "mr-2 h-4 w-4" }),
2920
3282
  " Cancel Membership"
2921
3283
  ] }) }),
2922
- /* @__PURE__ */ jsxs(AlertDialogContent, { className: "max-h-[90vh] overflow-y-auto rounded-xl border border-border bg-background", children: [
3284
+ /* @__PURE__ */ jsxs(AlertDialogContent, { className: "max-h-[90vh] overflow-y-auto rounded-md border border-border bg-background", children: [
2923
3285
  /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
2924
3286
  /* @__PURE__ */ jsxs(AlertDialogTitle, { className: "flex items-center gap-2 text-lg font-semibold", children: [
2925
3287
  /* @__PURE__ */ jsx(TriangleAlert, { className: "h-5 w-5 text-destructive" }),
@@ -3880,45 +4242,6 @@ var SolanaWalletSection = ({
3880
4242
  ] });
3881
4243
  };
3882
4244
  var WalletManagement = (props) => /* @__PURE__ */ jsx(SolanaWalletSection, { ...props });
3883
- var wallets = [
3884
- {
3885
- id: "phantom",
3886
- name: "Phantom",
3887
- icon: "https://phantom.app/img/logo.png"
3888
- },
3889
- {
3890
- id: "solflare",
3891
- name: "Solflare",
3892
- icon: "https://solflare.com/favicon.ico"
3893
- }
3894
- ];
3895
- var WalletModal = ({ open, onOpenChange }) => {
3896
- const [expandedWallet, setExpandedWallet] = useState(null);
3897
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx(DialogContent, { className: "max-w-md border border-border bg-background", children: /* @__PURE__ */ jsx("div", { className: "space-y-4", children: wallets.map((wallet) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border/80", children: [
3898
- /* @__PURE__ */ jsxs(
3899
- "button",
3900
- {
3901
- className: "flex w-full items-center justify-between px-4 py-3",
3902
- onClick: () => setExpandedWallet((prev) => prev === wallet.id ? null : wallet.id),
3903
- children: [
3904
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-left", children: [
3905
- /* @__PURE__ */ jsx("img", { src: wallet.icon, alt: wallet.name, className: "h-8 w-8 rounded-full" }),
3906
- /* @__PURE__ */ jsxs("div", { children: [
3907
- /* @__PURE__ */ jsx("p", { className: "font-semibold", children: wallet.name }),
3908
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Connect via browser extension" })
3909
- ] })
3910
- ] }),
3911
- 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" })
3912
- ]
3913
- }
3914
- ),
3915
- expandedWallet === wallet.id && /* @__PURE__ */ jsxs("div", { className: "border-t border-border/60 px-4 py-3 text-sm text-muted-foreground", children: [
3916
- "Follow the prompts in your ",
3917
- wallet.name,
3918
- " wallet to approve access."
3919
- ] })
3920
- ] }, wallet.id)) }) }) });
3921
- };
3922
4245
  var Checkbox = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3923
4246
  CheckboxPrimitive.Root,
3924
4247
  {
@@ -3938,7 +4261,7 @@ var Checkbox = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__
3938
4261
  }
3939
4262
  ));
3940
4263
  Checkbox.displayName = CheckboxPrimitive.Root.displayName;
3941
- var initialState2 = {
4264
+ var initialState = {
3942
4265
  nameOnCard: "",
3943
4266
  cardNumber: "",
3944
4267
  expiration: "",
@@ -3946,7 +4269,7 @@ var initialState2 = {
3946
4269
  termsAccepted: false
3947
4270
  };
3948
4271
  var WalletDialog = ({ open, onOpenChange }) => {
3949
- const [form, setForm] = useState(initialState2);
4272
+ const [form, setForm] = useState(initialState);
3950
4273
  const [errors, setErrors] = useState({});
3951
4274
  const validators = useMemo(
3952
4275
  () => ({
@@ -3979,7 +4302,7 @@ var WalletDialog = ({ open, onOpenChange }) => {
3979
4302
  if (!validate()) return;
3980
4303
  console.log("[payments-ui] wallet dialog submit", form);
3981
4304
  onOpenChange(false);
3982
- setForm(initialState2);
4305
+ setForm(initialState);
3983
4306
  };
3984
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: [
3985
4308
  /* @__PURE__ */ jsxs(AlertDialogHeader, { className: "border-b border-border/60 pb-4", children: [
@@ -4026,7 +4349,7 @@ var WalletDialog = ({ open, onOpenChange }) => {
4026
4349
  ] })
4027
4350
  ] })
4028
4351
  ] }),
4029
- /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-border/70 bg-muted/10 p-4", children: [
4352
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-md border border-border/70 bg-muted/10 p-4", children: [
4030
4353
  /* @__PURE__ */ jsx(Checkbox, { id: "terms-agree", checked: form.termsAccepted, onCheckedChange: (checked) => updateField("termsAccepted", Boolean(checked)) }),
4031
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." })
4032
4355
  ] }),
@@ -4038,245 +4361,6 @@ var WalletDialog = ({ open, onOpenChange }) => {
4038
4361
  ] })
4039
4362
  ] }) });
4040
4363
  };
4041
- var SubscriptionSuccessDialog = ({
4042
- open,
4043
- planName = "Premium Plan",
4044
- amountLabel = "$0.00",
4045
- billingPeriodLabel = "billing period",
4046
- onClose
4047
- }) => {
4048
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (value) => {
4049
- if (!value) onClose();
4050
- }, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-sm text-center", children: [
4051
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
4052
- /* @__PURE__ */ jsxs(DialogTitle, { className: "flex flex-col items-center gap-3 text-foreground", children: [
4053
- /* @__PURE__ */ jsx(CheckCircle, { className: "h-10 w-10 text-primary" }),
4054
- "Subscription activated"
4055
- ] }),
4056
- /* @__PURE__ */ jsxs(DialogDescription, { className: "text-base text-muted-foreground", children: [
4057
- "You now have access to ",
4058
- planName,
4059
- ". Billing: ",
4060
- amountLabel,
4061
- " / ",
4062
- billingPeriodLabel,
4063
- "."
4064
- ] })
4065
- ] }),
4066
- /* @__PURE__ */ jsx(Button, { className: "mt-6 w-full", onClick: onClose, children: "Continue" })
4067
- ] }) });
4068
- };
4069
- var useSubscriptionActions = () => {
4070
- const { services } = usePaymentContext();
4071
- const ensurePrice = (priceId) => {
4072
- if (!priceId) {
4073
- throw new Error("payments-ui: priceId is required for subscription actions");
4074
- }
4075
- return priceId;
4076
- };
4077
- const subscribeWithCard = useCallback(
4078
- async ({
4079
- priceId,
4080
- processor = "nmi",
4081
- provider,
4082
- paymentToken,
4083
- billing
4084
- }) => {
4085
- const payload = {
4086
- priceId: ensurePrice(priceId),
4087
- paymentToken,
4088
- processor,
4089
- provider,
4090
- email: billing.email,
4091
- firstName: billing.firstName,
4092
- lastName: billing.lastName,
4093
- address1: billing.address1,
4094
- city: billing.city,
4095
- state: billing.stateRegion,
4096
- zipCode: billing.postalCode,
4097
- country: billing.country
4098
- };
4099
- return services.subscriptions.subscribe("nmi", payload);
4100
- },
4101
- [services]
4102
- );
4103
- const subscribeWithSavedMethod = useCallback(
4104
- async ({
4105
- priceId,
4106
- processor = "nmi",
4107
- provider,
4108
- paymentMethodId,
4109
- email
4110
- }) => {
4111
- const payload = {
4112
- priceId: ensurePrice(priceId),
4113
- paymentMethodId,
4114
- processor,
4115
- provider,
4116
- email
4117
- };
4118
- return services.subscriptions.subscribe("nmi", payload);
4119
- },
4120
- [services]
4121
- );
4122
- const subscribeWithCCBill = useCallback(
4123
- async ({
4124
- priceId,
4125
- email,
4126
- firstName,
4127
- lastName,
4128
- zipCode,
4129
- country,
4130
- processor = "ccbill"
4131
- }) => {
4132
- const payload = {
4133
- priceId: ensurePrice(priceId),
4134
- email,
4135
- firstName,
4136
- lastName,
4137
- zipCode,
4138
- country,
4139
- processor
4140
- };
4141
- return services.subscriptions.subscribe("ccbill", payload);
4142
- },
4143
- [services]
4144
- );
4145
- const generateFlexFormUrl = useCallback(
4146
- async ({
4147
- priceId,
4148
- firstName,
4149
- lastName,
4150
- address1,
4151
- city,
4152
- state,
4153
- zipCode,
4154
- country
4155
- }) => {
4156
- const payload = {
4157
- price_id: ensurePrice(priceId),
4158
- first_name: firstName,
4159
- last_name: lastName,
4160
- address1,
4161
- city,
4162
- state,
4163
- zip_code: zipCode,
4164
- country
4165
- };
4166
- return services.subscriptions.generateFlexFormUrl(payload);
4167
- },
4168
- [services]
4169
- );
4170
- return {
4171
- subscribeWithCard,
4172
- subscribeWithSavedMethod,
4173
- subscribeWithCCBill,
4174
- generateFlexFormUrl
4175
- };
4176
- };
4177
- var SubscriptionCheckoutModal = ({
4178
- open,
4179
- onOpenChange,
4180
- priceId,
4181
- usdAmount = 0,
4182
- planName,
4183
- amountLabel,
4184
- billingPeriodLabel,
4185
- userEmail,
4186
- provider = "mobius",
4187
- onSuccess,
4188
- enableSolanaPay = true
4189
- }) => {
4190
- const [showSuccess, setShowSuccess] = useState(false);
4191
- const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
4192
- const handleClose = useCallback(
4193
- (nextOpen) => {
4194
- onOpenChange(nextOpen);
4195
- if (!nextOpen) {
4196
- setShowSuccess(false);
4197
- }
4198
- },
4199
- [onOpenChange]
4200
- );
4201
- const ensurePrice = () => {
4202
- if (!priceId) {
4203
- throw new Error("Select a plan before subscribing.");
4204
- }
4205
- return priceId;
4206
- };
4207
- const notifySuccess = (result) => {
4208
- setShowSuccess(true);
4209
- onSuccess?.();
4210
- if (result && typeof window !== "undefined") {
4211
- console.debug("[payments-ui] subscription success", result);
4212
- }
4213
- };
4214
- const handleNewCardPayment = async ({ token, billing }) => {
4215
- await subscribeWithCard({
4216
- priceId: ensurePrice(),
4217
- provider,
4218
- paymentToken: token,
4219
- billing
4220
- });
4221
- notifySuccess();
4222
- };
4223
- const handleSavedMethodPayment = async ({ paymentMethodId }) => {
4224
- await subscribeWithSavedMethod({
4225
- priceId: ensurePrice(),
4226
- provider,
4227
- paymentMethodId,
4228
- email: userEmail ?? ""
4229
- });
4230
- notifySuccess();
4231
- };
4232
- const solanaSuccess = (result) => {
4233
- notifySuccess(result);
4234
- onOpenChange(false);
4235
- };
4236
- const summary = useMemo(() => {
4237
- if (!planName && !amountLabel) return null;
4238
- return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border/60 bg-muted/10 p-3 text-sm text-muted-foreground", children: [
4239
- /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: planName ?? "Selected plan" }),
4240
- /* @__PURE__ */ jsxs("p", { children: [
4241
- amountLabel ?? `$${usdAmount.toFixed(2)}`,
4242
- " ",
4243
- billingPeriodLabel ? `/ ${billingPeriodLabel}` : ""
4244
- ] })
4245
- ] });
4246
- }, [planName, amountLabel, billingPeriodLabel, usdAmount]);
4247
- return /* @__PURE__ */ jsxs(Fragment, { children: [
4248
- /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-3xl", children: [
4249
- !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: [
4250
- /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
4251
- " Select a subscription plan to continue."
4252
- ] }),
4253
- /* @__PURE__ */ jsx(
4254
- PaymentExperience,
4255
- {
4256
- priceId: priceId ?? "",
4257
- usdAmount,
4258
- checkoutSummary: summary,
4259
- onNewCardPayment: priceId ? handleNewCardPayment : void 0,
4260
- onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
4261
- enableNewCard: Boolean(priceId),
4262
- enableStoredMethods: Boolean(priceId),
4263
- enableSolanaPay: enableSolanaPay && Boolean(priceId),
4264
- onSolanaSuccess: solanaSuccess
4265
- }
4266
- )
4267
- ] }) }),
4268
- /* @__PURE__ */ jsx(
4269
- SubscriptionSuccessDialog,
4270
- {
4271
- open: showSuccess,
4272
- onClose: () => setShowSuccess(false),
4273
- planName,
4274
- amountLabel: amountLabel ?? `$${usdAmount.toFixed(2)}`,
4275
- billingPeriodLabel
4276
- }
4277
- )
4278
- ] });
4279
- };
4280
4364
  var useTokenBalance = (tokens) => {
4281
4365
  const { publicKey } = useWallet();
4282
4366
  const { connection } = useConnection();
@@ -4796,6 +4880,6 @@ var useAlternativePaymentProvider = () => {
4796
4880
  return { openFlexForm, isLoading, error };
4797
4881
  };
4798
4882
 
4799
- export { BillingHistory, CancelMembershipDialog, CardDetailsForm, CardPaymentService, EmptyWalletState, PaymentApp, PaymentExperience, PaymentMethodService, PaymentMethodsSection, PaymentProvider, SolanaPaymentSelector, SolanaPaymentService, SolanaWalletSection, SolanaWalletService, StoredPaymentMethods, SubscriptionCheckoutModal, SubscriptionService, SubscriptionSuccessDialog, TokenCatalog, WalletCard, WalletDialog, WalletGateway, WalletManagement, WalletModal, createPaymentStore, useAlternativePaymentProvider, useDirectWalletPayment, usePaymentContext, usePaymentMethodService, usePaymentMethods, usePaymentStatus, usePaymentStore, useSolanaDirectPayment, useSolanaQrPayment, useSolanaService, useSubscriptionActions, useSupportedTokens, useTokenBalance, useWalletConnection, useWalletList, useWalletVerification };
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 };
4800
4884
  //# sourceMappingURL=index.js.map
4801
4885
  //# sourceMappingURL=index.js.map