@b3dotfun/sdk 0.1.68-alpha.0 → 0.1.68-alpha.2

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 (34) hide show
  1. package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckout.d.ts +3 -1
  2. package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckout.js +2 -2
  3. package/dist/cjs/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +3 -1
  4. package/dist/cjs/anyspend/react/components/checkout/CheckoutPaymentPanel.js +2 -2
  5. package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +3 -1
  6. package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.js +3 -1
  7. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.d.ts +2 -0
  8. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
  9. package/dist/cjs/anyspend/services/anyspend.d.ts +2 -1
  10. package/dist/cjs/anyspend/services/anyspend.js +2 -1
  11. package/dist/cjs/global-account/react/hooks/useAuthentication.js +9 -13
  12. package/dist/esm/anyspend/react/components/checkout/AnySpendCheckout.d.ts +3 -1
  13. package/dist/esm/anyspend/react/components/checkout/AnySpendCheckout.js +2 -2
  14. package/dist/esm/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +3 -1
  15. package/dist/esm/anyspend/react/components/checkout/CheckoutPaymentPanel.js +2 -2
  16. package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +3 -1
  17. package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.js +3 -1
  18. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.d.ts +2 -0
  19. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
  20. package/dist/esm/anyspend/services/anyspend.d.ts +2 -1
  21. package/dist/esm/anyspend/services/anyspend.js +2 -1
  22. package/dist/esm/global-account/react/hooks/useAuthentication.js +9 -13
  23. package/dist/types/anyspend/react/components/checkout/AnySpendCheckout.d.ts +3 -1
  24. package/dist/types/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +3 -1
  25. package/dist/types/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +3 -1
  26. package/dist/types/anyspend/react/hooks/useAnyspendCreateOnrampOrder.d.ts +2 -0
  27. package/dist/types/anyspend/services/anyspend.d.ts +2 -1
  28. package/package.json +1 -1
  29. package/src/anyspend/react/components/checkout/AnySpendCheckout.tsx +4 -0
  30. package/src/anyspend/react/components/checkout/CheckoutPaymentPanel.tsx +4 -0
  31. package/src/anyspend/react/components/checkout/FiatCheckoutPanel.tsx +5 -0
  32. package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +3 -0
  33. package/src/anyspend/services/anyspend.ts +3 -0
  34. package/src/global-account/react/hooks/useAuthentication.ts +9 -14
@@ -113,5 +113,7 @@ export interface AnySpendCheckoutProps {
113
113
  onDiscountApplied?: (result: DiscountResult) => void;
114
114
  /** Async function to validate a discount code. Returns DiscountResult. */
115
115
  validateDiscount?: (code: string) => Promise<DiscountResult>;
116
+ /** When true, fees are added on top of the amount (payer pays more, receiver gets exact amount) */
117
+ feeOnTop?: boolean;
116
118
  }
117
- export declare function AnySpendCheckout({ mode, recipientAddress, destinationTokenAddress, destinationTokenChainId, items, totalAmount: totalAmountOverride, organizationName, organizationLogo, themeColor, buttonText, checkoutSessionId, onSuccess, onError, returnUrl, returnLabel, classes, footer, defaultPaymentMethod, senderAddress, slots, content, theme, showPoints, showOrderId, shipping: shippingProp, tax, discount: discountProp, summaryLines, formSchema, formComponent, onFormSubmit, shippingOptions, collectShippingAddress, onShippingChange: onShippingChangeProp, enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, }: AnySpendCheckoutProps): import("react/jsx-runtime").JSX.Element;
119
+ export declare function AnySpendCheckout({ mode, recipientAddress, destinationTokenAddress, destinationTokenChainId, items, totalAmount: totalAmountOverride, organizationName, organizationLogo, themeColor, buttonText, checkoutSessionId, onSuccess, onError, returnUrl, returnLabel, classes, footer, defaultPaymentMethod, senderAddress, slots, content, theme, showPoints, showOrderId, shipping: shippingProp, tax, discount: discountProp, summaryLines, formSchema, formComponent, onFormSubmit, shippingOptions, collectShippingAddress, onShippingChange: onShippingChangeProp, enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, feeOnTop, }: AnySpendCheckoutProps): import("react/jsx-runtime").JSX.Element;
@@ -21,7 +21,7 @@ formSchema, formComponent, onFormSubmit,
21
21
  // New shipping props
22
22
  shippingOptions, collectShippingAddress, onShippingChange: onShippingChangeProp,
23
23
  // New discount props
