Skip to main content

Order status lifecycle

Every AnySpend order moves through these states:
Order statuses
enum 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",
}
StatusWhat’s happeningUser action
scanning_deposit_transactionWaiting for blockchain confirmationNone — wait
waiting_stripe_paymentProcessing card paymentMay need to complete 3D Secure
sending_token_from_vaultSending tokens for swapNone — automatic
relayCross-chain transaction in progressNone — wait
executedDoneNone
expiredOrder expired before payment arrivedCreate a new order
refundingAutomatic refund in progressNone — wait
refundedRefund sentCheck wallet for refunded tokens
failureTransaction failedReview error details, retry

Error codes

Payment errors

User doesn’t have enough tokens. Prompt them to add funds or switch to a different token.
if (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.
if (error.message === "INVALID_TOKEN_ADDRESS") {
  toast.error("This token isn't supported. Please choose another.");
}
Transaction amount is below the minimum threshold.
if (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.
if (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.
if (error.message === "SLIPPAGE") {
  toast.warning("Price moved. Retrying...");
  retryWithHigherSlippage();
}
RPC connection issue or chain congestion. Retry after a short delay.
if (error.message === "NETWORK_ERROR") {
  toast.error("Network issue. Check your connection and try again.");
}
Price quote is stale. Fetch a new one.
if (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.
if (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

Payment with error handling
import { 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

Order tracker
import { 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:
Error boundary
import 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:
Error message helper
function 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

Discord

Get help from the community and support team

GitHub

Report bugs and request features
When reporting issues, include: error message/code, steps to reproduce, browser/device info, and order ID if applicable.