@b3dotfun/sdk 0.0.10 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +1 -1
  2. package/dist/cjs/anyspend/react/components/common/PaymentStripeWeb2.js +62 -25
  3. package/dist/cjs/anyspend/react/components/webview/WebviewOnrampPayment.js +29 -6
  4. package/dist/cjs/anyspend/react/providers/AnyspendProvider.d.ts +1 -0
  5. package/dist/cjs/anyspend/react/providers/AnyspendProvider.js +3 -1
  6. package/dist/cjs/anyspend/react/providers/StripeRedirectHandler.d.ts +8 -0
  7. package/dist/cjs/anyspend/react/providers/StripeRedirectHandler.js +41 -0
  8. package/dist/cjs/global-account/react/components/B3DynamicModal.js +2 -2
  9. package/dist/cjs/global-account/react/components/ui/dialog.js +1 -1
  10. package/dist/cjs/global-account/react/hooks/index.d.ts +1 -0
  11. package/dist/cjs/global-account/react/hooks/index.js +4 -1
  12. package/dist/cjs/global-account/react/hooks/useAccountWallet.js +0 -4
  13. package/dist/cjs/global-account/react/hooks/useProfile.d.ts +38 -0
  14. package/dist/cjs/global-account/react/hooks/useProfile.js +72 -0
  15. package/dist/esm/anyspend/react/components/AnySpendCustom.js +1 -1
  16. package/dist/esm/anyspend/react/components/common/PaymentStripeWeb2.js +64 -27
  17. package/dist/esm/anyspend/react/components/webview/WebviewOnrampPayment.js +30 -7
  18. package/dist/esm/anyspend/react/providers/AnyspendProvider.d.ts +1 -0
  19. package/dist/esm/anyspend/react/providers/AnyspendProvider.js +4 -2
  20. package/dist/esm/anyspend/react/providers/StripeRedirectHandler.d.ts +8 -0
  21. package/dist/esm/anyspend/react/providers/StripeRedirectHandler.js +38 -0
  22. package/dist/esm/global-account/react/components/B3DynamicModal.js +2 -2
  23. package/dist/esm/global-account/react/components/ui/dialog.js +1 -1
  24. package/dist/esm/global-account/react/hooks/index.d.ts +1 -0
  25. package/dist/esm/global-account/react/hooks/index.js +1 -0
  26. package/dist/esm/global-account/react/hooks/useAccountWallet.js +0 -4
  27. package/dist/esm/global-account/react/hooks/useProfile.d.ts +38 -0
  28. package/dist/esm/global-account/react/hooks/useProfile.js +68 -0
  29. package/dist/styles/index.css +1 -1
  30. package/dist/types/anyspend/react/providers/AnyspendProvider.d.ts +1 -0
  31. package/dist/types/anyspend/react/providers/StripeRedirectHandler.d.ts +8 -0
  32. package/dist/types/global-account/react/hooks/index.d.ts +1 -0
  33. package/dist/types/global-account/react/hooks/useProfile.d.ts +38 -0
  34. package/package.json +24 -23
  35. package/src/anyspend/react/components/AnySpendCustom.tsx +1 -1
  36. package/src/anyspend/react/components/common/PaymentStripeWeb2.tsx +86 -35
  37. package/src/anyspend/react/components/webview/WebviewOnrampPayment.tsx +39 -7
  38. package/src/anyspend/react/providers/AnyspendProvider.tsx +6 -1
  39. package/src/anyspend/react/providers/StripeRedirectHandler.tsx +45 -0
  40. package/src/global-account/react/components/B3DynamicModal.tsx +2 -2
  41. package/src/global-account/react/components/ui/dialog.tsx +3 -4
  42. package/src/global-account/react/components/ui/drawer.tsx +0 -2
  43. package/src/global-account/react/hooks/index.ts +7 -0
  44. package/src/global-account/react/hooks/useAccountWallet.tsx +0 -5
  45. package/src/global-account/react/hooks/useProfile.ts +141 -0
  46. package/src/styles/index.css +1 -1
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { USDC_BASE } from "../../../../anyspend/index.js";
3
- import { useStripeClientSecret } from "../../../../anyspend/react/index.js";
4
3
  import { STRIPE_CONFIG } from "../../../../anyspend/constants/index.js";