24
- enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, }) {
24
+ enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, feeOnTop, }) {
25
25
  // ===== Form state =====
26
26
  const [formData, setFormData] = (0, react_2.useState)({});
27
27
  const [selectedShipping, setSelectedShipping] = (0, react_2.useState)(null);
@@ -154,5 +154,5 @@ enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount,
154
154
  (shippingOptions && shippingOptions.length > 0) ||
155
155
  collectShippingAddress ||
156
156
  enableDiscountCode;
157
- return ((0, jsx_runtime_1.jsx)(AnySpendFingerprintWrapper_1.AnySpendFingerprintWrapper, { fingerprint: fingerprint, children: (0, jsx_runtime_1.jsx)(AnySpendCustomizationContext_1.AnySpendCustomizationProvider, { slots: slots, content: content, theme: theme, children: (0, jsx_runtime_1.jsx)(CheckoutLayout_1.CheckoutLayout, { mode: mode, paymentPanel: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [hasFormContent && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-6", children: [(0, jsx_runtime_1.jsx)(CheckoutFormPanel_1.CheckoutFormPanel, { formSchema: formSchema, formComponent: formComponent, shippingOptions: shippingOptions, collectShippingAddress: collectShippingAddress, enableDiscountCode: enableDiscountCode, validateDiscount: validateDiscount, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, classes: classes, formData: formData, onFormDataChange: handleFormDataChange, selectedShipping: selectedShipping, onShippingChange: handleShippingChange, appliedDiscount: appliedDiscount, onDiscountApplied: handleDiscountApplied, onDiscountRemoved: handleDiscountRemoved, shippingAddress: shippingAddress, onShippingAddressChange: setShippingAddress, checkoutFormSlot: slots?.checkoutForm }), (0, jsx_runtime_1.jsx)("div", { className: "mt-6 border-t border-gray-200 dark:border-neutral-700" })] })), (0, jsx_runtime_1.jsx)(CheckoutPaymentPanel_1.CheckoutPaymentPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: computedTotal, buttonText: buttonText, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, classes: classes, defaultPaymentMethod: defaultPaymentMethod, senderAddress: senderAddress, showPoints: showPoints, showOrderId: showOrderId, callbackMetadata: checkoutFormMetadata, isFormValid: isFormValid })] }), cartPanel: (0, jsx_runtime_1.jsx)(CheckoutCartPanel_1.CheckoutCartPanel, { items: items, totalAmount: computedTotal, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, organizationName: organizationName, organizationLogo: organizationLogo, classes: classes, footer: footer, shipping: effectiveShipping, tax: typeof tax === "string" ? { amount: tax } : tax, discount: effectiveDiscount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), classes: classes }) }) }));
157
+ return ((0, jsx_runtime_1.jsx)(AnySpendFingerprintWrapper_1.AnySpendFingerprintWrapper, { fingerprint: fingerprint, children: (0, jsx_runtime_1.jsx)(AnySpendCustomizationContext_1.AnySpendCustomizationProvider, { slots: slots, content: content, theme: theme, children: (0, jsx_runtime_1.jsx)(CheckoutLayout_1.CheckoutLayout, { mode: mode, paymentPanel: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [hasFormContent && ((0, jsx_runtime_1.jsxs)("div", { className: "mb-6", children: [(0, jsx_runtime_1.jsx)(CheckoutFormPanel_1.CheckoutFormPanel, { formSchema: formSchema, formComponent: formComponent, shippingOptions: shippingOptions, collectShippingAddress: collectShippingAddress, enableDiscountCode: enableDiscountCode, validateDiscount: validateDiscount, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, classes: classes, formData: formData, onFormDataChange: handleFormDataChange, selectedShipping: selectedShipping, onShippingChange: handleShippingChange, appliedDiscount: appliedDiscount, onDiscountApplied: handleDiscountApplied, onDiscountRemoved: handleDiscountRemoved, shippingAddress: shippingAddress, onShippingAddressChange: setShippingAddress, checkoutFormSlot: slots?.checkoutForm }), (0, jsx_runtime_1.jsx)("div", { className: "mt-6 border-t border-gray-200 dark:border-neutral-700" })] })), (0, jsx_runtime_1.jsx)(CheckoutPaymentPanel_1.CheckoutPaymentPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: computedTotal, buttonText: buttonText, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, classes: classes, defaultPaymentMethod: defaultPaymentMethod, senderAddress: senderAddress, showPoints: showPoints, showOrderId: showOrderId, callbackMetadata: checkoutFormMetadata, isFormValid: isFormValid, feeOnTop: feeOnTop })] }), cartPanel: (0, jsx_runtime_1.jsx)(CheckoutCartPanel_1.CheckoutCartPanel, { items: items, totalAmount: computedTotal, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, organizationName: organizationName, organizationLogo: organizationLogo, classes: classes, footer: footer, shipping: effectiveShipping, tax: typeof tax === "string" ? { amount: tax } : tax, discount: effectiveDiscount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), classes: classes }) }) }));
158
158
  }
@@ -26,6 +26,8 @@ interface CheckoutPaymentPanelProps {
26
26
  showOrderId?: boolean;
27
27
  /** Whether the checkout form is valid. When false, payment methods are disabled. */
28
28
  isFormValid?: boolean;
29
+ /** When true, fees are added on top (payer pays more, receiver gets exact amount) */
30
+ feeOnTop?: boolean;
29
31
  }
30
- export declare function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid, }: CheckoutPaymentPanelProps): import("react/jsx-runtime").JSX.Element;
32
+ export declare function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid, feeOnTop, }: CheckoutPaymentPanelProps): import("react/jsx-runtime").JSX.Element;
31
33
  export {};
@@ -29,7 +29,7 @@ function AmexLogo() {
29
29
  function CoinbaseLogo() {
30
30
  return ((0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", style: { width: 20, height: 20 }, "aria-label": "Coinbase", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "12", fill: "#0052FF" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 4.5a7.5 7.5 0 1 0 0 15 7.5 7.5 0 0 0 0-15zm-1.8 4.8h3.6c.33 0 .6.27.6.6v4.2c0 .33-.27.6-.6.6h-3.6a.6.6 0 0 1-.6-.6V9.9c0-.33.27-.6.6-.6z", fill: "white" })] }));
31
31
  }
32
- function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid = true, }) {
32
+ function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid = true, feeOnTop, }) {
33
33
  const [paymentMethod, setPaymentMethod] = (0, react_2.useState)(defaultPaymentMethod ?? null);
34
34
  // Restore activeOrderId from sessionStorage (handles page refresh / Coinbase return)
35
35
  const [activeOrderId, setActiveOrderId] = (0, react_2.useState)(() => {
@@ -60,5 +60,5 @@ function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, desti
60
60
  ? "bg-white dark:bg-neutral-900"
61
61
  : "bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
62
62
  const expandedPanelClass = (0, cn_1.cn)("anyspend-payment-method-panel border-t border-gray-100 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-900");
63
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-payment-panel flex flex-col gap-5", classes?.paymentPanel), children: [(0, jsx_runtime_1.jsx)("h2", { className: (0, cn_1.cn)("anyspend-payment-title text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.paymentTitle), children: "Payment" }), !isFormValid && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-amber-600 dark:text-amber-400", children: "Please complete the required fields above before proceeding to payment." })), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-payment-methods divide-y divide-gray-200 overflow-hidden rounded-xl border border-gray-200 dark:divide-neutral-700 dark:border-neutral-700", !isFormValid && "pointer-events-none opacity-50", classes?.paymentMethodSelector), children: [(0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-crypto", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "crypto" ? null : "crypto"), className: accordionButtonClass(paymentMethod === "crypto"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "crypto", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(lucide_react_1.Wallet, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Pay with crypto" })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "crypto" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(CryptoPayPanel_1.CryptoPayPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, buttonText: buttonText, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, senderAddress: senderAddress }) }) }, "crypto-panel")) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-card", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "card" ? null : "card"), className: accordionButtonClass(paymentMethod === "card"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "card", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(lucide_react_1.CreditCard, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Credit or debit card" }), (0, jsx_runtime_1.jsxs)("div", { className: "ml-auto flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(VisaLogo, {}), (0, jsx_runtime_1.jsx)(MastercardLogo, {}), (0, jsx_runtime_1.jsx)(AmexLogo, {})] })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "card" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(FiatCheckoutPanel_1.FiatCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "card-panel")) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-coinbase", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "coinbase" ? null : "coinbase"), className: accordionButtonClass(paymentMethod === "coinbase"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "coinbase", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(CoinbaseLogo, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Coinbase Pay" })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "coinbase" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(CoinbaseCheckoutPanel_1.CoinbaseCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "coinbase-panel")) })] })] })] }));
63
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-payment-panel flex flex-col gap-5", classes?.paymentPanel), children: [(0, jsx_runtime_1.jsx)("h2", { className: (0, cn_1.cn)("anyspend-payment-title text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.paymentTitle), children: "Payment" }), !isFormValid && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-amber-600 dark:text-amber-400", children: "Please complete the required fields above before proceeding to payment." })), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-payment-methods divide-y divide-gray-200 overflow-hidden rounded-xl border border-gray-200 dark:divide-neutral-700 dark:border-neutral-700", !isFormValid && "pointer-events-none opacity-50", classes?.paymentMethodSelector), children: [(0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-crypto", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "crypto" ? null : "crypto"), className: accordionButtonClass(paymentMethod === "crypto"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "crypto", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(lucide_react_1.Wallet, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Pay with crypto" })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "crypto" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(CryptoPayPanel_1.CryptoPayPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, buttonText: buttonText, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, senderAddress: senderAddress }) }) }, "crypto-panel")) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-card", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "card" ? null : "card"), className: accordionButtonClass(paymentMethod === "card"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "card", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(lucide_react_1.CreditCard, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Credit or debit card" }), (0, jsx_runtime_1.jsxs)("div", { className: "ml-auto flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(VisaLogo, {}), (0, jsx_runtime_1.jsx)(MastercardLogo, {}), (0, jsx_runtime_1.jsx)(AmexLogo, {})] })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "card" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(FiatCheckoutPanel_1.FiatCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, feeOnTop: feeOnTop }) }) }, "card-panel")) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-coinbase", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "coinbase" ? null : "coinbase"), className: accordionButtonClass(paymentMethod === "coinbase"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "coinbase", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(CoinbaseLogo, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Coinbase Pay" })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "coinbase" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(CoinbaseCheckoutPanel_1.CoinbaseCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "coinbase-panel")) })] })] })] }));
64
64
  }
@@ -15,6 +15,8 @@ interface FiatCheckoutPanelProps {
15
15
  onError?: (error: Error) => void;
16
16
  callbackMetadata?: Record<string, unknown>;
17
17
  classes?: AnySpendCheckoutClasses;
18
+ /** When true, fees are added on top (payer pays more, receiver gets exact amount) */
19
+ feeOnTop?: boolean;
18
20
  }
19
- export declare function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, }: FiatCheckoutPanelProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, feeOnTop, }: FiatCheckoutPanelProps): import("react/jsx-runtime").JSX.Element;
20
22
  export {};
