Error handling
Order statuses, error codes, and patterns for handling failures in AnySpend
Order status lifecycle
Every AnySpend order moves through these states:
typescriptenum OrderStatus { // Waiting SCANNING_DEPOSIT_TRANSACTION = "scanning_deposit_transaction", WAITING_STRIPE_PAYMENT = "waiting_stripe_payment", EXPIRED = "expired", // Processing SENDING_TOKEN_FROM_VAULT = "sending_token_from_vault", RELAY = "relay", // Done EXECUTED = "executed", // Failed REFUNDING = "refunding", REFUNDED = "refunded", FAILURE = "failure", }
| Status | What's happening | User action |
|---|---|---|
scanning_deposit_transaction | Waiting for blockchain confirmation | None -- wait |
waiting_stripe_payment | Processing card payment | May need to complete 3D Secure |
sending_token_from_vault | Sending tokens for swap | None -- automatic |
relay | Cross-chain transaction in progress | None -- wait |
executed | Done | None |
expired | Order expired before payment arrived | Create a new order |
refunding | Automatic refund in progress | None -- wait |
refunded | Refund sent | Check wallet for refunded tokens |
failure | Transaction failed | Review error details, retry |
Error codes
Payment errors
User doesn't have enough tokens. Prompt them to add funds or switch to a different token.
typescriptif (error.message === "INSUFFICIENT_BALANCE") { toast.error("Not enough funds. Add tokens or choose a different payment method."); }
Token contract isn't supported on the target chain. Show the user which tokens are available.
typescriptif (error.message === "INVALID_TOKEN_ADDRESS") { toast.error("This token isn't supported. Please choose another."); }
Transaction amount is below the minimum threshold.
typescriptif (error.message === "MINIMUM_AMOUNT_NOT_MET") { toast.error(`Minimum amount is $${minimumAmount}.`); }
Transaction amount exceeds the limit. Split into multiple transactions or reduce the amount.
typescriptif (error.message === "MAXIMUM_AMOUNT_EXCEEDED") { toast.error(`Maximum amount is $${maximumAmount}.`); }
Network errors
Price moved beyond tolerance during execution. Retry or wait for the market to settle.
typescriptif (error.message === "SLIPPAGE") { toast.warning("Price moved. Retrying..."); retryWithHigherSlippage(); }
RPC connection issue or chain congestion. Retry after a short delay.
typescriptif (error.message === "NETWORK_ERROR") { toast.error("Network issue. Check your connection and try again."); }
Price quote is stale. Fetch a new one.
typescriptif (error.message === "QUOTE_EXPIRED") { toast.info("Quote expired. Getting a fresh one..."); refreshQuoteAndRetry(); }
Requested chain isn't supported. Show the user which chains are available.
typescriptif (error.message === "CHAIN_NOT_SUPPORTED") { toast.error("This chain isn't supported."); showSupportedChains(); }
Contract errors
Smart contract execution failed. Double-check the contract parameters and state.
Gas limit was too low. Retry with a higher limit.
Nonce conflict from a pending transaction. Wait for the pending tx to confirm, then retry.
The contract reverted. Usually means a precondition wasn't met (e.g., sale ended, not enough allowance).
Error handling patterns
Per-component error handling
tsximport { useAnyspendCreateOrder } from "@b3dotfun/sdk/anyspend"; function PaymentComponent() { const [error, setError] = useState<string | null>(null); const [retryCount, setRetryCount] = useState(0); const { createOrder, isCreatingOrder } = useAnyspendCreateOrder({ onError: (error) => { switch (error.message) { case "INSUFFICIENT_BALANCE": setError("Not enough funds. Add tokens or try a different payment method."); break; case "SLIPPAGE": if (retryCount < 3) { setError("Price moved. Retrying..."); setTimeout(() => { setRetryCount((prev) => prev + 1); retryPayment(); }, 2000); } else { setError("Price too volatile right now. Try again later."); } break; case "QUOTE_EXPIRED": setError("Quote expired. Getting a fresh one..."); refreshQuote(); break; default: setError("Payment failed. Try again or contact support."); } }, onSuccess: () => { setError(null); setRetryCount(0); }, }); return ( <div> {error && ( <div className="error-banner"> <span>{error}</span> <button onClick={() => setError(null)}>Dismiss</button> </div> )} <button onClick={handlePayment} disabled={isCreatingOrder}> {isCreatingOrder ? "Processing..." : "Pay Now"} </button> </div> ); }
Order status monitoring
tsximport { useAnyspendOrderAndTransactions } from "@b3dotfun/sdk/anyspend"; function OrderTracker({ orderId }: { orderId: string }) { const { orderAndTransactions, isLoadingOrderAndTransactions, getOrderAndTransactionsError } = useAnyspendOrderAndTransactions(orderId); if (getOrderAndTransactionsError) { return ( <div> <p>Couldn't load order status. Check your connection and try again.</p> <button onClick={() => window.location.reload()}>Retry</button> </div> ); } if (!orderAndTransactions) return <div>Loading order status...</div>; const { order, executeTx, refundTxs } = orderAndTransactions.data; switch (order.status) { case "scanning_deposit_transaction": return <p>Waiting for payment confirmation (1-2 minutes)...</p>; case "relay": return <p>Processing cross-chain transaction...</p>; case "executed": return ( <div> <p>Done!</p> {executeTx && ( <a href={`https://basescan.org/tx/${executeTx.txHash}`} target="_blank"> View transaction </a> )} </div> ); case "failure": case "obtain_failed": return ( <div> <p>Transaction failed: {order.errorDetails || "Unknown error"}</p> <button onClick={() => createNewOrder()}>Try again</button> </div> ); case "refunded": return <p>Refund processed. Check your wallet.</p>; case "expired": return ( <div> <p>Order expired before payment arrived.</p> <button onClick={() => createNewOrder()}>Create new order</button> </div> ); default: return <p>Processing... (status: {order.status})</p>; } }
Error boundary
Wrap AnySpend components in an error boundary to catch rendering crashes:
tsximport React, { Component, ErrorInfo } from "react"; class AnySpendErrorBoundary extends Component< { children: React.ReactNode }, { hasError: boolean; error?: Error } > { state = { hasError: false, error: undefined as Error | undefined }; static getDerivedStateFromError(error: Error) { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error("AnySpend error:", error, errorInfo); // Report to your error tracking service (Sentry, etc.) } render() { if (this.state.hasError) { return ( <div> <h3>Something went wrong</h3> <p>{this.state.error?.message}</p> <button onClick={() => this.setState({ hasError: false })}>Try again</button> </div> ); } return this.props.children; } } // Usage function App() { return ( <AnySpendErrorBoundary> <AnyspendProvider> <YourApp /> </AnyspendProvider> </AnySpendErrorBoundary> ); }
User-friendly error messages
A helper that maps error codes to messages your users can actually understand:
tsxfunction getErrorMessage(error: Error) { const map: Record<string, { title: string; message: string; action?: string }> = { INSUFFICIENT_BALANCE: { title: "Insufficient balance", message: "You don't have enough funds for this transaction.", action: "Add funds or choose a different payment method.", }, SLIPPAGE: { title: "Price changed", message: "The price moved while processing your transaction.", action: "We'll retry with updated pricing.", }, NETWORK_ERROR: { title: "Connection issue", message: "Can't reach the blockchain network.", action: "Check your internet connection and try again.", }, QUOTE_EXPIRED: { title: "Quote expired", message: "The price quote is no longer valid.", action: "Getting a fresh quote...", }, }; return map[error.message] ?? { title: "Transaction failed", message: "Something went wrong.", action: "Try again or contact support.", }; }
Tips
- Keep users informed during long operations. Cross-chain transactions can take a few minutes -- show progress, not just a spinner.
- Retry transient errors (slippage, network, quote expired) with exponential backoff. Many resolve on the second try.
- Log errors with context (order ID, user address, timestamp) so you can debug issues after the fact.
- Offer fallbacks when possible. If cross-chain fails, suggest same-chain. If crypto fails, suggest fiat onramp.
Getting help
When reporting issues, include: error message/code, steps to reproduce, browser/device info, and order ID if applicable.