@b3dotfun/sdk 0.1.65-alpha.5 → 0.1.65-alpha.7

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 (59) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.d.ts +2 -0
  2. package/dist/cjs/anyspend/react/components/AnySpend.js +4 -2
  3. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
  4. package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +4 -2
  5. package/dist/cjs/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
  6. package/dist/cjs/anyspend/react/components/AnySpendDeposit.js +2 -2
  7. package/dist/cjs/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
  8. package/dist/cjs/anyspend/react/components/common/CryptoPaySection.js +3 -3
  9. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
  10. package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +3 -57
  11. package/dist/cjs/anyspend/react/components/common/PaySection.js +1 -1
  12. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
  13. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
  14. package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOrder.js +1 -0
  15. package/dist/cjs/anyspend/services/anyspend.d.ts +2 -1
  16. package/dist/cjs/anyspend/services/anyspend.js +2 -1
  17. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +2 -0
  18. package/dist/esm/anyspend/react/components/AnySpend.d.ts +2 -0
  19. package/dist/esm/anyspend/react/components/AnySpend.js +4 -2
  20. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
  21. package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +4 -2
  22. package/dist/esm/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
  23. package/dist/esm/anyspend/react/components/AnySpendDeposit.js +2 -2
  24. package/dist/esm/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
  25. package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +3 -3
  26. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
  27. package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +2 -56
  28. package/dist/esm/anyspend/react/components/common/PaySection.js +1 -1
  29. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +1 -0
  30. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
  31. package/dist/esm/anyspend/react/hooks/useAnyspendCreateOrder.js +1 -0
  32. package/dist/esm/anyspend/services/anyspend.d.ts +2 -1
  33. package/dist/esm/anyspend/services/anyspend.js +2 -1
  34. package/dist/esm/global-account/react/stores/useModalStore.d.ts +2 -0
  35. package/dist/types/anyspend/react/components/AnySpend.d.ts +2 -0
  36. package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +2 -0
  37. package/dist/types/anyspend/react/components/AnySpendDeposit.d.ts +3 -1
  38. package/dist/types/anyspend/react/components/common/CryptoPaySection.d.ts +1 -3
  39. package/dist/types/anyspend/react/components/common/OrderTokenAmount.d.ts +1 -4
  40. package/dist/types/anyspend/react/hooks/useAnyspendCreateOrder.d.ts +1 -0
  41. package/dist/types/anyspend/services/anyspend.d.ts +2 -1
  42. package/dist/types/global-account/react/stores/useModalStore.d.ts +2 -0
  43. package/package.json +1 -1
  44. package/src/anyspend/README.md +14 -0
  45. package/src/anyspend/docs/checkout-sessions.md +228 -0
  46. package/src/anyspend/docs/components.md +26 -0
  47. package/src/anyspend/docs/examples.md +58 -0
  48. package/src/anyspend/docs/hooks.md +32 -0
  49. package/src/anyspend/llms.txt +185 -0
  50. package/src/anyspend/react/components/AnySpend.tsx +6 -1
  51. package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +5 -1
  52. package/src/anyspend/react/components/AnySpendDeposit.tsx +5 -0
  53. package/src/anyspend/react/components/common/CryptoPaySection.tsx +0 -5
  54. package/src/anyspend/react/components/common/OrderTokenAmount.tsx +1 -70
  55. package/src/anyspend/react/components/common/PaySection.tsx +0 -1
  56. package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +1 -0
  57. package/src/anyspend/react/hooks/useAnyspendCreateOrder.ts +2 -0
  58. package/src/anyspend/services/anyspend.ts +3 -0
  59. package/src/global-account/react/stores/useModalStore.ts +2 -0
@@ -1,5 +1,5 @@
1
1
  import { components } from "../../../../anyspend/types/api";
2
- export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect, canEditAmount, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, skipAutoMaxOnTokenChange, }: {
2
+ export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect, canEditAmount, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, }: {
3
3
  disabled?: boolean;
4
4
  inputValue: string;
5
5
  onChangeInput: (value: string) => void;
@@ -18,7 +18,4 @@ export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput,
18
18
  onTokenSelect?: (token: components["schemas"]["Token"], event: {
19
19
  preventDefault: () => void;
20
20
  }) => void;
21
- walletAddress?: string | undefined;
22
- /** When true, skip auto-setting max balance when token changes (used for fixed destination amount mode) */
23
- skipAutoMaxOnTokenChange?: boolean;
24
21
  }): import("react/jsx-runtime").JSX.Element;