@@ -13,7 +13,7 @@ const react_stripe_js_1 = require("@stripe/react-stripe-js");
13
13
  const lucide_react_1 = require("lucide-react");
14
14
  const react_3 = require("motion/react");
15
15
  const react_4 = require("react");
16
- function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, }) {
16
+ function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, feeOnTop, }) {
17
17
  // Stable refs for callback props to avoid re-triggering effects
18
18
  const onErrorRef = (0, react_4.useRef)(onError);
19
19
  onErrorRef.current = onError;
@@ -123,6 +123,7 @@ function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinat
123
123
  },
124
124
  expectedDstAmount: totalAmount,
125
125
  callbackMetadata,
126
+ feeOnTop,
126
127
  });
127
128
  }
128
129
  }, [
@@ -142,6 +143,7 @@ function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinat
142
143
  geoData,
143
144
  callbackMetadata,
144
145
  createOrder,
146
+ feeOnTop,
145
147
  ]);
146
148
  // Loading geo/stripe support check (and quote for non-stablecoins)
147
149
  if (isLoadingGeo || (!isStablecoin && isLoadingAnyspendQuote)) {
@@ -10,6 +10,8 @@ export type OnrampOptions = {
10
10
  export type CreateOnrampOrderParams = Omit<CreateOrderParams, "srcChain" | "srcToken" | "srcAmount"> & {
11
11
  srcFiatAmount: string;
12
12
  onramp: OnrampOptions;
13
+ /** When true, fees are added on top of srcAmount (payer pays more, receiver gets exact amount) */
14
+ feeOnTop?: boolean;
13
15
  };
14
16
  export type UseAnyspendCreateOnrampOrderProps = {
15
17
  onSuccess?: (data: CreateOrderResponse) => void;
@@ -75,6 +75,7 @@ function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
75
75
  clientReferenceId,
76
76
  visitorData,
77
77
  callbackMetadata: params.callbackMetadata,
78
+ feeOnTop: params.feeOnTop,
78
79
  });
79
80
  }
80
81
  catch (error) {
@@ -6,7 +6,7 @@ export declare const anyspendService: {
6
6
  getTokenList: (chainId: number, query: string) => Promise<components["schemas"]["Token"][]>;
7
7
  getToken: (chainId: number, tokenAddress: string) => Promise<components["schemas"]["Token"]>;
8
8
  getQuote: (req: GetQuoteRequest, partnerId?: string) => Promise<GetQuoteResponse>;
9
- createOrder: ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, }: {
9
+ createOrder: ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, feeOnTop, }: {
10
10
  recipientAddress: string;
11
11
  type: string;
12
12
  srcChain: number;
@@ -22,6 +22,7 @@ export declare const anyspendService: {
22
22
  clientReferenceId?: string;
23
23
  visitorData?: VisitorData;
24
24
  callbackMetadata?: Record<string, unknown>;
25
+ feeOnTop?: boolean;
25
26
  }) => Promise<{
26
27
  success: boolean;
27
28
  message: string;
@@ -43,7 +43,7 @@ exports.anyspendService = {
43
43
  return data;
44
44
  },
45
45
  // Order related
46
- createOrder: async ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, }) => {
46
+ createOrder: async ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, feeOnTop, }) => {
47
47
  const accessToken = await app_1.default.authentication.getAccessToken();
48
48
  const response = await fetch(`${constants_1.ANYSPEND_MAINNET_BASE_URL}/orders`, {
49
49
  method: "POST",
@@ -68,6 +68,7 @@ exports.anyspendService = {
68
68
  partnerId,
69
69
  ...(clientReferenceId && { clientReferenceId }),
70
70
  ...(callbackMetadata && { callbackMetadata }),
71
+ ...(feeOnTop != null && { feeOnTop }),
71
72
  }),
72
73
  });
73
74
  const data = await response.json();
@@ -132,21 +132,17 @@ function useAuthentication(partnerId) {
132
132
  }
133
133
  }, [activeWallet, partnerId, authenticate, setIsAuthenticated, setIsAuthenticating, setUser, setHasStartedConnecting]);
