@b3dotfun/sdk 0.0.10-alpha.3 → 0.0.10-alpha.5

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 (23) hide show
  1. package/dist/cjs/anyspend/react/components/common/PaymentStripeWeb2.js +62 -25
  2. package/dist/cjs/anyspend/react/components/webview/WebviewOnrampPayment.js +29 -6
  3. package/dist/cjs/anyspend/react/providers/AnyspendProvider.d.ts +1 -0
  4. package/dist/cjs/anyspend/react/providers/AnyspendProvider.js +3 -1
  5. package/dist/cjs/anyspend/react/providers/StripeRedirectHandler.d.ts +8 -0
  6. package/dist/cjs/anyspend/react/providers/StripeRedirectHandler.js +41 -0
  7. package/dist/cjs/global-account/react/components/ui/dialog.js +1 -1
  8. package/dist/esm/anyspend/react/components/common/PaymentStripeWeb2.js +64 -27
  9. package/dist/esm/anyspend/react/components/webview/WebviewOnrampPayment.js +30 -7
  10. package/dist/esm/anyspend/react/providers/AnyspendProvider.d.ts +1 -0
  11. package/dist/esm/anyspend/react/providers/AnyspendProvider.js +4 -2
  12. package/dist/esm/anyspend/react/providers/StripeRedirectHandler.d.ts +8 -0
  13. package/dist/esm/anyspend/react/providers/StripeRedirectHandler.js +38 -0
  14. package/dist/esm/global-account/react/components/ui/dialog.js +1 -1
  15. package/dist/styles/index.css +1 -1
  16. package/dist/types/anyspend/react/providers/AnyspendProvider.d.ts +1 -0
  17. package/dist/types/anyspend/react/providers/StripeRedirectHandler.d.ts +8 -0
  18. package/package.json +1 -1
  19. package/src/anyspend/react/components/common/PaymentStripeWeb2.tsx +86 -35
  20. package/src/anyspend/react/components/webview/WebviewOnrampPayment.tsx +39 -7
  21. package/src/anyspend/react/providers/AnyspendProvider.tsx +6 -1
  22. package/src/anyspend/react/providers/StripeRedirectHandler.tsx +45 -0
  23. package/src/global-account/react/components/ui/dialog.tsx +0 -1
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.default = PaymentStripeWeb2;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const anyspend_1 = require("../../../../anyspend");
9
- const react_1 = require("../../../../anyspend/react");
10
9
  const constants_1 = require("../../../../anyspend/constants");
10
+ const react_1 = require("../../../../anyspend/react");
11
11
  const react_2 = require("../../../../global-account/react");
12
12
  const payment_utils_1 = require("../../../../shared/utils/payment.utils");
13
13
  const react_stripe_js_1 = require("@stripe/react-stripe-js");