5
- import { ShinyButton, useB3 } from "../../../../global-account/react/index.js";
4
+ import { useStripeClientSecret } from "../../../../anyspend/react/index.js";
5
+ import { ShinyButton, useB3, useModalStore } from "../../../../global-account/react/index.js";
6
6
  import { formatStripeAmount } from "../../../../shared/utils/payment.utils.js";
7
- import { Elements, PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";
7
+ import { AddressElement, Elements, PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";
8
8
  import { loadStripe } from "@stripe/stripe-js";
9
9
  import { HelpCircle, Info, X } from "lucide-react";
10
10
  import { useEffect, useState } from "react";
@@ -34,11 +34,13 @@ function StripeErrorState({ error }) {
34
34
  function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
35
35
  const stripe = useStripe();
36
36
  const elements = useElements();
37
+ const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
37
38
  const [loading, setLoading] = useState(false);
38
39
  const [message, setMessage] = useState(null);
39
40
  const [amount, setAmount] = useState(null);
40
41
  const [stripeReady, setStripeReady] = useState(false);
41
42
  const [showHowItWorks, setShowHowItWorks] = useState(false);
43
+ const [showAddressElement, setShowAddressElement] = useState(false);
42
44
  useEffect(() => {
43
45
  if (stripe && elements) {
44
46
  setStripeReady(true);
@@ -62,6 +64,12 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
62
64
  };
63
65
  fetchPaymentIntent();
64
66
  }, [clientSecret, stripe]);
67
+ // Handle payment element changes
68
+ const handlePaymentElementChange = (event) => {
69
+ // Show address element only for card payments
70
+ console.log("@@stripe-web2-payment:payment-element-change:", JSON.stringify(event, null, 2));
71
+ setShowAddressElement(event.value.type === "card");
72
+ };
65
73
  const handleSubmit = async (e) => {
66
74
  e.preventDefault();
67
75
  if (!stripe || !elements) {
@@ -72,25 +80,29 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
72
80
  setMessage(null);
73
81
  try {
74
82
  console.log("@@stripe-web2-payment:confirming-payment:", JSON.stringify({ orderId: order.id }, null, 2));
75
- const { error, paymentIntent } = await stripe.confirmPayment({
83
+ const result = (await stripe.confirmPayment({
76
84
  elements,
77
- redirect: "if_required",
78
- });
79
- if (error) {
80
- console.error("@@stripe-web2-payment:error:", JSON.stringify(error, null, 2));
81
- setMessage(error.message || "An unexpected error occurred.");
82
- }
83
- else if (paymentIntent && paymentIntent.status === "succeeded") {
84
- console.log("@@stripe-web2-payment:success:", JSON.stringify({ orderId: order.id, paymentIntentId: paymentIntent.id }, null, 2));
85
- // Payment succeeded without redirect - handle success in the modal
86
- setMessage(null);
87
- // Add waitingForDeposit=true to query params
88
- const currentUrl = new URL(window.location.href);
89
- currentUrl.searchParams.set("waitingForDeposit", "true");
90
- window.history.replaceState(null, "", currentUrl.toString());
91
- // Call the success callback if provided
92
- onPaymentSuccess?.(paymentIntent);
85
+ confirmParams: {
86
+ return_url: `${window.location.origin}/?orderId=${order.id}&waitingForDeposit=true&fromStripe=true`,
87
+ },
88
+ }));
89
+ if (result.error) {
90
+ // This point will only be reached if there is an immediate error.
91
+ // Otherwise, the customer will be redirected to the `return_url`.
92
+ console.error("@@stripe-web2-payment:error:", JSON.stringify(result.error, null, 2));
93
+ setMessage(result.error.message || "An unexpected error occurred.");
94
+ return;
93
95
  }
96
+ // At this point TypeScript knows result.paymentIntent exists and error is undefined
97
+ console.log("@@stripe-web2-payment:success:", JSON.stringify({ orderId: order.id, paymentIntentId: result.paymentIntent.id }, null, 2));
98
+ // Payment succeeded without redirect - handle success in the modal
99
+ setMessage(null);
100
+ // Add waitingForDeposit=true to query params
101
+ const currentUrl = new URL(window.location.href);
102
+ currentUrl.searchParams.set("waitingForDeposit", "true");
103
+ window.history.replaceState(null, "", currentUrl.toString());
104
+ // Call the success callback if provided
105
+ onPaymentSuccess?.(result.paymentIntent);
94
106
  }
95
107
  catch (error) {
96
108
  console.error("@@stripe-web2-payment:confirmation-error:", JSON.stringify(error, null, 2));
@@ -100,17 +112,27 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
100
112
  setLoading(false);
101
113
  }
102
114
  };
115
+ // Handle 3DS redirect
116
+ useEffect(() => {
117
+ // Check if we're returning from Stripe
118
+ const url = new URL(window.location.href);
119
+ const fromStripe = url.searchParams.get("fromStripe");
120
+ const paymentIntent = url.searchParams.get("payment_intent");
121
+ console.log("@@stripe-web2-payment:fromStripe:", fromStripe);
122
+ console.log("@@stripe-web2-payment:paymentIntent:", paymentIntent);
123
+ if (fromStripe && paymentIntent) {
124
+ // Close the modal as we're returning from 3DS
125
+ setB3ModalOpen(true);
126
+ // Clean up URL params
127
+ url.searchParams.delete("fromStripe");
128
+ window.history.replaceState({}, "", url.toString());
129
+ }
130
+ }, [setB3ModalOpen]);
103
131
  if (!stripeReady) {
104
132
  return _jsx(StripeLoadingState, {});
105
133
  }
106
134
  const stripeElementOptions = {
107
135
  layout: "tabs",
108
- defaultValues: {
109
- billingDetails: {
110
- name: "",
111
- email: "",
112
- },
113
- },
114
136
  fields: {
115
137
  billingDetails: "auto",
116
138
  },
@@ -138,7 +160,22 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
138
160
  const finalAmount = Number(amount);
139
161
  const calculatedFee = finalAmount - originalAmount;
140
162
  return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between text-sm", children: [_jsx("span", { className: "text-as-primary/60", children: "Amount" }), _jsxs("span", { className: "text-as-primary", children: ["$", originalAmount.toFixed(2)] })] }), _jsxs("div", { className: "flex items-center justify-between text-sm", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "text-as-primary/60", children: "Processing fee" }), _jsx(Tooltip, { content: `Credit card companies charge a processing fee of 5.4% + $0.30 for all transactions.\n\nThis fee covers secure payment processing and fraud protection.`, children: _jsx(Info, { className: "text-as-primary/40 hover:text-as-primary/60 h-3 w-3 transition-colors" }) })] }), _jsxs("span", { className: "text-as-primary", children: ["$", calculatedFee.toFixed(2)] })] }), _jsx("div", { className: "border-as-stroke border-t pt-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-as-primary font-semibold", children: "Total Amount" }), _jsxs("span", { className: "text-as-primary text-2xl font-bold", children: ["$", finalAmount.toFixed(2), " USD"] })] }) })] }));
141
- })()] }))] }), _jsxs("div", { className: "bg-as-on-surface-1 w-full rounded-2xl p-6", children: [_jsx("div", { className: "text-as-primary mb-4 text-lg font-semibold", children: "Payment Details" }), _jsx(PaymentElement, { options: stripeElementOptions })] }), message && (_jsxs("div", { className: "bg-as-red/10 border-as-red/20 flex w-full items-center gap-3 rounded-2xl border p-4", children: [_jsx("div", { className: "bg-as-red flex h-6 w-6 shrink-0 items-center justify-center rounded-full", children: _jsx("svg", { className: "h-4 w-4 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), _jsx("div", { className: "text-as-red text-sm font-medium", children: message })] })), _jsx(ShinyButton, { type: "submit", accentColor: "hsl(var(--as-brand))", disabled: !stripe || !elements || loading, className: "relative w-full py-4 text-lg font-semibold", children: loading ? (_jsxs("div", { className: "flex items-center justify-center gap-3", children: [_jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-current border-t-transparent" }), _jsx("span", { className: "text-white", children: "Processing Payment..." })] })) : (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx("span", { className: "text-white", children: "Complete Payment" }), amount && _jsxs("span", { className: "text-white/90", children: ["$", Number(amount).toFixed(2)] })] })) })] }), showHowItWorks && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: _jsxs("div", { className: "bg-as-on-surface-1 relative max-h-[80vh] w-full max-w-2xl overflow-y-auto rounded-2xl p-6", children: [_jsxs("div", { className: "mb-6 flex items-center justify-between", children: [_jsx("h2", { className: "text-as-primary text-xl font-semibold", children: "How it works" }), _jsx("button", { onClick: () => setShowHowItWorks(false), className: "text-as-primary/60 hover:text-as-primary transition-colors", children: _jsx(X, { className: "h-6 w-6" }) })] }), _jsxs("div", { className: "space-y-6", children: [_jsx(PaymentMethodIcons, {}), _jsx(HowItWorks, { steps: howItWorksSteps })] })] }) }))] }));
163
+ })()] }))] }), _jsxs("div", { className: "bg-as-on-surface-1 w-full rounded-2xl p-6", children: [_jsx("div", { className: "text-as-primary mb-4 text-lg font-semibold", children: "Payment Details" }), _jsx(PaymentElement, { onChange: handlePaymentElementChange, options: stripeElementOptions }), showAddressElement && (_jsx(AddressElement, { options: {
164
+ mode: "billing",
165
+ fields: {
166
+ phone: "always",
167
+ },
168
+ // More granular control
169
+ display: {
170
+ name: "split", // or 'split' for first/last name separately
171
+ },
172
+ // Validation
173
+ validation: {
174
+ phone: {
175
+ required: "auto", // or 'always', 'never'
176
+ },
177
+ },
178
+ } }))] }), message && (_jsxs("div", { className: "bg-as-red/10 border-as-red/20 flex w-full items-center gap-3 rounded-2xl border p-4", children: [_jsx("div", { className: "bg-as-red flex h-6 w-6 shrink-0 items-center justify-center rounded-full", children: _jsx("svg", { className: "h-4 w-4 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), _jsx("div", { className: "text-as-red text-sm font-medium", children: message })] })), _jsx(ShinyButton, { type: "submit", accentColor: "hsl(var(--as-brand))", disabled: !stripe || !elements || loading, className: "relative w-full py-4 text-lg font-semibold", children: loading ? (_jsxs("div", { className: "flex items-center justify-center gap-3", children: [_jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-current border-t-transparent" }), _jsx("span", { className: "text-white", children: "Processing Payment..." })] })) : (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx("span", { className: "text-white", children: "Complete Payment" }), amount && _jsxs("span", { className: "text-white/90", children: ["$", Number(amount).toFixed(2)] })] })) })] }), showHowItWorks && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: _jsxs("div", { className: "bg-as-on-surface-1 relative max-h-[80vh] w-full max-w-2xl overflow-y-auto rounded-2xl p-6", children: [_jsxs("div", { className: "mb-6 flex items-center justify-between", children: [_jsx("h2", { className: "text-as-primary text-xl font-semibold", children: "How it works" }), _jsx("button", { onClick: () => setShowHowItWorks(false), className: "text-as-primary/60 hover:text-as-primary transition-colors", children: _jsx(X, { className: "h-6 w-6" }) })] }), _jsxs("div", { className: "space-y-6", children: [_jsx(PaymentMethodIcons, {}), _jsx(HowItWorks, { steps: howItWorksSteps })] })] }) }))] }));
142
179
  }