134
134
  const logout = (0, react_2.useCallback)(async (callback) => {
135
- if (activeWallet) {
136
- debug("@@logout:activeWallet", activeWallet);
137
- disconnect(activeWallet);
138
- debug("@@logout:activeWallet", activeWallet);
139
- }
140
- // Log out of each wallet
135
+ // Only disconnect ecosystem/smart wallets, preserve EOA wallets (e.g. MetaMask)
136
+ // so they remain available after re-login
141
137
  wallets.forEach(wallet => {
142
- console.log("@@logging out", wallet);
143
- disconnect(wallet);
138
+ debug("@@logout:wallet", wallet.id);
139
+ if (wallet.id.startsWith("ecosystem.") || wallet.id === "smart") {
140
+ disconnect(wallet);
141
+ }
144
142
  });
145
- // Delete localStorage thirdweb:connected-wallet-ids
146
- // https://npc-labs.slack.com/archives/C070E6HNG85/p1750185115273099
143
+ // Clear user-specific storage but preserve wallet connection state
144
+ // so EOA wallets (e.g. MetaMask) can auto-reconnect on next login
147
145
  if (typeof localStorage !== "undefined") {
148
- localStorage.removeItem("thirdweb:connected-wallet-ids");
149
- localStorage.removeItem("wagmi.store");
150
146
  localStorage.removeItem("lastAuthProvider");
151
147
  localStorage.removeItem("b3-user");
152
148
  }
@@ -159,7 +155,7 @@ function useAuthentication(partnerId) {
159
155
  if (onLogoutCallback) {
160
156
  await onLogoutCallback();
161
157
  }
162
- }, [activeWallet, disconnect, wallets, setIsAuthenticated, setUser, setIsConnected, onLogoutCallback]);
158
+ }, [disconnect, wallets, setIsAuthenticated, setUser, setIsConnected, onLogoutCallback]);
163
159
  const onConnect = (0, react_2.useCallback)(async (_walleAutoConnectedWith, allConnectedWallets) => {
164
160
  debug("@@useAuthentication:onConnect", { _walleAutoConnectedWith, allConnectedWallets });
165
161
  try {
@@ -113,5 +113,7 @@ export interface AnySpendCheckoutProps {
113
113
  onDiscountApplied?: (result: DiscountResult) => void;
114
114
  /** Async function to validate a discount code. Returns DiscountResult. */
115
115
  validateDiscount?: (code: string) => Promise<DiscountResult>;
116
+ /** When true, fees are added on top of the amount (payer pays more, receiver gets exact amount) */
117
+ feeOnTop?: boolean;
116
118
  }
117
- export declare function AnySpendCheckout({ mode, recipientAddress, destinationTokenAddress, destinationTokenChainId, items, totalAmount: totalAmountOverride, organizationName, organizationLogo, themeColor, buttonText, checkoutSessionId, onSuccess, onError, returnUrl, returnLabel, classes, footer, defaultPaymentMethod, senderAddress, slots, content, theme, showPoints, showOrderId, shipping: shippingProp, tax, discount: discountProp, summaryLines, formSchema, formComponent, onFormSubmit, shippingOptions, collectShippingAddress, onShippingChange: onShippingChangeProp, enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, }: AnySpendCheckoutProps): import("react/jsx-runtime").JSX.Element;
119
+ export declare function AnySpendCheckout({ mode, recipientAddress, destinationTokenAddress, destinationTokenChainId, items, totalAmount: totalAmountOverride, organizationName, organizationLogo, themeColor, buttonText, checkoutSessionId, onSuccess, onError, returnUrl, returnLabel, classes, footer, defaultPaymentMethod, senderAddress, slots, content, theme, showPoints, showOrderId, shipping: shippingProp, tax, discount: discountProp, summaryLines, formSchema, formComponent, onFormSubmit, shippingOptions, collectShippingAddress, onShippingChange: onShippingChangeProp, enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, feeOnTop, }: AnySpendCheckoutProps): import("react/jsx-runtime").JSX.Element;
@@ -18,7 +18,7 @@ formSchema, formComponent, onFormSubmit,
18
18
  // New shipping props
19
19
  shippingOptions, collectShippingAddress, onShippingChange: onShippingChangeProp,
20
20
  // New discount props
21
- enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, }) {
21
+ enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, feeOnTop, }) {
22
22
  // ===== Form state =====
23
23
  const [formData, setFormData] = useState({});
24
24
  const [selectedShipping, setSelectedShipping] = useState(null);
@@ -151,5 +151,5 @@ enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount,
151
151
  (shippingOptions && shippingOptions.length > 0) ||
152
152
  collectShippingAddress ||
153
153
  enableDiscountCode;
154
- return (_jsx(AnySpendFingerprintWrapper, { fingerprint: fingerprint, children: _jsx(AnySpendCustomizationProvider, { slots: slots, content: content, theme: theme, children: _jsx(CheckoutLayout, { mode: mode, paymentPanel: _jsxs(_Fragment, { children: [hasFormContent && (_jsxs("div", { className: "mb-6", children: [_jsx(CheckoutFormPanel, { formSchema: formSchema, formComponent: formComponent, shippingOptions: shippingOptions, collectShippingAddress: collectShippingAddress, enableDiscountCode: enableDiscountCode, validateDiscount: validateDiscount, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, classes: classes, formData: formData, onFormDataChange: handleFormDataChange, selectedShipping: selectedShipping, onShippingChange: handleShippingChange, appliedDiscount: appliedDiscount, onDiscountApplied: handleDiscountApplied, onDiscountRemoved: handleDiscountRemoved, shippingAddress: shippingAddress, onShippingAddressChange: setShippingAddress, checkoutFormSlot: slots?.checkoutForm }), _jsx("div", { className: "mt-6 border-t border-gray-200 dark:border-neutral-700" })] })), _jsx(CheckoutPaymentPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: computedTotal, buttonText: buttonText, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, classes: classes, defaultPaymentMethod: defaultPaymentMethod, senderAddress: senderAddress, showPoints: showPoints, showOrderId: showOrderId, callbackMetadata: checkoutFormMetadata, isFormValid: isFormValid })] }), cartPanel: _jsx(CheckoutCartPanel, { items: items, totalAmount: computedTotal, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, organizationName: organizationName, organizationLogo: organizationLogo, classes: classes, footer: footer, shipping: effectiveShipping, tax: typeof tax === "string" ? { amount: tax } : tax, discount: effectiveDiscount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), classes: classes }) }) }));
154
+ return (_jsx(AnySpendFingerprintWrapper, { fingerprint: fingerprint, children: _jsx(AnySpendCustomizationProvider, { slots: slots, content: content, theme: theme, children: _jsx(CheckoutLayout, { mode: mode, paymentPanel: _jsxs(_Fragment, { children: [hasFormContent && (_jsxs("div", { className: "mb-6", children: [_jsx(CheckoutFormPanel, { formSchema: formSchema, formComponent: formComponent, shippingOptions: shippingOptions, collectShippingAddress: collectShippingAddress, enableDiscountCode: enableDiscountCode, validateDiscount: validateDiscount, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, classes: classes, formData: formData, onFormDataChange: handleFormDataChange, selectedShipping: selectedShipping, onShippingChange: handleShippingChange, appliedDiscount: appliedDiscount, onDiscountApplied: handleDiscountApplied, onDiscountRemoved: handleDiscountRemoved, shippingAddress: shippingAddress, onShippingAddressChange: setShippingAddress, checkoutFormSlot: slots?.checkoutForm }), _jsx("div", { className: "mt-6 border-t border-gray-200 dark:border-neutral-700" })] })), _jsx(CheckoutPaymentPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: computedTotal, buttonText: buttonText, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, classes: classes, defaultPaymentMethod: defaultPaymentMethod, senderAddress: senderAddress, showPoints: showPoints, showOrderId: showOrderId, callbackMetadata: checkoutFormMetadata, isFormValid: isFormValid, feeOnTop: feeOnTop })] }), cartPanel: _jsx(CheckoutCartPanel, { items: items, totalAmount: computedTotal, tokenSymbol: tokenSymbol, tokenDecimals: tokenDecimals, organizationName: organizationName, organizationLogo: organizationLogo, classes: classes, footer: footer, shipping: effectiveShipping, tax: typeof tax === "string" ? { amount: tax } : tax, discount: effectiveDiscount, summaryLines: summaryLines, usdEquivalent: usdEquivalent }), classes: classes }) }) }));
155
155
  }
