@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.
- package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckout.d.ts +3 -1
- package/dist/cjs/anyspend/react/components/checkout/AnySpendCheckout.js +2 -2
- package/dist/cjs/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +3 -1
- package/dist/cjs/anyspend/react/components/checkout/CheckoutPaymentPanel.js +2 -2
- package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +3 -1
- package/dist/cjs/anyspend/react/components/checkout/FiatCheckoutPanel.js +3 -1
- package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.d.ts +2 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
- package/dist/cjs/anyspend/services/anyspend.d.ts +2 -1
- package/dist/cjs/anyspend/services/anyspend.js +2 -1
- package/dist/cjs/global-account/react/hooks/useAuthentication.js +9 -13
- package/dist/esm/anyspend/react/components/checkout/AnySpendCheckout.d.ts +3 -1
- package/dist/esm/anyspend/react/components/checkout/AnySpendCheckout.js +2 -2
- package/dist/esm/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +3 -1
- package/dist/esm/anyspend/react/components/checkout/CheckoutPaymentPanel.js +2 -2
- package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +3 -1
- package/dist/esm/anyspend/react/components/checkout/FiatCheckoutPanel.js +3 -1
- package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.d.ts +2 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
- package/dist/esm/anyspend/services/anyspend.d.ts +2 -1
- package/dist/esm/anyspend/services/anyspend.js +2 -1
- package/dist/esm/global-account/react/hooks/useAuthentication.js +9 -13
- package/dist/types/anyspend/react/components/checkout/AnySpendCheckout.d.ts +3 -1
- package/dist/types/anyspend/react/components/checkout/CheckoutPaymentPanel.d.ts +3 -1
- package/dist/types/anyspend/react/components/checkout/FiatCheckoutPanel.d.ts +3 -1
- package/dist/types/anyspend/react/hooks/useAnyspendCreateOnrampOrder.d.ts +2 -0
- package/dist/types/anyspend/services/anyspend.d.ts +2 -1
- package/package.json +1 -1
- package/src/anyspend/react/components/checkout/AnySpendCheckout.tsx +4 -0
- package/src/anyspend/react/components/checkout/CheckoutPaymentPanel.tsx +4 -0
- package/src/anyspend/react/components/checkout/FiatCheckoutPanel.tsx +5 -0
- package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +3 -0
- package/src/anyspend/services/anyspend.ts +3 -0
- 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;
|
|
@@ -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
|
-
|
|
136
|
-
|
|
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
|
-
|
|
143
|
-
|
|
138
|
+
debug("@@logout:wallet", wallet.id);
|
|
139
|
+
if (wallet.id.startsWith("ecosystem.") || wallet.id === "smart") {
|
|
140
|
+
disconnect(wallet);
|
|
141
|
+
}
|
|
144
142
|
});
|
|
145
|
-
//
|
|
146
|
-
//
|
|
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
|
-
}, [
|
|
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;
|
|
@@ -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
|
-
|
|
130
|
-
|
|
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
|
-
|
|
137
|
-
|
|
132
|
+
debug("@@logout:wallet", wallet.id);
|
|
133
|
+
if (wallet.id.startsWith("ecosystem.") || wallet.id === "smart") {
|
|
134
|
+
disconnect(wallet);
|
|
135
|
+
}
|
|
138
136
|
});
|
|
139
|
-
//
|
|
140
|
-
//
|
|
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
|
-
}, [
|
|
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
|
@@ -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
|
-
|
|
157
|
-
|
|
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
|
-
|
|
165
|
-
|
|
159
|
+
debug("@@logout:wallet", wallet.id);
|
|
160
|
+
if (wallet.id.startsWith("ecosystem.") || wallet.id === "smart") {
|
|
161
|
+
disconnect(wallet);
|
|
162
|
+
}
|
|
166
163
|
});
|
|
167
164
|
|
|
168
|
-
//
|
|
169
|
-
//
|
|
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
|
-
[
|
|
184
|
+
[disconnect, wallets, setIsAuthenticated, setUser, setIsConnected, onLogoutCallback],
|
|
190
185
|
);
|
|
191
186
|
|
|
192
187
|
const onConnect = useCallback(
|