143
180
  // Add tooltip component
144
181
  function Tooltip({ children, content }) {
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { getChainName, STRIPE_CONFIG } from "../../../../anyspend/index.js";
3
3
  import { useAnyspendCreateOnrampOrder, useGeoOnrampOptions, useStripeClientSecret } from "../../../../anyspend/react/index.js";
4
4
  import centerTruncate from "../../../../shared/utils/centerTruncate.js";
5
- import { Elements, PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";
5
+ import { AddressElement, Elements, PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";
6
6
  import { loadStripe } from "@stripe/stripe-js";
7
7
  import { motion } from "framer-motion";
8
8
  import { Loader2 } from "lucide-react";
@@ -16,6 +16,7 @@ function StripePaymentForm({ order, onPaymentSuccess, }) {
16
16
  const elements = useElements();
17
17
  const [isProcessing, setIsProcessing] = useState(false);
18
18
  const [error, setError] = useState(null);
19
+ const [showAddressElement, setShowAddressElement] = useState(false);
19
20
  const handleSubmit = async (e) => {
20
21
  e.preventDefault();
21
22
  if (!stripe || !elements) {
@@ -45,16 +46,38 @@ function StripePaymentForm({ order, onPaymentSuccess, }) {
45
46
  setIsProcessing(false);
46
47
  }
47
48
  };
49
+ // Handle payment element changes
50
+ const handlePaymentElementChange = (event) => {
51
+ // Show address element only for card payments
52
+ console.log("@@stripe-web2-payment:payment-element-change:", JSON.stringify(event, null, 2));
53
+ setShowAddressElement(event.value.type === "card");
54
+ };
48
55
  const stripeElementOptions = {
49
56
  layout: "tabs",
50
- defaultValues: {
51
- billingDetails: {
52
- name: "",
53
- email: "",
54
- },
57
+ fields: {
58
+ billingDetails: "auto",
59
+ },
60
+ wallets: {
61
+ applePay: "auto",
62
+ googlePay: "auto",
55
63
  },
56
64
  };
57
- return (_jsx("form", { onSubmit: handleSubmit, className: "w-full", children: _jsx("div", { className: "overflow-hidden rounded-xl bg-white", children: _jsxs("div", { className: "px-6 py-4", children: [_jsx("h2", { className: "mb-4 text-lg font-semibold", children: "Payment Details" }), _jsx(PaymentElement, { options: stripeElementOptions }), error && (_jsx("div", { className: "mt-4 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-600", children: error })), _jsx("button", { type: "submit", disabled: !stripe || isProcessing, className: "mt-6 w-full rounded-xl bg-blue-600 px-4 py-3 font-medium text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50", children: isProcessing ? (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx(Loader2, { className: "h-5 w-5 animate-spin" }), _jsx("span", { children: "Processing..." })] })) : (_jsx("span", { children: "Complete Payment" })) })] }) }) }));
65
+ return (_jsx("form", { onSubmit: handleSubmit, className: "w-full", children: _jsx("div", { className: "overflow-hidden rounded-xl bg-white", children: _jsxs("div", { className: "px-6 py-4", children: [_jsx("h2", { className: "mb-4 text-lg font-semibold", children: "Payment Details" }), _jsx(PaymentElement, { options: stripeElementOptions, onChange: handlePaymentElementChange }), showAddressElement && (_jsx("div", { className: "mt-4", children: _jsx(AddressElement, { options: {
66
+ mode: "billing",
67
+ fields: {
68
+ phone: "always",
69
+ },
70
+ // More granular control
71
+ display: {
72
+ name: "split", // or 'split' for first/last name separately
73
+ },
74
+ // Validation
75
+ validation: {
76
+ phone: {
77
+ required: "auto", // or 'always', 'never'
78
+ },
79
+ },
80
+ } }) })), error && (_jsx("div", { className: "mt-4 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-600", children: error })), _jsx("button", { type: "submit", disabled: !stripe || isProcessing, className: "mt-6 w-full rounded-xl bg-blue-600 px-4 py-3 font-medium text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50", children: isProcessing ? (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx(Loader2, { className: "h-5 w-5 animate-spin" }), _jsx("span", { children: "Processing..." })] })) : (_jsx("span", { children: "Complete Payment" })) })] }) }) }));
58
81
  }