@@ -40,11 +40,13 @@ function StripeErrorState({ error }) {
40
40
  function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
41
41
  const stripe = (0, react_stripe_js_1.useStripe)();
42
42
  const elements = (0, react_stripe_js_1.useElements)();
43
+ const setB3ModalOpen = (0, react_2.useModalStore)(state => state.setB3ModalOpen);
43
44
  const [loading, setLoading] = (0, react_3.useState)(false);
44
45
  const [message, setMessage] = (0, react_3.useState)(null);
45
46
  const [amount, setAmount] = (0, react_3.useState)(null);
46
47
  const [stripeReady, setStripeReady] = (0, react_3.useState)(false);
47
48
  const [showHowItWorks, setShowHowItWorks] = (0, react_3.useState)(false);
49
+ const [showAddressElement, setShowAddressElement] = (0, react_3.useState)(false);
48
50
  (0, react_3.useEffect)(() => {
49
51
  if (stripe && elements) {
50
52
  setStripeReady(true);
@@ -68,6 +70,12 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
68
70
  };
69
71
  fetchPaymentIntent();
70
72
  }, [clientSecret, stripe]);
73
+ // Handle payment element changes
74
+ const handlePaymentElementChange = (event) => {
75
+ // Show address element only for card payments
76
+ console.log("@@stripe-web2-payment:payment-element-change:", JSON.stringify(event, null, 2));
77
+ setShowAddressElement(event.value.type === "card");
78
+ };
71
79
  const handleSubmit = async (e) => {
72
80
  e.preventDefault();
73
81
  if (!stripe || !elements) {
@@ -78,25 +86,29 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
78
86
  setMessage(null);
79
87
  try {
80
88
  console.log("@@stripe-web2-payment:confirming-payment:", JSON.stringify({ orderId: order.id }, null, 2));
81
- const { error, paymentIntent } = await stripe.confirmPayment({
89
+ const result = (await stripe.confirmPayment({
82
90
  elements,
83
- redirect: "if_required",
84
- });
85
- if (error) {
86
- console.error("@@stripe-web2-payment:error:", JSON.stringify(error, null, 2));
87
- setMessage(error.message || "An unexpected error occurred.");
88
- }
89
- else if (paymentIntent && paymentIntent.status === "succeeded") {
90
- console.log("@@stripe-web2-payment:success:", JSON.stringify({ orderId: order.id, paymentIntentId: paymentIntent.id }, null, 2));
91
- // Payment succeeded without redirect - handle success in the modal
92
- setMessage(null);
93
- // Add waitingForDeposit=true to query params
94
- const currentUrl = new URL(window.location.href);
95
- currentUrl.searchParams.set("waitingForDeposit", "true");
96
- window.history.replaceState(null, "", currentUrl.toString());
97
- // Call the success callback if provided
98
- onPaymentSuccess?.(paymentIntent);
91
+ confirmParams: {
92
+ return_url: `${window.location.origin}/?orderId=${order.id}&waitingForDeposit=true&fromStripe=true`,
93
+ },
94
+ }));
95
+ if (result.error) {
96
+ // This point will only be reached if there is an immediate error.
97
+ // Otherwise, the customer will be redirected to the `return_url`.
98
+ console.error("@@stripe-web2-payment:error:", JSON.stringify(result.error, null, 2));
99
+ setMessage(result.error.message || "An unexpected error occurred.");
100
+ return;
99
101
  }
102
+ // At this point TypeScript knows result.paymentIntent exists and error is undefined
103
+ console.log("@@stripe-web2-payment:success:", JSON.stringify({ orderId: order.id, paymentIntentId: result.paymentIntent.id }, null, 2));
104
+ // Payment succeeded without redirect - handle success in the modal
105
+ setMessage(null);
106
+ // Add waitingForDeposit=true to query params
107
+ const currentUrl = new URL(window.location.href);
108
+ currentUrl.searchParams.set("waitingForDeposit", "true");
109
+ window.history.replaceState(null, "", currentUrl.toString());
110
+ // Call the success callback if provided
111
+ onPaymentSuccess?.(result.paymentIntent);
100
112
  }
101
113
  catch (error) {
102
114
  console.error("@@stripe-web2-payment:confirmation-error:", JSON.stringify(error, null, 2));
@@ -106,17 +118,27 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
106
118
  setLoading(false);
107
119
  }
108
120
  };
121
+ // Handle 3DS redirect
122
+ (0, react_3.useEffect)(() => {
123
+ // Check if we're returning from Stripe
124
+ const url = new URL(window.location.href);
125
+ const fromStripe = url.searchParams.get("fromStripe");
126
+ const paymentIntent = url.searchParams.get("payment_intent");
127
+ console.log("@@stripe-web2-payment:fromStripe:", fromStripe);
128
+ console.log("@@stripe-web2-payment:paymentIntent:", paymentIntent);
129
+ if (fromStripe && paymentIntent) {
130
+ // Close the modal as we're returning from 3DS
131
+ setB3ModalOpen(true);
132
+ // Clean up URL params
133
+ url.searchParams.delete("fromStripe");
134
+ window.history.replaceState({}, "", url.toString());
135
+ }
136
+ }, [setB3ModalOpen]);
109
137
  if (!stripeReady) {
110
138
  return (0, jsx_runtime_1.jsx)(StripeLoadingState, {});
111
139
  }
112
140
  const stripeElementOptions = {
113
141
  layout: "tabs",
114
- defaultValues: {
115
- billingDetails: {
116
- name: "",
117
- email: "",
118
- },
119
- },
120
142
  fields: {
121
143
  billingDetails: "auto",
122
144
  },
@@ -144,7 +166,22 @@ function StripePaymentForm({ order, clientSecret, onPaymentSuccess, }) {
144
166
  const finalAmount = Number(amount);
145
167
  const calculatedFee = finalAmount - originalAmount;
146
168
  return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between text-sm", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-primary/60", children: "Amount" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-as-primary", children: ["$", originalAmount.toFixed(2)] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between text-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-primary/60", children: "Processing fee" }), (0, jsx_runtime_1.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: (0, jsx_runtime_1.jsx)(lucide_react_1.Info, { className: "text-as-primary/40 hover:text-as-primary/60 h-3 w-3 transition-colors" }) })] }), (0, jsx_runtime_1.jsxs)("span", { className: "text-as-primary", children: ["$", calculatedFee.toFixed(2)] })] }), (0, jsx_runtime_1.jsx)("div", { className: "border-as-stroke border-t pt-2", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-as-primary font-semibold", children: "Total Amount" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-as-primary text-2xl font-bold", children: ["$", finalAmount.toFixed(2), " USD"] })] }) })] }));
