Docs Navigation

Embedded Wallet

Embedded Wallet: x402 Payments

Pay x402-gated HTTP APIs from your Host app with the 1Shot embedded wallet — EIP-3009 via eth_signTypedData_v4, wired through @x402/core and @x402/evm.

Overview

x402 is an HTTP payment protocol: a resource server responds with 402 Payment Required, the client signs a payment payload, and retries the request with a payment header. The 1Shot embedded wallet is a plain EOA — compatible with the x402 exact scheme and EIP-3009 (transferWithAuthorization). See x402 wallet compatibility for the full matrix.

Signing happens through eth_signTypedData_v4 on proxy.ethereum. The user approves EIP-712 typed data in the Branding and Signing layers; your Host receives only the signature.

Note

Buyer vs seller — this page is for Host apps whose users pay x402-gated APIs. To facilitate and settle x402 on your backend, see Dev Platform: x402 Facilitator.

Motivation

Most apps still treat payments and API access as separate systems: sign up, store a card, manage subscriptions, then call an API with a bearer token. x402 collapses that into one HTTP round trip — the same request that fetches a resource can carry the payment. For embedded-wallet apps, that means users pay with stablecoins they already hold, without leaving your product or creating a seller-specific account.

Because x402 is HTTP-native, any client that can sign EIP-3009 typed data can buy access — browsers, agents, scripts, or your Host app with the 1Shot wallet. You monetize usage at the edge instead of building billing portals, API key dashboards, and subscription tiers for every new endpoint.

  • Pay-to-read articles and reports — gate a single URL or PDF behind a micropayment. The user pays once, the server returns the content, and you skip monthly subscriptions for occasional readers.
  • Per-call tools and agent actions — charge per inference, search, scrape, or workflow step. Agents and automations can pay for exactly the calls they make instead of pre-funding a platform balance.
  • Premium API endpoints — expose market data, enrichment, or proprietary datasets with HTTP 402 instead of API keys and rate-plan negotiation. CoinGecko's x402 test endpoints are a live example of this pattern.
  • In-app unlocks — sell one-off access inside your product: a generated image, an export, a compliance check, or a gated dashboard view, settled in USDC on the chain the seller accepts.
  • Agent-to-service commerce — autonomous agents fetch paid resources on behalf of users; the embedded wallet signs EIP-3009 authorizations after the user approves the spend in your Host UI.

What @x402/core and @x402/evm do

The npm packages handle protocol parsing and EIP-3009 payload construction. Your Host app bridges them to the embedded wallet.

  • @x402/corex402Client and x402HTTPClient decode PAYMENT-REQUIRED, build payment payloads, and encode/decode payment headers.
  • @x402/evmregisterExactEvmScheme constructs EIP-3009 EIP-712 typed data and calls your ClientEvmSigner.signTypedData.
  • Your Host implements — an EIP-1193 bridge from ClientEvmSigner to proxy.ethereum.request({ method: "eth_signTypedData_v4", ... }). The wallet playground x402 tab is the reference implementation.

Prerequisites

Install

npm install @x402/core @x402/evm @1shotapi/ows-provider viem
  • User connected via eth_requestAccounts on proxy.ethereum.
  • USDC or another stablecoin advertised in the 402 accepts entry, on the required chain.
  • Wallet on the correct chain — call wallet_switchEthereumChain when the 402 challenge specifies a different network.

Implement the signer adapter

@x402/evm expects a ClientEvmSigner with address and signTypedData. Forward typed data to the embedded wallet's EIP-1193 provider.

EIP-1193 ClientEvmSigner

import type { EIP1193Provider } from "@1shotapi/ows-provider";
import type { ClientEvmSigner } from "@x402/evm";

/** EIP-712 payloads from @x402/evm use bigint for uint256 fields; JSON-RPC must not. */
function toJsonSafeTypedData(value: unknown): unknown {
  if (typeof value === "bigint") return value.toString();
  if (Array.isArray(value)) return value.map(toJsonSafeTypedData);
  if (value !== null && typeof value === "object") {
    return Object.fromEntries(
      Object.entries(value as Record<string, unknown>).map(([k, v]) => [
        k,
        toJsonSafeTypedData(v),
      ]),
    );
  }
  return value;
}