@@ -26,6 +26,8 @@ interface CheckoutPaymentPanelProps {
26
26
  showOrderId?: boolean;
27
27
  /** Whether the checkout form is valid. When false, payment methods are disabled. */
28
28
  isFormValid?: boolean;
29
+ /** When true, fees are added on top (payer pays more, receiver gets exact amount) */
30
+ feeOnTop?: boolean;
29
31
  }
30
- export declare function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid, }: CheckoutPaymentPanelProps): import("react/jsx-runtime").JSX.Element;
32
+ export declare function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid, feeOnTop, }: CheckoutPaymentPanelProps): import("react/jsx-runtime").JSX.Element;
31
33
  export {};
@@ -26,7 +26,7 @@ function AmexLogo() {
26
26
  function CoinbaseLogo() {
27
27
  return (_jsxs("svg", { viewBox: "0 0 24 24", style: { width: 20, height: 20 }, "aria-label": "Coinbase", children: [_jsx("circle", { cx: "12", cy: "12", r: "12", fill: "#0052FF" }), _jsx("path", { d: "M12 4.5a7.5 7.5 0 1 0 0 15 7.5 7.5 0 0 0 0-15zm-1.8 4.8h3.6c.33 0 .6.27.6.6v4.2c0 .33-.27.6-.6.6h-3.6a.6.6 0 0 1-.6-.6V9.9c0-.33.27-.6.6-.6z", fill: "white" })] }));
28
28
  }
29
- export function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid = true, }) {
29
+ export function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid = true, feeOnTop, }) {
30
30
  const [paymentMethod, setPaymentMethod] = useState(defaultPaymentMethod ?? null);
31
31
  // Restore activeOrderId from sessionStorage (handles page refresh / Coinbase return)
32
32
  const [activeOrderId, setActiveOrderId] = useState(() => {
@@ -57,5 +57,5 @@ export function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress
57
57
  ? "bg-white dark:bg-neutral-900"
58
58
  : "bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
59
59
  const expandedPanelClass = cn("anyspend-payment-method-panel border-t border-gray-100 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-900");
60
- return (_jsxs("div", { className: cn("anyspend-payment-panel flex flex-col gap-5", classes?.paymentPanel), children: [_jsx("h2", { className: cn("anyspend-payment-title text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.paymentTitle), children: "Payment" }), !isFormValid && (_jsx("p", { className: "text-sm text-amber-600 dark:text-amber-400", children: "Please complete the required fields above before proceeding to payment." })), _jsxs("div", { className: cn("anyspend-payment-methods divide-y divide-gray-200 overflow-hidden rounded-xl border border-gray-200 dark:divide-neutral-700 dark:border-neutral-700", !isFormValid && "pointer-events-none opacity-50", classes?.paymentMethodSelector), children: [_jsxs("div", { className: "anyspend-method-crypto", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "crypto" ? null : "crypto"), className: accordionButtonClass(paymentMethod === "crypto"), children: [_jsx(RadioCircle, { selected: paymentMethod === "crypto", themeColor: themeColor }), _jsx(Wallet, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Pay with crypto" })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "crypto" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(CryptoPayPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, buttonText: buttonText, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, senderAddress: senderAddress }) }) }, "crypto-panel")) })] }), _jsxs("div", { className: "anyspend-method-card", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "card" ? null : "card"), className: accordionButtonClass(paymentMethod === "card"), children: [_jsx(RadioCircle, { selected: paymentMethod === "card", themeColor: themeColor }), _jsx(CreditCard, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Credit or debit card" }), _jsxs("div", { className: "ml-auto flex items-center gap-1", children: [_jsx(VisaLogo, {}), _jsx(MastercardLogo, {}), _jsx(AmexLogo, {})] })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "card" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(FiatCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "card-panel")) })] }), _jsxs("div", { className: "anyspend-method-coinbase", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "coinbase" ? null : "coinbase"), className: accordionButtonClass(paymentMethod === "coinbase"), children: [_jsx(RadioCircle, { selected: paymentMethod === "coinbase", themeColor: themeColor }), _jsx(CoinbaseLogo, {}), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Coinbase Pay" })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "coinbase" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(CoinbaseCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "coinbase-panel")) })] })] })] }));
60
+ return (_jsxs("div", { className: cn("anyspend-payment-panel flex flex-col gap-5", classes?.paymentPanel), children: [_jsx("h2", { className: cn("anyspend-payment-title text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.paymentTitle), children: "Payment" }), !isFormValid && (_jsx("p", { className: "text-sm text-amber-600 dark:text-amber-400", children: "Please complete the required fields above before proceeding to payment." })), _jsxs("div", { className: cn("anyspend-payment-methods divide-y divide-gray-200 overflow-hidden rounded-xl border border-gray-200 dark:divide-neutral-700 dark:border-neutral-700", !isFormValid && "pointer-events-none opacity-50", classes?.paymentMethodSelector), children: [_jsxs("div", { className: "anyspend-method-crypto", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "crypto" ? null : "crypto"), className: accordionButtonClass(paymentMethod === "crypto"), children: [_jsx(RadioCircle, { selected: paymentMethod === "crypto", themeColor: themeColor }), _jsx(Wallet, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Pay with crypto" })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "crypto" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(CryptoPayPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, buttonText: buttonText, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, senderAddress: senderAddress }) }) }, "crypto-panel")) })] }), _jsxs("div", { className: "anyspend-method-card", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "card" ? null : "card"), className: accordionButtonClass(paymentMethod === "card"), children: [_jsx(RadioCircle, { selected: paymentMethod === "card", themeColor: themeColor }), _jsx(CreditCard, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Credit or debit card" }), _jsxs("div", { className: "ml-auto flex items-center gap-1", children: [_jsx(VisaLogo, {}), _jsx(MastercardLogo, {}), _jsx(AmexLogo, {})] })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "card" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(FiatCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, feeOnTop: feeOnTop }) }) }, "card-panel")) })] }), _jsxs("div", { className: "anyspend-method-coinbase", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "coinbase" ? null : "coinbase"), className: accordionButtonClass(paymentMethod === "coinbase"), children: [_jsx(RadioCircle, { selected: paymentMethod === "coinbase", themeColor: themeColor }), _jsx(CoinbaseLogo, {}), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Coinbase Pay" })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "coinbase" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(CoinbaseCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "coinbase-panel")) })] })] })] }));
61
61
  }