@@ -1,63 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ChevronsUpDown } from "lucide-react";
4
- import { useEffect, useRef } from "react";
5
4
  import { NumericFormat } from "react-number-format";
6
- import { formatUnits } from "viem";
7
5
  import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID, getAvailableChainIds } from "../../../../anyspend/index.js";
8
- import { getNativeRequired } from "../../../../anyspend/utils/chain.js";
9
- import { isNativeToken } from "../../../../anyspend/utils/token.js";
10
- import { Button, useTokenBalance } from "../../../../global-account/react/index.js";
6
+ import { Button } from "../../../../global-account/react/index.js";
11
7
  import { cn } from "../../../../shared/utils/index.js";
12
8
  import { TokenSelector } from "@relayprotocol/relay-kit-ui";
13
9
  import { ChainTokenIcon } from "./ChainTokenIcon.js";
14
- export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect = false, canEditAmount = true, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, skipAutoMaxOnTokenChange = false, }) {
15
- // Track previous token to detect changes
16
- const prevTokenRef = useRef(token.address);
17
- // Only get token balance when context is "from" (for setting max amount)
18
- const { rawBalance } = useTokenBalance({
19
- token,
20
- address: context === "from" && walletAddress ? walletAddress : undefined,
21
- });
22
- useEffect(() => {
23
- // Only handle "from" context
24
- if (context !== "from")
25
- return;
26
- // Skip auto-max when in fixed destination amount mode
27
- if (skipAutoMaxOnTokenChange) {
28
- prevTokenRef.current = token.address;
29
- return;
30
- }
31
- // Check if token changed or if this is the initial load with balance
32
- const isTokenChanged = prevTokenRef.current !== token.address;
33
- if (isTokenChanged && rawBalance) {
34
- console.log(`Setting max balance - Token: ${token.address}, Changed: ${isTokenChanged}`);
35
- // Calculate max amount with gas reserve for native tokens
36
- let maxAmount;
37
- if (isNativeToken(token.address)) {
38
- const gasReserve = getNativeRequired(token.chainId);
39
- // Ensure we don't go negative
40
- maxAmount = rawBalance > gasReserve ? rawBalance - gasReserve : BigInt(0);
41
- }
42
- else {
43
- // For ERC20 tokens, use full balance
44
- maxAmount = rawBalance;
45
- }
46
- // Set the max amount as input value
47
- onChangeInput(formatUnits(maxAmount, token.decimals));
48
- // Update refs
49
- prevTokenRef.current = token.address;
50
- }
51
- }, [
52
- token.address,
53
- token.chainId,
54
- token.decimals,
55
- chainId,
56
- context,
57
- onChangeInput,
58
- rawBalance,
59
- skipAutoMaxOnTokenChange,
60
- ]);
10
+ export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect = false, canEditAmount = true, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, }) {
61
11
  const handleTokenSelect = (newToken) => {
62
12
  const token = {
63
13
  address: newToken.address,
@@ -80,11 +30,7 @@ export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context,
80
30
  return; // Early return if callback prevented default behavior
81
31
  }
82
32
  }
83
- // Mark that we're about to change tokens
84
- prevTokenRef.current = "changing"; // Temporary value to force effect
85
- // Set the chain ID first
86
33
  setChainId(newToken.chainId);
87
- // Then set the new token - the useEffect will handle setting the max balance
88
34
  setToken(token);
89
35
  };
90
36
  return (_jsx("div", { className: cn("border-as-stroke flex w-full flex-col gap-2 rounded-xl", className), children: _jsxs("div", { className: cn("flex items-center justify-between gap-3", innerClassName), children: [!canEditAmount ? (_jsx("h2", { className: cn("text-3xl font-medium text-white", amountClassName), children: inputValue || "--" })) : (_jsx(NumericFormat, { decimalSeparator: ".", allowedDecimalSeparators: [","], thousandSeparator: true, inputMode: "decimal", autoComplete: "off", autoCorrect: "off", type: "text", placeholder: "0.00", minLength: 1, maxLength: 30, spellCheck: "false", className: cn("placeholder:text-as-primary/70 disabled:text-as-primary/70 text-as-primary w-full bg-transparent text-4xl font-semibold leading-[42px] outline-none sm:text-[30px]", amountClassName), pattern: "^[0-9]*[.,]?[0-9]*$", disabled: disabled, value: inputValue, allowNegative: false, onChange: e => onChangeInput(e.currentTarget.value) }, `input-${token.address}-${chainId}`)), !hideTokenSelect && (_jsx(TokenSelector, { address: address, chainIdsFilter: getAvailableChainIds(context), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: getAvailableChainIds(context), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID], setToken: handleTokenSelect, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: _jsxs(Button, { variant: "outline", role: "combobox", className: cn("token-selector-button bg-b3-react-background border-as-stroke flex h-auto w-fit shrink-0 items-center justify-center gap-2 rounded-xl border-2 px-2 py-1 pr-2 text-center", tokenSelectClassName), children: [token.metadata.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[chainId].logoUrl, tokenUrl: token.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: token.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[chainId].name })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) }, `selector-${context}-${token.address}-${chainId}`))] }) }, `${context}-${token.address}-${chainId}`));
