From 785f0f73dccfd7bc668f242c0ba938476509e504 Mon Sep 17 00:00:00 2001 From: "eul.noh" Date: Mon, 29 Jun 2026 15:57:52 +0900 Subject: [PATCH] docs: add x402 payments section at /x402 Add a standalone x402 documentation product (its own Docusaurus instance served at /x402, with its own navbar tab), modeled on the CDP x402 docs: - intro (overview/landing), how-it-works (roles, 402 flow + body, schemes), networks-and-token, quickstart (sellers/buyers), facilitator, sdk - new `x402` content-docs plugin instance + sidebarsX402.ts + navbar entry, x402 added to local search indexing The facilitator page documents the hosted HPP facilitators (facilitator.hpp.io / facilitator-sepolia.hpp.io) and is intended as the link target for the x402-foundation facilitator registry. --- docusaurus.config.ts | 21 +++- sidebarsX402.ts | 22 ++++ x402/facilitator.mdx | 155 ++++++++++++++++++++++++++ x402/how-it-works.md | 126 +++++++++++++++++++++ x402/intro.md | 45 ++++++++ x402/networks-and-token.mdx | 87 +++++++++++++++ x402/quickstart-buyers.mdx | 215 ++++++++++++++++++++++++++++++++++++ x402/quickstart-sellers.mdx | 138 +++++++++++++++++++++++ x402/sdk.md | 69 ++++++++++++ 9 files changed, 877 insertions(+), 1 deletion(-) create mode 100644 sidebarsX402.ts create mode 100644 x402/facilitator.mdx create mode 100644 x402/how-it-works.md create mode 100644 x402/intro.md create mode 100644 x402/networks-and-token.mdx create mode 100644 x402/quickstart-buyers.mdx create mode 100644 x402/quickstart-sellers.mdx create mode 100644 x402/sdk.md diff --git a/docusaurus.config.ts b/docusaurus.config.ts index d52c70e..4fa8554 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -92,6 +92,18 @@ const config: Config = { sidebarPath: false, }, ], + // x402 payment-protocol docs — a standalone product section served at /x402 + // (modeled on docs.cdp.coinbase.com/x402), independent of the main HPP docs. + [ + '@docusaurus/plugin-content-docs', + { + id: 'x402', + path: 'x402', + routeBasePath: 'x402', + sidebarPath: './sidebarsX402.ts', + editUrl: 'https://github.com/hpp-io/docs/tree/main/', + }, + ], ], themes: [ [ @@ -99,7 +111,7 @@ const config: Config = { { hashed: true, indexDocs: true, - docsRouteBasePath: ['/', 'hub', 'hpp-router'], + docsRouteBasePath: ['/', 'hub', 'hpp-router', 'x402'], highlightSearchTermsOnTargetPage: true, language: ['en'], }, @@ -137,6 +149,13 @@ const config: Config = { label: 'HPP Router', position: 'right', }, + { + type: 'docSidebar', + sidebarId: 'x402Sidebar', + docsPluginId: 'x402', + label: 'x402', + position: 'right', + }, { href: 'https://github.com/hpp-io/docs', label: 'GitHub', diff --git a/sidebarsX402.ts b/sidebarsX402.ts new file mode 100644 index 0000000..a420739 --- /dev/null +++ b/sidebarsX402.ts @@ -0,0 +1,22 @@ +import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; + +// Sidebar for the standalone "x402" product docs (the `x402` docs instance, served at /x402). +// Modeled on the CDP x402 docs: Overview -> concepts -> quickstarts -> facilitator -> reference. +// Add new pages here as you create them under `x402/` (the id is the file path without extension). +const sidebars: SidebarsConfig = { + x402Sidebar: [ + {type: 'doc', id: 'intro', label: 'Overview'}, + 'how-it-works', + 'networks-and-token', + { + type: 'category', + label: 'Quickstart', + collapsed: false, + items: ['quickstart-sellers', 'quickstart-buyers'], + }, + 'facilitator', + 'sdk', + ], +}; + +export default sidebars; diff --git a/x402/facilitator.mdx b/x402/facilitator.mdx new file mode 100644 index 0000000..5e81e1b --- /dev/null +++ b/x402/facilitator.mdx @@ -0,0 +1,155 @@ +--- +title: HPP Facilitator +sidebar_label: Facilitator +description: HPP's hosted x402 facilitator — verify and settle USDC.e payments on HPP Mainnet and Sepolia, with gasless upto settlement. +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# HPP Facilitator + +A **facilitator** is the service an x402 resource server delegates payment **verification** and +**onchain settlement** to. HPP operates production facilitators for both its Mainnet and Sepolia +networks, so sellers never have to run an RPC node, manage a settlement key, or hold buyer funds — +the facilitator only verifies signatures and submits the settlement transaction. + +## Endpoints + +| Network | Base URL | +| --- | --- | +| HPP Mainnet (`eip155:190415`) | `https://facilitator.hpp.io` | +| HPP Sepolia (`eip155:181228`) | `https://facilitator-sepolia.hpp.io` | + +Each facilitator exposes the standard x402 facilitator API: + +| Route | Method | Purpose | +| --- | --- | --- | +| `/supported` | `GET` | Lists the `(scheme, network)` pairs and extensions the facilitator supports. | +| `/verify` | `POST` | Verifies a signed payment payload without settling. | +| `/settle` | `POST` | Submits the payment onchain and returns a settlement receipt. | + +**CORS is enabled**, so browser-based clients can call the facilitator directly. + +You can confirm what a facilitator supports at any time: + +```bash +curl https://facilitator.hpp.io/supported +``` + +```json +{ + "kinds": [ + { "x402Version": 2, "scheme": "exact", "network": "eip155:190415" }, + { "x402Version": 2, "scheme": "upto", "network": "eip155:190415", + "extra": { "facilitatorAddress": "0xaA03c7BD2fAf554db507D78CD3bF126a3DD2E033" } } + ], + "extensions": ["eip2612GasSponsoring"], + "signers": { "eip155:*": ["0xaA03c7BD2fAf554db507D78CD3bF126a3DD2E033"] } +} +``` + +The Sepolia facilitator (`https://facilitator-sepolia.hpp.io/supported`) returns the same shape with +`network: "eip155:181228"` and its own signer `0x420bd05aB1103b0F4A02B39Ca07C92677b5CFD9d`. + +## Supported schemes + +| Scheme | Standard | Notes | +| --- | --- | --- | +| `exact` | EIP-3009 (`transferWithAuthorization`) | Fixed-price payments. | +| `upto` | Permit2 | Authorize a maximum, settle the actual amount. Supports gasless settlement. | + +Both schemes settle in **USDC.e** (`0x401eCb1D350407f13ba348573E5630B83638E30D`, 6 decimals) on both +networks. See [How it works → Payment schemes](./how-it-works.md#payment-schemes). + +## Connecting to it + +You don't call the facilitator directly in normal use — your resource server does. Point a +facilitator client at the right base URL: + +```ts +import { HTTPFacilitatorClient } from "@x402/core/server"; + +// HPP Mainnet +const facilitator = new HTTPFacilitatorClient({ url: "https://facilitator.hpp.io" }); +// HPP Sepolia +// const facilitator = new HTTPFacilitatorClient({ url: "https://facilitator-sepolia.hpp.io" }); +``` + +Then register your schemes against it — see [Quickstart: Sellers](./quickstart-sellers.mdx). Buyers +never configure the facilitator at all; they discover it implicitly from the seller's `402` challenge. + +## Verify and settle + +`HTTPFacilitatorClient` wraps the two POST endpoints. Both take the same body — the buyer's signed +`paymentPayload` plus the `paymentRequirements` it is paying — and the SDK calls them for you during +`paymentMiddleware`. The shapes: + +```jsonc +// POST /verify and POST /settle request +{ + "x402Version": 2, + "paymentPayload": { /* the decoded X-PAYMENT payload the buyer signed */ }, + "paymentRequirements": { /* the matching accept from the 402 */ } +} +``` + +```jsonc +// POST /verify response — checks the signature WITHOUT moving funds +{ "isValid": true, "payer": "0x…" } +// on failure: { "isValid": false, "invalidReason": "...", "invalidMessage": "..." } +``` + +```jsonc +// POST /settle response — submits the payment onchain +{ + "success": true, + "transaction": "0x…", // settlement tx hash + "network": "eip155:190415", + "payer": "0x…", + "amount": "10000" // actual amount settled (for `upto`, ≤ the max) +} +// on failure: { "success": false, "errorReason": "...", "errorMessage": "...", "transaction": "0x…" } +``` + +In the SDK, a failed `/verify` or `/settle` surfaces **to the resource server** as `VerifyError` or +`SettleError` (exported from `@x402/core`), carrying `invalidReason` / `errorReason` for branching. +Buyers don't see these types — they get a non-2xx response instead (see +[Quickstart: Buyers → Handling failures](./quickstart-buyers.mdx#handling-failures)). + +## Gasless settlement (`upto`) + +For the `upto` scheme, HPP's facilitator advertises the **`eip2612GasSponsoring`** extension. This +lets the facilitator sponsor the buyer's one-time Permit2 approval via +[EIP-2612](https://eips.ethereum.org/EIPS/eip-2612), so a paying agent needs **only USDC.e and zero +native ETH** to transact. + +The **sponsoring signer** — the facilitator's onchain address that pays gas and submits settlement — +is the same value shown as `signers` and as the `upto` `facilitatorAddress` in `/supported`: + +| Network | Sponsoring signer (`facilitatorAddress`) | +| --- | --- | +| HPP Mainnet | `0xaA03c7BD2fAf554db507D78CD3bF126a3DD2E033` | +| HPP Sepolia | `0x420bd05aB1103b0F4A02B39Ca07C92677b5CFD9d` | + +When a seller offers an `upto` route, the SDK surfaces the `facilitatorAddress` and the +`eip2612GasSponsoring` extension in the `402` challenge automatically, and a compatible buyer signs +the gasless approval path with no extra configuration. + +## Self-hosting + +HPP's facilitator is a deployment of the standard open-source x402 facilitator, so you are not locked +in: you can run your own and point your resource server at it instead. Use HPP's hosted endpoints to +get started, and self-host if you need custom signing keys, private RPC, or independent operations. +See the [x402 SDK & reference](./sdk.md) for the upstream packages. + +## Quick reference + +| Field | HPP Mainnet | HPP Sepolia | +| --- | --- | --- | +| Facilitator URL | `https://facilitator.hpp.io` | `https://facilitator-sepolia.hpp.io` | +| Network (CAIP-2) | `eip155:190415` | `eip155:181228` | +| Token (USDC.e) | `0x401eCb1D350407f13ba348573E5630B83638E30D` | `0x401eCb1D350407f13ba348573E5630B83638E30D` | +| Schemes | `exact`, `upto` | `exact`, `upto` | +| Gasless extension | `eip2612GasSponsoring` | `eip2612GasSponsoring` | +| Sponsor signer | `0xaA03c7BD2fAf554db507D78CD3bF126a3DD2E033` | `0x420bd05aB1103b0F4A02B39Ca07C92677b5CFD9d` | diff --git a/x402/how-it-works.md b/x402/how-it-works.md new file mode 100644 index 0000000..ec1d463 --- /dev/null +++ b/x402/how-it-works.md @@ -0,0 +1,126 @@ +--- +title: How it works +sidebar_label: How it works +description: The x402 payment flow on HPP — the three roles, the 402 challenge, and the exact and upto payment schemes. +--- + +# How it works + +x402 turns a normal HTTP request into a paid one using the `402 Payment Required` status code. +Nothing about the transport changes — a paid endpoint is still a regular URL that returns `402` +until a valid payment is attached. + +## The three roles + +- **Resource server (the seller).** An HTTP service that puts a price on one or more routes. + When an unpaid request arrives, it replies with `402` and a list of acceptable payment terms + (`accepts`). When a paid request arrives, it verifies and settles the payment, then returns the resource. +- **Client (the buyer).** A browser, a backend, or an AI agent that wants the resource. It reads the + `402`, signs a stablecoin authorization for one of the offered terms, and retries with an `X-PAYMENT` header. +- **Facilitator.** A service that the resource server delegates two jobs to: **verify** a payment + signature, and **settle** it onchain. HPP operates the facilitators so sellers never run an RPC node + or hold a settlement key. See [Facilitator](./facilitator.mdx). + +## The payment flow + +```text + Buyer (client) Seller (resource server) HPP Facilitator + │ │ │ + │ 1. GET /paid/resource │ │ + │ ───────────────────────────────► │ │ + │ │ │ + │ 2. 402 Payment Required │ │ + │ { accepts: [ {scheme, │ │ + │ network, asset, amount, │ │ + │ payTo, … } ] } │ │ + │ ◄─────────────────────────────── │ │ + │ │ │ + │ 3. sign payment authorization │ │ + │ (EIP-3009 / Permit2) │ │ + │ │ │ + │ 4. GET /paid/resource │ │ + │ X-PAYMENT: │ │ + │ ───────────────────────────────► │ 5. verify(payload) │ + │ │ ──────────────────────────────► │ + │ │ ◄────────────────────────────── │ + │ │ 6. run the work / serve │ + │ │ 7. settle(payload) │ + │ │ ──────────────────────────────► │ ──► onchain tx + │ │ ◄────────────────────────────── │ + │ 8. 200 OK + resource │ │ + │ X-PAYMENT-RESPONSE: │ │ + │ ◄─────────────────────────────── │ │ +``` + +HPP's resource-server middleware uses **serve-then-settle**: it verifies the payment first, serves the +response, and settles after a successful result (`status < 400`). The buyer gets a settlement receipt +back in the `X-PAYMENT-RESPONSE` header. + +## The 402 challenge + +The body of the `402` response (x402 version 2) tells the buyer exactly how it may pay. Each entry in +`accepts` is one acceptable `(scheme, network)` with its price and asset: + +```json +{ + "x402Version": 2, + "resource": { + "url": "https://seller.example.com/paid/hello", + "description": "A paid hello-world endpoint." + }, + "accepts": [ + { + "scheme": "exact", + "network": "eip155:190415", + "amount": "10000", + "asset": "0x401eCb1D350407f13ba348573E5630B83638E30D", + "payTo": "0xYourReceivingAddress", + "maxTimeoutSeconds": 600, + "extra": { "name": "Bridged USDC", "version": "2" } + } + ], + "extensions": {} +} +``` + +- `amount` is in the asset's base units — `"10000"` is `0.01` USDC.e (6 decimals). +- `extra` carries the EIP-712 domain the buyer needs to sign (`exact`), and scheme-specific data such + as the `facilitatorAddress` for gas-sponsored `upto`. +- A multi-network or multi-scheme seller returns several `accepts`; the buyer's SDK selects one (next + section). The settlement receipt comes back base64-encoded in the `X-PAYMENT-RESPONSE` header and + decodes to `{ success, transaction, network, payer, amount? }`. + +## Payment schemes + +A *scheme* defines how the buyer authorizes payment and how the facilitator settles it onchain. The +seller lists one or more schemes per route, in priority order; the buyer's SDK picks the first one it supports. +HPP supports two upstream-standard schemes: + +### `exact` + +Pay an **exact** amount using [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) +(`transferWithAuthorization`). The buyer signs a transfer for the precise price; the facilitator +submits it. Simple and final — best when the price is known up front. + +### `upto` + +Authorize **up to** a maximum using [Permit2](https://github.com/Uniswap/permit2), and settle the +actual amount consumed (which may be less than the cap). This fits metered or usage-based pricing +where the final cost isn't known until the work runs. + +On HPP, `upto` also supports **gasless settlement**: the one-time Permit2 approval can be sponsored +via [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612), so a paying agent needs **zero native ETH** — +only USDC.e. See [Facilitator → Gasless settlement](./facilitator.mdx#gasless-settlement-upto). + +> Both schemes are part of the standard x402 specification. A facilitator advertises exactly which +> `(network, scheme)` pairs it supports at its `/supported` endpoint, and the seller's accepts must be +> a subset of that. + +Each payment authorization is **single-use**: it carries a unique nonce and a validity window, so a +captured `X-PAYMENT` header cannot be replayed for a second charge, and the buyer signs a fresh +authorization for every request. + +## Next + +- [Networks & Token](./networks-and-token.mdx) — the chains and the USDC.e asset. +- [Quickstart: Sellers](./quickstart-sellers.mdx) / [Buyers](./quickstart-buyers.mdx) — working code. diff --git a/x402/intro.md b/x402/intro.md new file mode 100644 index 0000000..cf5f4f4 --- /dev/null +++ b/x402/intro.md @@ -0,0 +1,45 @@ +--- +title: x402 on HPP +sidebar_label: Overview +slug: / +description: Accept and pay for HTTP resources with onchain stablecoins on HPP using the x402 payment protocol. +--- + +# x402 on HPP + +**x402** is an open payment protocol that revives the long-dormant HTTP `402 Payment Required` +status code. It lets any HTTP endpoint charge for access with onchain stablecoins — no API keys, +no accounts, no manual invoicing. A client requests a resource, receives a `402` with machine-readable +payment terms, pays by signing a stablecoin authorization, and retries. Settlement happens onchain. + +HPP runs **production x402 facilitators on both its Mainnet and Sepolia networks**, so you can +monetize an API — or pay for one — using **USDC.e** with a few lines of code. + +## Why x402 + +- **Built for agents and machines.** Payment is a single signed message on a standard HTTP + response. No human-in-the-loop, no dashboard signup. AI agents can discover a price and pay it autonomously. +- **No accounts, no custody.** Payment moves from the buyer's wallet to the seller's `payTo` address; + HPP's facilitator never custodies funds — it only verifies the signature and submits the settlement + transaction (a direct transfer for `exact`, or a Permit2 transfer for `upto`). +- **Stablecoin-native.** Prices are quoted in USDC.e. What the buyer signs is what the seller receives. +- **Open standard.** x402 is maintained by the [x402 Foundation](https://github.com/x402-foundation/x402), + and HPP uses the same `@x402` SDK that works on Base, Polygon, and others — pointed at HPP's networks + and facilitator. One small setup step registers USDC.e as HPP's default asset (shown in the quickstarts). + +## What HPP provides + +| Piece | What it is | +| --- | --- | +| **Facilitators** | Hosted `verify` / `settle` services for HPP Mainnet (`facilitator.hpp.io`) and HPP Sepolia (`facilitator-sepolia.hpp.io`). See [Facilitator](./facilitator.mdx). | +| **Networks** | HPP Mainnet (`eip155:190415`) and HPP Sepolia (`eip155:181228`), recognized natively by the `@x402` SDK. See [Networks & Token](./networks-and-token.mdx). | +| **Token** | USDC.e (Bridged USDC) at `0x401eCb1D350407f13ba348573E5630B83638E30D` on both chains. | +| **Schemes** | `exact` (EIP-3009) and `upto` (Permit2 with optional gasless settlement). See [How it works](./how-it-works.md). | + +## Where to go next + +- **[How it works](./how-it-works.md)** — the `402` flow, the three roles, and the payment schemes. +- **[Networks & Token](./networks-and-token.mdx)** — chain IDs, RPC endpoints, and the USDC.e asset. +- **[Quickstart: Sellers](./quickstart-sellers.mdx)** — charge for an HTTP endpoint. +- **[Quickstart: Buyers](./quickstart-buyers.mdx)** — pay for an x402 endpoint from a client or agent. +- **[Facilitator](./facilitator.mdx)** — endpoints, supported schemes, gasless settlement, and self-hosting. diff --git a/x402/networks-and-token.mdx b/x402/networks-and-token.mdx new file mode 100644 index 0000000..9194f7c --- /dev/null +++ b/x402/networks-and-token.mdx @@ -0,0 +1,87 @@ +--- +title: Networks & Token +sidebar_label: Networks & Token +description: HPP Mainnet and Sepolia network parameters and the USDC.e settlement token used by x402. +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Networks & Token + +x402 on HPP runs on two networks. Both are recognized natively by the `@x402` SDK using their +[CAIP-2](https://chainagnostic.org/CAIPs/caip-2) identifiers (`eip155:`), and both settle in +the same **USDC.e** stablecoin. + +## Networks + + + + +| Field | Value | +| --- | --- | +| Network name | HPP Mainnet | +| CAIP-2 (x402 `network`) | `eip155:190415` | +| Chain ID | `190415` | +| RPC endpoint | `https://mainnet.hpp.io` | +| Block explorer | https://explorer.hpp.io/ | +| Gas token | ETH | +| Facilitator | `https://facilitator.hpp.io` | + + + + +| Field | Value | +| --- | --- | +| Network name | HPP Sepolia (testnet) | +| CAIP-2 (x402 `network`) | `eip155:181228` | +| Chain ID | `181228` | +| RPC endpoint | `https://sepolia.hpp.io` | +| Block explorer | https://sepolia-explorer.hpp.io/ | +| Gas token | ETH | +| Facilitator | `https://facilitator-sepolia.hpp.io` | + +Get testnet ETH from the [HPP Sepolia Faucet](https://faucet.hpp.io/). + + + + +See [Network Information](/getting-started/network-information) for WSS endpoints and Chainlist links. + +## Token: USDC.e + +All x402 payments on HPP settle in **USDC.e** (Bridged USDC). The contract address is the **same on +both networks**: + +| Field | Value | +| --- | --- | +| Symbol | USDC.e | +| Address (Mainnet & Sepolia) | `0x401eCb1D350407f13ba348573E5630B83638E30D` | +| Decimals | `6` | +| EIP-712 domain name | `Bridged USDC` | +| EIP-712 domain version | `2` | + +Because USDC.e has **6 decimals**, prices in the x402 `accepts` are expressed as integer base units: + +| Human price | `amount` (base units) | +| --- | --- | +| `$0.01` | `10000` | +| `$0.10` | `100000` | +| `$1.00` | `1000000` | + +> The EIP-712 domain (`Bridged USDC` / version `2`) matters for `exact` (EIP-3009) signatures. Register +> USDC.e as HPP's default asset in the SDK so it is filled in automatically — see the +> [Quickstart: Sellers](./quickstart-sellers.mdx#2-register-usdce) snippet. + +## Funding + +Because every payment is denominated in **USDC.e**, a buyer needs a USDC.e balance before it can pay — +ETH alone only covers gas (and `upto` can be made gasless, needing no ETH at all). + +- **Gas (ETH).** On testnet, get HPP Sepolia ETH from the [HPP Sepolia Faucet](https://faucet.hpp.io/). +- **USDC.e.** USDC.e is Bridged USDC. Bring it onto HPP by bridging from the parent chain — see the + [Bridge guide](/community/bridge). For testnet USDC.e to run the quickstarts, reach out through + [Official Links](/community/official-links). + +Verify a balance any time on the [block explorer](https://explorer.hpp.io/) (mainnet) or +[Sepolia explorer](https://sepolia-explorer.hpp.io/) by looking up the USDC.e contract above. diff --git a/x402/quickstart-buyers.mdx b/x402/quickstart-buyers.mdx new file mode 100644 index 0000000..92f4e93 --- /dev/null +++ b/x402/quickstart-buyers.mdx @@ -0,0 +1,215 @@ +--- +title: 'Quickstart: Buyers' +sidebar_label: 'Quickstart: Buyers' +description: Pay for an x402 endpoint on HPP from a client or AI agent using the @x402 SDK and a USDC.e wallet. +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Quickstart: Buyers + +Pay for any x402 endpoint on HPP by wrapping `fetch`. The SDK reads the `402` challenge, signs a +USDC.e authorization with your wallet, and retries automatically — your code just sees a `200`. + +### Prerequisites + +- A wallet (private key) holding **USDC.e** on the target network — payments are denominated in USDC.e, + not ETH. See [Networks & Token → Funding](./networks-and-token.mdx#funding) for how to acquire it. +- For the **`exact`** scheme, the wallet also needs a little **ETH** for gas. +- For **`upto`**, the buyer normally needs a one-time Permit2 approval. On HPP this approval can be + gas-sponsored via EIP-2612, so a wallet with **only USDC.e and zero ETH** can pay — see + [Facilitator → Gasless settlement](./facilitator.mdx#gasless-settlement-upto). + +:::caution +Use a dedicated test wallet on **HPP Sepolia** while integrating. Never hard-code a mainnet private +key — load it from a secret manager or environment variable, and treat any key in `process.env` as +production-sensitive. +::: + +## 1. Install + +```bash +npm install @x402/core @x402/evm @x402/fetch viem +``` + +## 2. Wrap fetch with payment + +Build a scheme client backed by your wallet, hand it to an `x402Client`, and wrap `fetch`. The buyer +needs **USDC.e** to pay; for `exact` it also needs a little ETH for gas, while `upto` can be made +[gasless](./facilitator.mdx#gasless-settlement-upto). + + + + +```ts +import { createPublicClient, defineChain, http } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { ExactEvmScheme } from "@x402/evm/exact/client"; +import { UptoEvmScheme } from "@x402/evm/upto/client"; +import { x402Client } from "@x402/core/client"; +import { wrapFetchWithPayment, decodePaymentResponseHeader } from "@x402/fetch"; + +const RPC = "https://mainnet.hpp.io"; +const NETWORK = "eip155:190415"; // CAIP-2 id used by x402 +const SCHEME = "upto"; // or "exact" + +// HPP is not a viem built-in chain, so define it. The scheme client needs a +// chain-configured public client for its on-chain reads (balances, allowances). +const hppMainnet = defineChain({ + id: 190415, + name: "HPP Mainnet", + nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, + rpcUrls: { default: { http: [RPC] } }, +}); + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); +const pub = createPublicClient({ chain: hppMainnet, transport: http(RPC) }); + +// The scheme client signs typed data with your wallet and reads chain state. +const signer = { + address: account.address, + signTypedData: (msg: any) => account.signTypedData(msg), + readContract: (args: any) => pub.readContract(args), +}; + +const schemeClient = + SCHEME === "upto" + ? new UptoEvmScheme(signer, { rpcUrl: RPC }) + : new ExactEvmScheme(signer, { rpcUrl: RPC }); + +const client = x402Client.fromConfig({ + schemes: [{ network: NETWORK, client: schemeClient }], + // Pick the accept matching the network + scheme you want from the 402. + paymentRequirementsSelector: (_v, accepts) => + accepts.find((a) => a.network === NETWORK && a.scheme === SCHEME) ?? accepts[0], +}); + +const fetchWithPay = wrapFetchWithPayment(fetch, client); +``` + + + + +```ts +import { createPublicClient, defineChain, http } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { ExactEvmScheme } from "@x402/evm/exact/client"; +import { UptoEvmScheme } from "@x402/evm/upto/client"; +import { x402Client } from "@x402/core/client"; +import { wrapFetchWithPayment, decodePaymentResponseHeader } from "@x402/fetch"; + +const RPC = "https://sepolia.hpp.io"; +const NETWORK = "eip155:181228"; // CAIP-2 id used by x402 +const SCHEME = "upto"; // or "exact" + +// HPP is not a viem built-in chain, so define it. The scheme client needs a +// chain-configured public client for its on-chain reads (balances, allowances). +const hppSepolia = defineChain({ + id: 181228, + name: "HPP Sepolia", + nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, + rpcUrls: { default: { http: [RPC] } }, +}); + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); +const pub = createPublicClient({ chain: hppSepolia, transport: http(RPC) }); + +const signer = { + address: account.address, + signTypedData: (msg: any) => account.signTypedData(msg), + readContract: (args: any) => pub.readContract(args), +}; + +const schemeClient = + SCHEME === "upto" + ? new UptoEvmScheme(signer, { rpcUrl: RPC }) + : new ExactEvmScheme(signer, { rpcUrl: RPC }); + +const client = x402Client.fromConfig({ + schemes: [{ network: NETWORK, client: schemeClient }], + paymentRequirementsSelector: (_v, accepts) => + accepts.find((a) => a.network === NETWORK && a.scheme === SCHEME) ?? accepts[0], +}); + +const fetchWithPay = wrapFetchWithPayment(fetch, client); +``` + + + + +## 3. Call the paid endpoint + +```ts +const res = await fetchWithPay("https://seller.example.com/paid/hello", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ hi: 1 }), +}); + +console.log(res.status); // 200 +console.log(await res.json()); // the resource + +// Onchain settlement receipt, if the server returned one. +const header = res.headers.get("x-payment-response"); +if (header) { + const receipt = decodePaymentResponseHeader(header); + // { success, transaction, network, payer, amount?, ... } + console.log("settled tx:", receipt.transaction, "on", receipt.network); + // For `upto`, `receipt.amount` is the ACTUAL amount charged (≤ the authorized max). +} +``` + +The first call to an unpaid URL returns `402`; `wrapFetchWithPayment` signs the authorization and +re-sends it transparently, so you only observe the final `200`. Use `receipt.transaction` to link the +settlement on the [block explorer](./networks-and-token.mdx). + +## Handling failures + +A payment can fail in two distinct places. + +**Before sending.** If the client can't build a payment — no registered scheme matches any offered +`(network, scheme)`, or the wallet can't sign — `wrapFetchWithPayment` throws a plain `Error`: + +```ts +try { + const res = await fetchWithPay(url, { method: "POST" /* … */ }); + // … use res … +} catch (err) { + // e.g. "Failed to create payment payload: …" when no registered scheme matches + // the seller's accepts, or "Payment already attempted" after a retry is exhausted. + console.error("payment not sent:", (err as Error).message); +} +``` + +So make sure your registered schemes overlap with what the seller advertises in the `402` — otherwise +the client has nothing valid to sign. + +**After sending.** The resource server verifies and settles the payment. If it rejects the payment +(bad or expired authorization, insufficient USDC.e) or settlement fails onchain, you receive a +**non-2xx HTTP response** — not a thrown error. Check the status: + +```ts +if (!res.ok) { + console.error("server rejected payment:", res.status, await res.text()); +} +``` + +The verify/settle decision happens on the resource server via the facilitator; the typed `VerifyError` +/ `SettleError` are raised *there*, not on the buyer — see +[Facilitator → Verify and settle](./facilitator.mdx#verify-and-settle). + +> **Use the SDK client to encode payments.** The `X-PAYMENT` header has a specific encoding that the +> resource-server middleware validates. Hand-rolling the header will be rejected — always pay through +> `wrapFetchWithPayment` (or the MCP/agent wrappers built on the same client). + +## Agents and MCP + +The same `x402Client` powers higher-level wrappers — for example a payment-aware MCP client — so an AI +agent can discover a price from a `402` and pay it without any human step. Selecting between `exact` +and `upto` is automatic: the seller lists schemes in priority order and the client honors that order. + +## Next + +- [Facilitator](./facilitator.mdx) — how settlement works and how to go gasless. +- [SDK & reference](./sdk.md) — packages, network aliases, and upstream links. diff --git a/x402/quickstart-sellers.mdx b/x402/quickstart-sellers.mdx new file mode 100644 index 0000000..ff6c0af --- /dev/null +++ b/x402/quickstart-sellers.mdx @@ -0,0 +1,138 @@ +--- +title: 'Quickstart: Sellers' +sidebar_label: 'Quickstart: Sellers' +description: Charge for an HTTP endpoint on HPP with x402 — Express resource server, USDC.e pricing, and HPP's hosted facilitator. +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Quickstart: Sellers + +Put a price on an HTTP route and let HPP's facilitator handle verification and onchain settlement. +This example uses the `@x402/express` middleware, but the same pieces apply to any HTTP framework +the SDK supports. + +## 1. Install + +```bash +npm install @x402/express @x402/core @x402/evm express +``` + +## 2. Register USDC.e + +HPP's USDC.e is registered in the SDK's default-asset map so the `exact` (EIP-3009) domain +(`Bridged USDC` / version `2` / 6 decimals) is filled in automatically. Run this once at startup, +before constructing the resource server: + +```ts +import { DEFAULT_STABLECOINS } from "@x402/evm"; + +const USDC_E = { + address: "0x401eCb1D350407f13ba348573E5630B83638E30D", + name: "Bridged USDC", + version: "2", + decimals: 6, +}; +DEFAULT_STABLECOINS["eip155:190415"] = USDC_E; // HPP Mainnet +DEFAULT_STABLECOINS["eip155:181228"] = USDC_E; // HPP Sepolia +``` + +## 3. Build the resource server + +Point a facilitator client at HPP's hosted facilitator and register the schemes you want to accept +on each network. + + + + +```ts +import { x402ResourceServer } from "@x402/express"; +import { ExactEvmScheme } from "@x402/evm/exact/server"; +import { UptoEvmScheme } from "@x402/evm/upto/server"; +import { HTTPFacilitatorClient } from "@x402/core/server"; + +const facilitator = new HTTPFacilitatorClient({ url: "https://facilitator.hpp.io" }); + +const resourceServer = new x402ResourceServer([facilitator]); +resourceServer.register("eip155:190415", new ExactEvmScheme()); +resourceServer.register("eip155:190415", new UptoEvmScheme()); +``` + + + + +```ts +import { x402ResourceServer } from "@x402/express"; +import { ExactEvmScheme } from "@x402/evm/exact/server"; +import { UptoEvmScheme } from "@x402/evm/upto/server"; +import { HTTPFacilitatorClient } from "@x402/core/server"; + +const facilitator = new HTTPFacilitatorClient({ url: "https://facilitator-sepolia.hpp.io" }); + +const resourceServer = new x402ResourceServer([facilitator]); +resourceServer.register("eip155:181228", new ExactEvmScheme()); +resourceServer.register("eip155:181228", new UptoEvmScheme()); +``` + + + + +> **Only register what the facilitator supports.** The resource server throws if you advertise a +> `(network, scheme)` pair that the facilitator does not return from `/supported`. `exact` and `upto` +> are supported on both HPP networks. You can pass multiple facilitator clients to +> `x402ResourceServer([...])` to serve more than one network from a single process. + +## 4. Price your routes and mount the middleware + +Each route lists one or more `accepts` (one per `(network, scheme)`), priced in USDC.e base units. + +```ts +import express from "express"; +import { paymentMiddleware } from "@x402/express"; +import type { RoutesConfig } from "@x402/core/server"; + +const PAY_TO = "0xYourReceivingAddress"; // where settled USDC.e lands + +const routes: RoutesConfig = { + "POST /paid/hello": { + description: "A paid hello-world endpoint.", + accepts: [ + { + scheme: "exact", + network: "eip155:190415", + payTo: PAY_TO, + price: { + amount: "10000", // 0.01 USDC.e (6 decimals) + asset: "0x401eCb1D350407f13ba348573E5630B83638E30D", + extra: { name: "Bridged USDC", version: "2" }, + }, + maxTimeoutSeconds: 600, + }, + ], + }, +}; + +const app = express(); +app.use(paymentMiddleware(routes, resourceServer)); + +app.post("/paid/hello", (_req, res) => { + res.json({ message: "Thanks for paying — here is your resource." }); +}); + +app.listen(4021, () => console.log("x402 resource server on :4021")); +``` + +That's it. An unpaid `POST /paid/hello` now returns `402` with your terms; a request carrying a valid +`X-PAYMENT` header is verified, served, and settled onchain — and the handler returns the resource. + +## Accepting both schemes + +To offer `exact` and `upto` on the same route, list both in `accepts` (priority order — the buyer's +SDK picks the first it supports) and register both schemes on the network. `upto` routes can also +advertise gasless settlement; see [Facilitator → Gasless settlement](./facilitator.mdx#gasless-settlement-upto). + +## Next + +- [Quickstart: Buyers](./quickstart-buyers.mdx) — pay this endpoint from a client or agent. +- [Facilitator](./facilitator.mdx) — endpoints, supported schemes, and self-hosting. diff --git a/x402/sdk.md b/x402/sdk.md new file mode 100644 index 0000000..e836452 --- /dev/null +++ b/x402/sdk.md @@ -0,0 +1,69 @@ +--- +title: SDK & reference +sidebar_label: SDK & reference +description: The @x402 SDK packages, HPP network identifiers, and links to the x402 specification. +--- + +# SDK & reference + +HPP works with the standard **`@x402`** SDK maintained by the +[x402 Foundation](https://github.com/x402-foundation/x402). There is no HPP-specific fork — HPP +Mainnet and Sepolia are recognized via their CAIP-2 network identifiers, the same way the SDK +recognizes Base, Polygon, and other EVM chains. + +## Packages + +| Package | Used by | Purpose | +| --- | --- | --- | +| `@x402/core` | both | `x402Client`, `HTTPFacilitatorClient`, `RoutesConfig` types. | +| `@x402/evm` | both | EVM scheme clients (`exact`, `upto`) and the `DEFAULT_STABLECOINS` asset map. | +| `@x402/express` | sellers | `paymentMiddleware` and `x402ResourceServer` for Express. | +| `@x402/fetch` | buyers | `wrapFetchWithPayment` to make a paying `fetch`. | +| `viem` | buyers | Wallet account and chain utilities (`privateKeyToAccount`, `defineChain`). | + +Scheme clients are imported per role and scheme: + +```ts +// Seller (resource server) +import { ExactEvmScheme } from "@x402/evm/exact/server"; +import { UptoEvmScheme } from "@x402/evm/upto/server"; + +// Buyer (client) +import { ExactEvmScheme } from "@x402/evm/exact/client"; +import { UptoEvmScheme } from "@x402/evm/upto/client"; +``` + +## HPP network identifiers + +x402 uses [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) network identifiers. Use these values for +the `network` field everywhere in the SDK: + +| Network | x402 `network` | +| --- | --- | +| HPP Mainnet | `eip155:190415` | +| HPP Sepolia | `eip155:181228` | + +## Asset + +| Field | Value | +| --- | --- | +| USDC.e address (both chains) | `0x401eCb1D350407f13ba348573E5630B83638E30D` | +| Decimals | `6` | +| EIP-712 domain | name `Bridged USDC`, version `2` | + +Register it in the SDK's default-asset map so EIP-3009 domains are filled in automatically — see +[Quickstart: Sellers → Register USDC.e](./quickstart-sellers.mdx#2-register-usdce). + +## Discovery + +x402 includes an optional **discovery** mechanism (the "bazaar" extension) that lets sellers publish +their paid endpoints so buyers and agents can find them. A seller opts in by declaring the discovery +extension on its routes; HPP operates a discovery service that indexes published HPP x402 services. +Discovery is not required to accept or make payments — it is an add-on for marketplace-style use. + +## Links + +- **x402 specification & SDK** — https://github.com/x402-foundation/x402 +- **HPP Facilitator** — [endpoints and schemes](./facilitator.mdx) +- **HPP Networks & Token** — [chain IDs and RPC](./networks-and-token.mdx) +- **HPP Network Information** — [WSS, Chainlist, explorer](/getting-started/network-information)