export function createX402Eip1193Signer(
  provider: EIP1193Provider,
  address: `0x${string}`,
): ClientEvmSigner {
  return {
    address,
    async signTypedData({ domain, types, primaryType, message }) {
      const typedData = toJsonSafeTypedData({
        domain,
        types,
        primaryType,
        message,
      });
      return (await provider.request({
        method: "eth_signTypedData_v4",
        params: [address, typedData],
      })) as `0x${string}`;
    },
  };
}
Note

BigInt shim@x402/evm passes bigint for uint256 fields. JSON-RPC and postMessage cannot serialize BigInt; convert to decimal strings before the RPC call.

Payment flow in the Host

Use a deliberate two-step UX: fetch payment details, show the user amount/network/asset/recipient, then purchase on confirmation. This matches the playground x402 tab and avoids surprise signing prompts.

Purchase after user confirmation

import { x402Client } from "@x402/core/client";
import { x402HTTPClient } from "@x402/core/http";
import type { PaymentRequired } from "@x402/core/types";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { getAddress } from "viem";

async function purchaseResource({
  url,
  paymentRequired,
  provider,
  account,
  fetchWithPaymentHeaders,
}: {
  url: string;
  paymentRequired: PaymentRequired;
  provider: EIP1193Provider;
  account: string;
  fetchWithPaymentHeaders: (
    url: string,
    headers: Record<string, string>,
  ) => Promise<Response>;
}) {
  const checksumAddress = getAddress(account) as `0x${string}`;
  const signer = createX402Eip1193Signer(provider, checksumAddress);

  const client = new x402Client();
  client.setSpendControls(false); // Host shows its own purchase confirmation.
  registerExactEvmScheme(client, { signer });

  const httpClient = new x402HTTPClient(client);
  const paymentPayload = await httpClient.createPaymentPayload(paymentRequired);
  const paymentHeaders = httpClient.encodePaymentSignatureHeader(paymentPayload);

  const response = await fetchWithPaymentHeaders(url, paymentHeaders);
  if (!response.ok) {
    throw new Error(`Paid request failed with HTTP ${response.status}`);
  }
  return response;
}
Note

For server-side / private-key buyers, @x402/fetch offers wrapFetchWithPayment. That path is not suitable for passkey embedded wallets — use the EIP-1193 adapter above instead.

  1. GET the resource URL → receive HTTP 402 and a PAYMENT-REQUIRED header (v2) or v1 body.
  2. x402HTTPClient.getPaymentRequiredResponse(...) → parse amount, network, asset, and payTo for your UI.
  3. User confirms → createPaymentPayload(paymentRequired) → wallet EIP-712 consent.
  4. encodePaymentSignatureHeader(payload) → retry GET with PAYMENT-SIGNATURE / X-PAYMENT.
  5. Parse the response body and optional PAYMENT-RESPONSE settlement header.

Browser CORS and proxy pattern

Browsers cannot read cross-origin 402 challenges unless the API sends CORS headers and exposes payment headers via Access-Control-Expose-Headers. Most x402 resources are third-party origins, so a same-origin proxy on your backend is the recommended pattern.

  • Your proxy forwards GET requests upstream and returns status, body, and x402 headers to your Host UI.
  • On the paid retry, forward PAYMENT-SIGNATURE / X-PAYMENT upstream.
  • The playground proxy (/playground/x402/proxy) is a reference — GET-only upstream, x402 header allowlist, 5 MB cap. Implement an equivalent on your origin; do not call the marketing-site proxy in production.

Supported schemes

  • Documented in this guideexact scheme with EIP-3009 on eip155:* networks. The playground x402 tab exercises this path end to end.
  • Permit2 — the wallet supports Permit2 the same way: @x402/evm supplies different EIP-712 typed data, and your Host forwards it through eth_signTypedData_v4. No separate wallet capability is required — only the x402 asset-transfer method and scheme registration differ from EIP-3009.
  • Not covered here yetupto and Solana/non-EVM accepts. See x402 schemes overview for the full protocol matrix.

Analytics

EIP-3009 signing surfaces on proxy.analytics as TypedSign, TypedSignFailed, and TypedSignCancelled. Forward these to your analytics pipeline alongside other wallet events.

Note

See Embedded Wallet: Analytics for the full subscribe → forward → GA4 setup.

Test in the playground

Open the wallet playgroundx402 tab. The CoinGecko demo URL is pre-filled. Flow: Fetch payment details → connect wallet → switch network if prompted → Purchase → view the JSON response in the modal.