147
- })()] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-as-on-surface-1 w-full rounded-2xl p-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary mb-4 text-lg font-semibold", children: "Payment Details" }), (0, jsx_runtime_1.jsx)(react_stripe_js_1.PaymentElement, { options: stripeElementOptions })] }), message && ((0, jsx_runtime_1.jsxs)("div", { className: "bg-as-red/10 border-as-red/20 flex w-full items-center gap-3 rounded-2xl border p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-as-red flex h-6 w-6 shrink-0 items-center justify-center rounded-full", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-4 w-4 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-red text-sm font-medium", children: message })] })), (0, jsx_runtime_1.jsx)(react_2.ShinyButton, { type: "submit", accentColor: "hsl(var(--as-brand))", disabled: !stripe || !elements || loading, className: "relative w-full py-4 text-lg font-semibold", children: loading ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-current border-t-transparent" }), (0, jsx_runtime_1.jsx)("span", { className: "text-white", children: "Processing Payment..." })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-white", children: "Complete Payment" }), amount && (0, jsx_runtime_1.jsxs)("span", { className: "text-white/90", children: ["$", Number(amount).toFixed(2)] })] })) })] }), showHowItWorks && ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: (0, jsx_runtime_1.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: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-6 flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-as-primary text-xl font-semibold", children: "How it works" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setShowHowItWorks(false), className: "text-as-primary/60 hover:text-as-primary transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "h-6 w-6" }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [(0, jsx_runtime_1.jsx)(PaymentMethodIcons_1.default, {}), (0, jsx_runtime_1.jsx)(HowItWorks_1.default, { steps: howItWorksSteps })] })] }) }))] }));
169
+ })()] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "bg-as-on-surface-1 w-full rounded-2xl p-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary mb-4 text-lg font-semibold", children: "Payment Details" }), (0, jsx_runtime_1.jsx)(react_stripe_js_1.PaymentElement, { onChange: handlePaymentElementChange, options: stripeElementOptions }), showAddressElement && ((0, jsx_runtime_1.jsx)(react_stripe_js_1.AddressElement, { options: {
170
+ mode: "billing",
171
+ fields: {
172
+ phone: "always",
173
+ },
174
+ // More granular control
175
+ display: {
176
+ name: "split", // or 'split' for first/last name separately
177
+ },
178
+ // Validation
179
+ validation: {
180
+ phone: {
181
+ required: "auto", // or 'always', 'never'
182
+ },
183
+ },
184
+ } }))] }), message && ((0, jsx_runtime_1.jsxs)("div", { className: "bg-as-red/10 border-as-red/20 flex w-full items-center gap-3 rounded-2xl border p-4", children: [(0, jsx_runtime_1.jsx)("div", { className: "bg-as-red flex h-6 w-6 shrink-0 items-center justify-center rounded-full", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-4 w-4 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-red text-sm font-medium", children: message })] })), (0, jsx_runtime_1.jsx)(react_2.ShinyButton, { type: "submit", accentColor: "hsl(var(--as-brand))", disabled: !stripe || !elements || loading, className: "relative w-full py-4 text-lg font-semibold", children: loading ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-current border-t-transparent" }), (0, jsx_runtime_1.jsx)("span", { className: "text-white", children: "Processing Payment..." })] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-white", children: "Complete Payment" }), amount && (0, jsx_runtime_1.jsxs)("span", { className: "text-white/90", children: ["$", Number(amount).toFixed(2)] })] })) })] }), showHowItWorks && ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: (0, jsx_runtime_1.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: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-6 flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-as-primary text-xl font-semibold", children: "How it works" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setShowHowItWorks(false), className: "text-as-primary/60 hover:text-as-primary transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "h-6 w-6" }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-6", children: [(0, jsx_runtime_1.jsx)(PaymentMethodIcons_1.default, {}), (0, jsx_runtime_1.jsx)(HowItWorks_1.default, { steps: howItWorksSteps })] })] }) }))] }));
148
185
  }