59
82
  export function WebviewOnrampPayment({ srcAmountOnRamp, recipientAddress, destinationToken, anyspendQuote, onPaymentSuccess, userId, partnerId, }) {
60
83
  const [stableAmountForGeo, setStableAmountForGeo] = useState(srcAmountOnRamp);
@@ -11,6 +11,7 @@ interface AnyspendProviderProps {
11
11
  * - Optimized for performance with React.memo
12
12
  * - Safe to use at the application root
13
13
  * - Configures sensible defaults for query caching
14
+ * - Handles Stripe payment redirects and modal state
14
15
  *
15
16
  * @example
16
17
  * ```tsx
@@ -1,8 +1,9 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { TooltipProvider } from "../../../global-account/react/index.js";
4
4
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5
5
  import { useState } from "react";
6
+ import { StripeRedirectHandler } from "./StripeRedirectHandler.js";
6
7
  const defaultQueryClientConfig = {
7
8
  defaultOptions: {
8
9
  queries: {
@@ -21,6 +22,7 @@ const defaultQueryClientConfig = {
21
22
  * - Optimized for performance with React.memo
22
23
  * - Safe to use at the application root
23
24
  * - Configures sensible defaults for query caching
25
+ * - Handles Stripe payment redirects and modal state
24
26
  *
25
27
  * @example
26
28
  * ```tsx
@@ -35,5 +37,5 @@ const defaultQueryClientConfig = {
35
37
  */
36
38
  export const AnyspendProvider = function AnyspendProvider({ children }) {
37
39
  const [queryClient] = useState(() => new QueryClient(defaultQueryClientConfig));
38
- return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(TooltipProvider, { children: children }) }));
40
+ return (_jsx(QueryClientProvider, { client: queryClient, children: _jsxs(TooltipProvider, { children: [_jsx(StripeRedirectHandler, {}), children] }) }));
39
41
  };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Handles Stripe payment redirects by showing the order details modal
