@doujins/payments-ui 0.0.10 → 0.0.12

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.cjs CHANGED
@@ -20,6 +20,10 @@ var splToken = require('@solana/spl-token');
20
20
  var QRCode = require('qrcode');
21
21
  var zustand = require('zustand');
22
22
  var TabsPrimitive = require('@radix-ui/react-tabs');
23
+ var AlertDialogPrimitive = require('@radix-ui/react-alert-dialog');
24
+ var walletAdapterReactUi = require('@solana/wallet-adapter-react-ui');
25
+ var bs58 = require('bs58');
26
+ var CheckboxPrimitive = require('@radix-ui/react-checkbox');
23
27
 
24
28
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
25
29
 
@@ -49,6 +53,9 @@ var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive)
49
53
  var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
50
54
  var QRCode__default = /*#__PURE__*/_interopDefault(QRCode);
51
55
  var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
56
+ var AlertDialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(AlertDialogPrimitive);
57
+ var bs58__default = /*#__PURE__*/_interopDefault(bs58);
58
+ var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
52
59
 
53
60
  // src/context/PaymentContext.tsx
54
61
 
@@ -331,6 +338,38 @@ var SubscriptionService = class {
331
338
  body: { ...payload }
332
339
  });
333
340
  }
341
+ async getPaymentHistory(params) {
342
+ const limit = params?.limit ?? 10;
343
+ const offset = params?.offset ?? 0;
344
+ const query = {
345
+ limit: String(limit),
346
+ offset: String(offset)
347
+ };
348
+ if (params?.type) {
349
+ query.type = params.type;
350
+ }
351
+ const response = await this.api.get("/subscriptions/purchases", {
352
+ query
353
+ });
354
+ const totalItems = response?.total_items ?? 0;
355
+ const pageSize = limit;
356
+ const pageNumber = response?.page ?? (pageSize > 0 ? Math.floor(offset / pageSize) + 1 : 1);
357
+ const totalPages = response?.total_pages ?? (pageSize > 0 ? Math.ceil(totalItems / pageSize) : void 0);
358
+ return {
359
+ data: response?.data ?? [],
360
+ total_items: totalItems,
361
+ limit,
362
+ offset,
363
+ page: pageNumber,
364
+ page_size: pageSize,
365
+ total_pages: totalPages
366
+ };
367
+ }
368
+ async cancelSubscription(feedback) {
369
+ return this.api.post("/subscriptions/cancel", {
370
+ body: feedback ? { feedback } : void 0
371
+ });
372
+ }
334
373
  serializePayload(platform, payload) {
335
374
  if (platform === "nmi") {
336
375
  const data2 = payload;
@@ -375,6 +414,48 @@ var SubscriptionService = class {
375
414
  }
376
415
  };
377
416
 
417
+ // src/services/SolanaWalletService.ts
418
+ var SolanaWalletService = class {
419
+ constructor(api) {
420
+ this.api = api;
421
+ }
422
+ async list() {
423
+ const response = await this.api.get(
424
+ "/wallet/solana"
425
+ );
426
+ if (Array.isArray(response)) {
427
+ return response;
428
+ }
429
+ if ("wallets" in response && Array.isArray(response.wallets)) {
430
+ return response.wallets;
431
+ }
432
+ if ("wallet" in response) {
433
+ return response.wallet ? [response.wallet] : [];
434
+ }
435
+ if ("address" in response) {
436
+ return [response];
437
+ }
438
+ return [];
439
+ }
440
+ async requestChallenge(wallet) {
441
+ return this.api.post("/wallet/solana/challenge", {
442
+ body: { wallet }
443
+ });
444
+ }
445
+ async verify(wallet, signature, nonce) {
446
+ const body = { wallet, signature };
447
+ if (nonce) {
448
+ body.nonce = nonce;
449
+ }
450
+ return this.api.post("/wallet/solana/verify", { body });
451
+ }
452
+ async remove(wallet) {
453
+ await this.api.delete("/wallet/solana", {
454
+ query: { wallet }
455
+ });
456
+ }
457
+ };
458
+
378
459
  // src/core/PaymentApp.ts
379
460
  var PaymentApp = class {
380
461
  constructor(options) {
@@ -424,6 +505,7 @@ var PaymentApp = class {
424
505
  this.resolveAuthToken
425
506
  );
426
507
  const solanaPayments = new SolanaPaymentService(billingApi);
508
+ const solanaWallets = new SolanaWalletService(billingApi);
427
509
  const paymentMethods = new PaymentMethodService(accountApi);
428
510
  const cardPayments = new CardPaymentService(this.config);
429
511
  const walletGateway = new WalletGateway();
@@ -433,6 +515,7 @@ var PaymentApp = class {
433
515
  cardPayments,
434
516
  paymentMethods,
435
517
  solanaPayments,
518
+ solanaWallets,
436
519
  tokenCatalog,
437
520
  walletGateway,
438
521
  subscriptions,
@@ -2685,155 +2768,1697 @@ var PaymentExperience = ({
2685
2768
  )
2686
2769
  ] });
2687
2770
  };
2688
- var useTokenBalance = (tokens) => {
2689
- const { publicKey } = walletAdapterReact.useWallet();
2690
- const { connection } = walletAdapterReact.useConnection();
2691
- const [balances, setBalances] = React3.useState([]);
2692
- const [isLoading, setIsLoading] = React3.useState(false);
2693
- const [error, setError] = React3.useState(null);
2694
- const fetchTokenBalance = React3.useCallback(
2695
- async (token, walletAddress) => {
2696
- try {
2697
- const mintPublicKey = new web3_js.PublicKey(token.mint);
2698
- const associatedTokenAddress = await splToken.getAssociatedTokenAddress(
2699
- mintPublicKey,
2700
- walletAddress
2701
- );
2702
- try {
2703
- const tokenAccount = await splToken.getAccount(
2704
- connection,
2705
- associatedTokenAddress
2706
- );
2707
- const balance = Number(tokenAccount.amount);
2708
- const uiAmount = balance / Math.pow(10, token.decimals);
2709
- return {
2710
- token,
2711
- balance,
2712
- uiAmount,
2713
- hasBalance: balance > 0
2714
- };
2715
- } catch (accountError) {
2716
- return {
2717
- token,
2718
- balance: 0,
2719
- uiAmount: 0,
2720
- hasBalance: false
2721
- };
2722
- }
2723
- } catch (error2) {
2724
- console.error(`Failed to fetch balance for ${token.symbol}:`, error2);
2725
- return {
2726
- token,
2727
- balance: 0,
2728
- uiAmount: 0,
2729
- hasBalance: false
2730
- };
2731
- }
2732
- },
2733
- [connection]
2734
- );
2735
- const tokensKey = React3.useMemo(() => tokens.map((t) => t.mint).join(","), [tokens]);
2771
+ var Table = React3__namespace.forwardRef(
2772
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2773
+ "table",
2774
+ {
2775
+ ref,
2776
+ className: cn("w-full caption-bottom text-sm", className),
2777
+ ...props
2778
+ }
2779
+ )
2780
+ );
2781
+ Table.displayName = "Table";
2782
+ var TableHeader = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
2783
+ TableHeader.displayName = "TableHeader";
2784
+ var TableBody = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
2785
+ TableBody.displayName = "TableBody";
2786
+ var TableFooter = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("tfoot", { ref, className: cn("bg-muted/50 font-medium text-muted-foreground", className), ...props }));
2787
+ TableFooter.displayName = "TableFooter";
2788
+ var TableRow = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("tr", { ref, className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className), ...props }));
2789
+ TableRow.displayName = "TableRow";
2790
+ var TableHead = React3__namespace.forwardRef(
2791
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2792
+ "th",
2793
+ {
2794
+ ref,
2795
+ className: cn(
2796
+ "h-10 px-2 text-left align-middle text-xs font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
2797
+ className
2798
+ ),
2799
+ ...props
2800
+ }
2801
+ )
2802
+ );
2803
+ TableHead.displayName = "TableHead";
2804
+ var TableCell = React3__namespace.forwardRef(
2805
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2806
+ "td",
2807
+ {
2808
+ ref,
2809
+ className: cn("p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className),
2810
+ ...props
2811
+ }
2812
+ )
2813
+ );
2814
+ TableCell.displayName = "TableCell";
2815
+ var TableCaption = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
2816
+ TableCaption.displayName = "TableCaption";
2817
+ var AlertDialog = AlertDialogPrimitive__namespace.Root;
2818
+ var AlertDialogTrigger = AlertDialogPrimitive__namespace.Trigger;
2819
+ var AlertDialogPortal = AlertDialogPrimitive__namespace.Portal;
2820
+ var AlertDialogOverlay = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2821
+ AlertDialogPrimitive__namespace.Overlay,
2822
+ {
2823
+ ref,
2824
+ className: cn(
2825
+ "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0",
2826
+ className
2827
+ ),
2828
+ ...props
2829
+ }
2830
+ ));
2831
+ AlertDialogOverlay.displayName = AlertDialogPrimitive__namespace.Overlay.displayName;
2832
+ var AlertDialogContent = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogPortal, { children: [
2833
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogOverlay, {}),
2834
+ /* @__PURE__ */ jsxRuntime.jsx(
2835
+ AlertDialogPrimitive__namespace.Content,
2836
+ {
2837
+ ref,
2838
+ className: cn(
2839
+ "fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-popover p-6 text-popover-foreground shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
2840
+ className
2841
+ ),
2842
+ ...props
2843
+ }
2844
+ )
2845
+ ] }));
2846
+ AlertDialogContent.displayName = AlertDialogPrimitive__namespace.Content.displayName;
2847
+ var AlertDialogHeader = ({
2848
+ className,
2849
+ ...props
2850
+ }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col space-y-2 text-center sm:text-left", className), ...props });
2851
+ AlertDialogHeader.displayName = "AlertDialogHeader";
2852
+ var AlertDialogFooter = ({
2853
+ className,
2854
+ ...props
2855
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
2856
+ "div",
2857
+ {
2858
+ className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:gap-2", className),
2859
+ ...props
2860
+ }
2861
+ );
2862
+ AlertDialogFooter.displayName = "AlertDialogFooter";
2863
+ var AlertDialogTitle = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(AlertDialogPrimitive__namespace.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
2864
+ AlertDialogTitle.displayName = AlertDialogPrimitive__namespace.Title.displayName;
2865
+ var AlertDialogDescription = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(AlertDialogPrimitive__namespace.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
2866
+ AlertDialogDescription.displayName = AlertDialogPrimitive__namespace.Description.displayName;
2867
+ var AlertDialogAction = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(AlertDialogPrimitive__namespace.Action, { ref, className: cn(buttonVariants(), className), ...props }));
2868
+ AlertDialogAction.displayName = AlertDialogPrimitive__namespace.Action.displayName;
2869
+ var AlertDialogCancel = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2870
+ AlertDialogPrimitive__namespace.Cancel,
2871
+ {
2872
+ ref,
2873
+ className: cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className),
2874
+ ...props
2875
+ }
2876
+ ));
2877
+ AlertDialogCancel.displayName = AlertDialogPrimitive__namespace.Cancel.displayName;
2878
+ var Textarea = React3__namespace.forwardRef(
2879
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
2880
+ "textarea",
2881
+ {
2882
+ ref,
2883
+ className: cn(
2884
+ "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
2885
+ className
2886
+ ),
2887
+ ...props
2888
+ }
2889
+ )
2890
+ );
2891
+ Textarea.displayName = "Textarea";
2892
+ var notifyDefault = (payload) => {
2893
+ const level = payload.status === "destructive" ? "error" : "info";
2894
+ console[level === "error" ? "error" : "log"]("[payments-ui] cancellation", payload);
2895
+ };
2896
+ var CancelMembershipDialog = ({
2897
+ minReasonLength = 15,
2898
+ onCancelled,
2899
+ onNotify
2900
+ }) => {
2901
+ const { services } = usePaymentContext();
2902
+ const notify = onNotify ?? notifyDefault;
2903
+ const [cancelReason, setCancelReason] = React3.useState("");
2904
+ const [isOpen, setIsOpen] = React3.useState(false);
2905
+ const [isReasonValid, setIsReasonValid] = React3.useState(false);
2906
+ const [hasInteracted, setHasInteracted] = React3.useState(false);
2907
+ const [isSubmitting, setIsSubmitting] = React3.useState(false);
2736
2908
  React3.useEffect(() => {
2737
- if (!publicKey || tokens.length === 0) {
2738
- setBalances([]);
2739
- return;
2909
+ const trimmed = cancelReason.trim();
2910
+ setIsReasonValid(trimmed.length >= minReasonLength);
2911
+ }, [cancelReason, minReasonLength]);
2912
+ const handleOpenChange = (open) => {
2913
+ setIsOpen(open);
2914
+ if (!open) {
2915
+ setCancelReason("");
2916
+ setIsReasonValid(false);
2917
+ setHasInteracted(false);
2918
+ setIsSubmitting(false);
2740
2919
  }
2741
- setIsLoading(true);
2742
- setError(null);
2743
- const fetchAllBalances = async () => {
2744
- try {
2745
- const balancePromises = tokens.map(
2746
- (token) => fetchTokenBalance(token, publicKey)
2747
- );
2748
- const tokenBalances = await Promise.all(balancePromises);
2749
- setBalances(tokenBalances);
2750
- } catch (error2) {
2751
- const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch token balances";
2752
- setError(errorMessage);
2753
- console.error("Failed to fetch token balances:", error2);
2754
- } finally {
2755
- setIsLoading(false);
2756
- }
2757
- };
2758
- fetchAllBalances();
2759
- }, [publicKey, tokensKey, fetchTokenBalance]);
2760
- const getTokenBalance = React3.useCallback(
2761
- (tokenSymbol) => {
2762
- return balances.find((balance) => balance.token.symbol === tokenSymbol);
2763
- },
2764
- [balances]
2765
- );
2766
- const hasSufficientBalance2 = React3.useCallback(
2767
- (tokenSymbol, requiredAmount) => {
2768
- const balance = getTokenBalance(tokenSymbol);
2769
- return balance ? balance.uiAmount >= requiredAmount : false;
2770
- },
2771
- [getTokenBalance]
2772
- );
2773
- const getFormattedBalance = React3.useCallback(
2774
- (tokenSymbol) => {
2775
- const balance = getTokenBalance(tokenSymbol);
2776
- if (!balance) return "0.00";
2777
- if (balance.uiAmount < 0.01) {
2778
- return balance.uiAmount.toFixed(6);
2779
- } else if (balance.uiAmount < 1) {
2780
- return balance.uiAmount.toFixed(4);
2781
- } else {
2782
- return balance.uiAmount.toFixed(2);
2783
- }
2784
- },
2785
- [getTokenBalance]
2786
- );
2787
- const refreshBalances = React3.useCallback(async () => {
2788
- if (!publicKey || tokens.length === 0) {
2789
- setBalances([]);
2920
+ };
2921
+ const handleReasonChange = (e) => {
2922
+ setCancelReason(e.target.value);
2923
+ if (!hasInteracted) {
2924
+ setHasInteracted(true);
2925
+ }
2926
+ };
2927
+ const handleConfirm = async () => {
2928
+ if (!isReasonValid) {
2929
+ setHasInteracted(true);
2790
2930
  return;
2791
2931
  }
2792
- setIsLoading(true);
2793
- setError(null);
2932
+ setIsSubmitting(true);
2794
2933
  try {
2795
- const balancePromises = tokens.map(
2796
- (token) => fetchTokenBalance(token, publicKey)
2797
- );
2798
- const tokenBalances = await Promise.all(balancePromises);
2799
- setBalances(tokenBalances);
2800
- } catch (error2) {
2801
- const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch token balances";
2802
- setError(errorMessage);
2803
- console.error("Failed to fetch token balances:", error2);
2934
+ await services.subscriptions.cancelSubscription(cancelReason.trim());
2935
+ notify({
2936
+ title: "Membership cancelled",
2937
+ description: "Your subscription has been cancelled successfully.",
2938
+ status: "success"
2939
+ });
2940
+ onCancelled?.();
2941
+ handleOpenChange(false);
2942
+ } catch (error) {
2943
+ const message = error instanceof Error ? error.message : "Unable to cancel membership";
2944
+ notify({ title: "Cancellation failed", description: message, status: "destructive" });
2804
2945
  } finally {
2805
- setIsLoading(false);
2946
+ setIsSubmitting(false);
2806
2947
  }
2807
- }, [publicKey, tokens, fetchTokenBalance]);
2808
- const getTotalValue = React3.useCallback(
2809
- (priceData) => {
2810
- if (!priceData) return 0;
2811
- return balances.reduce((total, balance) => {
2812
- const price = priceData[balance.token.symbol] || 0;
2813
- return total + balance.uiAmount * price;
2814
- }, 0);
2815
- },
2816
- [balances]
2817
- );
2818
- const sortedBalances = [...balances].sort((a, b) => b.uiAmount - a.uiAmount);
2819
- const positiveBalances = balances.filter((balance) => balance.hasBalance);
2820
- return {
2821
- balances: sortedBalances,
2822
- positiveBalances,
2823
- isLoading,
2824
- error,
2825
- refreshBalances,
2826
- getTokenBalance,
2827
- hasSufficientBalance: hasSufficientBalance2,
2828
- getFormattedBalance,
2829
- getTotalValue,
2830
- hasAnyBalance: positiveBalances.length > 0,
2831
- isConnected: !!publicKey
2832
2948
  };
2949
+ const showError = hasInteracted && !isReasonValid;
2950
+ return /* @__PURE__ */ jsxRuntime.jsxs(AlertDialog, { open: isOpen, onOpenChange: handleOpenChange, children: [
2951
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", className: "border-destructive/50 text-destructive", children: [
2952
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ban, { className: "mr-2 h-4 w-4" }),
2953
+ " Cancel Membership"
2954
+ ] }) }),
2955
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { className: "max-h-[90vh] overflow-y-auto rounded-xl border border-border bg-background", children: [
2956
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { children: [
2957
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogTitle, { className: "flex items-center gap-2 text-lg font-semibold", children: [
2958
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TriangleAlert, { className: "h-5 w-5 text-destructive" }),
2959
+ " Confirm Membership Cancellation"
2960
+ ] }),
2961
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogDescription, { className: "mt-3 space-y-3 text-muted-foreground", children: [
2962
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "You are about to cancel your membership. Please review the consequences:" }),
2963
+ /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "list-disc space-y-1 pl-5 text-sm", children: [
2964
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: "You will immediately lose access to premium features upon confirmation." }),
2965
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your benefits remain active until the end of the billing cycle." }),
2966
+ /* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your account will revert to the free plan afterwards." })
2967
+ ] })
2968
+ ] })
2969
+ ] }),
2970
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "my-4 space-y-2 py-2", children: [
2971
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "cancelReason", className: "text-sm font-medium", children: "Please provide a reason for cancellation (required):" }),
2972
+ /* @__PURE__ */ jsxRuntime.jsx(
2973
+ Textarea,
2974
+ {
2975
+ id: "cancelReason",
2976
+ value: cancelReason,
2977
+ onChange: handleReasonChange,
2978
+ placeholder: "Your feedback helps us improve...",
2979
+ className: cn(
2980
+ "w-full resize-none border-border bg-background",
2981
+ showError && "border-destructive"
2982
+ ),
2983
+ rows: 4,
2984
+ "aria-describedby": "reason-hint",
2985
+ "aria-invalid": showError
2986
+ }
2987
+ ),
2988
+ /* @__PURE__ */ jsxRuntime.jsx(
2989
+ "p",
2990
+ {
2991
+ id: "reason-hint",
2992
+ className: `text-xs ${showError ? "text-destructive" : "text-muted-foreground"}`,
2993
+ children: showError ? `Reason must be at least ${minReasonLength} characters long.` : `Minimum ${minReasonLength} characters required.`
2994
+ }
2995
+ )
2996
+ ] }),
2997
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { className: "mt-6 gap-2", children: [
2998
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", className: "border-border text-muted-foreground", children: "Keep Membership" }) }),
2999
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3000
+ Button,
3001
+ {
3002
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
3003
+ onClick: handleConfirm,
3004
+ disabled: !isReasonValid || isSubmitting,
3005
+ children: isSubmitting ? "Cancelling..." : "Confirm Cancellation"
3006
+ }
3007
+ ) })
3008
+ ] })
3009
+ ] })
3010
+ ] });
2833
3011
  };
