> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# x402 payment protocol

Build a full-stack application that pairs Openfort wallet infrastructure with Coinbase's x402 payment protocol to enable seamless HTTP-based USDC micropayments. The recipe supports two wallet approaches — **embedded wallets** for browser-based user payments and **backend wallets** for server-side headless operations — with **gasless transaction** support via either an Openfort paymaster or a Coinbase CDP facilitator.

:::tip
You'll build a React + Express app with HTTP 402-gated content that users unlock by paying USDC on Base. You'll set up both an embedded wallet paywall flow and a backend wallet headless payment flow, with gas sponsored so neither the user nor the server needs ETH.
:::

Video: x402 Payment Protocol demo — [https://www.youtube.com/watch?v=mDGlkVwMD-Q](https://www.youtube.com/watch?v=mDGlkVwMD-Q)

[View Sample Code](https://github.com/openfort-xyz/recipes-hub/tree/main/x402) — GitHub Repository. Complete source code for the x402 payment protocol integration with Openfort embedded wallets, React, and Vite.

## Build it with your agent

Adding this to an app you already have? Set up your coding agent with the [Openfort docs MCP server and skill](https://www.openfort.io/docs/overview/building-with-ai), then give it this prompt:

```text
Add x402 payments to this app using Openfort. Follow the "Add this to your app" section of
https://github.com/openfort-xyz/recipes-hub/blob/main/x402/AGENTS.md
```

The same file lists every Openfort primitive the recipe uses and the errors you're likely to hit, with their fixes.

## Getting started

:::note
* Node.js 22+
* [Openfort account](https://dashboard.openfort.io) with Shield credentials
* For backend wallet gas sponsorship: choose **Coinbase CDP** keys ([CDP Developer Portal](https://portal.cdp.coinbase.com)) or an **Openfort gas sponsorship** (from the dashboard's gas sponsorships tab)
:::

::::steps
### Set up your project

Clone the recipe and install dependencies for both frontend and backend:

```bash
pnpx gitpick openfort-xyz/recipes-hub/tree/main/x402 openfort-x402
cd openfort-x402
```

The project has two packages — a React + Vite frontend and an Express.js 5 backend:

```text
x402/
├── backend/              # Express.js 5 API server
│   ├── src/
│   │   ├── server.ts     # Server entry point
│   │   ├── config.ts     # Environment parsing
│   │   ├── routes.ts     # API endpoints
│   │   ├── payment.ts    # x402 payment verification & settlement
│   │   └── openfort.ts   # Openfort client + backend wallet helpers
│   └── .env.local.example
├── frontend/             # React + Vite application
│   ├── src/
│   │   ├── features/
│   │   │   ├── paywall/          # Embedded wallet paywall flow
│   │   │   └── backend-wallet/   # Backend wallet payment flow
│   │   └── integrations/
│   │       ├── openfort/         # Openfort providers & config
│   │       └── x402/             # x402 protocol helpers
│   └── .env.example
└── README.md
```

### Configure your Openfort credentials

Set up your project in the [Openfort Dashboard](https://dashboard.openfort.io):

1. Create an account or sign in
2. Get your API keys:
   * **Publishable Key** (`pk_test_...`) — used by the frontend
   * **Secret Key** (`sk_test_...`) — used by the backend
3. Set up **Shield** for embedded wallets and note the Shield keys
4. Create a **gas sponsorship** and note its ID (`pol_...`)

:::tip
If you plan to use the backend wallet flow, also create a **wallet secret** in the dashboard under **API keys** → **Backend wallets**. The backend wallet itself (`acc_...`) is created from the demo UI or the API.
:::

### Configure your environment

Create the frontend environment file:

```bash
cp frontend/.env.example frontend/.env.local
```

Fill in `frontend/.env.local`:

```bash
VITE_OPENFORT_PUBLISHABLE_KEY=pk_test_...
VITE_OPENFORT_FEE_SPONSORSHIP_ID=pol_...   # Optional — sponsors gas for embedded wallet payments
VITE_OPENFORT_SHIELD_PUBLISHABLE_KEY=your-shield-publishable-key
VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT=http://localhost:3007/api/protected-create-encryption-session
VITE_WALLET_CONNECT_PROJECT_ID=your-wallet-connect-project-id  # Optional — for external wallet connections
VITE_X402_RESOURCE_URL=http://localhost:3007/api/protected-content
VITE_X402_DEFAULT_AMOUNT=0.1
```

Create the backend environment file:

```bash
cp backend/.env.local.example backend/.env.local
```

Fill in `backend/.env.local`:

```bash
# Server
PORT=3007

# Openfort: Project Keys
OPENFORT_SECRET_KEY=sk_test_...

# Openfort: Shield (for embedded wallet recovery)
OPENFORT_SHIELD_PUBLISHABLE_KEY=
OPENFORT_SHIELD_SECRET_KEY=
OPENFORT_SHIELD_ENCRYPTION_KEY=    # Shown once when you create the Shield keys

# Backend wallet (Option B)
OPENFORT_WALLET_SECRET=            # Obtained in Dashboard > API Keys
OPENFORT_BACKEND_WALLET_ID=        # acc_xxx from the dashboard
OPENFORT_FEE_SPONSORSHIP_ID=       # Optional; pol_... from the gas sponsorships tab

# Optional alternative to Openfort gas sponsorship: Coinbase CDP facilitator
X402_FACILITATOR_URL=              # e.g. https://api.cdp.coinbase.com/platform/v2/x402
CDP_API_KEY_ID=                    # From CDP Developer Portal
CDP_API_KEY_SECRET=                # From CDP Developer Portal

# Payment (x402)
PAY_TO_ADDRESS=                    # Recipient address (0x...) for USDC
X402_NETWORK=base-sepolia          # base-sepolia or base
X402_RESOURCE=http://localhost:3007/api/protected-content
X402_DESCRIPTION=Access to premium content
X402_MIME_TYPE=application/json
X402_MAX_AMOUNT=100000             # 6 decimals — 100000 = 0.1 USDC
X402_TIMEOUT=300
X402_ASSET_ADDRESS=0x036CbD53842c5426634e7929541eC2318f3dCF7e  # USDC on Base Sepolia
X402_ASSET_NAME=USDC
X402_ASSET_VERSION=2

CORS_ORIGINS=http://localhost:5173,http://localhost:3007
```

:::note
`X402_RPC_URL` is optional — the server auto-resolves public endpoints for `base-sepolia` and `base`.
:::

### Understand the payment flow

Before running the app, here's what happens under the hood. The frontend configures Openfort with Shield for wallet management and a policy for gas sponsorship:

```tsx
// frontend/src/integrations/openfort/OpenfortProviders.tsx
<OpenfortProvider
  publishableKey={import.meta.env.VITE_OPENFORT_PUBLISHABLE_KEY!}
  walletConfig={{
    shieldPublishableKey: import.meta.env.VITE_OPENFORT_SHIELD_PUBLISHABLE_KEY!,
    createEncryptedSessionEndpoint: import.meta.env.VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT,
    ethereum: {
      ethereumFeeSponsorshipId: import.meta.env.VITE_OPENFORT_FEE_SPONSORSHIP_ID,
    },
  }}
  uiConfig={{
    authProviders: [
      AuthProvider.EMAIL_OTP,
      AuthProvider.GUEST,
      AuthProvider.GOOGLE,
      AuthProvider.WALLET,
    ],
    walletRecovery: { defaultMethod: RecoveryMethod.AUTOMATIC },
  }}
>
  {children}
</OpenfortProvider>
```

When a user accesses protected content, the backend returns HTTP 402 with payment requirements. The frontend then creates an EIP-712 `TransferWithAuthorization` signature (or a direct USDC transfer) and sends it back to unlock the content:

```typescript
// frontend/src/integrations/x402/payments.ts
const domain = {
  name: paymentRequirements.extra?.name ?? 'USD Coin',
  version: paymentRequirements.extra?.version ?? '2',
  chainId: getNetworkId(paymentRequirements.network),
  verifyingContract: getAddress(paymentRequirements.asset),
}

const signature = await client.signTypedData({
  account: accountAddress,
  domain,
  types: AUTHORIZATION_TYPES, // TransferWithAuthorization
  primaryType: 'TransferWithAuthorization',
  message: { from, to, value, validAfter, validBefore, nonce },
})
```

### Run the application

Start both servers in separate terminals:

```bash
# Terminal 1: Backend
cd backend
pnpm i
pnpm dev

# Terminal 2: Frontend
cd frontend
pnpm i
pnpm dev
```

Visit [http://localhost:5173](http://localhost:5173) to see the paywall in action. Sign in, create a wallet, and try paying for the protected content.
::::

## x402 protocol

x402 is an open payment standard developed by Coinbase that leverages the HTTP `402 Payment Required` status code to enable services to charge for API access and content directly over HTTP. This protocol allows:

* **Accountless Payments**: No need for user accounts or sessions
* **Programmatic Access**: Clients can automatically pay for resources
* **Privacy First**: Minimal data sharing required
* **Crypto-Native**: Fast and efficient blockchain-based payments
* **Flexible Monetization**: Pay-per-use model for APIs and content

## Wallet types

The recipe supports two wallet approaches, selectable via tabs in the demo UI.

### Embedded wallet

The **Embedded wallet** uses Openfort's client-side wallet infrastructure. The wallet lives in the user's browser, managed by [Openfort Shield](https://www.openfort.io/docs/products/embedded-wallet) for key management and recovery.

The flow:

1. User authenticates via Openfort (email OTP, Google, guest, or external wallet)
2. Openfort creates a smart account with Shield handling key storage and automatic recovery
3. The user signs USDC payments directly in the browser via the Openfort React SDK and wagmi
4. Depending on the gas mode, the transaction is either submitted on-chain (with gas sponsored by an Openfort policy) or signed off-chain as an EIP-3009 `TransferWithAuthorization` for facilitator settlement

This approach is ideal for **user-facing applications** where end users interact with the paywall directly.

### Backend wallet

The **Backend wallet** uses Openfort's server-side wallet API for headless signing. No browser wallet is needed — the server holds the signing key via `OPENFORT_WALLET_SECRET`.

The flow:

1. A backend wallet is created via the Openfort API (or from the demo UI)
2. The server signs EIP-3009 `TransferWithAuthorization` payloads using the backend wallet key
3. Payments are either settled on-chain gaslessly (via Openfort transaction intents) or sent as payment headers for facilitator verification

This approach is ideal for **AI agents**, **automated services**, or any **server-to-server** payment scenario where no human interaction is required.

## Gas sponsorship

Both wallet types support gasless transactions — the payer never needs to hold ETH for gas. The recipe demonstrates two mutually exclusive approaches.

### Via Openfort policy (paymaster)

Openfort's [gas sponsorship](https://www.openfort.io/docs/configuration/gas-sponsorship) uses a paymaster pattern to cover gas costs on behalf of the user or backend wallet.

**Embedded wallet:** When `VITE_OPENFORT_FEE_SPONSORSHIP_ID` is set, the Openfort smart account routes transactions through a paymaster. The user signs a USDC `transfer()` via wagmi, and Openfort's infrastructure sponsors the gas according to the policy rules. The resulting transaction hash is sent to the backend for on-chain verification.

**Backend wallet:** The server calls `submitTransferWithAuthorizationGasless()`, which sends the USDC transfer with `openfort.accounts.evm.backend.sendTransaction({ account, chainId, rpcUrl, interactions, policy })`. The SDK delegates the backend wallet EOA to an [EIP-7702 Delegated Account](https://www.openfort.io/docs/products/server/evm/gasless-transactions) on first use and signs the authorization itself, so there is no separate upgrade step. `policy` is `OPENFORT_FEE_SPONSORSHIP_ID` when set; otherwise the project's gas sponsorship applies. The server then polls the transaction until it has a hash.

### Via facilitator (Coinbase CDP)

The x402 facilitator is a third-party service that verifies payment signatures, submits on-chain settlement, and pays gas. The recipe integrates with Coinbase's CDP facilitator.

**Both wallet types:** The wallet signs an EIP-712 `TransferWithAuthorization` off-chain (no gas needed for signing). The backend sends this signed payload to the CDP facilitator's `/verify` and `/settle` endpoints. The facilitator validates the signature, submits the `transferWithAuthorization` call on-chain, and covers the gas cost.

Set `X402_FACILITATOR_URL`, `CDP_API_KEY_ID`, and `CDP_API_KEY_SECRET` (from [CDP Developer Portal](https://portal.cdp.coinbase.com)) in the backend configuration.

## How it works

The recipe supports two payment paths per wallet type, plus the gas sponsorship options above.

**Embedded wallet (browser signs)**

* **Openfort policy (gasless):** User signs a USDC `transfer()`. Openfort's paymaster sponsors gas. Client sends `X-Transaction-Hash` to `/api/protected-content`. Server calls `verifyOnChainPayment`.
* **Facilitator (gasless):** User signs EIP-712 `TransferWithAuthorization` off-chain. Client sends `PAYMENT-SIGNATURE` header. Server calls `verifyWithFacilitator` + `settleWithFacilitator`.

**Backend wallet (server signs)**

* **Openfort policy (gasless):** Client calls `/api/backend-wallet/test-payment?gasMode=openfort-policy`. Server sends the transfer through `submitTransferWithAuthorizationGasless` (`backend.sendTransaction`) and returns the settlement result directly.
* **Facilitator (gasless):** Client calls `/api/backend-wallet/test-payment?gasMode=facilitator`. Server returns `paymentHeader`. Client sends it to `/api/protected-content`. Server calls `verifyWithFacilitator` + `settleWithFacilitator`.

Both wallet types share the same `/api/protected-content` endpoint and payment verification logic where applicable.

The backend verifies on-chain payments by checking transaction receipts for matching USDC Transfer events:

```typescript
// backend/src/payment.ts
const logs = parseEventLogs({
  abi: erc20Abi,
  eventName: "Transfer",
  logs: receipt.logs,
});

const matchingLog = logs.find(
  (log) =>
    getAddress(log.args.to) === payTo &&
    log.args.value >= requiredAmount,
);
```

## API routes

### Embedded wallet

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/protected-content` | GET | Protected resource. Returns HTTP 402 and payment requirements. Client retries with `X-Transaction-Hash` (direct transfer) or `PAYMENT-SIGNATURE` (facilitator) to receive content. |
| `/api/protected-create-encryption-session` | POST | Creates an encrypted session for Openfort Shield (embedded wallet recovery). |

### Backend wallet

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/backend-wallet/status` | GET | Returns wallet configuration state, payer address, recipient address, network, and required amount |
| `/api/backend-wallet/create` | POST | Creates a new Openfort backend wallet; returns id and address |
| `/api/backend-wallet/test-payment` | GET | Signs payment with the backend wallet. Accepts `?gasMode=facilitator` or `?gasMode=openfort-policy` to choose the gas sponsorship path. Returns paymentHeader or completes on-chain settlement (Openfort path returns full response). |

## Use cases

* **API Monetization**: Charge per API call or data access
* **Content Paywalls**: Micropayments for articles, videos, or premium content
* **AI Services**: Pay-per-query for AI models and agents
* **Data Access**: Monetize database queries or data feeds
* **Compute Resources**: Pay-as-you-go cloud computing

## Next steps

* [x402 Protocol Specification](https://github.com/coinbase/x402)
* [Embedded Wallet Guide](https://www.openfort.io/docs/products/embedded-wallet)
* [Server Wallets](https://www.openfort.io/docs/products/server)
* [Gas Sponsorship](https://www.openfort.io/docs/configuration/gas-sponsorship)
* [EIP-7702 Delegated Accounts](https://www.openfort.io/docs/products/embedded-wallet/react/wallet/actions/eip-7702-authorization)
* [Base Network Documentation](https://docs.base.org/)