3
+ * when returning from Stripe's payment flow.
4
+ *
5
+ * This component should be mounted inside AnyspendProvider to ensure
6
+ * proper modal state management.
7
+ */
8
+ export declare function StripeRedirectHandler(): null;
@@ -0,0 +1,38 @@
1
+ "use client";
2
+ import { useModalStore } from "../../../global-account/react/index.js";
3
+ import { useEffect } from "react";
4
+ /**
5
+ * Handles Stripe payment redirects by showing the order details modal
6
+ * when returning from Stripe's payment flow.
7
+ *
8
+ * This component should be mounted inside AnyspendProvider to ensure
9
+ * proper modal state management.
10
+ */
11
+ export function StripeRedirectHandler() {
12
+ const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
13
+ const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
14
+ useEffect(() => {
15
+ const url = new URL(window.location.href);
16
+ const fromStripe = url.searchParams.get("fromStripe");
17
+ const paymentIntent = url.searchParams.get("payment_intent");
18
+ const orderId = url.searchParams.get("orderId");
19
+ console.log("@@stripe-web2-payment:fromStripe:", fromStripe);
20
+ console.log("@@stripe-web2-payment:paymentIntent:", paymentIntent);
21
+ console.log("@@stripe-web2-payment:orderId:", orderId);
22
+ if (fromStripe && paymentIntent && orderId) {
23
+ // Re-open the modal with the order details
24
+ setB3ModalOpen(true);
25
+ setB3ModalContentType({
26
+ type: "anyspendOrderDetails",
27
+ orderId,
28
+ });
29
+ // Clean up URL params
30
+ url.searchParams.delete("fromStripe");
31
+ url.searchParams.delete("payment_intent");
32
+ url.searchParams.delete("payment_intent_client_secret");
33
+ url.searchParams.delete("redirect_status");
34
+ window.history.replaceState({}, "", url.toString());
35
+ }
36
+ }, [setB3ModalOpen, setB3ModalContentType]);
37
+ return null;
38
+ }
@@ -28,7 +28,7 @@ export function B3DynamicModal() {
28
28
  "anySpendSignatureMint",
29
29
  "anySpendBondKit",
30
30
  ].find(type => contentType?.type === type)) {
31
- contentClass += " max-h-[90dvh] overflow-y-auto no-scrollbar w-full";
31
+ contentClass += " w-full";
32
32
  }
