> ## Documentation Index
> Fetch the complete documentation index at: https://docs.eluup.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# x402 Payment Flow

> Understand the x402 payment challenge, payment rejection, signatures, and settlement response.

This page explains what happens when an ELUUP paid endpoint receives a request, how the x402 challenge is represented, how payment failures are surfaced, and how successful settlement is confirmed.

## HTTP 402 — Payment Required

This is the most important section for client integration — read it carefully even if you're using `@x402/axios` (which handles most of this for you automatically).

### What a 402 response looks like

Unlike every other status code in this API, a `402` response is generated entirely by the x402 payment middleware — **not** by ELUUP's application code — and does **not** use the `{ success, data, error }` wrapper described in [Common Response Structure](/x402/api-reference#common-response-structure).

Instead, the real information lives in a response header called `payment-required`, whose value is a **base64-encoded JSON object**. The raw JSON response body on a `402` is typically empty (`{}`) — always decode the header, never rely on the body.

```typescript theme={null}
const header = response.headers["payment-required"];
const decoded = JSON.parse(Buffer.from(header, "base64").toString("utf-8"));
console.log(decoded);
```

Decoded shape:

```json theme={null}
{
  "x402Version": 2,
  "error": "<reason, or absent on the initial challenge>",
  "resource": {
    "url": "https://api.eluup.ai/v1/token-signals",
    "description": "",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "50000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x...",
      "maxTimeoutSeconds": 300,
      "extra": { "name": "USD Coin", "version": "2" }
    }
  ]
}
```

| Field               | Description                                                                                         |
| ------------------- | --------------------------------------------------------------------------------------------------- |
| `x402Version`       | Protocol version in use                                                                             |
| `error`             | Present when this 402 is a **rejection** of an attempted payment (not the initial unpaid challenge) |
| `resource.url`      | The endpoint being requested                                                                        |
| `accepts[].amount`  | Price, in the asset's base units (USDC has 6 decimals — `"50000"` = `$0.05`)                        |
| `accepts[].asset`   | The token contract address required (USDC on Base)                                                  |
| `accepts[].payTo`   | The recipient wallet — **always read this dynamically**, do not hardcode it                         |
| `accepts[].network` | CAIP-2 network ID the payment must be made on                                                       |

### Two different 402s: challenge vs. rejection

A `402` occurs in two distinct situations:

1. **Initial challenge** — you called a paid endpoint with no payment attached. This is expected and normal; `@x402/axios` handles it transparently by signing and retrying.
2. **Rejected payment** — you (or your client library) attached a payment, and it was rejected. This is the case worth debugging, and it includes an `error` field.

### Known rejection reasons

| `error` value                                                                        | Meaning                                                                                                                                                                                                          | Fix                                                                    |
| ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `self_send_not_allowed`                                                              | Your payer wallet is the same address as `payTo`                                                                                                                                                                 | Use a different wallet to pay than the one configured to receive funds |
| `invalid_payload: contract call failed: unable to call contract: execution reverted` | The on-chain payment simulation reverted. **This message is generic and does not distinguish the exact cause.** In practice this is most commonly insufficient USDC balance, but can have other on-chain causes. | See troubleshooting below                                              |

<Warning>
  ⚠️ **Known limitation:** the facilitator does not currently return a specific `insufficient_funds` reason — an underfunded wallet surfaces as the generic `execution reverted` message above. If you see this error, check your balance first before assuming another cause.
</Warning>

### Troubleshooting `execution reverted`

Check, in order:

1. **USDC balance** — does your payer wallet hold enough USDC, on **Base mainnet**, to cover `accepts[].amount` (in base units, 6 decimals)?
2. **Correct network** — is your USDC on Base mainnet (`eip155:8453`), not a testnet or a different chain? Balances do not carry across chains.
3. **Correct asset** — are you checking the balance of the exact `asset` contract address in the response (`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` for USDC on Base)?

### Important: a rejected retry does not throw

If you're using `wrapAxiosWithPayment`, be aware of this behavior: on the **initial** unpaid request, a 402 is handled transparently (signed, retried). But if the **retried, paid** request is *also* rejected (e.g. insufficient funds), the wrapper resolves the promise with the final `402` response rather than throwing — it lands in your `try` block, not your `catch` block.

**Do not assume a resolved promise means success.** Always check `response.status`:

```typescript theme={null}
const response = await api.post("/v1/token-signals", { tokens: [...] });

if (response.status >= 200 && response.status < 300) {
  // success — use response.data.data
} else if (response.status === 402) {
  const decoded = JSON.parse(
    Buffer.from(response.headers["payment-required"], "base64").toString("utf-8")
  );
  console.log("Payment failed:", decoded.error);
} else {
  console.log("Error:", response.data?.error);
}
```

### Inspecting what your client actually signed

To debug what payment payload your own client submitted, decode the `PAYMENT-SIGNATURE` request header the same way (base64 → JSON). It shows the `authorization.from` address, `value` (amount), and `nonce` that were signed — useful for confirming the payer address matches the wallet you expect to be funded.

***

## Successful Payment Response Header

Every successful paid request includes a `payment-response` header, base64-encoded, confirming the on-chain settlement:

```typescript theme={null}
const decoded = JSON.parse(
  Buffer.from(response.headers["payment-response"], "base64").toString("utf-8"),
);
```

Decoded shape:

```json theme={null}
{
  "success": true,
  "payer": "0x200FcBA0c87A8e8565dB5952521dDb3DAe763bC1",
  "transaction": "0x8f2830da31ce45e8715dc5a385813458eafa390d3de7df73e1b1a94d4779c08a",
  "network": "eip155:8453"
}
```

| Field         | Type      | Description                    |
| ------------- | --------- | ------------------------------ |
| `success`     | `boolean` | Payment processed successfully |
| `payer`       | `string`  | Wallet address that paid       |
| `transaction` | `string`  | On-chain transaction hash      |
| `network`     | `string`  | CAIP-2 network identifier      |

***

**x402 docs:** [Overview](/x402) · [Quickstart](/x402/quickstart) · [Payment Flow](/x402/payment-flow) · [API Reference](/x402/api-reference) · [Errors & Troubleshooting](/x402/errors) · [Examples](/x402/examples)