@@ -39,7 +39,7 @@ export function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, se
39
39
  useEffect(() => {
40
40
  appliedSrcMetadataRef.current = false;
41
41
  }, [selectedSrcToken.address, selectedSrcToken.chainId]);
42
- return (_jsxs(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, className: "pay-section bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary/50 flex h-7 items-center text-sm", children: "Pay" }), _jsx("button", { className: "text-as-tertiarry flex h-7 items-center gap-2 text-sm transition-colors focus:!outline-none", onClick: onSelectCryptoPaymentMethod, children: selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ? (_jsxs(_Fragment, { children: [isConnected ? (_jsx("div", { className: "flex items-center gap-1", children: connectedName ? formatUsername(connectedName) : shortenAddress(connectedAddress || "") })) : ("Connect wallet"), _jsx(ChevronRight, { className: "h-4 w-4" })] })) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET ? (_jsxs(_Fragment, { children: ["Global Account", _jsx(ChevronRight, { className: "h-4 w-4" })] })) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO ? (_jsxs(_Fragment, { children: ["Transfer crypto", _jsx(ChevronRight, { className: "h-4 w-4" })] })) : (_jsxs(_Fragment, { children: ["Select payment method", _jsx(ChevronRight, { className: "h-4 w-4" })] })) })] }), _jsx(OrderTokenAmount, { address: connectedAddress, walletAddress: connectedAddress, context: "from", inputValue: srcAmount, onChangeInput: value => {
42
+ return (_jsxs(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, className: "pay-section bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary/50 flex h-7 items-center text-sm", children: "Pay" }), _jsx("button", { className: "text-as-tertiarry flex h-7 items-center gap-2 text-sm transition-colors focus:!outline-none", onClick: onSelectCryptoPaymentMethod, children: selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ? (_jsxs(_Fragment, { children: [isConnected ? (_jsx("div", { className: "flex items-center gap-1", children: connectedName ? formatUsername(connectedName) : shortenAddress(connectedAddress || "") })) : ("Connect wallet"), _jsx(ChevronRight, { className: "h-4 w-4" })] })) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET ? (_jsxs(_Fragment, { children: ["Global Account", _jsx(ChevronRight, { className: "h-4 w-4" })] })) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO ? (_jsxs(_Fragment, { children: ["Transfer crypto", _jsx(ChevronRight, { className: "h-4 w-4" })] })) : (_jsxs(_Fragment, { children: ["Select payment method", _jsx(ChevronRight, { className: "h-4 w-4" })] })) })] }), _jsx(OrderTokenAmount, { address: connectedAddress, context: "from", inputValue: srcAmount, onChangeInput: value => {
43
43
  setIsSrcInputDirty(true);
44
44
  setSrcAmount(value);
45
45
  }, chainId: selectedSrcChainId, setChainId: setSelectedSrcChainId, token: selectedSrcToken, setToken: setSelectedSrcToken }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary/50 flex h-5 items-center text-sm", children: formatDisplayNumber(anyspendQuote?.data?.currencyIn?.amountUsd, {
@@ -71,6 +71,7 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
71
71
  partnerId,
72
72
  clientReferenceId,
73
73
  visitorData,
74
+ callbackMetadata: params.callbackMetadata,
74
75
  });
75
76
  }
76
77
  catch (error) {
@@ -18,6 +18,7 @@ export type CreateOrderParams = {
18
18
  creatorAddress?: string;
19
19
  payload?: any;
20
20
  metadata?: Record<string, any>;
21
+ callbackMetadata?: Record<string, unknown>;
21
22
  };
22
23
  export type UseAnyspendCreateOrderProps = {
23
24
  onSuccess?: (data: any) => void;
@@ -60,6 +60,7 @@ export function useAnyspendCreateOrder({ onSuccess, onError } = {}) {
60
60
  partnerId,
61
61
  clientReferenceId,
62
62
  visitorData,
63
+ callbackMetadata: params.callbackMetadata,
63
64
  });
64
65
  }