33
33
  if ([
34
34
  "anySpendNft",
@@ -90,5 +90,5 @@ export function B3DynamicModal() {
90
90
  const ModalContent = isMobile ? DrawerContent : DialogContent;
91
91
  const ModalTitle = isMobile ? DrawerTitle : DialogTitle;
92
92
  const ModalDescription = isMobile ? DrawerDescription : DialogDescription;
93
- return (_jsx(ModalComponent, { open: isOpen, onOpenChange: setB3ModalOpen, children: _jsxs(ModalContent, { className: contentClass, hideCloseButton: hideCloseButton, children: [_jsx(ModalTitle, { className: "sr-only hidden", children: contentType?.type || "Modal" }), _jsx(ModalDescription, { className: "sr-only hidden", children: contentType?.type || "Modal Body" }), _jsxs("div", { className: "overflow-auto", children: [history.length > 0 && contentType?.showBackButton && (_jsxs("button", { onClick: navigateBack, className: "b3-modal-back-button mb-4 flex items-center gap-2 transition-colors hover:text-white", children: [_jsx("span", { children: "\u2190" }), _jsx("span", { className: "text-sm", children: "Back" })] })), renderContent()] })] }) }));
93
+ return (_jsx(ModalComponent, { open: isOpen, onOpenChange: setB3ModalOpen, children: _jsxs(ModalContent, { className: contentClass, hideCloseButton: hideCloseButton, children: [_jsx(ModalTitle, { className: "sr-only hidden", children: contentType?.type || "Modal" }), _jsx(ModalDescription, { className: "sr-only hidden", children: contentType?.type || "Modal Body" }), _jsxs("div", { className: "no-scrollbar max-h-[90dvh] overflow-auto sm:max-h-[80dvh]", children: [history.length > 0 && contentType?.showBackButton && (_jsxs("button", { onClick: navigateBack, className: "b3-modal-back-button mb-4 flex items-center gap-2 transition-colors hover:text-white", children: [_jsx("span", { children: "\u2190" }), _jsx("span", { className: "text-sm", children: "Back" })] })), renderContent()] })] }) }));
94
94
  }
@@ -12,7 +12,7 @@ const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(D
12
12
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
13
13
  const DialogContent = React.forwardRef(({ className, children, hideCloseButton = false, closeBtnClassName, ...props }, ref) => {
14
14
  const container = typeof window !== "undefined" ? document.getElementById("b3-root") : null;
15
- return (_jsxs(DialogPortal, { container: container, children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("bg-b3-react-background fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg !outline-none", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-500", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]", "data-[state=closed]:rotate-x-10 data-[state=open]:rotate-x-0", "[perspective:1200px] [transform-style:preserve-3d] sm:rounded-xl", "ease-spring transition-all", className), ...props, children: [children, !hideCloseButton && (_jsxs(DialogPrimitive.Close, { className: cn("data-[state=open]:bg-accent data-[state=open]:text-b3-react-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:text-gray-400", closeBtnClassName), children: [_jsx(X, { className: "h-5 w-5" }), _jsx("span", { className: "sr-only", children: "Close" })] }))] })] }));
15
+ return (_jsxs(DialogPortal, { container: container, children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("bg-b3-react-background fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border p-6 shadow-lg !outline-none", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-500", "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95", "data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]", "[perspective:1200px] [transform-style:preserve-3d] sm:rounded-xl", "transition-all ease-out", className), ...props, children: [children, !hideCloseButton && (_jsxs(DialogPrimitive.Close, { className: cn("data-[state=open]:bg-b3-react-background data-[state=open]:text-b3-react-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:text-gray-400", closeBtnClassName), children: [_jsx(X, { className: "h-5 w-5" }), _jsx("span", { className: "sr-only", children: "Close" })] }))] })] }));
16
16
  });
17
17
  DialogContent.displayName = DialogPrimitive.Content.displayName;
18
18
  const DialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props }));