149
186
  // Add tooltip component
150
187
  function Tooltip({ children, content }) {
@@ -22,6 +22,7 @@ function StripePaymentForm({ order, onPaymentSuccess, }) {
22
22
  const elements = (0, react_stripe_js_1.useElements)();
23
23
  const [isProcessing, setIsProcessing] = (0, react_2.useState)(false);
24
24
  const [error, setError] = (0, react_2.useState)(null);
25
+ const [showAddressElement, setShowAddressElement] = (0, react_2.useState)(false);
25
26
  const handleSubmit = async (e) => {
26
27
  e.preventDefault();
27
28
  if (!stripe || !elements) {
@@ -51,16 +52,38 @@ function StripePaymentForm({ order, onPaymentSuccess, }) {
51
52
  setIsProcessing(false);
52
53
  }
53
54
  };
55
+ // Handle payment element changes
56
+ const handlePaymentElementChange = (event) => {
57
+ // Show address element only for card payments
58
+ console.log("@@stripe-web2-payment:payment-element-change:", JSON.stringify(event, null, 2));
59
+ setShowAddressElement(event.value.type === "card");
60
+ };
54
61
  const stripeElementOptions = {
55
62
  layout: "tabs",
56
- defaultValues: {
57
- billingDetails: {
58
- name: "",
59
- email: "",
60
- },
63
+ fields: {
64
+ billingDetails: "auto",
65
+ },
66
+ wallets: {
67
+ applePay: "auto",
68
+ googlePay: "auto",
61
69
  },
62
70
  };
63
- return ((0, jsx_runtime_1.jsx)("form", { onSubmit: handleSubmit, className: "w-full", children: (0, jsx_runtime_1.jsx)("div", { className: "overflow-hidden rounded-xl bg-white", children: (0, jsx_runtime_1.jsxs)("div", { className: "px-6 py-4", children: [(0, jsx_runtime_1.jsx)("h2", { className: "mb-4 text-lg font-semibold", children: "Payment Details" }), (0, jsx_runtime_1.jsx)(react_stripe_js_1.PaymentElement, { options: stripeElementOptions }), error && ((0, jsx_runtime_1.jsx)("div", { className: "mt-4 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-600", children: error })), (0, jsx_runtime_1.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 ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-5 w-5 animate-spin" }), (0, jsx_runtime_1.jsx)("span", { children: "Processing..." })] })) : ((0, jsx_runtime_1.jsx)("span", { children: "Complete Payment" })) })] }) }) }));
71
+ return ((0, jsx_runtime_1.jsx)("form", { onSubmit: handleSubmit, className: "w-full", children: (0, jsx_runtime_1.jsx)("div", { className: "overflow-hidden rounded-xl bg-white", children: (0, jsx_runtime_1.jsxs)("div", { className: "px-6 py-4", children: [(0, jsx_runtime_1.jsx)("h2", { className: "mb-4 text-lg font-semibold", children: "Payment Details" }), (0, jsx_runtime_1.jsx)(react_stripe_js_1.PaymentElement, { options: stripeElementOptions, onChange: handlePaymentElementChange }), showAddressElement && ((0, jsx_runtime_1.jsx)("div", { className: "mt-4", children: (0, jsx_runtime_1.jsx)(react_stripe_js_1.AddressElement, { options: {
72
+ mode: "billing",
73
+ fields: {
74
+ phone: "always",
75
+ },
76
+ // More granular control
77
+ display: {
78
+ name: "split", // or 'split' for first/last name separately
79
+ },
80
+ // Validation
81
+ validation: {
82
+ phone: {
83
+ required: "auto", // or 'always', 'never'
84
+ },
85
+ },
86
+ } }) })), error && ((0, jsx_runtime_1.jsx)("div", { className: "mt-4 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-600", children: error })), (0, jsx_runtime_1.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 ? ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-center gap-2", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-5 w-5 animate-spin" }), (0, jsx_runtime_1.jsx)("span", { children: "Processing..." })] })) : ((0, jsx_runtime_1.jsx)("span", { children: "Complete Payment" })) })] }) }) }));
64
87
  }
65
88
  function WebviewOnrampPayment({ srcAmountOnRamp, recipientAddress, destinationToken, anyspendQuote, onPaymentSuccess, userId, partnerId, }) {
66
89
  const [stableAmountForGeo, setStableAmountForGeo] = (0, react_2.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
@@ -6,6 +6,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
6
6
  const react_1 = require("../../../global-account/react");
7
7
  const react_query_1 = require("@tanstack/react-query");
8
8
  const react_2 = require("react");
9
+ const StripeRedirectHandler_1 = require("./StripeRedirectHandler");
9
10
  const defaultQueryClientConfig = {
10
11
  defaultOptions: {
11
12
  queries: {
@@ -24,6 +25,7 @@ const defaultQueryClientConfig = {
24
25
  * - Optimized for performance with React.memo
25
26
  * - Safe to use at the application root
26
27
  * - Configures sensible defaults for query caching
28
+ * - Handles Stripe payment redirects and modal state
27
29
  *
28
30
  * @example
29
31
  * ```tsx
@@ -38,6 +40,6 @@ const defaultQueryClientConfig = {
38
40
  */
39
41
  const AnyspendProvider = function AnyspendProvider({ children }) {
40
42
  const [queryClient] = (0, react_2.useState)(() => new react_query_1.QueryClient(defaultQueryClientConfig));
41
- return ((0, jsx_runtime_1.jsx)(react_query_1.QueryClientProvider, { client: queryClient, children: (0, jsx_runtime_1.jsx)(react_1.TooltipProvider, { children: children }) }));
43
+ return ((0, jsx_runtime_1.jsx)(react_query_1.QueryClientProvider, { client: queryClient, children: (0, jsx_runtime_1.jsxs)(react_1.TooltipProvider, { children: [(0, jsx_runtime_1.jsx)(StripeRedirectHandler_1.StripeRedirectHandler, {}), children] }) }));
42
44
  };
43
45
  exports.AnyspendProvider = AnyspendProvider;
@@ -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,41 @@
1
+ "use strict";
2
+ "use client";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.StripeRedirectHandler = StripeRedirectHandler;
5
+ const react_1 = require("../../../global-account/react");
6
+ const react_2 = require("react");
7
+ /**
8
+ * Handles Stripe payment redirects by showing the order details modal
9
+ * when returning from Stripe's payment flow.
10
+ *
11
+ * This component should be mounted inside AnyspendProvider to ensure
12
+ * proper modal state management.
13
+ */
14
+ function StripeRedirectHandler() {
15
+ const setB3ModalOpen = (0, react_1.useModalStore)(state => state.setB3ModalOpen);
16
+ const setB3ModalContentType = (0, react_1.useModalStore)(state => state.setB3ModalContentType);
17
+ (0, react_2.useEffect)(() => {
18
+ const url = new URL(window.location.href);
19
+ const fromStripe = url.searchParams.get("fromStripe");
20
+ const paymentIntent = url.searchParams.get("payment_intent");
21
+ const orderId = url.searchParams.get("orderId");
22
+ console.log("@@stripe-web2-payment:fromStripe:", fromStripe);
23
+ console.log("@@stripe-web2-payment:paymentIntent:", paymentIntent);
24
+ console.log("@@stripe-web2-payment:orderId:", orderId);
25
+ if (fromStripe && paymentIntent && orderId) {
26
+ // Re-open the modal with the order details
27
+ setB3ModalOpen(true);
28
+ setB3ModalContentType({
29
+ type: "anyspendOrderDetails",
30
+ orderId,
31
+ });
32
+ // Clean up URL params
33
+ url.searchParams.delete("fromStripe");
34
+ url.searchParams.delete("payment_intent");
35
+ url.searchParams.delete("payment_intent_client_secret");
36
+ url.searchParams.delete("redirect_status");
37
+ window.history.replaceState({}, "", url.toString());
38
+ }
39
+ }, [setB3ModalOpen, setB3ModalContentType]);
40
+ return null;
41
+ }
@@ -53,7 +53,7 @@ exports.DialogOverlay = DialogOverlay;
53
53
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
54
54
  const DialogContent = React.forwardRef(({ className, children, hideCloseButton = false, closeBtnClassName, ...props }, ref) => {
55
55
  const container = typeof window !== "undefined" ? document.getElementById("b3-root") : null;
56
- return ((0, jsx_runtime_1.jsxs)(DialogPortal, { container: container, children: [(0, jsx_runtime_1.jsx)(DialogOverlay, {}), (0, jsx_runtime_1.jsxs)(DialogPrimitive.Content, { ref: ref, className: (0, utils_1.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%]", "data-[state=closed]:[transform:rotateX(10deg)] data-[state=open]:[transform:rotateX(0deg)]", "[perspective:1200px] [transform-style:preserve-3d] sm:rounded-xl", "transition-all ease-out", className), ...props, children: [children, !hideCloseButton && ((0, jsx_runtime_1.jsxs)(DialogPrimitive.Close, { className: (0, utils_1.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: [(0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "h-5 w-5" }), (0, jsx_runtime_1.jsx)("span", { className: "sr-only", children: "Close" })] }))] })] }));
56
+ return ((0, jsx_runtime_1.jsxs)(DialogPortal, { container: container, children: [(0, jsx_runtime_1.jsx)(DialogOverlay, {}), (0, jsx_runtime_1.jsxs)(DialogPrimitive.Content, { ref: ref, className: (0, utils_1.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 && ((0, jsx_runtime_1.jsxs)(DialogPrimitive.Close, { className: (0, utils_1.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: [(0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "h-5 w-5" }), (0, jsx_runtime_1.jsx)("span", { className: "sr-only", children: "Close" })] }))] })] }));
57
57
  });
58
58
  exports.DialogContent = DialogContent;
59
59
  DialogContent.displayName = DialogPrimitive.Content.displayName;
@@ -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
+ }