65
66
  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, }: {
9
+ createOrder: ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, }: {
10
10
  recipientAddress: string;
11
11
  type: string;
12
12
  srcChain: number;
@@ -21,6 +21,7 @@ export declare const anyspendService: {
21
21
  partnerId?: string;
22
22
  clientReferenceId?: string;
23
23
  visitorData?: VisitorData;
24
+ callbackMetadata?: Record<string, unknown>;
24
25
  }) => Promise<{
25
26
  success: boolean;
26
27
  message: string;
@@ -36,7 +36,7 @@ export const anyspendService = {
36
36
  return data;
37
37
  },
38
38
  // Order related
39
- createOrder: async ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, }) => {
39
+ createOrder: async ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, }) => {
40
40
  const accessToken = await app.authentication.getAccessToken();
41
41
  const response = await fetch(`${ANYSPEND_MAINNET_BASE_URL}/orders`, {
42
42
  method: "POST",
@@ -60,6 +60,7 @@ export const anyspendService = {
60
60
  creatorAddress,
61
61
  partnerId,
62
62
  ...(clientReferenceId && { clientReferenceId }),
63
+ ...(callbackMetadata && { callbackMetadata }),
63
64
  }),
64
65
  });
65
66
  const data = await response.json();
@@ -536,6 +536,8 @@ export interface AnySpendDepositModalProps extends BaseModalProps {
536
536
  classes?: AnySpendAllClasses;
537
537
  /** Whether to allow direct transfer without swap */
538
538
  allowDirectTransfer?: boolean;
539
+ /** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
540
+ callbackMetadata?: Record<string, unknown>;
539
541
  }
540
542
  /**
541
543
  * Union type of all possible modal content types
@@ -53,4 +53,6 @@ export declare function AnySpend(props: {
53
53
  allowDirectTransfer?: boolean;
54
54
  /** Fixed destination token amount (in wei/smallest unit). When provided, user cannot change the amount. */
55
55
  destinationTokenAmount?: string;
56
+ /** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
57
+ callbackMetadata?: Record<string, unknown>;
56
58
  }): import("react/jsx-runtime").JSX.Element;
@@ -44,6 +44,8 @@ export interface AnySpendCustomExactInProps {
44
44
  classes?: AnySpendCustomExactInClasses;
45
45
  /** When true, allows direct transfer without swap if source and destination token/chain are the same */
46
46
  allowDirectTransfer?: boolean;
47
+ /** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
48
+ callbackMetadata?: Record<string, unknown>;
47
49
  }
48
50
  export declare function AnySpendCustomExactIn(props: AnySpendCustomExactInProps): import("react/jsx-runtime").JSX.Element;
49
51
  export {};
@@ -101,6 +101,8 @@ export interface AnySpendDepositProps {
101
101
  allowDirectTransfer?: boolean;
102
102
  /** Fixed destination token amount (in wei/smallest unit). When provided, user cannot change the amount. */
103
103
  destinationTokenAmount?: string;
104
+ /** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
105
+ callbackMetadata?: Record<string, unknown>;
104
106
  }
105
107
  /**
106
108
  * A flexible deposit component that wraps AnySpendCustomExactIn with optional chain selection.
@@ -137,4 +139,4 @@ export interface AnySpendDepositProps {
137
139
  * onSuccess={(amount) => console.log(`Deposited ${amount}`)}
138
140
  * />
139
141
  */
140
- export declare function AnySpendDeposit({ loadOrder, mode, recipientAddress, paymentType: initialPaymentType, sourceTokenAddress, sourceTokenChainId: initialSourceChainId, destinationTokenAddress, destinationTokenChainId, onSuccess, onOpenCustomModal, mainFooter, onTokenSelect, customUsdInputValues, preferEoa, minDestinationAmount, header, orderType, depositContractConfig, showChainSelection, supportedChains, minPoolSize, topChainsCount, onClose, returnToHomeUrl, customRecipientLabel, returnHomeLabel, isCustomDeposit, classes, allowDirectTransfer, destinationTokenAmount, }: AnySpendDepositProps): import("react/jsx-runtime").JSX.Element | null;
142
+ export declare function AnySpendDeposit({ loadOrder, mode, recipientAddress, paymentType: initialPaymentType, sourceTokenAddress, sourceTokenChainId: initialSourceChainId, destinationTokenAddress, destinationTokenChainId, onSuccess, onOpenCustomModal, mainFooter, onTokenSelect, customUsdInputValues, preferEoa, minDestinationAmount, header, orderType, depositContractConfig, showChainSelection, supportedChains, minPoolSize, topChainsCount, onClose, returnToHomeUrl, customRecipientLabel, returnHomeLabel, isCustomDeposit, classes, allowDirectTransfer, destinationTokenAmount, callbackMetadata, }: AnySpendDepositProps): import("react/jsx-runtime").JSX.Element | null;
@@ -18,8 +18,6 @@ interface CryptoPaySectionProps {
18
18
  }) => void;