@@ -15,6 +15,8 @@ interface FiatCheckoutPanelProps {
15
15
  onError?: (error: Error) => void;
16
16
  callbackMetadata?: Record<string, unknown>;
17
17
  classes?: AnySpendCheckoutClasses;
18
+ /** When true, fees are added on top (payer pays more, receiver gets exact amount) */
19
+ feeOnTop?: boolean;
18
20
  }
19
- export declare function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, }: FiatCheckoutPanelProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, feeOnTop, }: FiatCheckoutPanelProps): import("react/jsx-runtime").JSX.Element;
20
22
  export {};
@@ -10,7 +10,7 @@ import { AddressElement, Elements, PaymentElement, useElements, useStripe } from
10
10
  import { Loader2, Lock } from "lucide-react";
11
11
  import { AnimatePresence, motion } from "motion/react";
12
12
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
13
- export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, }) {
13
+ export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, feeOnTop, }) {
14
14
  // Stable refs for callback props to avoid re-triggering effects
15
15
  const onErrorRef = useRef(onError);
16
16
  onErrorRef.current = onError;
@@ -120,6 +120,7 @@ export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, d
120
120
  },
121
121
  expectedDstAmount: totalAmount,
122
122
  callbackMetadata,
123
+ feeOnTop,
123
124
  });
124
125
  }
125
126
  }, [
@@ -139,6 +140,7 @@ export function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, d
139
140
  geoData,
140
141
  callbackMetadata,
141
142
  createOrder,
143
+ feeOnTop,
142
144
  ]);
143
145
  // Loading geo/stripe support check (and quote for non-stablecoins)
144
146
  if (isLoadingGeo || (!isStablecoin && isLoadingAnyspendQuote)) {
@@ -10,6 +10,8 @@ export type OnrampOptions = {
10
10
  export type CreateOnrampOrderParams = Omit<CreateOrderParams, "srcChain" | "srcToken" | "srcAmount"> & {
11
11
  srcFiatAmount: string;
12
12
  onramp: OnrampOptions;
13
+ /** When true, fees are added on top of srcAmount (payer pays more, receiver gets exact amount) */
14
+ feeOnTop?: boolean;
13
15
  };
14
16
  export type UseAnyspendCreateOnrampOrderProps = {
15
17
  onSuccess?: (data: CreateOrderResponse) => void;
@@ -72,6 +72,7 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
72
72
  clientReferenceId,
73
73
  visitorData,
74
74
  callbackMetadata: params.callbackMetadata,
75
+ feeOnTop: params.feeOnTop,
75
76
  });
76
77
  }
77
78
  catch (error) {
@@ -6,7 +6,7 @@ export declare const anyspendService: {
6
6
  getTokenList: (chainId: number, query: string) => Promise<components["schemas"]["Token"][]>;
7
7
  getToken: (chainId: number, tokenAddress: string) => Promise<components["schemas"]["Token"]>;
8
8
  getQuote: (req: GetQuoteRequest, partnerId?: string) => Promise<GetQuoteResponse>;
9
- createOrder: ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, }: {
9
+ createOrder: ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, feeOnTop, }: {
10
10
  recipientAddress: string;
11
11
  type: string;
12
12
  srcChain: number;
@@ -22,6 +22,7 @@ export declare const anyspendService: {
22
22
  clientReferenceId?: string;
23
23
  visitorData?: VisitorData;
24
24
  callbackMetadata?: Record<string, unknown>;
25
+ feeOnTop?: boolean;
25
26
  }) => Promise<{
26
27
  success: boolean;
27
28
  message: string;
@@ -37,7 +37,7 @@ export const anyspendService = {
37
37
  return data;
38
38
  },
39
39
  // Order related
40
- createOrder: async ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, }) => {
40
+ createOrder: async ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, feeOnTop, }) => {
41
41
  const accessToken = await app.authentication.getAccessToken();
42
42
  const response = await fetch(`${ANYSPEND_MAINNET_BASE_URL}/orders`, {
43
43
  method: "POST",
@@ -62,6 +62,7 @@ export const anyspendService = {
62
62
  partnerId,
63
63
  ...(clientReferenceId && { clientReferenceId }),
64
64
  ...(callbackMetadata && { callbackMetadata }),
65
+ ...(feeOnTop != null && { feeOnTop }),
65
66
  }),
66
67
  });
67
68
  const data = await response.json();
@@ -126,21 +126,17 @@ export function useAuthentication(partnerId) {
126
126
  }
127
127
  }, [activeWallet, partnerId, authenticate, setIsAuthenticated, setIsAuthenticating, setUser, setHasStartedConnecting]);
