402 Payment Required на запрос без токена, при этом в поле accepts: [...] перечисляются принимаемые цепочки / активы / цены; клиент подписывает авторизацию локально (на EVM это Permit2 / EIP-712, на Solana это авторизация передачи токенов SPL), помещает закодированный в base64 envelope в заголовок PAYMENT-SIGNATURE и повторно отправляет запрос. После проверки сервер действительно производит расчет в цепочке и возвращает бизнес-результат.
Клиент X402 от Ace Data Cloud напрямую вызывает целевой API и использует402 Payment Requiredиaccepts, возвращенные в этом запросе, в качестве цены и основы для подписи. Платежные возможности Facilitator можно проверить по/.well-known/x402.
@acedatacloud/sdk и acedatacloud оба предоставляют хук paymentHandler: когда запрос, отправленный самим SDK, получает 402, вызывается ваш внедренный обработчик для получения заголовка PAYMENT-SIGNATURE, после чего повторно отправляется оригинальный запрос. Используя @acedatacloud/x402-client / acedatacloud-x402 вместе с SDK, весь процесс полностью прозрачен для бизнес-кода — вам нужно только client.openai.chat.completions.create(...), это выглядит так же, как и в режиме токена, но на нижнем уровне это оплата по вызову, без необходимости предварительной зарядки.
В этой статье:
- Реально протестирован путь “без токена + внедрение X402 обработчика” на стороне TS (T12 проверка)
- Перечислены различия в двух цепочках подписания EVM / Solana
- Предложены три адаптации: режим приватного ключа
viem, режим браузерного кошелька, режим PythonEVMAccountSigner - Разъяснено поле
preferScheme/prefer_scheme, которое легко может вызвать проблемы
I. Обзор протокола (обязательно к прочтению)
Успешный вызов X402 включает 3 HTTP RTT:PAYMENT-SIGNATURE. Структура (выдержка):
x402Version: 2, и с помощью объекта accepted объявляется выбранная scheme и network (идентификатор CAIP-2).
preferScheme / prefer_scheme используется для выбора предпочтений, когда сервер одновременно предлагает несколько схем. Если сервер предоставляет только exact, это поле будет проигнорировано; если установлено upto, но сервер не предоставляет, будет использован первый подходящий вариант.
II. TypeScript: браузерный кошелек + сервер viem два способа использования
Установка
Полная подпись createX402PaymentHandler
(ctx) => Promise<{ headers: Record<string, string> }> , что точно соответствует сигнатуре хука paymentHandler SDK.
Способ 1: Браузер (MetaMask / WalletConnect)
MaxUint256, записанная в цепочку); второй — это подпись EIP-712 для X402 envelope (не записывается в цепочку, просто для проверки facilitator). Последующие вызовы требуют только второй подписи, что в итоге выглядит как “один раз нажать на подпись → получить результат”.
Способ 2: Node сервер + viem приватный ключ (подходит для бэкенда / CLI)
@acedatacloud/x402-client на стороне TS принимает только EIP-1193 провайдер — он не управляет приватными ключами напрямую. В сценарии Node / CLI стандартный подход — использовать viem для упаковки приватного ключа в WalletClient, а затем использовать @ethereumjs/util или внутреннюю адаптацию EIP-1193 viem.
Если вы считаете, что адаптация EIP-1193 в viem недостаточно стабильна, вы также можете использовать более низкоуровневыйsignEVMUptoPayment, самостоятельно связавaccepts → signed envelope → PAYMENT-SIGNATURE header, пропустив хуки SDK; однако рекомендуется все же предпочестьcreateX402PaymentHandler, чтобы не поддерживать обновления протокола самостоятельно.
Использование 3: Solana
exact, поэтому preferScheme не работает на Solana.
Три, Python: режим приватного ключа
Pythonacedatacloud-x402 использует прямую подпись приватным ключом (без абстракции EIP-1193), что больше подходит для серверов / исполнителей задач.
Установка
EVM (Base / Skale)
Solana
Одноразовое одобрение (только для EVM в первый раз)
На EVM Base X402 использует Permit2, требуется, чтобы кошелек сделал одноMaxUint256 одобрение для контракта Permit2 на USDC. acedatacloud-x402 включает в себя approve_permit2:
Четыре, проверка реального выполнения
Цель тестирования: TS SDK не передает токен, внедряет X402 обработчик, может нормально строить и инициировать запрос (не расходуя настоящие USDC на блокчейне для легкой проверки).- Не передав
apiToken, SDK создается без ошибок, что подтверждает, что X402 режим действительно является законной заменой токена. createX402PaymentHandlerвозвращает функцию (хука), SDK вызывает ее только при получении 402.- Полноценное тестирование оплаты на реальном блокчейне не включено в этот учебник, так как оно связано с реальным списанием USDC; вы можете обратиться к X402 интеграционному руководству для примеров e2e.
Python сторонаcreate_x402_payment_handlerтакже прошла аналогичную проверку — возвращаемое значение функции является вызываемым, и при внедренииpayment_handler=...AceDataCloud(...)создается без ошибок. Оба конца согласованы по смыслу.
Пять, сравнение с «Bearer token режимом»
Шесть, распространенные ловушки
- Класс chat должен иметь
preferScheme=upto: использованиеexactзаставит facilitator удерживать USDC поmaxAmountRequired(не по фактическому использованию). - Не передавайте голый приватный ключ в
createX402PaymentHandlerна стороне Node: пакет TS не принимает{ privateKey }, он должен быть упакован в EIP-1193 провайдер (рекомендуется viemWalletClient). - Первый вызов — это двойная подпись: первый раз подписывается Permit2 approve (в цепочке, с газом), второй раз подписывается X402 envelope (не в цепочке). В последующих вызовах остается только второй раз.
- В Solana нет концепции Permit2: просто подписывайте авторизацию на передачу SPL токенов, не требуется approve; но в настоящее время на цепочке Solana поддерживается только
exact. - Разделение ошибок бизнес-логики и ошибок платежей: 402 → ошибка обработчика выбрасывает
X402SignError(конкретный тип зависит от цепочки); последующие повторные отправки будут иметь ошибки бизнес-интерфейса (401 / 422 / 5xx), которые по-прежнему классифицируются как обычные исключения SDK. - Самый стабильный способ адаптации
viem:evmProvider: walletClient as anyпотеряет проверку типов, но обеспечит наилучшую совместимость; если хотите сохранить типы, используйте.transport.requestот viem, чтобы отдельно упаковать объект{ request }.