19
19
  onShowFeeDetail?: () => void;
20
20
  classes?: CryptoPaySectionClasses;
21
- /** When true, skip auto-setting max balance when token changes (used for fixed destination amount mode) */
22
- skipAutoMaxOnTokenChange?: boolean;
23
21
  }
24
- export declare function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, selectedSrcToken, setSelectedSrcToken, srcAmount, setSrcAmount, isSrcInputDirty, setIsSrcInputDirty, selectedCryptoPaymentMethod, onSelectCryptoPaymentMethod, anyspendQuote, onTokenSelect, onShowFeeDetail, classes, skipAutoMaxOnTokenChange, }: CryptoPaySectionProps): import("react/jsx-runtime").JSX.Element;
22
+ export declare function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, selectedSrcToken, setSelectedSrcToken, srcAmount, setSrcAmount, isSrcInputDirty, setIsSrcInputDirty, selectedCryptoPaymentMethod, onSelectCryptoPaymentMethod, anyspendQuote, onTokenSelect, onShowFeeDetail, classes, }: CryptoPaySectionProps): import("react/jsx-runtime").JSX.Element;
25
23
  export {};
@@ -1,5 +1,5 @@
1
1
  import { components } from "@b3dotfun/sdk/anyspend/types/api";
2
- export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect, canEditAmount, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, skipAutoMaxOnTokenChange, }: {
2
+ export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect, canEditAmount, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, }: {
3
3
  disabled?: boolean;
4
4
  inputValue: string;
5
5
  onChangeInput: (value: string) => void;
@@ -18,7 +18,4 @@ export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput,
18
18
  onTokenSelect?: (token: components["schemas"]["Token"], event: {
19
19
  preventDefault: () => void;
20
20
  }) => void;
21
- walletAddress?: string | undefined;
22
- /** When true, skip auto-setting max balance when token changes (used for fixed destination amount mode) */
23
- skipAutoMaxOnTokenChange?: boolean;
24
21
  }): import("react/jsx-runtime").JSX.Element;
@@ -18,6 +18,7 @@ export type CreateOrderParams = {
18
18
  creatorAddress?: string;
19
19
  payload?: any;
20
20
  metadata?: Record<string, any>;
21
+ callbackMetadata?: Record<string, unknown>;
21
22
  };