128
128
  const logout = useCallback(async (callback) => {
129
- if (activeWallet) {
130
- debug("@@logout:activeWallet", activeWallet);
131
- disconnect(activeWallet);
132
- debug("@@logout:activeWallet", activeWallet);
133
- }
134
- // Log out of each wallet
129
+ // Only disconnect ecosystem/smart wallets, preserve EOA wallets (e.g. MetaMask)
130
+ // so they remain available after re-login
135
131
  wallets.forEach(wallet => {
136
- console.log("@@logging out", wallet);
137
- disconnect(wallet);
132
+ debug("@@logout:wallet", wallet.id);
133
+ if (wallet.id.startsWith("ecosystem.") || wallet.id === "smart") {
134
+ disconnect(wallet);
135
+ }
138
136
  });
139
- // Delete localStorage thirdweb:connected-wallet-ids
140
- // https://npc-labs.slack.com/archives/C070E6HNG85/p1750185115273099
137
+ // Clear user-specific storage but preserve wallet connection state
138
+ // so EOA wallets (e.g. MetaMask) can auto-reconnect on next login
141
139
  if (typeof localStorage !== "undefined") {
142
- localStorage.removeItem("thirdweb:connected-wallet-ids");
143
- localStorage.removeItem("wagmi.store");
144
140
  localStorage.removeItem("lastAuthProvider");
145
141
  localStorage.removeItem("b3-user");
146
142
  }
@@ -153,7 +149,7 @@ export function useAuthentication(partnerId) {
153
149
  if (onLogoutCallback) {
154
150
  await onLogoutCallback();
155
151
  }
156
- }, [activeWallet, disconnect, wallets, setIsAuthenticated, setUser, setIsConnected, onLogoutCallback]);
152
+ }, [disconnect, wallets, setIsAuthenticated, setUser, setIsConnected, onLogoutCallback]);
157
153
  const onConnect = useCallback(async (_walleAutoConnectedWith, allConnectedWallets) => {
158
154
  debug("@@useAuthentication:onConnect", { _walleAutoConnectedWith, allConnectedWallets });
159
155
  try {
@@ -113,5 +113,7 @@ export interface AnySpendCheckoutProps {
113
113
  onDiscountApplied?: (result: DiscountResult) => void;
114
114
  /** Async function to validate a discount code. Returns DiscountResult. */
115
115
  validateDiscount?: (code: string) => Promise<DiscountResult>;
116
+ /** When true, fees are added on top of the amount (payer pays more, receiver gets exact amount) */
117
+ feeOnTop?: boolean;
116
118
  }
117
- export declare function AnySpendCheckout({ mode, recipientAddress, destinationTokenAddress, destinationTokenChainId, items, totalAmount: totalAmountOverride, organizationName, organizationLogo, themeColor, buttonText, checkoutSessionId, onSuccess, onError, returnUrl, returnLabel, classes, footer, defaultPaymentMethod, senderAddress, slots, content, theme, showPoints, showOrderId, shipping: shippingProp, tax, discount: discountProp, summaryLines, formSchema, formComponent, onFormSubmit, shippingOptions, collectShippingAddress, onShippingChange: onShippingChangeProp, enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, }: AnySpendCheckoutProps): import("react/jsx-runtime").JSX.Element;
119
+ export declare function AnySpendCheckout({ mode, recipientAddress, destinationTokenAddress, destinationTokenChainId, items, totalAmount: totalAmountOverride, organizationName, organizationLogo, themeColor, buttonText, checkoutSessionId, onSuccess, onError, returnUrl, returnLabel, classes, footer, defaultPaymentMethod, senderAddress, slots, content, theme, showPoints, showOrderId, shipping: shippingProp, tax, discount: discountProp, summaryLines, formSchema, formComponent, onFormSubmit, shippingOptions, collectShippingAddress, onShippingChange: onShippingChangeProp, enableDiscountCode, onDiscountApplied: onDiscountAppliedProp, validateDiscount, feeOnTop, }: AnySpendCheckoutProps): import("react/jsx-runtime").JSX.Element;
@@ -26,6 +26,8 @@ interface CheckoutPaymentPanelProps {
26
26
  showOrderId?: boolean;
27
27
  /** Whether the checkout form is valid. When false, payment methods are disabled. */
28
28
  isFormValid?: boolean;
29
+ /** When true, fees are added on top (payer pays more, receiver gets exact amount) */
30
+ feeOnTop?: boolean;
29
31
  }
30
- export declare function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid, }: CheckoutPaymentPanelProps): import("react/jsx-runtime").JSX.Element;
32
+ export declare function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, buttonText, themeColor, returnUrl, returnLabel, onSuccess, onError, callbackMetadata, classes, defaultPaymentMethod, senderAddress, showPoints, showOrderId, isFormValid, feeOnTop, }: CheckoutPaymentPanelProps): import("react/jsx-runtime").JSX.Element;
31
33
  export {};
@@ -15,6 +15,8 @@ interface FiatCheckoutPanelProps {
15
15
  onError?: (error: Error) => void;
16
16
  callbackMetadata?: Record<string, unknown>;
17
17
  classes?: AnySpendCheckoutClasses;
18
+ /** When true, fees are added on top (payer pays more, receiver gets exact amount) */
19
+ feeOnTop?: boolean;
18
20
  }
19
- export declare function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, }: FiatCheckoutPanelProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function FiatCheckoutPanel({ recipientAddress, destinationTokenAddress, destinationTokenChainId, totalAmount, themeColor, onSuccess, onOrderCreated, onError, callbackMetadata, classes, feeOnTop, }: FiatCheckoutPanelProps): import("react/jsx-runtime").JSX.Element;
20
22
  export {};
