Платить через x402 можно по-разному. Разбираем три схемы оплаты — разовый фиксированный платёж, оплату по факту использования и накопительные платёжные каналы — с техническими деталями и примерами кода на TypeScript, Go и Python.
В x402 за оплату отвечает схема оплаты (scheme): она задаёт семантику платежа, то есть как именно считается и списывается сумма. Схема не привязана к конкретному блокчейну — она работает поверх любой поддерживаемой сети : EVM (Ethereum, Base, Optimism...), Solana, TON, Stellar и других. Сегодня протокол поддерживает три схемы:
| Схема | Семантика | Типичный сценарий |
|---|---|---|
| exact | Разовый платёж на определенную сумму | Фиксированный тариф - скачивание файла, разовый доступ к ресурсу. |
| upto | Оплата по факту использования, но не выше заявленного максимума | Генерация текста LLM, трафик, compute — биллинг «по счётчику». Там, где сервер не может определить точную сумму заранее. |
| batch-settlement | Множество мелких платежей через платёжный канал, которые затем закрываются одной итоговой транзакцией. | Множество микроплатежей от одного клиента |
Схема 1: exact — фиксированная цена
exact — самая простая и базовая схема. Продавец объявляет одну конкретную сумму, покупатель подписывает платёж ровно на эту сумму, фасилитатор её проводит. Никаких пересчётов: сколько объявили — столько и списали.
Это подходящая отправная точка для большинства проектов. Если вы не уверены, какая схема нужна — почти наверняка вам нужна
exact.
Как это работает
На EVM схема exact поддерживает два механизма перевода токенов:
| Метод | Описание |
|---|---|
eip3009 |
Использует встроенную в токен функцию transferWithAuthorization (EIP-3009). Так работает USDC. Это метод по умолчанию, когда токен его поддерживает. |
permit2 |
Использует Uniswap Permit2 плюс x402-прокси. Позволяет принимать любой ERC-20 токен, даже если в нём нет EIP-3009. Может потребовать однократного approve. |
EIP-3009 — это стандарт, который позволяет владельцу токена подписать разрешение на перевод офчейн (без газа), а кто угодно (фасилитатор) затем исполняет этот перевод ончейн. Покупатель подписывает структуру по стандарту EIP-712 (типизированные данные):
// go/mechanisms/evm/types.go
type ExactEIP3009Authorization struct {
From string // адрес плательщика
To string // адрес получателя
Value string // сумма в минимальных единицах токена
ValidAfter string // не действует раньше этого времени (Unix)
ValidBefore string // не действует позже этого времени (Unix)
Nonce string // 32-байтный уникальный nonce (защита от повтора)
}
Подписанное поручение вместе с подписью отправляется обратно серверу в заголовке. Фасилитатор проверяет подпись и отправляет ончейн-транзакцию — газ платит фасилитатор, а не покупатель. Это важная деталь: покупателю не нужен «газовый» нативный токен сети, достаточно стейблкоина.
Для токенов без EIP-3009 используется Permit2 — единый контракт-«разрешитель» от Uniswap. Покупатель подписывает структуру PermitWitnessTransferFrom, где, помимо токена и суммы, есть поле witness — дополнительные данные (адрес получателя), которые проверяет ончейн-прокси x402:
type Permit2Authorization struct {
From string // владелец/подписант
Permitted Permit2TokenPermissions // токен + сумма
Spender string // адрес x402Permit2Proxy
Nonce string
Deadline string // срок годности подписи
Witness Permit2Witness // { To, ValidAfter }
}
Пример: сервер (TypeScript)
Сервер регистрирует реализацию схемы для нужных сетей и защищает маршрут:
import { HTTPFacilitatorClient } from "@x402/core/server";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { ExactSvmScheme } from "@x402/svm/exact/server";
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator",
});
const resourceServer = new x402ResourceServer(facilitatorClient)
.register("eip155:84532", new ExactEvmScheme())
.register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme());
app.use(
paymentMiddleware(
{
"GET /weather": {
accepts: [
{ scheme: "exact", price: "$0.001", network: "eip155:84532", payTo: "0xYourAddress" },
{ scheme: "exact", price: "$0.001", network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", payTo: "YourSolanaAddress" },
],
description: "Weather data",
mimeType: "application/json",
},
},
resourceServer,
),
);
Пример: клиент (Go)
Клиент регистрирует схему со своим подписантом (приватным ключом). Шаблон eip155:* означает «для любой EVM-сети»:
import (
x402 "github.com/x402-foundation/x402/go/v2"
exactevm "github.com/x402-foundation/x402/go/v2/mechanisms/evm/exact/client"
exactsvm "github.com/x402-foundation/x402/go/v2/mechanisms/svm/exact/client"
evmsigners "github.com/x402-foundation/x402/go/v2/signers/evm"
svmsigners "github.com/x402-foundation/x402/go/v2/signers/svm"
)
evmSigner, err := evmsigners.NewClientSignerFromPrivateKey(os.Getenv("EVM_PRIVATE_KEY"))
if err != nil {
log.Fatal(err)
}
svmSigner, err := svmsigners.NewClientSignerFromPrivateKey(os.Getenv("SVM_PRIVATE_KEY"))
if err != nil {
log.Fatal(err)
}
x402Client := x402.Newx402Client().
Register("eip155:*", exactevm.NewExactEvmScheme(evmSigner, nil)).
Register("solana:*", exactsvm.NewExactSvmScheme(svmSigner))
Схема 2: upto — оплата по факту использования
В exact цена известна заранее. Но что делать, если финальная стоимость зависит от того, сколько ресурсов реально потреблено? Заранее это определить невозможно.
Схема upto решает именно это. Продавец объявляет максимальную цену за один запрос. Покупатель подписывает платёж на этот максимум. А сервер при формировании ответа выбирает фактическую сумму ≤ максимума и списывает только её.
Эта схема полезна для:
- Генерации текста LLM (оплата за фактически сгенерированные токены).
- Оплаты за объём трафика, объем результата или процессорное время.
Это «биллинг по счётчику» в рамках одного запроса: вы заранее «бронируете» лимит, а платите по факту.
Как это работает
В отличие от exact, схема upto всегда использует Permit2, потому что финальная сумма неизвестна в момент подписи покупателем. Покупатель авторизует максимум, а право списать фактическую сумму получает конкретный фасилитатор.
Ключевое отличие в структуре witness — здесь добавляется поле facilitator:
// go/mechanisms/evm/types.go
type UptoPermit2Witness struct {
To string // получатель средств
Facilitator string // ТОЛЬКО этот адрес может вызвать settle() ончейн
ValidAfter string // Unix-время начала действия
}
Это важная защита: подпись привязана к конкретному фасилитатору. Только он может провести расчёт. Фасилитатор объявляет свой адрес (facilitatorAddress) в требованиях оплаты, а клиент «вшивает» его в подпись.
Settlement overrides: как сервер указывает фактическую сумму
Магия upto — в переопределении расчёта (settlement override). После того как сервер вычислил реальную стоимость, он вызывает setSettlementOverrides с фактической суммой. Сумму можно задать тремя способами:
| Формат | Пример | Значение |
|---|---|---|
| Сырые атомарные единицы | "50000" |
Списать ровно 50 000 минимальных единиц токена |
| Процент | "50%" |
Списать 50% от максимума маршрута |
| Долларовая цена | "$0.05" |
Пересчитать $0.05 в единицы токена |
Установка "0" означает «не списывать ничего за этот запрос».
Пример: сервер (TypeScript)
import { paymentMiddleware, setSettlementOverrides, x402ResourceServer } from "@x402/express";
import { UptoEvmScheme } from "@x402/evm/upto/server";
const resourceServer = new x402ResourceServer(facilitatorClient)
.register("eip155:84532", new UptoEvmScheme());
app.use(paymentMiddleware({
"GET /api/generate": {
accepts: {
scheme: "upto",
price: "$0.10", // максимум, который авторизует покупатель
network: "eip155:84532",
payTo: "0xYourAddress",
},
description: "AI text generation billed by usage",
},
}, resourceServer));
app.get("/api/generate", (req, res) => {
const actualUsage = computeActualCost(); // например, по числу токенов LLM
setSettlementOverrides(res, { amount: String(actualUsage) }); // спишем по факту
res.json({ result: "..." });
});
Пример: сервер (Go)
routes := x402http.RoutesConfig{
"GET /api/generate": {
Accepts: x402http.PaymentOptions{
{ Scheme: "upto", Price: "$0.10", Network: "eip155:84532", PayTo: "0xYourAddress" },
},
Description: "AI text generation billed by usage",
},
}
mux.HandleFunc("GET /api/generate", func(w http.ResponseWriter, r *http.Request) {
actualUsage := computeActualCost()
nethttpmw.SetSettlementOverrides(w, &x402.SettlementOverrides{
Amount: fmt.Sprintf("%d", actualUsage),
})
_ = json.NewEncoder(w).Encode(map[string]string{"result": "..."})
})
Пример: клиент (Go)
Клиент может зарегистрировать upto рядом с exact, если ходит и к фиксированным, и к «счётчиковым» ресурсам:
x402Client := x402.Newx402Client().
Register("eip155:*", exactevm.NewExactEvmScheme(evmSigner, nil)).
Register("eip155:*", uptoevm.NewUptoEvmScheme(evmSigner, nil))
Схема 3: batch-settlement — каналы для микроплатежей
Представьте API, который вызывают тысячи раз в минуту по доле цента за вызов. Если каждый такой микроплатёж проводить отдельной ончейн-транзакцией, то:
- комиссия за газ многократно превысит саму оплату;
- каждый расчёт будет ждать подтверждения блока — это медленно.
batch-settlement решает обе проблемы с помощью однонаправленных платёжных каналов (state channels). Деньги вносятся в эскроу один раз, а дальше каждый запрос подтверждается дешёвой офчейн-подписью. Ончейн-расчёт происходит редко и пакетом.
Когда выбирать:
- Повторяющиеся и высокочастотные API-вызовы.
- Любая нагрузка, где много мелких платежей по одному были бы слишком дорогими или медленными.
Как это работает: ваучеры и каналы
Жизненный цикл канала:
- Депозит (Deposit). Клиент один раз вносит ERC-20 средства в ончейн-эскроу. Депозит проводится через EIP-3009 или Permit2 и отправляется фасилитатором.
- Ваучер (Voucher). Каждый платный запрос несёт подписанный накопительный ваучер — суммарную сумму, которую сервер вправе забрать из канала на текущий момент. Ваучеры подписываются офчейн.
- Проверка (Verify). Сервер проверяет ваучер и сразу отдаёт ответ, не дожидаясь ончейн-перевода.
- Списание (Claim). Менеджер каналов на стороне сервера периодически забирает последние ваучеры из множества каналов одной транзакцией.
- Расчёт (Settle). Списанные средства отдельной транзакцией переводятся получателю.
- Возврат (Refund). Остаток средств можно вернуть плательщику после того, как все ваучеры списаны.
Поскольку ваучеры накопительные, серверу достаточно предъявить ончейн только последний ваучер канала — он содержит итоговую сумму. Тысячи запросов превращаются в одну подпись на списание.
Цена по-прежнему задаёт максимум за один запрос, и сервер может списать меньше через те же settlement overrides, что и в upto.
Депозит
Клиент не вносит сумму ровно под один запрос — это было бы неэффективно. Он вносит запас, кратный максимальной цене:
| Поле | Описание |
|---|---|
depositMultiplier |
Вносит сумма × множитель от объявленного максимума за запрос. По умолчанию 5, минимум 3. |
depositStrategy |
Опциональный колбэк: лимиты, динамический размер депозита, отказ от автопополнения. |
Пример: сервер (Go)
Серверу нужно настроить хранилище каналов и запустить менеджер, который будет списывать, рассчитывать и возвращать средства по расписанию:
cfg := &batchedserver.BatchSettlementEvmSchemeServerConfig{
ReceiverAuthorizerSigner: receiverAuthorizerSigner,
WithdrawDelay: 86400,
Storage: batchedserver.NewFileChannelStorage(batchsettlement.FileChannelStorageOptions{
Directory: "./channels",
}),
}
scheme := batchedserver.NewBatchSettlementEvmScheme(receiverAddress, cfg)
manager := scheme.CreateChannelManager(facilitatorClient, x402.Network("eip155:84532"))
manager.Start(batchedserver.AutoSettlementConfig{
ClaimIntervalSecs: 60, // забирать ваучеры каждую минуту
SettleIntervalSecs: 300, // переводить получателю каждые 5 минут
RefundIntervalSecs: 3600, // возвращать простаивающие каналы раз в час
MaxClaimsPerBatch: 100, // до 100 каналов в одной транзакции
})
routes := x402http.RoutesConfig{
"GET /weather": {
Accepts: x402http.PaymentOptions{
{ Scheme: batchsettlement.SchemeBatched, Price: "$0.01", Network: x402.Network("eip155:84532"), PayTo: receiverAddress },
},
Description: "Weather data",
MimeType: "application/json",
},
}
Пример: клиент (TypeScript)
Клиентский SDK берёт на себя всё: депозиты, подпись ваучеров, восстановление состояния канала и пересинхронизацию через «корректирующие» 402-ответы. Разработчику достаточно зарегистрировать схему и делать обычные запросы:
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { toClientEvmSigner } from "@x402/evm";
import { BatchSettlementEvmScheme } from "@x402/evm/batch-settlement/client";
import { createPublicClient, http } from "viem";
import { baseSepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
const signer = toClientEvmSigner(account, publicClient);
const batchScheme = new BatchSettlementEvmScheme(signer, {
depositPolicy: { depositMultiplier: 5 },
});
const client = new x402Client();
client.register("eip155:*", batchScheme);
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("https://api.example.com/weather");
Менеджер каналов хранит последний ваучер и состояние сессии каждого канала. Для одного процесса годится файловое хранилище (FileChannelStorage). Для serverless и в случае нескольких сервисов нужно общее атомарное хранилище — например, Redis, — чтобы обновления каналов были консистентны между процессами.
Бонус: как x402 проверяет, что вы — это вы
x402 поддерживает не только обычные кошельки (EOA). EVM-реализация умеет верифицировать подписи нескольких типов, что видно по коду (go/mechanisms/evm/):
- EOA (
verify_eoa.go) — классическая ECDSA-подпись приватным ключом. Самый частый случай. - EIP-1271 (
verify_1271.go) — подписи от смарт-контрактных кошельков (Safe, Argent и т. п.). Контракт сам решает, валидна ли подпись, через методisValidSignature. - ERC-6492 (
erc6492.go) — подписи от смарт-кошельков, которые ещё не задеплоены ончейн. Подпись «оборачивается» данными для развёртывания (адрес фабрики + calldata), так что её можно проверить даже до создания кошелька. - EIP-7702 (
erc7702.go) — новый стандарт, позволяющий обычному EOA временно «получить» код смарт-контракта в рамках транзакции.
Все платёжные поручения подписываются по стандарту EIP-712 — это «типизированные данные», которые кошелёк показывает пользователю в читаемом виде (домен, тип сообщения, поля), а не как нечитаемый хеш. Структура TypedDataDomain привязывает подпись к конкретному контракту и сети (chainId), что не даёт переиспользовать подпись в другой сети:
type TypedDataDomain struct {
Name string
Version string
ChainID *big.Int // привязка к конкретной сети
VerifyingContract string // привязка к конкретному контракту
}
Все три схемы можно комбинировать на одном сервере: разные маршруты — разные схемы. И сервер может предлагать в accepts сразу несколько сетей (например, EVM и Solana), а клиент выберет ту, для которой у него есть средства и зарегистрированная реализация.
Схемы покрывают фундаментально разные паттерны оплаты:
exact— «заплати ровно столько» для фиксированных цен;upto— «заплати по факту, но не больше лимита» для биллинга по использованию;batch-settlement— «накапливай и рассчитывайся пакетами» для высокочастотных микроплатежей.
Полезные ссылки
- Спецификация
exact: https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact.md - Спецификация
upto: https://github.com/x402-foundation/x402/blob/main/specs/schemes/upto/scheme_upto.md - Спецификация
batch-settlement: https://github.com/x402-foundation/x402/blob/main/specs/schemes/batch-settlement/scheme_batch_settlement.md - Примеры (TypeScript, Go, Python): https://github.com/x402-foundation/x402/tree/main/examples