22
23
  export type UseAnyspendCreateOrderProps = {
23
24
  onSuccess?: (data: any) => 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, }: {
9
+ createOrder: ({ recipientAddress, type, srcChain, dstChain, srcTokenAddress, dstTokenAddress, srcAmount, payload, onramp, metadata, creatorAddress, partnerId, clientReferenceId, visitorData, callbackMetadata, }: {
10
10
  recipientAddress: string;
11
11
  type: string;
12
12
  srcChain: number;
@@ -21,6 +21,7 @@ export declare const anyspendService: {
21
21
  partnerId?: string;
22
22
  clientReferenceId?: string;
23
23
  visitorData?: VisitorData;
24
+ callbackMetadata?: Record<string, unknown>;
24
25
  }) => Promise<{
25
26
  success: boolean;
26
27
  message: string;
@@ -536,6 +536,8 @@ export interface AnySpendDepositModalProps extends BaseModalProps {
536
536
  classes?: AnySpendAllClasses;
537
537
  /** Whether to allow direct transfer without swap */
538
538
  allowDirectTransfer?: boolean;
539
+ /** Opaque metadata passed to the order for callbacks (e.g., workflow form data) */
540
+ callbackMetadata?: Record<string, unknown>;
539
541
  }
540
542
  /**
541
543
  * Union type of all possible modal content types
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.1.65-alpha.5",
3
+ "version": "0.1.65-alpha.7",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -81,6 +81,7 @@ That's it! Your users can now purchase NFTs with any token from any supported ch
81
81
  | [🧩 Components API](./docs/components.md) | Pre-built React components for common use cases |
82
82
  | [🪝 Hooks API](./docs/hooks.md) | React hooks for custom implementations |
83
83
  | [💡 Examples & Use Cases](./docs/examples.md) | Real-world integration examples |
84
+ | [🛒 Checkout Sessions](./docs/checkout-sessions.md) | Stripe-like checkout sessions for merchant integrations |
84
85
  | [⚠️ Error Handling](./docs/error-handling.md) | Error handling patterns and troubleshooting |
85
86
  | [🤝 Contributing](./docs/contributing.md) | How to contribute to AnySpend |
86
87
 
@@ -123,6 +124,19 @@ That's it! Your users can now purchase NFTs with any token from any supported ch
123
124
  />
124
125
  ```
125
126
 
127
+ ### Merchant Checkout Sessions
128
+ ```tsx
129
+ <AnySpend
130
+ defaultActiveTab="fiat"
131
+ destinationTokenAddress="0x..." // USDC
132
+ recipientAddress={userAddress}
133
+ checkoutSession={{
134
+ success_url: "https://myshop.com/success?session={SESSION_ID}",
135
+ metadata: { sku: "widget-1" },
136
+ }}
137
+ />
138
+ ```
139
+
126
140
  ## 🌐 Supported Networks
127
141
 
128
142
  - **Ethereum** (Mainnet & Sepolia)
@@ -0,0 +1,228 @@
1
+ # Checkout Sessions
2
+
3
+ Stripe-like checkout sessions for AnySpend. Sessions are decoupled from orders — create a session first, then create an order when the user is ready to pay.
4
+
5
+ ## Flow
6
+
7
+ ```
8
+ 1. Merchant creates session POST /checkout-sessions
9
+ <- { id, status: "open" }
10
+
11
+ 2. User picks payment method POST /orders { checkoutSessionId }
12
+ <- { id, globalAddress, oneClickBuyUrl }
13
+
14
+ 3. User pays Crypto: send to globalAddress
15
+ Onramp: redirect to oneClickBuyUrl
16
+
17
+ 4. Merchant polls for completion GET /checkout-sessions/:id
18
+ <- { status: "complete", order_id }
19
+ ```
20
+
21
+ ### Why Decoupled?
22
+
23
+ Session creation is instant (DB-only, no external calls). The order is created separately when the user commits to a payment method. This means:
24
+
25
+ - Payment method doesn't need to be known at session creation
26
+ - A hosted checkout page can let users choose how to pay
27
+ - Session creation never fails due to external API errors
28
+
29
+ ## Session Status Lifecycle
30
+
31
+ ```
32
+ open --> processing --> complete
33
+ |
34
+ └--> expired
35
+ ```
36
+
37
+ | Status | When |
38
+ |--------|------|
39
+ | `open` | Created, waiting for order/payment |
40
+ | `processing` | Payment received, order executing |
41
+ | `complete` | Order executed successfully |
42
+ | `expired` | TTL expired, payment failed, or manually expired |
43
+
44
+ ## API
45
+
46
+ ### `POST /checkout-sessions` — Create Session
47
+
48
+ Creates a lightweight session. No order, no external API calls.
49
+
50
+ ```json
51
+ {
52
+ "success_url": "https://merchant.com/success?session_id={SESSION_ID}",
53
+ "cancel_url": "https://merchant.com/cancel",
54
+ "metadata": { "sku": "widget-1" },
55
+ "client_reference_id": "merchant-order-456",
56
+ "expires_in": 1800
57
+ }
58
+ ```
59
+
60
+ All fields are optional. Payment config (amount, tokens, chains) lives on the order, not the session.
61
+
62
+ ### `POST /orders` — Create Order with Session Linking
63
+
64
+ Pass `checkoutSessionId` in the standard order creation request to link the order to a session.
65
+
66
+ ```json
67
+ {
68
+ "recipientAddress": "0x...",
69
+ "srcChain": 8453,
70
+ "dstChain": 8453,
71
+ "srcTokenAddress": "0x...",
72
+ "dstTokenAddress": "0x...",
73
+ "srcAmount": "1000000",
74
+ "type": "swap",
75
+ "payload": { "expectedDstAmount": "1000000" },
76
+ "checkoutSessionId": "550e8400-..."
77
+ }
78
+ ```
79
+
80
+ **Validation:**
81
+ - Session must exist (`400` if not found)
82
+ - Session must be `open` (`400` if expired/processing/complete)
83
+ - Session must not already have an order (`409 Conflict`)
84
+
85
+ ### `GET /checkout-sessions/:id` — Retrieve Session
86
+
87
+ Returns current session state. Status is synced from the underlying order on each retrieval.
88
+
89
+ | Query Param | Description |
90
+ |-------------|-------------|
91
+ | `include=order` | Embed the full order object with transactions |
92
+
93
+ ### `POST /checkout-sessions/:id/expire` — Manually Expire
94
+
95
+ Only works on sessions with status `open`.
96
+
97
+ ## Redirect URL Templates
98
+
99
+ Use template variables in `success_url` and `cancel_url`:
100
+
101
+ | Variable | Replaced with |
102
+ |----------|--------------|
103
+ | `{SESSION_ID}` | The checkout session UUID |
104
+ | `{ORDER_ID}` | Same value (alias) |
105
+
106
+ If no template variable is present, `?sessionId=<uuid>` is appended automatically.
107
+
108
+ ## SDK Integration
109
+
110
+ ### Service Methods
111
+
112
+ ```typescript
113
+ // Create a checkout session
114
+ const session = await anyspend.createCheckoutSession({
115
+ success_url: "https://mysite.com/success/{SESSION_ID}",
116
+ metadata: { sku: "widget-1" },
117
+ });
118
+
119
+ // Retrieve session status
120
+ const session = await anyspend.getCheckoutSession(sessionId);
121
+ ```
122
+
123
+ ### React Hooks
124
+
125
+ #### `useCreateCheckoutSession`
126
+
127
+ Mutation hook for creating sessions.
128
+
129
+ ```tsx
130
+ import { useCreateCheckoutSession } from "@b3dotfun/sdk/anyspend";
131
+
132
+ const { mutate: createSession, data, isPending } = useCreateCheckoutSession();
133
+ ```
134
+
135
+ #### `useCheckoutSession`
136
+
137
+ Query hook with auto-polling. Stops polling when status reaches `complete` or `expired`.
138
+
139
+ ```tsx
140
+ import { useCheckoutSession } from "@b3dotfun/sdk/anyspend";
141
+
142
+ const { data: session, isLoading } = useCheckoutSession(sessionId);
143
+ ```
144
+
145
+ ### Component `checkoutSession` Prop
146
+
147
+ The `<AnySpend>`, `<AnySpendCustom>`, and `<AnySpendCustomExactIn>` components accept an optional `checkoutSession` prop:
148
+
149
+ ```tsx
150
+ <AnySpend
151
+ defaultActiveTab="fiat"
152
+ destinationTokenAddress="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
153
+ destinationTokenChainId={8453}
154
+ recipientAddress="0x..."
155
+ checkoutSession={{
156
+ success_url: "https://myshop.com/success?session={SESSION_ID}",
157
+ cancel_url: "https://myshop.com/cancel",
158
+ metadata: { sku: "widget-1" },
159
+ }}
160
+ />
161
+ ```
162
+
163
+ When the `checkoutSession` prop is set, the component automatically creates a session before creating the order, and uses the session's `success_url` for redirects. Without the prop, existing flows are unchanged.
164
+
165
+ ## Examples
166
+
167
+ ### Crypto Payment
168
+
169
+ ```typescript
170
+ // 1. Create session
171
+ const session = await fetch("/checkout-sessions", {
172
+ method: "POST",
173
+ body: JSON.stringify({
174
+ success_url: "https://mysite.com/success/{SESSION_ID}",
175
+ metadata: { sku: "widget-1" },
176
+ }),
177
+ }).then(r => r.json());
178
+
179
+ // 2. Create order linked to session
180
+ const order = await fetch("/orders", {
181
+ method: "POST",
182
+ body: JSON.stringify({
183
+ recipientAddress: "0x...",
184
+ srcChain: 8453,
185
+ dstChain: 8453,
186
+ srcTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
187
+ dstTokenAddress: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
188
+ srcAmount: "1000000",
189
+ type: "swap",
190
+ payload: { expectedDstAmount: "1000000" },
191
+ checkoutSessionId: session.data.id,
192
+ }),
193
+ }).then(r => r.json());
194
+
195
+ // 3. User sends crypto to order.data.globalAddress
196
+
197
+ // 4. Poll session until complete
198
+ const poll = setInterval(async () => {
199
+ const s = await fetch(`/checkout-sessions/${session.data.id}`).then(r => r.json());
200
+ if (s.data.status === "complete") {
201
+ clearInterval(poll);
202
+ // redirect to success_url or show confirmation
203
+ }
204
+ }, 3000);
205
+ ```
206
+
207
+ ### Onramp Payment (Coinbase/Stripe)
208
+
209
+ ```typescript
210
+ // Steps 1-2 same as above, but include onramp config in order creation:
211
+ const order = await fetch("/orders", {
212
+ method: "POST",
213
+ body: JSON.stringify({
214
+ // ... same order fields ...
215
+ checkoutSessionId: session.data.id,
216
+ onramp: {
217
+ vendor: "coinbase",
218
+ payment_method: "card",
219
+ country: "US",
220
+ },
221
+ }),
222
+ }).then(r => r.json());
223
+
224
+ // Redirect user to vendor checkout page
225
+ window.location.href = order.data.oneClickBuyUrl;
226
+
227
+ // After vendor redirects back, poll GET /checkout-sessions/:id for completion
228
+ ```
@@ -207,6 +207,32 @@ const stakingCalldata = encodeFunctionData({
207
207
  />
208
208
  ```
209
209
 
210
+ ### Checkout Session Prop
211
+
212
+ The `<AnySpend>`, `<AnySpendCustom>`, and `<AnySpendCustomExactIn>` components accept an optional `checkoutSession` prop for merchant checkout flows. When set, the component creates a session before the order and uses the session's redirect URLs. See [Checkout Sessions](./checkout-sessions.md) for the full guide.
213
+
214
+ ```tsx
215
+ <AnySpend
216
+ defaultActiveTab="fiat"
217
+ destinationTokenAddress="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
218
+ destinationTokenChainId={8453}
219
+ recipientAddress="0x..."
220
+ checkoutSession={{
221
+ success_url: "https://myshop.com/success?session={SESSION_ID}",
222
+ cancel_url: "https://myshop.com/cancel",
223
+ metadata: { sku: "widget-1" },
224
+ }}
225
+ />
226
+ ```
227
+
228
+ | Prop | Type | Description |
229
+ |------|------|-------------|
230
+ | `checkoutSession.success_url` | `string` | Redirect URL on completion. Supports `{SESSION_ID}` template. |
231
+ | `checkoutSession.cancel_url` | `string` | Redirect URL on cancellation |
232
+ | `checkoutSession.metadata` | `Record<string, string>` | Custom metadata attached to the session |
233
+
234
+ ---
235
+
210
236
  ## Specialized Components
211
237
 
212
238
  ### `<AnySpendNFT>`
@@ -720,8 +720,66 @@ function PortfolioRebalancer() {
720
720
  }
721
721
  ```
722
722
 
723
+ ## 🛒 Checkout Sessions (Merchant Integration)
724
+
725
+ ### Hosted Checkout with Payment Choice
726
+
727
+ Let users choose their payment method (crypto or fiat) after session creation.
728
+
729
+ ```tsx
730
+ import { AnySpend } from "@b3dotfun/sdk/anyspend/react";
731
+
732
+ function MerchantCheckout({ sku, price }: { sku: string; price: string }) {
733
+ const [userAddress] = useWallet();
734
+
735
+ return (
736
+ <AnySpend
737
+ defaultActiveTab="fiat"
738
+ destinationTokenAddress="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
739
+ destinationTokenChainId={8453}
740
+ recipientAddress={userAddress}
741
+ checkoutSession={{
742
+ success_url: "https://myshop.com/success?session={SESSION_ID}",
743
+ cancel_url: "https://myshop.com/cancel",
744
+ metadata: { sku, price },
745
+ }}
746
+ />
747
+ );
748
+ }
749
+ ```
750
+
751
+ ### Server-Side Session with Custom Polling
752
+
753
+ ```tsx
754
+ import {
755
+ useCreateCheckoutSession,
756
+ useCheckoutSession,
757
+ } from "@b3dotfun/sdk/anyspend";
758
+
759
+ function ServerCheckout() {
760
+ const { mutate: createSession, data: session } = useCreateCheckoutSession();
761
+ const { data: sessionStatus } = useCheckoutSession(session?.data?.id);
762
+
763
+ useEffect(() => {
764
+ createSession({
765
+ success_url: "https://mysite.com/success/{SESSION_ID}",
766
+ metadata: { sku: "widget-1" },
767
+ });
768
+ }, []);
769
+
770
+ if (sessionStatus?.data?.status === "complete") {
771
+ return <div>Payment complete!</div>;
772
+ }
773
+
774
+ // Render order creation UI...
775
+ }
776
+ ```
777
+
778
+ See [Checkout Sessions](./checkout-sessions.md) for the full guide including API details and the session lifecycle.
779
+
723
780
  ## Next Steps
724
781
 
782
+ - [Checkout Sessions →](./checkout-sessions.md)
725
783
  - [Error Handling Guide →](./error-handling.md)
726
784
  - [Components Reference →](./components.md)
727
785
  - [Hooks Reference →](./hooks.md)