@@ -20,6 +20,7 @@ export { useMediaQuery } from "./useMediaQuery";
20
20
  export { useNativeBalance, useNativeBalanceFromRPC } from "./useNativeBalance";
21
21
  export { useOnchainName } from "./useOnchainName";
22
22
  export { useOneBalance } from "./useOneBalance";
23
+ export { useProfile, useProfilePreference, type Profile, type CombinedProfile, type PreferenceRequestBody, } from "./useProfile";
23
24
  export { useQueryB3 } from "./useQueryB3";
24
25
  export { useQueryBSMNT } from "./useQueryBSMNT";
25
26
  export { useRemoveSessionKey } from "./useRemoveSessionKey";
@@ -20,6 +20,7 @@ export { useMediaQuery } from "./useMediaQuery.js";
20
20
  export { useNativeBalance, useNativeBalanceFromRPC } from "./useNativeBalance.js";
21
21
  export { useOnchainName } from "./useOnchainName.js";
22
22
  export { useOneBalance } from "./useOneBalance.js";
23
+ export { useProfile, useProfilePreference, } from "./useProfile.js";
23
24
  export { useQueryB3 } from "./useQueryB3.js";
24
25
  export { useQueryBSMNT } from "./useQueryBSMNT.js";
25
26
  export { useRemoveSessionKey } from "./useRemoveSessionKey.js";