2834
- var useDirectWalletPayment = () => {
2835
- const { publicKey, signTransaction, connected } = walletAdapterReact.useWallet();
2836
- const solanaService = useSolanaService();
3012
+ var notifyDefault2 = (payload) => {
3013
+ const level = payload.status === "destructive" ? "error" : "info";
3014
+ console[level === "error" ? "error" : "log"]("[payments-ui] billing", payload);
3015
+ };
3016
+ var BillingHistory = ({
3017
+ pageSize = 10,
3018
+ initialPage = 1,
3019
+ enableCancel = true,
3020
+ onNotify
3021
+ }) => {
3022
+ const { services } = usePaymentContext();
3023
+ const notify = onNotify ?? notifyDefault2;
3024
+ const [isExpanded, setIsExpanded] = React3.useState(false);
3025
+ const observerRef = React3.useRef(null);
3026
+ const loadMoreRef = React3.useRef(null);
3027
+ const historyQuery = reactQuery.useInfiniteQuery({
3028
+ queryKey: ["payments-ui", "billing-history", pageSize],
3029
+ queryFn: async ({ pageParam = initialPage }) => {
3030
+ const offset = (pageParam - 1) * pageSize;
3031
+ return services.subscriptions.getPaymentHistory({ limit: pageSize, offset });
3032
+ },
3033
+ initialPageParam: initialPage,
3034
+ getNextPageParam: (lastPage) => {
3035
+ const nextOffset = (lastPage.offset ?? 0) + lastPage.data.length;
3036
+ if (lastPage.total_items <= nextOffset) {
3037
+ return void 0;
3038
+ }
3039
+ return lastPage.page ? lastPage.page + 1 : initialPage + 1;
3040
+ },
3041
+ staleTime: 5 * 60 * 1e3
3042
+ });
3043
+ React3.useEffect(() => {
3044
+ if (!loadMoreRef.current || !isExpanded) return;
3045
+ observerRef.current = new IntersectionObserver((entries) => {
3046
+ const [entry] = entries;
3047
+ if (entry?.isIntersecting && historyQuery.hasNextPage && !historyQuery.isFetchingNextPage) {
3048
+ historyQuery.fetchNextPage().catch(() => {
3049
+ notify({ title: "Failed to load more history", status: "destructive" });
3050
+ });
3051
+ }
3052
+ });
3053
+ observerRef.current.observe(loadMoreRef.current);
3054
+ return () => {
3055
+ observerRef.current?.disconnect();
3056
+ };
3057
+ }, [historyQuery, isExpanded, notify]);
3058
+ const payments = React3.useMemo(() => {
3059
+ const data = historyQuery.data;
3060
+ return data?.pages ?? [];
3061
+ }, [historyQuery.data]);
3062
+ const formatDate = (value) => new Date(value).toLocaleDateString("en-US", {
3063
+ year: "numeric",
3064
+ month: "short",
3065
+ day: "numeric"
3066
+ });
3067
+ const formatAmount = (amount, currency) => {
3068
+ try {
3069
+ return new Intl.NumberFormat("en-US", {
3070
+ style: "currency",
3071
+ currency
3072
+ }).format(amount);
3073
+ } catch {
3074
+ return `${amount.toFixed(2)} ${currency}`;
3075
+ }
3076
+ };
3077
+ return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "border-0 bg-background/5 shadow-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 sm:p-6", children: [
3078
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex cursor-pointer items-center justify-between", onClick: () => setIsExpanded((prev) => !prev), children: [
3079
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3080
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl font-semibold", children: "Transaction History" }),
3081
+ /* @__PURE__ */ jsxRuntime.jsx(CardDescription, { children: "Record of billing history" })
3082
+ ] }),
3083
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: cn("h-5 w-5 text-muted-foreground transition-transform", isExpanded && "rotate-180") })
3084
+ ] }),
3085
+ /* @__PURE__ */ jsxRuntime.jsx(
3086
+ "div",
3087
+ {
3088
+ className: cn(
3089
+ "overflow-hidden transition-all duration-300",
3090
+ isExpanded ? "mt-4 max-h-[1000px] opacity-100" : "max-h-0 opacity-0"
3091
+ ),
3092
+ children: /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "p-0 pt-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
3093
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between", children: [
3094
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Review your account activity below" }),
3095
+ enableCancel && /* @__PURE__ */ jsxRuntime.jsx(CancelMembershipDialog, { onNotify: notify })
3096
+ ] }),
3097
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-[300px] overflow-y-auto rounded-lg border border-border/70", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: historyQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "p-4 text-center text-sm text-muted-foreground", children: "Loading..." }) : historyQuery.isError ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "p-4 text-center text-sm text-destructive", children: "Error loading billing history." }) : /* @__PURE__ */ jsxRuntime.jsxs(Table, { children: [
3098
+ /* @__PURE__ */ jsxRuntime.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(TableRow, { className: "border-border/60", children: [
3099
+ /* @__PURE__ */ jsxRuntime.jsx(TableHead, { children: "Reference" }),
3100
+ /* @__PURE__ */ jsxRuntime.jsx(TableHead, { children: "Date" }),
3101
+ /* @__PURE__ */ jsxRuntime.jsx(TableHead, { children: "Amount" }),
3102
+ /* @__PURE__ */ jsxRuntime.jsx(TableHead, { children: "Processor" }),
3103
+ /* @__PURE__ */ jsxRuntime.jsx(TableHead, { children: "Status" })
3104
+ ] }) }),
3105
+ /* @__PURE__ */ jsxRuntime.jsx(TableBody, { children: payments.map(
3106
+ (page) => page.data.map((payment) => /* @__PURE__ */ jsxRuntime.jsxs(TableRow, { className: "border-border/40", children: [
3107
+ /* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "font-mono text-sm", children: payment.id.slice(0, 7).toUpperCase() }),
3108
+ /* @__PURE__ */ jsxRuntime.jsx(TableCell, { children: formatDate(payment.purchased_at) }),
3109
+ /* @__PURE__ */ jsxRuntime.jsx(TableCell, { children: formatAmount(payment.amount, payment.currency) }),
3110
+ /* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "capitalize", children: payment.processor }),
3111
+ /* @__PURE__ */ jsxRuntime.jsx(TableCell, { children: /* @__PURE__ */ jsxRuntime.jsx(Badge, { className: "bg-emerald-500/10 text-emerald-400", children: (payment.status || "completed").toLowerCase() }) })
3112
+ ] }, payment.id))
3113
+ ) })
3114
+ ] }) }) }),
3115
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: loadMoreRef, className: "h-10 w-full", children: historyQuery.isFetchingNextPage && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-sm text-muted-foreground", children: "Loading more..." }) })
3116
+ ] }) })
3117
+ }
3118
+ )
3119
+ ] }) });
3120
+ };
3121
+ var formatCardLabel2 = (method) => {
3122
+ const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
3123
+ const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
3124
+ return `${brand} ${lastFour}`.trim();
3125
+ };
3126
+ var notifyDefault3 = (payload) => {
3127
+ const level = payload.status === "destructive" ? "error" : "info";
3128
+ console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
3129
+ };
3130
+ var PaymentMethodsSection = ({
3131
+ isAuthenticated = true,
3132
+ userEmail,
3133
+ provider = "mobius",
3134
+ defaultCountry = "US",
3135
+ collectPrefix = "account-card",
3136
+ onNotify
3137
+ }) => {
3138
+ const paymentMethods = usePaymentMethodService();
3139
+ const queryClient = reactQuery.useQueryClient();
3140
+ const [isModalOpen, setIsModalOpen] = React3.useState(false);
3141
+ const [deletingId, setDeletingId] = React3.useState(null);
3142
+ const notify = onNotify ?? notifyDefault3;
3143
+ const queryKey = ["payments-ui", "payment-methods"];
3144
+ const paymentQuery = reactQuery.useQuery({
3145
+ queryKey,
3146
+ queryFn: () => paymentMethods.list({ pageSize: 50 }),
3147
+ enabled: isAuthenticated,
3148
+ staleTime: 3e4
3149
+ });
3150
+ const createMutation = reactQuery.useMutation({
3151
+ mutationFn: (payload) => paymentMethods.create(payload),
3152
+ onSuccess: () => {
3153
+ notify({ title: "Card added successfully", status: "success" });
3154
+ setIsModalOpen(false);
3155
+ void queryClient.invalidateQueries({ queryKey });
3156
+ },
3157
+ onError: (error) => {
3158
+ notify({
3159
+ title: "Unable to add card",
3160
+ description: error.message,
3161
+ status: "destructive"
3162
+ });
3163
+ }
3164
+ });
3165
+ const deleteMutation = reactQuery.useMutation({
3166
+ mutationFn: (id) => paymentMethods.remove(id),
3167
+ onMutate: (id) => setDeletingId(id),
3168
+ onSuccess: () => {
3169
+ notify({ title: "Card removed", status: "success" });
3170
+ void queryClient.invalidateQueries({ queryKey });
3171
+ },
3172
+ onError: (error) => {
3173
+ notify({
3174
+ title: "Unable to remove card",
3175
+ description: error.message,
3176
+ status: "destructive"
3177
+ });
3178
+ },
3179
+ onSettled: () => setDeletingId(null)
3180
+ });
3181
+ React3.useEffect(() => {
3182
+ if (!isModalOpen) {
3183
+ createMutation.reset();
3184
+ }
3185
+ }, [createMutation, isModalOpen]);
3186
+ const payments = React3.useMemo(() => paymentQuery.data?.data ?? [], [paymentQuery.data]);
3187
+ const loading = paymentQuery.isLoading || paymentQuery.isFetching;
3188
+ const handleCardTokenize = (token, billing) => {
3189
+ const payload = {
3190
+ payment_token: token,
3191
+ first_name: billing.firstName,
3192
+ last_name: billing.lastName,
3193
+ address1: billing.address1,
3194
+ address2: billing.address2,
3195
+ city: billing.city,
3196
+ state: billing.stateRegion,
3197
+ zip: billing.postalCode,
3198
+ country: billing.country,
3199
+ email: billing.email,
3200
+ provider: billing.provider
3201
+ };
3202
+ createMutation.mutate(payload);
3203
+ };
3204
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "border-0 bg-background/5 shadow-lg", children: [
3205
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "flex flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
3206
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3207
+ /* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { className: "flex items-center gap-2 text-xl", children: [
3208
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.WalletCards, { className: "h-5 w-5 text-primary" }),
3209
+ " Payment Methods"
3210
+ ] }),
3211
+ /* @__PURE__ */ jsxRuntime.jsx(CardDescription, { children: "Manage your saved billing cards" })
3212
+ ] }),
3213
+ /* @__PURE__ */ jsxRuntime.jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
3214
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "mr-2 h-4 w-4" }),
3215
+ " Add card"
3216
+ ] })
3217
+ ] }),
3218
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "space-y-4", children: loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center py-10 text-muted-foreground", children: [
3219
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
3220
+ " Loading cards..."
3221
+ ] }) : payments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-dashed border-border/60 bg-muted/10 p-6 text-sm text-muted-foreground", children: "No saved payment methods yet." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: payments.map((method) => /* @__PURE__ */ jsxRuntime.jsxs(
3222
+ "div",
3223
+ {
3224
+ className: "rounded-lg border border-border/80 bg-background/40 p-4 shadow-sm",
3225
+ children: [
3226
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 md:flex-row md:items-center md:justify-between", children: [
3227
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3228
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-base font-medium text-foreground", children: formatCardLabel2(method) }),
3229
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
3230
+ "Added on",
3231
+ " ",
3232
+ method.created_at ? new Date(method.created_at).toLocaleDateString() : "unknown date"
3233
+ ] })
3234
+ ] }),
3235
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
3236
+ /* @__PURE__ */ jsxRuntime.jsx(
3237
+ Badge,
3238
+ {
3239
+ variant: method.is_active ? "default" : "secondary",
3240
+ className: method.is_active ? "bg-emerald-500/20 text-emerald-400" : "",
3241
+ children: method.is_active ? "Active" : "Inactive"
3242
+ }
3243
+ ),
3244
+ method.failure_reason && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "destructive", children: method.failure_reason })
3245
+ ] })
3246
+ ] }),
3247
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
3248
+ Button,
3249
+ {
3250
+ variant: "ghost",
3251
+ className: "text-destructive hover:text-destructive",
3252
+ disabled: deletingId === method.id && deleteMutation.isPending,
3253
+ onClick: () => deleteMutation.mutate(method.id),
3254
+ children: [
3255
+ deletingId === method.id && deleteMutation.isPending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "mr-2 h-4 w-4" }),
3256
+ "Remove"
3257
+ ]
3258
+ }
3259
+ ) })
3260
+ ]
3261
+ },
3262
+ method.id
3263
+ )) }) }),
3264
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "max-h-[95vh] overflow-y-auto border border-border bg-background", children: [
3265
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
3266
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Add a new card" }),
3267
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "Your card details are tokenized securely via our payment provider." })
3268
+ ] }),
3269
+ /* @__PURE__ */ jsxRuntime.jsx(
3270
+ CardDetailsForm,
3271
+ {
3272
+ visible: isModalOpen,
3273
+ collectPrefix,
3274
+ submitting: createMutation.isPending,
3275
+ submitLabel: "Save card",
3276
+ defaultValues: {
3277
+ email: userEmail ?? "",
3278
+ country: defaultCountry,
3279
+ provider
3280
+ },
3281
+ externalError: createMutation.error?.message ?? null,
3282
+ onTokenize: handleCardTokenize,
3283
+ className: "rounded-2xl border border-border bg-muted/20 p-6"
3284
+ }
3285
+ )
3286
+ ] }) })
3287
+ ] });
3288
+ };
3289
+ var useWalletList = (options = {}) => {
3290
+ const { services } = usePaymentContext();
3291
+ const [state, setState] = React3.useState({
3292
+ wallets: [],
3293
+ isLoading: false,
3294
+ error: null
3295
+ });
3296
+ const fetchWallets = React3.useCallback(async () => {
3297
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
3298
+ try {
3299
+ const wallets2 = await services.solanaWallets.list();
3300
+ setState({ wallets: wallets2, isLoading: false, error: null });
3301
+ return wallets2;
3302
+ } catch (error) {
3303
+ const message = error instanceof Error ? error.message : "Failed to load wallets";
3304
+ console.error("payments-ui: wallet list fetch failed", error);
3305
+ setState((prev) => ({ ...prev, isLoading: false, error: message }));
3306
+ throw error;
3307
+ }
3308
+ }, [services.solanaWallets]);
3309
+ const deleteWallet = React3.useCallback(
3310
+ async (walletIdOrAddress) => {
3311
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
3312
+ try {
3313
+ await services.solanaWallets.remove(walletIdOrAddress);
3314
+ setState((prev) => ({
3315
+ ...prev,
3316
+ wallets: prev.wallets.filter(
3317
+ (wallet) => wallet.id !== walletIdOrAddress && wallet.address !== walletIdOrAddress
3318
+ ),
3319
+ isLoading: false
3320
+ }));
3321
+ } catch (error) {
3322
+ const message = error instanceof Error ? error.message : "Failed to remove wallet";
3323
+ console.error("payments-ui: wallet removal failed", error);
3324
+ setState((prev) => ({ ...prev, isLoading: false, error: message }));
3325
+ throw error;
3326
+ }
3327
+ },
3328
+ [services.solanaWallets]
3329
+ );
3330
+ const addWallet = React3.useCallback((wallet) => {
3331
+ setState((prev) => ({ ...prev, wallets: [...prev.wallets, wallet] }));
3332
+ }, []);
3333
+ const updateWallet = React3.useCallback((walletId, updates) => {
3334
+ setState((prev) => ({
3335
+ ...prev,
3336
+ wallets: prev.wallets.map(
3337
+ (wallet) => wallet.id === walletId ? { ...wallet, ...updates } : wallet
3338
+ )
3339
+ }));
3340
+ }, []);
3341
+ const clearError = React3.useCallback(() => {
3342
+ setState((prev) => ({ ...prev, error: null }));
3343
+ }, []);
3344
+ const findWalletByAddress = React3.useCallback(
3345
+ (address) => state.wallets.find((wallet) => wallet.address === address),
3346
+ [state.wallets]
3347
+ );
3348
+ const getVerifiedWallets = React3.useCallback(
3349
+ () => state.wallets.filter((wallet) => wallet.is_verified),
3350
+ [state.wallets]
3351
+ );
3352
+ React3.useEffect(() => {
3353
+ if (options.autoFetch !== false) {
3354
+ fetchWallets().catch(() => {
3355
+ });
3356
+ }
3357
+ }, [fetchWallets, options.autoFetch]);
3358
+ return {
3359
+ wallets: state.wallets,
3360
+ isLoading: state.isLoading,
3361
+ error: state.error,
3362
+ fetchWallets,
3363
+ deleteWallet,
3364
+ addWallet,
3365
+ updateWallet,
3366
+ clearError,
3367
+ findWalletByAddress,
3368
+ getVerifiedWallets,
3369
+ hasVerifiedWallets: state.wallets.some((w) => w.is_verified),
3370
+ totalWallets: state.wallets.length
3371
+ };
3372
+ };
3373
+ var useWalletVerification = () => {
3374
+ const { services } = usePaymentContext();
3375
+ const { publicKey, signMessage } = walletAdapterReact.useWallet();
3376
+ const [state, setState] = React3.useState({
3377
+ isVerifying: false,
3378
+ isVerified: false,
3379
+ error: null
3380
+ });
3381
+ const signAndVerifyWallet = React3.useCallback(
3382
+ async (walletAddress, message, nonce) => {
3383
+ if (!publicKey || !signMessage) {
3384
+ throw new Error("Wallet not connected or signing unavailable");
3385
+ }
3386
+ if (publicKey.toBase58() !== walletAddress) {
3387
+ throw new Error("Connected wallet does not match target wallet");
3388
+ }
3389
+ setState((prev) => ({ ...prev, isVerifying: true, error: null }));
3390
+ try {
3391
+ const encodedMessage = new TextEncoder().encode(message);
3392
+ const signature = await signMessage(encodedMessage);
3393
+ const signatureBase58 = bs58__default.default.encode(signature);
3394
+ const response = await services.solanaWallets.verify(
3395
+ walletAddress,
3396
+ signatureBase58,
3397
+ nonce
3398
+ );
3399
+ setState({ isVerifying: false, isVerified: response.verified, error: null });
3400
+ return response;
3401
+ } catch (error) {
3402
+ const message2 = error instanceof Error ? error.message : "Wallet verification failed";
3403
+ console.error("payments-ui: wallet verification failed", error);
3404
+ setState({ isVerifying: false, isVerified: false, error: message2 });
3405
+ throw error;
3406
+ }
3407
+ },
3408
+ [publicKey, signMessage, services.solanaWallets]
3409
+ );
3410
+ const clearError = React3.useCallback(() => {
3411
+ setState((prev) => ({ ...prev, error: null }));
3412
+ }, []);
3413
+ const resetVerification = React3.useCallback(() => {
3414
+ setState({ isVerifying: false, isVerified: false, error: null });
3415
+ }, []);
3416
+ const autoVerifyWallet = React3.useCallback(
3417
+ async (walletAddress, message, nonce) => {
3418
+ try {
3419
+ return await signAndVerifyWallet(walletAddress, message, nonce);
3420
+ } catch (error) {
3421
+ console.warn("payments-ui: auto-verification skipped", error);
3422
+ return null;
3423
+ }
3424
+ },
3425
+ [signAndVerifyWallet]
3426
+ );
3427
+ return {
3428
+ ...state,
3429
+ signAndVerifyWallet,
3430
+ autoVerifyWallet,
3431
+ clearError,
3432
+ resetVerification
3433
+ };
3434
+ };
3435
+ var useWalletConnection = () => {
3436
+ const { services } = usePaymentContext();
3437
+ const { publicKey, connected, connecting, disconnect, wallet } = walletAdapterReact.useWallet();
3438
+ const [state, setState] = React3.useState({
3439
+ isConnected: false,
3440
+ isConnecting: false,
3441
+ publicKey: null,
3442
+ wallets: [],
3443
+ isLoading: false,
3444
+ error: null
3445
+ });
3446
+ React3.useEffect(() => {
3447
+ setState((prev) => ({
3448
+ ...prev,
3449
+ isConnected: connected,
3450
+ isConnecting: connecting,
3451
+ publicKey: publicKey?.toBase58() ?? null
3452
+ }));
3453
+ }, [connected, connecting, publicKey]);
3454
+ const connectWalletToBackend = React3.useCallback(
3455
+ async (address) => {
3456
+ if (!address) {
3457
+ throw new Error("Wallet address is required");
3458
+ }
3459
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
3460
+ try {
3461
+ const challenge = await services.solanaWallets.requestChallenge(address);
3462
+ setState((prev) => ({ ...prev, isLoading: false }));
3463
+ return challenge;
3464
+ } catch (error) {
3465
+ const message = error instanceof Error ? error.message : "Wallet challenge failed";
3466
+ console.error("payments-ui: wallet challenge failed", error);
3467
+ setState((prev) => ({ ...prev, isLoading: false, error: message }));
3468
+ throw error;
3469
+ }
3470
+ },
3471
+ [services.solanaWallets]
3472
+ );
3473
+ const connectWallet = React3.useCallback(async () => {
3474
+ if (!wallet) {
3475
+ throw new Error("No wallet adapter selected");
3476
+ }
3477
+ setState((prev) => ({ ...prev, isConnecting: true, error: null }));
3478
+ try {
3479
+ await wallet.adapter.connect();
3480
+ if (publicKey) {
3481
+ await connectWalletToBackend(publicKey.toBase58());
3482
+ }
3483
+ } catch (error) {
3484
+ const message = error instanceof Error ? error.message : "Failed to connect wallet";
3485
+ console.error("payments-ui: wallet connection failed", error);
3486
+ setState((prev) => ({ ...prev, isConnecting: false, error: message }));
3487
+ throw error;
3488
+ } finally {
3489
+ setState((prev) => ({ ...prev, isConnecting: false }));
3490
+ }
3491
+ }, [wallet, publicKey, connectWalletToBackend]);
3492
+ const disconnectWallet = React3.useCallback(async () => {
3493
+ try {
3494
+ await disconnect();
3495
+ setState((prev) => ({
3496
+ ...prev,
3497
+ isConnected: false,
3498
+ publicKey: null,
3499
+ wallets: [],
3500
+ error: null
3501
+ }));
3502
+ } catch (error) {
3503
+ const message = error instanceof Error ? error.message : "Failed to disconnect wallet";
3504
+ console.error("payments-ui: wallet disconnect failed", error);
3505
+ setState((prev) => ({ ...prev, error: message }));
3506
+ throw error;
3507
+ }
3508
+ }, [disconnect]);
3509
+ const clearError = React3.useCallback(() => {
3510
+ setState((prev) => ({ ...prev, error: null }));
3511
+ }, []);
3512
+ return {
3513
+ ...state,
3514
+ walletName: wallet?.adapter.name,
3515
+ walletIcon: wallet?.adapter.icon,
3516
+ connectWallet,
3517
+ disconnectWallet,
3518
+ connectWalletToBackend,
3519
+ clearError
3520
+ };
3521
+ };
3522
+ var notifyDefault4 = (payload) => {
3523
+ console.log("[payments-ui] wallet-card", payload);
3524
+ };
3525
+ var WalletCard = ({
3526
+ wallet,
3527
+ isPrimary = false,
3528
+ isConnected = false,
3529
+ balance,
3530
+ onSetPrimary,
3531
+ onVerify,
3532
+ onDelete,
3533
+ isVerifying = false,
3534
+ isDeleting = false,
3535
+ notify = notifyDefault4
3536
+ }) => {
3537
+ const [isCopying, setIsCopying] = React3.useState(false);
3538
+ const formatAddress = (address) => address.length <= 12 ? address : `${address.slice(0, 4)}...${address.slice(-4)}`;
3539
+ const copyToClipboard = async (text) => {
3540
+ setIsCopying(true);
3541
+ try {
3542
+ await navigator.clipboard.writeText(text);
3543
+ notify({ title: "Copied", description: "Wallet address copied to clipboard" });
3544
+ } catch (error) {
3545
+ notify({ title: "Failed to copy", description: error.message, status: "destructive" });
3546
+ } finally {
3547
+ setTimeout(() => setIsCopying(false), 500);
3548
+ }
3549
+ };
3550
+ const openExplorer = () => {
3551
+ window.open(`https://solscan.io/account/${wallet.address}`, "_blank", "noopener,noreferrer");
3552
+ };
3553
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3554
+ "div",
3555
+ {
3556
+ className: cn(
3557
+ "rounded-lg border bg-background/40 p-4 transition-shadow",
3558
+ isPrimary && "border-primary/50 shadow-lg",
3559
+ isConnected && "ring-1 ring-primary"
3560
+ ),
3561
+ children: [
3562
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
3563
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
3564
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-primary/20", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 w-5 rounded-full bg-gradient-to-br from-primary to-primary/60" }) }),
3565
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
3566
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3567
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-sm font-medium", children: formatAddress(wallet.address) }),
3568
+ isPrimary && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 rounded-full bg-yellow-500/20 px-2 py-0.5 text-xs text-yellow-500", children: [
3569
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Star, { className: "h-3 w-3 fill-yellow-500 text-yellow-500" }),
3570
+ " Primary"
3571
+ ] }),
3572
+ wallet.is_verified ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-xs text-emerald-400", children: [
3573
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-3.5 w-3.5" }),
3574
+ " Verified"
3575
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-xs text-amber-400", children: [
3576
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3.5 w-3.5" }),
3577
+ " Unverified"
3578
+ ] }),
3579
+ isConnected && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded bg-blue-500/20 px-2 py-0.5 text-xs text-blue-400", children: "Connected" })
3580
+ ] }),
3581
+ balance !== null && typeof balance !== "undefined" && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [
3582
+ "Balance: ",
3583
+ balance.toFixed(4),
3584
+ " SOL"
3585
+ ] }),
3586
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
3587
+ "Added ",
3588
+ new Date(wallet.created_at).toLocaleDateString()
3589
+ ] })
3590
+ ] })
3591
+ ] }) }),
3592
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
3593
+ /* @__PURE__ */ jsxRuntime.jsx(
3594
+ Button,
3595
+ {
3596
+ variant: "ghost",
3597
+ size: "icon",
3598
+ className: "h-8 w-8",
3599
+ onClick: () => copyToClipboard(wallet.address),
3600
+ disabled: isCopying,
3601
+ children: isCopying ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-4 w-4" })
3602
+ }
3603
+ ),
3604
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", onClick: openExplorer, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-4 w-4" }) })
3605
+ ] })
3606
+ ] }),
3607
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [
3608
+ !isPrimary && onSetPrimary && wallet.is_verified && /* @__PURE__ */ jsxRuntime.jsxs(
3609
+ Button,
3610
+ {
3611
+ variant: "outline",
3612
+ size: "sm",
3613
+ className: "border-primary/40 text-primary",
3614
+ onClick: () => onSetPrimary(wallet.id),
3615
+ children: [
3616
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Star, { className: "mr-1 h-3 w-3" }),
3617
+ " Set Primary"
3618
+ ]
3619
+ }
3620
+ ),
3621
+ !wallet.is_verified && onVerify && isConnected && /* @__PURE__ */ jsxRuntime.jsx(
3622
+ Button,
3623
+ {
3624
+ variant: "outline",
3625
+ size: "sm",
3626
+ className: "border-emerald-500/40 text-emerald-400",
3627
+ onClick: () => onVerify(wallet),
3628
+ disabled: isVerifying,
3629
+ children: isVerifying ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3630
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-1 h-3 w-3 animate-spin" }),
3631
+ " Verifying..."
3632
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3633
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Shield, { className: "mr-1 h-3 w-3" }),
3634
+ " Verify"
3635
+ ] })
3636
+ }
3637
+ ),
3638
+ onDelete && /* @__PURE__ */ jsxRuntime.jsx(
3639
+ Button,
3640
+ {
3641
+ variant: "outline",
3642
+ size: "sm",
3643
+ className: "ml-auto border-destructive/40 text-destructive",
3644
+ onClick: () => onDelete(wallet.id),
3645
+ disabled: isDeleting,
3646
+ children: isDeleting ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3 w-3" })
3647
+ }
3648
+ )
3649
+ ] })
3650
+ ]
3651
+ }
3652
+ );
3653
+ };
3654
+ var EmptyWalletState = () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [
3655
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wallet, { className: "mb-4 h-12 w-12 text-muted-foreground" }),
3656
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-2 text-lg font-medium", children: "No wallets connected" }),
3657
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-6 text-sm text-muted-foreground", children: "Connect your Solana wallet to get started" }),
3658
+ /* @__PURE__ */ jsxRuntime.jsx(walletAdapterReactUi.WalletMultiButton, { className: "!bg-primary text-primary-foreground hover:!bg-primary/90" })
3659
+ ] });
3660
+ var notifyDefault5 = (payload) => {
3661
+ console.log("[payments-ui] solana-wallets", payload);
3662
+ };
3663
+ var SolanaWalletSection = ({
3664
+ onNotify,
3665
+ rpcUrl
3666
+ }) => {
3667
+ const notify = onNotify ?? notifyDefault5;
3668
+ const { config } = usePaymentContext();
3669
+ const { connected, publicKey, disconnect } = walletAdapterReact.useWallet();
3670
+ const { wallets: wallets2, isLoading, deleteWallet, fetchWallets } = useWalletList();
3671
+ const { signAndVerifyWallet } = useWalletVerification();
3672
+ const walletConnection = useWalletConnection();
3673
+ const [primaryWalletId, setPrimaryWalletId] = React3.useState(null);
3674
+ const [showOtherWallets, setShowOtherWallets] = React3.useState(false);
3675
+ const [balances, setBalances] = React3.useState({});
3676
+ const [verifyingWalletId, setVerifyingWalletId] = React3.useState(null);
3677
+ const [deletingWalletId, setDeletingWalletId] = React3.useState(null);
3678
+ const [walletToDelete, setWalletToDelete] = React3.useState(null);
3679
+ const [isRegistering, setIsRegistering] = React3.useState(false);
3680
+ const rpcEndpoint = rpcUrl || config.solanaRpcUrl || "https://api.mainnet-beta.solana.com";
3681
+ const connection = React3.useMemo(() => new web3_js.Connection(rpcEndpoint), [rpcEndpoint]);
3682
+ const primaryWallet = wallets2.find((w) => w.id === primaryWalletId) ?? wallets2.find((w) => w.is_verified) ?? null;
3683
+ const otherWallets = wallets2.filter((w) => w.id !== primaryWallet?.id);
3684
+ const fetchBalance = React3.useCallback(
3685
+ async (address) => {
3686
+ try {
3687
+ const pubkey = new web3_js.PublicKey(address);
3688
+ const balance = await connection.getBalance(pubkey);
3689
+ return balance / web3_js.LAMPORTS_PER_SOL;
3690
+ } catch (error) {
3691
+ console.error("payments-ui: failed to fetch balance", error);
3692
+ return null;
3693
+ }
3694
+ },
3695
+ [connection]
3696
+ );
3697
+ const fetchAllBalances = React3.useCallback(async () => {
3698
+ const results = await Promise.all(
3699
+ wallets2.map(async (wallet) => ({ address: wallet.address, balance: await fetchBalance(wallet.address) }))
3700
+ );
3701
+ const next = {};
3702
+ results.forEach(({ address, balance }) => {
3703
+ if (balance !== null && typeof balance !== "undefined") {
3704
+ next[address] = balance;
3705
+ }
3706
+ });
3707
+ setBalances(next);
3708
+ }, [wallets2, fetchBalance]);
3709
+ React3.useEffect(() => {
3710
+ if (wallets2.length > 0) {
3711
+ fetchAllBalances().catch(() => void 0);
3712
+ }
3713
+ }, [wallets2, fetchAllBalances]);
3714
+ React3.useEffect(() => {
3715
+ const verifiedWallet = wallets2.find((w) => w.is_verified);
3716
+ if (verifiedWallet && !primaryWalletId) {
3717
+ setPrimaryWalletId(verifiedWallet.id);
3718
+ }
3719
+ }, [wallets2, primaryWalletId]);
3720
+ const registerWallet = React3.useCallback(async () => {
3721
+ if (!connected || !publicKey || isRegistering) return;
3722
+ setIsRegistering(true);
3723
+ try {
3724
+ const challenge = await walletConnection.connectWalletToBackend(publicKey.toBase58());
3725
+ if (!challenge?.message) {
3726
+ throw new Error("Failed to retrieve wallet verification challenge");
3727
+ }
3728
+ await signAndVerifyWallet(challenge.wallet, challenge.message, challenge.nonce);
3729
+ await fetchWallets();
3730
+ notify({
3731
+ title: "Wallet verified",
3732
+ description: "Your wallet has been linked successfully.",
3733
+ status: "success"
3734
+ });
3735
+ } catch (error) {
3736
+ notify({
3737
+ title: "Wallet verification failed",
3738
+ description: error instanceof Error ? error.message : "Failed to verify wallet",
3739
+ status: "destructive"
3740
+ });
3741
+ } finally {
3742
+ setIsRegistering(false);
3743
+ }
3744
+ }, [connected, publicKey, isRegistering, walletConnection, signAndVerifyWallet, fetchWallets, notify]);
3745
+ React3.useEffect(() => {
3746
+ if (connected && publicKey && !walletConnection.isConnecting && wallets2.length === 0) {
3747
+ registerWallet().catch(() => void 0);
3748
+ }
3749
+ }, [connected, publicKey, walletConnection.isConnecting, wallets2.length, registerWallet]);
3750
+ const handleVerifyWallet = React3.useCallback(
3751
+ async (wallet) => {
3752
+ if (!connected || publicKey?.toBase58() !== wallet.address) {
3753
+ notify({
3754
+ title: "Connect wallet first",
3755
+ description: "Please connect the wallet you want to verify.",
3756
+ status: "destructive"
3757
+ });
3758
+ return;
3759
+ }
3760
+ setVerifyingWalletId(wallet.id);
3761
+ try {
3762
+ const challenge = await walletConnection.connectWalletToBackend(wallet.address);
3763
+ if (!challenge?.message) {
3764
+ throw new Error("Failed to retrieve verification challenge");
3765
+ }
3766
+ await signAndVerifyWallet(challenge.wallet, challenge.message, challenge.nonce);
3767
+ await fetchWallets();
3768
+ notify({ title: "Wallet verified", status: "success" });
3769
+ } catch (error) {
3770
+ notify({
3771
+ title: "Verification failed",
3772
+ description: error instanceof Error ? error.message : "Failed to verify wallet",
3773
+ status: "destructive"
3774
+ });
3775
+ } finally {
3776
+ setVerifyingWalletId(null);
3777
+ }
3778
+ },
3779
+ [connected, publicKey, walletConnection, signAndVerifyWallet, fetchWallets, notify]
3780
+ );
3781
+ const handleDeleteWallet = React3.useCallback(
3782
+ async (walletId) => {
3783
+ setDeletingWalletId(walletId);
3784
+ try {
3785
+ await deleteWallet(walletId);
3786
+ if (primaryWalletId === walletId) {
3787
+ setPrimaryWalletId(null);
3788
+ }
3789
+ if (connected && publicKey) {
3790
+ const deletedWallet = wallets2.find((w) => w.id === walletId);
3791
+ if (deletedWallet?.address === publicKey.toBase58()) {
3792
+ await disconnect();
3793
+ }
3794
+ }
3795
+ notify({ title: "Wallet removed", status: "success" });
3796
+ } catch (error) {
3797
+ notify({
3798
+ title: "Failed to remove wallet",
3799
+ description: error instanceof Error ? error.message : "Please try again",
3800
+ status: "destructive"
3801
+ });
3802
+ } finally {
3803
+ setDeletingWalletId(null);
3804
+ setWalletToDelete(null);
3805
+ }
3806
+ },
3807
+ [deleteWallet, primaryWalletId, connected, publicKey, wallets2, disconnect, notify]
3808
+ );
3809
+ const handleSetPrimary = (walletId) => {
3810
+ setPrimaryWalletId(walletId);
3811
+ notify({ title: "Primary wallet updated", status: "success" });
3812
+ };
3813
+ const isWalletRegistered = connected && publicKey && wallets2.some((w) => w.address === publicKey.toBase58());
3814
+ if (isLoading && wallets2.length === 0) {
3815
+ return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "border-0 bg-background/5", children: [
3816
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl", children: "Solana Wallets" }) }),
3817
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center py-8", children: [
3818
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-6 w-6 animate-spin text-muted-foreground" }),
3819
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Loading wallets..." })
3820
+ ] }) })
3821
+ ] });
3822
+ }
3823
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3824
+ /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "border-0 bg-background/5", children: [
3825
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { children: [
3826
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-xl", children: "Solana Wallets" }),
3827
+ /* @__PURE__ */ jsxRuntime.jsx(CardDescription, { children: "Connect and manage your Solana wallets for payments" })
3828
+ ] }),
3829
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "space-y-4", children: wallets2.length === 0 && !connected ? /* @__PURE__ */ jsxRuntime.jsx(EmptyWalletState, {}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3830
+ primaryWallet && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3831
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-medium uppercase tracking-wide text-muted-foreground", children: "Primary Wallet" }),
3832
+ /* @__PURE__ */ jsxRuntime.jsx(
3833
+ WalletCard,
3834
+ {
3835
+ wallet: primaryWallet,
3836
+ isPrimary: true,
3837
+ isConnected: connected && publicKey?.toBase58() === primaryWallet.address,
3838
+ balance: balances[primaryWallet.address],
3839
+ onVerify: handleVerifyWallet,
3840
+ onDelete: (id) => {
3841
+ const target = wallets2.find((w) => w.id === id);
3842
+ if (target) setWalletToDelete(target);
3843
+ },
3844
+ isVerifying: verifyingWalletId === primaryWallet.id,
3845
+ isDeleting: deletingWalletId === primaryWallet.id,
3846
+ notify
3847
+ }
3848
+ )
3849
+ ] }),
3850
+ otherWallets.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3851
+ /* @__PURE__ */ jsxRuntime.jsxs(
3852
+ "button",
3853
+ {
3854
+ className: "mb-3 flex w-full items-center justify-between text-left",
3855
+ onClick: () => setShowOtherWallets((prev) => !prev),
3856
+ children: [
3857
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: [
3858
+ "Other Wallets (",
3859
+ otherWallets.length,
3860
+ ")"
3861
+ ] }),
3862
+ showOtherWallets ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
3863
+ ]
3864
+ }
3865
+ ),
3866
+ showOtherWallets && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: otherWallets.map((wallet) => /* @__PURE__ */ jsxRuntime.jsx(
3867
+ WalletCard,
3868
+ {
3869
+ wallet,
3870
+ isConnected: connected && publicKey?.toBase58() === wallet.address,
3871
+ balance: balances[wallet.address],
3872
+ onSetPrimary: handleSetPrimary,
3873
+ onVerify: handleVerifyWallet,
3874
+ onDelete: (id) => {
3875
+ const target = wallets2.find((w) => w.id === id);
3876
+ if (target) setWalletToDelete(target);
3877
+ },
3878
+ isVerifying: verifyingWalletId === wallet.id,
3879
+ isDeleting: deletingWalletId === wallet.id,
3880
+ notify
3881
+ },
3882
+ wallet.id
3883
+ )) })
3884
+ ] }),
3885
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-4", children: connected && publicKey ? isWalletRegistered ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
3886
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-emerald-500/40 bg-emerald-500/10 p-3 text-sm text-emerald-200", children: [
3887
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-4 w-4" }),
3888
+ " Connected wallet is verified and linked to your account."
3889
+ ] }),
3890
+ /* @__PURE__ */ jsxRuntime.jsx(walletAdapterReactUi.WalletMultiButton, { className: "w-full !bg-primary text-primary-foreground" })
3891
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
3892
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-amber-500/40 bg-amber-500/10 p-3 text-sm text-amber-200", children: "Your connected wallet is not registered with your account." }),
3893
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
3894
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: registerWallet, disabled: isRegistering || walletConnection.isConnecting, className: "flex-1 bg-primary text-primary-foreground", children: isRegistering ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3895
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
3896
+ " Registering..."
3897
+ ] }) : "Register This Wallet" }),
3898
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: () => disconnect(), variant: "outline", className: "border-border", children: "Disconnect" })
3899
+ ] })
3900
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(walletAdapterReactUi.WalletMultiButton, { className: "w-full !bg-primary text-primary-foreground" }) })
3901
+ ] }) })
3902
+ ] }),
3903
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialog, { open: !!walletToDelete, onOpenChange: () => setWalletToDelete(null), children: /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { children: [
3904
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { children: [
3905
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogTitle, { children: "Remove Wallet" }),
3906
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogDescription, { children: "Are you sure you want to remove this wallet from your account? This action cannot be undone." })
3907
+ ] }),
3908
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { children: [
3909
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { children: "Cancel" }),
3910
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogAction, { onClick: () => walletToDelete && handleDeleteWallet(walletToDelete.id), className: "bg-destructive text-destructive-foreground", children: "Remove" })
3911
+ ] })
3912
+ ] }) })
3913
+ ] });
3914
+ };
3915
+ var WalletManagement = (props) => /* @__PURE__ */ jsxRuntime.jsx(SolanaWalletSection, { ...props });
3916
+ var wallets = [
3917
+ {
3918
+ id: "phantom",
3919
+ name: "Phantom",
3920
+ icon: "https://phantom.app/img/logo.png"
3921
+ },
3922
+ {
3923
+ id: "solflare",
3924
+ name: "Solflare",
3925
+ icon: "https://solflare.com/favicon.ico"
3926
+ }
3927
+ ];
3928
+ var WalletModal = ({ open, onOpenChange }) => {
3929
+ const [expandedWallet, setExpandedWallet] = React3.useState(null);
3930
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsx(DialogContent, { className: "max-w-md border border-border bg-background", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: wallets.map((wallet) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border/80", children: [
3931
+ /* @__PURE__ */ jsxRuntime.jsxs(
3932
+ "button",
3933
+ {
3934
+ className: "flex w-full items-center justify-between px-4 py-3",
3935
+ onClick: () => setExpandedWallet((prev) => prev === wallet.id ? null : wallet.id),
3936
+ children: [
3937
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 text-left", children: [
3938
+ /* @__PURE__ */ jsxRuntime.jsx("img", { src: wallet.icon, alt: wallet.name, className: "h-8 w-8 rounded-full" }),
3939
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3940
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-semibold", children: wallet.name }),
3941
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Connect via browser extension" })
3942
+ ] })
3943
+ ] }),
3944
+ expandedWallet === wallet.id ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
3945
+ ]
3946
+ }
3947
+ ),
3948
+ expandedWallet === wallet.id && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border/60 px-4 py-3 text-sm text-muted-foreground", children: [
3949
+ "Follow the prompts in your ",
3950
+ wallet.name,
3951
+ " wallet to approve access."
3952
+ ] })
3953
+ ] }, wallet.id)) }) }) });
3954
+ };
3955
+ var Checkbox = React3__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3956
+ CheckboxPrimitive__namespace.Root,
3957
+ {
3958
+ ref,
3959
+ className: cn(
3960
+ "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
3961
+ className
3962
+ ),
3963
+ ...props,
3964
+ children: /* @__PURE__ */ jsxRuntime.jsx(
3965
+ CheckboxPrimitive__namespace.Indicator,
3966
+ {
3967
+ className: cn("flex items-center justify-center text-current"),
3968
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3 w-3" })
3969
+ }
3970
+ )
3971
+ }
3972
+ ));
3973
+ Checkbox.displayName = CheckboxPrimitive__namespace.Root.displayName;
3974
+ var initialState2 = {
3975
+ nameOnCard: "",
3976
+ cardNumber: "",
3977
+ expiration: "",
3978
+ cvv: "",
3979
+ termsAccepted: false
3980
+ };
3981
+ var WalletDialog = ({ open, onOpenChange }) => {
3982
+ const [form, setForm] = React3.useState(initialState2);
3983
+ const [errors, setErrors] = React3.useState({});
3984
+ const validators = React3.useMemo(
3985
+ () => ({
3986
+ nameOnCard: (value) => !value ? "Name is required" : void 0,
3987
+ cardNumber: (value) => /^\d{16}$/.test(value) ? void 0 : "Card number must be 16 digits",
3988
+ expiration: (value) => /^(0[1-9]|1[0-2])\/([2-9]\d)$/.test(value) ? void 0 : "Must be in MM/YY format",
3989
+ cvv: (value) => /^\d{3,4}$/.test(value) ? void 0 : "CVV must be 3 or 4 digits",
3990
+ termsAccepted: (value) => value ? void 0 : "You must accept the terms"
3991
+ }),
3992
+ []
3993
+ );
3994
+ const updateField = (field, value) => {
3995
+ setForm((prev) => ({ ...prev, [field]: value }));
3996
+ setErrors((prev) => ({ ...prev, [field]: void 0 }));
3997
+ };
3998
+ const validate = () => {
3999
+ const next = {};
4000
+ Object.keys(validators).forEach((key) => {
4001
+ const validator = validators[key];
4002
+ const message = validator?.(form[key]);
4003
+ if (message) {
4004
+ next[key] = message;
4005
+ }
4006
+ });
4007
+ setErrors(next);
4008
+ return Object.keys(next).length === 0;
4009
+ };
4010
+ const handleSubmit = (event) => {
4011
+ event.preventDefault();
4012
+ if (!validate()) return;
4013
+ console.log("[payments-ui] wallet dialog submit", form);
4014
+ onOpenChange(false);
4015
+ setForm(initialState2);
4016
+ };
4017
+ return /* @__PURE__ */ jsxRuntime.jsx(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { className: "max-h-[95vh] max-w-lg overflow-y-auto rounded-2xl border border-border bg-background", children: [
4018
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { className: "border-b border-border/60 pb-4", children: [
4019
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2", children: [
4020
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Shield, { className: "h-5 w-5 text-primary" }),
4021
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogTitle, { className: "text-center text-base font-semibold uppercase tracking-wide", children: "Secure Payment via Mobius Pay" })
4022
+ ] }),
4023
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-center text-sm text-muted-foreground", children: "$23 USD per month, cancel at any time." })
4024
+ ] }),
4025
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-5 px-2 py-4 sm:px-4", children: [
4026
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
4027
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4028
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Name on Card" }),
4029
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
4030
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UserRound, { className: "h-4 w-4" }) }),
4031
+ /* @__PURE__ */ jsxRuntime.jsx(Input, { value: form.nameOnCard, onChange: (e) => updateField("nameOnCard", e.target.value), className: "pl-10" })
4032
+ ] }),
4033
+ errors.nameOnCard && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.nameOnCard })
4034
+ ] }),
4035
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4036
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Credit Card Number" }),
4037
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
4038
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "h-4 w-4" }) }),
4039
+ /* @__PURE__ */ jsxRuntime.jsx(Input, { value: form.cardNumber, onChange: (e) => updateField("cardNumber", e.target.value), className: "pl-10" })
4040
+ ] }),
4041
+ errors.cardNumber && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.cardNumber })
4042
+ ] }),
4043
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
4044
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
4045
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Expiration" }),
4046
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
4047
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { className: "h-4 w-4" }) }),
4048
+ /* @__PURE__ */ jsxRuntime.jsx(Input, { value: form.expiration, onChange: (e) => updateField("expiration", e.target.value), className: "pl-10", placeholder: "MM/YY" })
4049
+ ] }),
4050
+ errors.expiration && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.expiration })
4051
+ ] }),
4052
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
4053
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "CVV" }),
4054
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
4055
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.KeyRound, { className: "h-4 w-4" }) }),
4056
+ /* @__PURE__ */ jsxRuntime.jsx(Input, { value: form.cvv, onChange: (e) => updateField("cvv", e.target.value), className: "pl-10" })
4057
+ ] }),
4058
+ errors.cvv && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.cvv })
4059
+ ] })
4060
+ ] })
4061
+ ] }),
4062
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-border/70 bg-muted/10 p-4", children: [
4063
+ /* @__PURE__ */ jsxRuntime.jsx(Checkbox, { id: "terms-agree", checked: form.termsAccepted, onCheckedChange: (checked) => updateField("termsAccepted", Boolean(checked)) }),
4064
+ /* @__PURE__ */ jsxRuntime.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." })
4065
+ ] }),
4066
+ errors.termsAccepted && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: errors.termsAccepted }),
4067
+ /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { className: "flex gap-2", children: [
4068
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", className: "flex-1", children: "Subscribe" }),
4069
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", className: "flex-1", children: "Close" }) })
4070
+ ] })
4071
+ ] })
4072
+ ] }) });
4073
+ };
4074
+ var SubscriptionSuccessDialog = ({
4075
+ open,
4076
+ planName = "Premium Plan",
4077
+ amountLabel = "$0.00",
4078
+ billingPeriodLabel = "billing period",
4079
+ onClose
4080
+ }) => {
4081
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange: (value) => {
4082
+ if (!value) onClose();
4083
+ }, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "max-w-sm text-center", children: [
4084
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
4085
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogTitle, { className: "flex flex-col items-center gap-3 text-foreground", children: [
4086
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-10 w-10 text-primary" }),
4087
+ "Subscription activated"
4088
+ ] }),
4089
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogDescription, { className: "text-base text-muted-foreground", children: [
4090
+ "You now have access to ",
4091
+ planName,
4092
+ ". Billing: ",
4093
+ amountLabel,
4094
+ " / ",
4095
+ billingPeriodLabel,
4096
+ "."
4097
+ ] })
4098
+ ] }),
4099
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { className: "mt-6 w-full", onClick: onClose, children: "Continue" })
4100
+ ] }) });
4101
+ };
4102
+ var useSubscriptionActions = () => {
4103
+ const { services } = usePaymentContext();
4104
+ const ensurePrice = (priceId) => {
4105
+ if (!priceId) {
4106
+ throw new Error("payments-ui: priceId is required for subscription actions");
4107
+ }
4108
+ return priceId;
4109
+ };
4110
+ const subscribeWithCard = React3.useCallback(
4111
+ async ({
4112
+ priceId,
4113
+ processor = "nmi",
4114
+ provider,
4115
+ paymentToken,
4116
+ billing
4117
+ }) => {
4118
+ const payload = {
4119
+ priceId: ensurePrice(priceId),
4120
+ paymentToken,
4121
+ processor,
4122
+ provider,
4123
+ email: billing.email,
4124
+ firstName: billing.firstName,
4125
+ lastName: billing.lastName,
4126
+ address1: billing.address1,
4127
+ city: billing.city,
4128
+ state: billing.stateRegion,
4129
+ zipCode: billing.postalCode,
4130
+ country: billing.country
4131
+ };
4132
+ return services.subscriptions.subscribe("nmi", payload);
4133
+ },
4134
+ [services]
4135
+ );
4136
+ const subscribeWithSavedMethod = React3.useCallback(
4137
+ async ({
4138
+ priceId,
4139
+ processor = "nmi",
4140
+ provider,
4141
+ paymentMethodId,
4142
+ email
4143
+ }) => {
4144
+ const payload = {
4145
+ priceId: ensurePrice(priceId),
4146
+ paymentMethodId,
4147
+ processor,
4148
+ provider,
4149
+ email
4150
+ };
4151
+ return services.subscriptions.subscribe("nmi", payload);
4152
+ },
4153
+ [services]
4154
+ );
4155
+ const subscribeWithCCBill = React3.useCallback(
4156
+ async ({
4157
+ priceId,
4158
+ email,
4159
+ firstName,
4160
+ lastName,
4161
+ zipCode,
4162
+ country,
4163
+ processor = "ccbill"
4164
+ }) => {
4165
+ const payload = {
4166
+ priceId: ensurePrice(priceId),
4167
+ email,
4168
+ firstName,
4169
+ lastName,
4170
+ zipCode,
4171
+ country,
4172
+ processor
4173
+ };
4174
+ return services.subscriptions.subscribe("ccbill", payload);
4175
+ },
4176
+ [services]
4177
+ );
4178
+ const generateFlexFormUrl = React3.useCallback(
4179
+ async ({
4180
+ priceId,
4181
+ firstName,
4182
+ lastName,
4183
+ address1,
4184
+ city,
4185
+ state,
4186
+ zipCode,
4187
+ country
4188
+ }) => {
4189
+ const payload = {
4190
+ price_id: ensurePrice(priceId),
4191
+ first_name: firstName,
4192
+ last_name: lastName,
4193
+ address1,
4194
+ city,
4195
+ state,
4196
+ zip_code: zipCode,
4197
+ country
4198
+ };
4199
+ return services.subscriptions.generateFlexFormUrl(payload);
4200
+ },
4201
+ [services]
4202
+ );
4203
+ return {
4204
+ subscribeWithCard,
4205
+ subscribeWithSavedMethod,
4206
+ subscribeWithCCBill,
4207
+ generateFlexFormUrl
4208
+ };
4209
+ };
4210
+ var SubscriptionCheckoutModal = ({
4211
+ open,
4212
+ onOpenChange,
4213
+ priceId,
4214
+ usdAmount = 0,
4215
+ planName,
4216
+ amountLabel,
4217
+ billingPeriodLabel,
4218
+ userEmail,
4219
+ provider = "mobius",
4220
+ onSuccess,
4221
+ enableSolanaPay = true
4222
+ }) => {
4223
+ const [showSuccess, setShowSuccess] = React3.useState(false);
4224
+ const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
4225
+ const handleClose = React3.useCallback(
4226
+ (nextOpen) => {
4227
+ onOpenChange(nextOpen);
4228
+ if (!nextOpen) {
4229
+ setShowSuccess(false);
4230
+ }
4231
+ },
4232
+ [onOpenChange]
4233
+ );
4234
+ const ensurePrice = () => {
4235
+ if (!priceId) {
4236
+ throw new Error("Select a plan before subscribing.");
4237
+ }
4238
+ return priceId;
4239
+ };
4240
+ const notifySuccess = (result) => {
4241
+ setShowSuccess(true);
4242
+ onSuccess?.();
4243
+ if (result && typeof window !== "undefined") {
4244
+ console.debug("[payments-ui] subscription success", result);
4245
+ }
4246
+ };
4247
+ const handleNewCardPayment = async ({ token, billing }) => {
4248
+ await subscribeWithCard({
4249
+ priceId: ensurePrice(),
4250
+ provider,
4251
+ paymentToken: token,
4252
+ billing
4253
+ });
4254
+ notifySuccess();
4255
+ };
4256
+ const handleSavedMethodPayment = async ({ paymentMethodId }) => {
4257
+ await subscribeWithSavedMethod({
4258
+ priceId: ensurePrice(),
4259
+ provider,
4260
+ paymentMethodId,
4261
+ email: userEmail ?? ""
4262
+ });
4263
+ notifySuccess();
4264
+ };
4265
+ const solanaSuccess = (result) => {
4266
+ notifySuccess(result);
4267
+ onOpenChange(false);
4268
+ };
4269
+ const summary = React3.useMemo(() => {
4270
+ if (!planName && !amountLabel) return null;
4271
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-border/60 bg-muted/10 p-3 text-sm text-muted-foreground", children: [
4272
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-foreground", children: planName ?? "Selected plan" }),
4273
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
4274
+ amountLabel ?? `$${usdAmount.toFixed(2)}`,
4275
+ " ",
4276
+ billingPeriodLabel ? `/ ${billingPeriodLabel}` : ""
4277
+ ] })
4278
+ ] });
4279
+ }, [planName, amountLabel, billingPeriodLabel, usdAmount]);
4280
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4281
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "sm:max-w-3xl", children: [
4282
+ !priceId && /* @__PURE__ */ jsxRuntime.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: [
4283
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-4 w-4" }),
4284
+ " Select a subscription plan to continue."
4285
+ ] }),
4286
+ /* @__PURE__ */ jsxRuntime.jsx(
4287
+ PaymentExperience,
4288
+ {
4289
+ priceId: priceId ?? "",
4290
+ usdAmount,
4291
+ checkoutSummary: summary,
4292
+ onNewCardPayment: priceId ? handleNewCardPayment : void 0,
4293
+ onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
4294
+ enableNewCard: Boolean(priceId),
4295
+ enableStoredMethods: Boolean(priceId),
4296
+ enableSolanaPay: enableSolanaPay && Boolean(priceId),
4297
+ onSolanaSuccess: solanaSuccess
4298
+ }
4299
+ )
4300
+ ] }) }),
4301
+ /* @__PURE__ */ jsxRuntime.jsx(
4302
+ SubscriptionSuccessDialog,
4303
+ {
4304
+ open: showSuccess,
4305
+ onClose: () => setShowSuccess(false),
4306
+ planName,
4307
+ amountLabel: amountLabel ?? `$${usdAmount.toFixed(2)}`,
4308
+ billingPeriodLabel
4309
+ }
4310
+ )
4311
+ ] });
4312
+ };
4313
+ var useTokenBalance = (tokens) => {
4314
+ const { publicKey } = walletAdapterReact.useWallet();
4315
+ const { connection } = walletAdapterReact.useConnection();
4316
+ const [balances, setBalances] = React3.useState([]);
4317
+ const [isLoading, setIsLoading] = React3.useState(false);
4318
+ const [error, setError] = React3.useState(null);
4319
+ const fetchTokenBalance = React3.useCallback(
4320
+ async (token, walletAddress) => {
4321
+ try {
4322
+ const mintPublicKey = new web3_js.PublicKey(token.mint);
4323
+ const associatedTokenAddress = await splToken.getAssociatedTokenAddress(
4324
+ mintPublicKey,
4325
+ walletAddress
4326
+ );
4327
+ try {
4328
+ const tokenAccount = await splToken.getAccount(
4329
+ connection,
4330
+ associatedTokenAddress
4331
+ );
4332
+ const balance = Number(tokenAccount.amount);
4333
+ const uiAmount = balance / Math.pow(10, token.decimals);
4334
+ return {
4335
+ token,
4336
+ balance,
4337
+ uiAmount,
4338
+ hasBalance: balance > 0
4339
+ };
4340
+ } catch (accountError) {
4341
+ return {
4342
+ token,
4343
+ balance: 0,
4344
+ uiAmount: 0,
4345
+ hasBalance: false
4346
+ };
4347
+ }
4348
+ } catch (error2) {
4349
+ console.error(`Failed to fetch balance for ${token.symbol}:`, error2);
4350
+ return {
4351
+ token,
4352
+ balance: 0,
4353
+ uiAmount: 0,
4354
+ hasBalance: false
4355
+ };
4356
+ }
4357
+ },
4358
+ [connection]
4359
+ );
4360
+ const tokensKey = React3.useMemo(() => tokens.map((t) => t.mint).join(","), [tokens]);
4361
+ React3.useEffect(() => {
4362
+ if (!publicKey || tokens.length === 0) {
4363
+ setBalances([]);
4364
+ return;
4365
+ }
4366
+ setIsLoading(true);
4367
+ setError(null);
4368
+ const fetchAllBalances = async () => {
4369
+ try {
4370
+ const balancePromises = tokens.map(
4371
+ (token) => fetchTokenBalance(token, publicKey)
4372
+ );
4373
+ const tokenBalances = await Promise.all(balancePromises);
4374
+ setBalances(tokenBalances);
4375
+ } catch (error2) {
4376
+ const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch token balances";
4377
+ setError(errorMessage);
4378
+ console.error("Failed to fetch token balances:", error2);
4379
+ } finally {
4380
+ setIsLoading(false);
4381
+ }
4382
+ };
4383
+ fetchAllBalances();
4384
+ }, [publicKey, tokensKey, fetchTokenBalance]);
4385
+ const getTokenBalance = React3.useCallback(
4386
+ (tokenSymbol) => {
4387
+ return balances.find((balance) => balance.token.symbol === tokenSymbol);
4388
+ },
4389
+ [balances]
4390
+ );
4391
+ const hasSufficientBalance2 = React3.useCallback(
4392
+ (tokenSymbol, requiredAmount) => {
4393
+ const balance = getTokenBalance(tokenSymbol);
4394
+ return balance ? balance.uiAmount >= requiredAmount : false;
4395
+ },
4396
+ [getTokenBalance]
4397
+ );
4398
+ const getFormattedBalance = React3.useCallback(
4399
+ (tokenSymbol) => {
4400
+ const balance = getTokenBalance(tokenSymbol);
4401
+ if (!balance) return "0.00";
4402
+ if (balance.uiAmount < 0.01) {
4403
+ return balance.uiAmount.toFixed(6);
4404
+ } else if (balance.uiAmount < 1) {
4405
+ return balance.uiAmount.toFixed(4);
4406
+ } else {
4407
+ return balance.uiAmount.toFixed(2);
4408
+ }
4409
+ },
4410
+ [getTokenBalance]
4411
+ );
4412
+ const refreshBalances = React3.useCallback(async () => {
4413
+ if (!publicKey || tokens.length === 0) {
4414
+ setBalances([]);
4415
+ return;
4416
+ }
4417
+ setIsLoading(true);
4418
+ setError(null);
4419
+ try {
4420
+ const balancePromises = tokens.map(
4421
+ (token) => fetchTokenBalance(token, publicKey)
4422
+ );
4423
+ const tokenBalances = await Promise.all(balancePromises);
4424
+ setBalances(tokenBalances);
4425
+ } catch (error2) {
4426
+ const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch token balances";
4427
+ setError(errorMessage);
4428
+ console.error("Failed to fetch token balances:", error2);
4429
+ } finally {
4430
+ setIsLoading(false);
4431
+ }
4432
+ }, [publicKey, tokens, fetchTokenBalance]);
4433
+ const getTotalValue = React3.useCallback(
4434
+ (priceData) => {
4435
+ if (!priceData) return 0;
4436
+ return balances.reduce((total, balance) => {
4437
+ const price = priceData[balance.token.symbol] || 0;
4438
+ return total + balance.uiAmount * price;
4439
+ }, 0);
4440
+ },
4441
+ [balances]
4442
+ );
4443
+ const sortedBalances = [...balances].sort((a, b) => b.uiAmount - a.uiAmount);
4444
+ const positiveBalances = balances.filter((balance) => balance.hasBalance);
4445
+ return {
4446
+ balances: sortedBalances,
4447
+ positiveBalances,
4448
+ isLoading,
4449
+ error,
4450
+ refreshBalances,
4451
+ getTokenBalance,
4452
+ hasSufficientBalance: hasSufficientBalance2,
4453
+ getFormattedBalance,
4454
+ getTotalValue,
4455
+ hasAnyBalance: positiveBalances.length > 0,
4456
+ isConnected: !!publicKey
4457
+ };
4458
+ };
4459
+ var useDirectWalletPayment = () => {
4460
+ const { publicKey, signTransaction, connected } = walletAdapterReact.useWallet();
4461
+ const solanaService = useSolanaService();
2837
4462
  const [paymentState, setPaymentState] = React3.useState({
2838
4463
  loading: false,
2839
4464
  error: null,
@@ -3176,128 +4801,60 @@ var usePaymentStatus = (options = {}) => {
3176
4801
  isPending: getConfirmationStatus() === "pending"
3177
4802
  };
3178
4803
  };
3179
- var useSubscriptionActions = () => {
3180
- const { services } = usePaymentContext();
3181
- const ensurePrice = (priceId) => {
3182
- if (!priceId) {
3183
- throw new Error("payments-ui: priceId is required for subscription actions");
3184
- }
3185
- return priceId;
3186
- };
3187
- const subscribeWithCard = React3.useCallback(
3188
- async ({
3189
- priceId,
3190
- processor = "nmi",
3191
- provider,
3192
- paymentToken,
3193
- billing
3194
- }) => {
3195
- const payload = {
3196
- priceId: ensurePrice(priceId),
3197
- paymentToken,
3198
- processor,
3199
- provider,
3200
- email: billing.email,
3201
- firstName: billing.firstName,
3202
- lastName: billing.lastName,
3203
- address1: billing.address1,
3204
- city: billing.city,
3205
- state: billing.stateRegion,
3206
- zipCode: billing.postalCode,
3207
- country: billing.country
3208
- };
3209
- return services.subscriptions.subscribe("nmi", payload);
3210
- },
3211
- [services]
3212
- );
3213
- const subscribeWithSavedMethod = React3.useCallback(
3214
- async ({
3215
- priceId,
3216
- processor = "nmi",
3217
- provider,
3218
- paymentMethodId,
3219
- email
3220
- }) => {
3221
- const payload = {
3222
- priceId: ensurePrice(priceId),
3223
- paymentMethodId,
3224
- processor,
3225
- provider,
3226
- email
3227
- };
3228
- return services.subscriptions.subscribe("nmi", payload);
3229
- },
3230
- [services]
3231
- );
3232
- const subscribeWithCCBill = React3.useCallback(
3233
- async ({
3234
- priceId,
3235
- email,
3236
- firstName,
3237
- lastName,
3238
- zipCode,
3239
- country,
3240
- processor = "ccbill"
3241
- }) => {
3242
- const payload = {
3243
- priceId: ensurePrice(priceId),
3244
- email,
3245
- firstName,
3246
- lastName,
3247
- zipCode,
3248
- country,
3249
- processor
3250
- };
3251
- return services.subscriptions.subscribe("ccbill", payload);
3252
- },
3253
- [services]
3254
- );
3255
- const generateFlexFormUrl = React3.useCallback(
3256
- async ({
3257
- priceId,
3258
- firstName,
3259
- lastName,
3260
- address1,
3261
- city,
3262
- state,
3263
- zipCode,
3264
- country
3265
- }) => {
3266
- const payload = {
3267
- price_id: ensurePrice(priceId),
3268
- first_name: firstName,
3269
- last_name: lastName,
3270
- address1,
3271
- city,
3272
- state,
3273
- zip_code: zipCode,
3274
- country
3275
- };
3276
- return services.subscriptions.generateFlexFormUrl(payload);
4804
+ var useAlternativePaymentProvider = () => {
4805
+ const [isLoading, setIsLoading] = React3.useState(false);
4806
+ const [error, setError] = React3.useState(null);
4807
+ const { generateFlexFormUrl } = useSubscriptionActions();
4808
+ const openFlexForm = React3.useCallback(
4809
+ async (payload) => {
4810
+ setIsLoading(true);
4811
+ setError(null);
4812
+ try {
4813
+ const response = await generateFlexFormUrl(payload);
4814
+ if (response?.iframe_url) {
4815
+ window.location.href = response.iframe_url;
4816
+ } else {
4817
+ throw new Error("Unable to launch payment provider.");
4818
+ }
4819
+ } catch (err) {
4820
+ const message = err instanceof Error ? err.message : "Failed to open payment provider.";
4821
+ setError(message);
4822
+ console.error("[payments-ui] failed to open alternative payment provider", err);
4823
+ } finally {
4824
+ setIsLoading(false);
4825
+ }
3277
4826
  },
3278
- [services]
4827
+ [generateFlexFormUrl]
3279
4828
  );
3280
- return {
3281
- subscribeWithCard,
3282
- subscribeWithSavedMethod,
3283
- subscribeWithCCBill,
3284
- generateFlexFormUrl
3285
- };
4829
+ return { openFlexForm, isLoading, error };
3286
4830
  };
3287
4831
 
4832
+ exports.BillingHistory = BillingHistory;
4833
+ exports.CancelMembershipDialog = CancelMembershipDialog;
3288
4834
  exports.CardDetailsForm = CardDetailsForm;
3289
4835
  exports.CardPaymentService = CardPaymentService;
4836
+ exports.EmptyWalletState = EmptyWalletState;
3290
4837
  exports.PaymentApp = PaymentApp;
3291
4838
  exports.PaymentExperience = PaymentExperience;
3292
4839
  exports.PaymentMethodService = PaymentMethodService;
4840
+ exports.PaymentMethodsSection = PaymentMethodsSection;
3293
4841
  exports.PaymentProvider = PaymentProvider;
3294
4842
  exports.SolanaPaymentSelector = SolanaPaymentSelector;
3295
4843
  exports.SolanaPaymentService = SolanaPaymentService;
4844
+ exports.SolanaWalletSection = SolanaWalletSection;
4845
+ exports.SolanaWalletService = SolanaWalletService;
3296
4846
  exports.StoredPaymentMethods = StoredPaymentMethods;
4847
+ exports.SubscriptionCheckoutModal = SubscriptionCheckoutModal;
3297
4848
  exports.SubscriptionService = SubscriptionService;
4849
+ exports.SubscriptionSuccessDialog = SubscriptionSuccessDialog;
3298
4850
  exports.TokenCatalog = TokenCatalog;
4851
+ exports.WalletCard = WalletCard;
4852
+ exports.WalletDialog = WalletDialog;
3299
4853
  exports.WalletGateway = WalletGateway;
4854
+ exports.WalletManagement = WalletManagement;
4855
+ exports.WalletModal = WalletModal;
3300
4856
  exports.createPaymentStore = createPaymentStore;
4857
+ exports.useAlternativePaymentProvider = useAlternativePaymentProvider;
3301
4858
  exports.useDirectWalletPayment = useDirectWalletPayment;
3302
4859
  exports.usePaymentContext = usePaymentContext;
3303
4860
  exports.usePaymentMethodService = usePaymentMethodService;
@@ -3310,5 +4867,8 @@ exports.useSolanaService = useSolanaService;
3310
4867
  exports.useSubscriptionActions = useSubscriptionActions;
3311
4868
  exports.useSupportedTokens = useSupportedTokens;
3312
4869
  exports.useTokenBalance = useTokenBalance;
4870
+ exports.useWalletConnection = useWalletConnection;
4871
+ exports.useWalletList = useWalletList;
4872
+ exports.useWalletVerification = useWalletVerification;
3313
4873
  //# sourceMappingURL=index.cjs.map
3314
4874
  //# sourceMappingURL=index.cjs.map