@@ -10,6 +10,8 @@ export type OnrampOptions = {
10
10
  export type CreateOnrampOrderParams = Omit<CreateOrderParams, "srcChain" | "srcToken" | "srcAmount"> & {
11
11
  srcFiatAmount: string;
12
12
  onramp: OnrampOptions;
13
+ /** When true, fees are added on top of srcAmount (payer pays more, receiver gets exact amount) */
14
+ feeOnTop?: boolean;
13
15
  };
14
16
  export type UseAnyspendCreateOnrampOrderProps = {
15
17
  onSuccess?: (data: CreateOrderResponse) => void;
@@ -6,7 +6,7 @@ export declare const anyspendService: {
6
6
  getTokenList: (chainId: number, query: string) => Promise<components["schemas"]["Token"][]>;
7
7
  getToken: (chainId: number, tokenAddress: string) => Promise<components["schemas"]["Token"]>;
8
8
  getQuote: (req: GetQuoteRequest, partnerId?: string) => Promise<GetQuoteResponse>;
9
- createOrder: ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, }: {
9
+ createOrder: ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, feeOnTop, }: {
10
10
  recipientAddress: string;
11
11
  type: string;
12
12
  srcChain: number;
@@ -22,6 +22,7 @@ export declare const anyspendService: {
22
22
  clientReferenceId?: string;
23
23
  visitorData?: VisitorData;
24
24
  callbackMetadata?: Record<string, unknown>;
25
+ feeOnTop?: boolean;
25
26
  }) => Promise<{
26
27
  success: boolean;
27
28
  message: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.1.68-alpha.0",
3
+ "version": "0.1.68-alpha.2",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -130,6 +130,8 @@ export interface AnySpendCheckoutProps {
130
130
  onDiscountApplied?: (result: DiscountResult) => void;
131
131
  /** Async function to validate a discount code. Returns DiscountResult. */
132
132
  validateDiscount?: (code: string) => Promise<DiscountResult>;
133
+ /** When true, fees are added on top of the amount (payer pays more, receiver gets exact amount) */
134
+ feeOnTop?: boolean;
133
135
  }
134
136
 
135
137
  const emptyAddress: AddressData = { street: "", city: "", state: "", zip: "", country: "" };
@@ -175,6 +177,7 @@ export function AnySpendCheckout({
175
177
  enableDiscountCode,
176
178
  onDiscountApplied: onDiscountAppliedProp,
177
179
  validateDiscount,
180
+ feeOnTop,
178
181
  }: AnySpendCheckoutProps) {
179
182
  // ===== Form state =====
180
183
  const [formData, setFormData] = useState<Record<string, unknown>>({});
@@ -370,6 +373,7 @@ export function AnySpendCheckout({
370
373
  showOrderId={showOrderId}
371
374
  callbackMetadata={checkoutFormMetadata}
372
375
  isFormValid={isFormValid}
376
+ feeOnTop={feeOnTop}
373
377
  />
374
378
  </>
375
379
  }
@@ -37,6 +37,8 @@ interface CheckoutPaymentPanelProps {
37
37
  showOrderId?: boolean;
38
38
  /** Whether the checkout form is valid. When false, payment methods are disabled. */
39
39
  isFormValid?: boolean;
40
+ /** When true, fees are added on top (payer pays more, receiver gets exact amount) */
41
+ feeOnTop?: boolean;
40
42
  }
41
43
 
42
44
  function RadioCircle({ selected, themeColor }: { selected: boolean; themeColor?: string }) {
@@ -132,6 +134,7 @@ export function CheckoutPaymentPanel({
132
134
  showPoints,
133
135
  showOrderId,
134
136
  isFormValid = true,
137
+ feeOnTop,
135
138
  }: CheckoutPaymentPanelProps) {
136
139
  const [paymentMethod, setPaymentMethod] = useState<PaymentMethod | null>(defaultPaymentMethod ?? null);
137
140
 
@@ -291,6 +294,7 @@ export function CheckoutPaymentPanel({
291
294
  onError={onError}
292
295
  callbackMetadata={callbackMetadata}
293
296
  classes={classes}
297
+ feeOnTop={feeOnTop}
294
298
  />
295
299
  </div>
296
300
  </motion.div>
@@ -31,6 +31,8 @@ interface FiatCheckoutPanelProps {
31
31
  onError?: (error: Error) => void;
32
32
  callbackMetadata?: Record<string, unknown>;
33
33
  classes?: AnySpendCheckoutClasses;
34
+ /** When true, fees are added on top (payer pays more, receiver gets exact amount) */
35
+ feeOnTop?: boolean;
34
36
  }
35
37
 
36
38
  export function FiatCheckoutPanel({
@@ -44,6 +46,7 @@ export function FiatCheckoutPanel({
44
46
  onError,
45
47
  callbackMetadata,
46
48
  classes,
49
+ feeOnTop,
47
50
  }: FiatCheckoutPanelProps) {
48
51
  // Stable refs for callback props to avoid re-triggering effects
49
52
  const onErrorRef = useRef(onError);
@@ -170,6 +173,7 @@ export function FiatCheckoutPanel({
170
173
  },
171
174
  expectedDstAmount: totalAmount,
172
175
  callbackMetadata,
176
+ feeOnTop,
173
177
  });
174
178
  }
175
179
  }, [
@@ -189,6 +193,7 @@ export function FiatCheckoutPanel({
189
193
  geoData,
190
194
  callbackMetadata,
191
195
  createOrder,
196
+ feeOnTop,
192
197
  ]);
193
198
 
194
199
  // Loading geo/stripe support check (and quote for non-stablecoins)
@@ -24,6 +24,8 @@ export type OnrampOptions = {
24
24
  export type CreateOnrampOrderParams = Omit<CreateOrderParams, "srcChain" | "srcToken" | "srcAmount"> & {
25
25
  srcFiatAmount: string;
26
26
  onramp: OnrampOptions;
27
+ /** When true, fees are added on top of srcAmount (payer pays more, receiver gets exact amount) */
28
+ feeOnTop?: boolean;
27
29
  };
28
30
 
29
31
  export type UseAnyspendCreateOnrampOrderProps = {
@@ -115,6 +117,7 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError }: UseAnyspend
115
117
  clientReferenceId,
116
118
  visitorData,
117
119
  callbackMetadata: params.callbackMetadata,
120
+ feeOnTop: params.feeOnTop,
118
121
  });
119
122
  } catch (error: any) {
120
123
  // If the error has a response with message and statusCode, throw that
@@ -72,6 +72,7 @@ export const anyspendService = {
72
72
  clientReferenceId,
73
73
  visitorData,
74
74
  callbackMetadata,
75
+ feeOnTop,
75
76
  }: {
76
77
  recipientAddress: string;
77
78
  type: string;
@@ -88,6 +89,7 @@ export const anyspendService = {
88
89
  clientReferenceId?: string;
89
90
  visitorData?: VisitorData;
90
91
  callbackMetadata?: Record<string, unknown>;
92
+ feeOnTop?: boolean;
91
93
  }) => {
92
94
  const accessToken = await app.authentication.getAccessToken();
93
95
  const response = await fetch(`${ANYSPEND_MAINNET_BASE_URL}/orders`, {
@@ -113,6 +115,7 @@ export const anyspendService = {
113
115
  partnerId,
114
116
  ...(clientReferenceId && { clientReferenceId }),
115
117
  ...(callbackMetadata && { callbackMetadata }),
118
+ ...(feeOnTop != null && { feeOnTop }),
116
119
  }),
117
120
  });
118
121
  const data: CreateOrderResponse = await response.json();
@@ -153,23 +153,18 @@ export function useAuthentication(partnerId: string) {
153
153
 
154
154
  const logout = useCallback(
155
155
  async (callback?: () => void) => {
156
- if (activeWallet) {
157
- debug("@@logout:activeWallet", activeWallet);
158
- disconnect(activeWallet);
159
- debug("@@logout:activeWallet", activeWallet);
160
- }
161
-
162
- // Log out of each wallet
156
+ // Only disconnect ecosystem/smart wallets, preserve EOA wallets (e.g. MetaMask)
157
+ // so they remain available after re-login
163
158
  wallets.forEach(wallet => {
164
- console.log("@@logging out", wallet);
165
- disconnect(wallet);
159
+ debug("@@logout:wallet", wallet.id);
160
+ if (wallet.id.startsWith("ecosystem.") || wallet.id === "smart") {
161
+ disconnect(wallet);
162
+ }
166
163
  });
167
164
 
168
- // Delete localStorage thirdweb:connected-wallet-ids
169
- // https://npc-labs.slack.com/archives/C070E6HNG85/p1750185115273099
165
+ // Clear user-specific storage but preserve wallet connection state
166
+ // so EOA wallets (e.g. MetaMask) can auto-reconnect on next login
170
167
  if (typeof localStorage !== "undefined") {
171
- localStorage.removeItem("thirdweb:connected-wallet-ids");
172
- localStorage.removeItem("wagmi.store");
173
168
  localStorage.removeItem("lastAuthProvider");
174
169
  localStorage.removeItem("b3-user");
175
170
  }
@@ -186,7 +181,7 @@ export function useAuthentication(partnerId: string) {
186
181
  await onLogoutCallback();
187
182
  }
188
183
  },
189
- [activeWallet, disconnect, wallets, setIsAuthenticated, setUser, setIsConnected, onLogoutCallback],
184
+ [disconnect, wallets, setIsAuthenticated, setUser, setIsConnected, onLogoutCallback],
190
185
  );
191
186
 
192
187
  const onConnect = useCallback(