@@ -59,9 +59,5 @@ export function useAccountWallet() {
59
59
  smartWalletIcon,
60
60
  walletImage,
61
61
  ]);
62
- useEffect(() => {
63
- console.log(`account`, account);
64
- console.log(`useAccountWallet`, res);
65
- }, [account, res]);
66
62
  return res;
67
63
  }
@@ -0,0 +1,38 @@
1
+ export interface Profile {
2
+ type: string;
3
+ address?: string;
4
+ name?: string;
5
+ avatar?: string | null;
6
+ bio?: string | null;
7
+ displayName?: string | null;
8
+ }
9
+ export interface CombinedProfile {
10
+ name: string | null;
11
+ address: string | null;
12
+ avatar: string | null;
13
+ bio: string | null;
14
+ displayName: string | null;
15
+ profiles: Profile[];
16
+ }
17
+ export interface PreferenceRequestBody {
18
+ key: string;
19
+ preferredType: string;
20
+ signature: string;
21
+ signer: string;
22
+ timestamp: number;
23
+ }
24
+ export declare function useProfile({ address, name, fresh, }: {
25
+ address?: string;
26
+ name?: string;
27
+ fresh?: boolean;
28
+ }, options?: {
29
+ enabled?: boolean;
30
+ refetchInterval?: number;
31
+ staleTime?: number;
32
+ }): import("@tanstack/react-query").UseQueryResult<CombinedProfile, Error>;
33
+ export declare function useProfilePreference(): {
34
+ setPreference: (key: string, preferredType: string, signerAddress: string, signMessage: (message: string) => Promise<string>) => Promise<{
35
+ success: boolean;
36
+ }>;
37
+ };
38
+ export default useProfile;
@@ -0,0 +1,68 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ const PROFILES_API_URL = "https://profiles.b3.fun";
3
+ async function fetchProfile({ address, name, fresh = false, }) {
4
+ if (!address && !name) {
5
+ throw new Error("Either address or name must be provided");
6
+ }
7
+ const params = new URLSearchParams();
8
+ if (address)
9
+ params.append("address", address);
10
+ if (name)
11
+ params.append("name", name);
12
+ if (fresh)
13
+ params.append("fresh", "true");
14
+ const response = await fetch(`${PROFILES_API_URL}?${params.toString()}`);
15
+ if (!response.ok) {
16
+ throw new Error(`Failed to fetch profile: ${response.statusText}`);
17
+ }
18
+ return response.json();
19
+ }
20
+ async function setProfilePreference({ key, preferredType, signature, signer, timestamp, }) {
21
+ const response = await fetch(`${PROFILES_API_URL}/preference`, {
22
+ method: "POST",
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ },
26
+ body: JSON.stringify({
27
+ key,
28
+ preferredType,
29
+ signature,
30
+ signer,
31
+ timestamp,
32
+ }),
33
+ });
34
+ if (!response.ok) {
35
+ throw new Error(`Failed to set preference: ${response.statusText}`);
36
+ }
37
+ return response.json();
38
+ }
39
+ export function useProfile({ address, name, fresh = false, }, options) {
40
+ return useQuery({
41
+ queryKey: ["profile", address || name, fresh],
42
+ queryFn: () => fetchProfile({ address, name, fresh }),
43
+ enabled: (options?.enabled ?? true) && (!!address || !!name),
44
+ refetchInterval: options?.refetchInterval,
45
+ staleTime: options?.staleTime ?? 5 * 60 * 1000, // 5 minutes default
46
+ });
47
+ }
48
+ export function useProfilePreference() {
49
+ const setPreference = async (key, preferredType, signerAddress, signMessage) => {
50
+ const timestamp = Math.floor(Date.now() / 1000);
51
+ const message = `SetProfilePreference:${key}:${preferredType}:${timestamp}`;
52
+ try {
53
+ const signature = await signMessage(message);
54
+ return setProfilePreference({
55
+ key,
56
+ preferredType,
57
+ signature,
58
+ signer: signerAddress,
59
+ timestamp,
60
+ });
61
+ }
62
+ catch (error) {
63
+ throw new Error(`Failed to set profile preference: ${error}`);
64
+ }
65
+ };
66
+ return { setPreference };
67
+ }
68
+ export default useProfile;