Как начать работу с x402 на Solana

Что такое x402?

x402 — это открытый протокол для нативных интернет-платежей. Код ошибки 402 означает «Payment Required» («Требуется оплата») и давно существует в спецификации HTTP, однако лишь теперь его практическое применение стало возможным благодаря развитию блокчейн-сетей. Протокол 402 реализует паттерн HTTP 402 Payment Required: сервер требует оплату перед тем, как вернуть защищённый ответ. На Solana это обычно реализуется путём запроса клиенту на выполнение небольшого перевода, после чего сервер верифицирует его в блокчейне и предоставляет контент.

На данный момент ещё неясно, какой из 402 SDK станет наиболее популярным. В этом руководстве мы покажем, как реализовать x402 с использованием минимального сервера и клиента, а также приведём список всех доступных 402 SDK с описанием текущей поддержки Solana.

Как это работает?

Существует несколько способов реализации x402 — от максимально лёгких до полностью управляемых.

Идея протокола: используется обычный HTTP. Клиент обращается к вашему URL → вы отвечаете 402 Payment Required с JSON-объектом Payment Requirements → клиент совершает оплату и повторяет запрос с заголовком X-PAYMENT → вы верифицируете/проводите расчёт → отвечаете 200 OK. Никаких аккаунтов, никакого OAuth.

Диаграмма потока x402Диаграмма потока x402

Примечание: фасилитатор полностью опционален — вы также можете реализовать собственную логику валидации буквально несколькими строками кода. Фасилитатор абстрагирует детали интеграции с блокчейном от сервера и клиента, что упрощает реализацию.

Ключевые элементы спецификации: структура PaymentRequirements, заголовок X-PAYMENT в кодировке base64, опциональный X-PAYMENT-RESPONSE при успехе, а также (опциональный) API фасилитатора для /verify, /settle, /supported. Текущая конкретная схема — exact (оплата фиксированной суммы). Другие схемы, например upto, находятся в стадии предложения.

Поддержка Solana: сам протокол не привязан к конкретной сети; на Solana поддерживаются все SPL-токены. Поддержка Solana доступна или находится в разработке для большинства 402 SDK.

Ниже представлен список доступных 402 SDK с описанием текущей поддержки Solana.

Варианты использования

x402 открывает широкий спектр сценариев микроплатежей и оплаты по факту использования, которые прежде были экономически нецелесообразны без блокчейна. Представьте, что Netflix платит за каждый просмотр, а Spotify — за каждую песню вместо подписки. Вот лишь несколько возможных идей, однако весь спектр возможностей ограничен лишь вашим воображением:

AI и агентная коммерция:

  • Доступ к AI Agent API: оплата за каждый вызов LLM-инференса, генерации изображений или API AI-модели (см. пример ACK)
  • Монетизация MCP-серверов: оплата за инструменты Model Context Protocol, источники данных и специализированные возможности агентов (см. MCPay.tech)
  • Платежи между агентами: возможность для автономных агентов совершать транзакции между собой за услуги и данные (см. пример a2a-x402)
  • Премиальные данные для обучения AI: продажа доступа к тщательно отобранным датасетам на основе оплаты за запрос

Контент и медиа:

  • Платный доступ к статьям: микроплатежи за каждую статью вместо полной подписки
  • Стриминг видео/аудио: оплата за просмотр или за минуту контента
  • Изображения в высоком разрешении: разблокировка загрузки в полном разрешении после оплаты (см. пример ACK) или пример x402 от Coinbase
  • Доступ к премиальным рассылкам: монетизация отдельных выпусков новостной рассылки

Сервисы для разработчиков:

  • Тарификация API: оплата за каждый RPC-вызов, запрос к базе данных или вычислительную единицу (см. пример Corbits)
  • Бессерверные функции: оплата за каждое выполнение отдельной функции

Данные и аналитика:

  • Рыночные данные в реальном времени: ценовые потоки с тарификацией за котировку или тик
  • Аналитические дашборды: разблокировка конкретных отчётов или экспортов данных
  • Данные IoT-сенсоров: микроплатежи за показания сенсоров из сетей DePIN

Игры и виртуальные товары:

  • Доступ к игровым серверам: оплата за сессию или за час
  • Загрузка модов и ресурсов: монетизация пользовательского контента
  • Взносы за участие в турнирах: автоматическое распределение призового фонда

Разное:

  • Фильтрация email/личных сообщений: требование оплаты для доступа к вашему почтовому ящику (защита от спама)
  • Вычислительные ресурсы: оплата за час CPU, минуту GPU или гигабайт хранилища
  • Доступ к VPN/прокси: тарификация по объёму трафика за ГБ
  • Единовременная загрузка файлов: продажа цифровых файлов без издержек на подписку (см. пример ACK)

Ключевое преимущество x402 на Solana — низкие транзакционные издержки (доли цента), что делает настоящие микроплатежи жизнеспособными, а также мгновенный расчёт, обеспечивающий контроль доступа в реальном времени.

SDK и их поддержка Solana

Этот список постоянно обновляется по мере выхода новых SDK или добавления поддержки Solana.

SDK / ПроектПоддержка SolanaПримечанияДокументация / URL
CorbitsДаУдобный SDK для 402 на SolanaДокументация
MCPay.techДаОплата MCP-серверов через микроплатежиСайт
PayAI FacilitatorДаФасилитатор x402 с поддержкой Solanapayai.network
CoinbaseДа / Python в разработкеЭталонная реализация протокола x402 от CoinbaseGitHub
ACKВ PRПротокол агентных платежей с поддержкой x402GitHub
CrossmintВ разработкеПлатежи, кошельки, агентные финансы; не специфично для x402crossmint.com
A2A x402 (Google)В разработкеПлатежи между агентами с использованием Google AIGitHub
Nexus (Thirdweb)В разработкеОбёртка x402 вокруг API-ключейNexus
x402scanН/П (Обозреватель)Обозреватель экосистемы x402 (не SDK)x402scan.com
Нативный примерДаМинимальный пример без зависимостейПримеры

Corbits

SDK с приоритетом Solana для быстрой реализации потоков x402 на Solana. Смотрите документацию: https://corbits.dev/

Пример оплаты RPC-запросов к Solana.

npm install @faremeter/payment-solana @faremeter/fetch @faremeter/info
@solana/web3.js

Создайте файл payer-wallet.json и пополните его небольшим количеством USDC и mainnet SOL.

import {
Keypair,
PublicKey,
VersionedTransaction,
Connection
} from "@solana/web3.js";
import { createPaymentHandler } from "@faremeter/payment-solana/exact";
import { wrap } from "@faremeter/fetch";
import { lookupKnownSPLToken } from "@faremeter/info/solana";
import * as fs from "fs";
// Load keypair from file
const keypairData = JSON.parse(fs.readFileSync("./payer-wallet.json", "utf-8"));
const keypair = Keypair.fromSecretKey(Uint8Array.from(keypairData));
const network = "mainnet-beta";
const connection = new Connection("https://api.mainnet.solana.com");
const usdcInfo = lookupKnownSPLToken(network, "USDC");
const usdcMint = new PublicKey(usdcInfo.address);
// Create wallet interface
const wallet = {
network,
publicKey: keypair.publicKey,
updateTransaction: async (tx: VersionedTransaction) => {
tx.sign([keypair]);
return tx;
}
};
// Setup payment handler
const handler = createPaymentHandler(wallet, usdcMint, connection);
const fetchWithPayer = wrap(fetch, { handlers: [handler] });
// Call the API - payment happens automatically
const response = await fetchWithPayer("https://helius.api.corbits.dev", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "getBlockHeight"
})
});
const data = await response.json();
console.log(data);
npx tsx e2e.ts

Это оплатит RPC-запрос и вернёт высоту блока, используя протокол corbits 402.

Coinbase

Эталонная реализация x402 от Coinbase предоставляет TypeScript-библиотеки и примеры как для клиентских, так и для серверных потоков. Репозиторий включает сквозные тесты, охватывающие 6 различных сценариев SVM (Solana Virtual Machine). Реализация включает верификацию платежей, генерацию квитанций и обработку ошибок.

Ключевые возможности:

  • Реализации клиента и сервера на TypeScript
  • Утилиты верификации платежей
  • Поддержка нескольких схем оплаты (точная сумма, сумма «до»)
  • Набор тестов с примерами транзакций Solana
  • Разделение логики протокола и бизнес-логики

Простой в использовании пример с минимальным сервером и клиентом можно найти здесь.

const app = express();
const PORT = 3000;
// Apply x402 payment middleware
// This automatically handles:
// - 402 responses with payment requirements
// - Payment verification (pre-flight checks)
// - Transaction submission via facilitator
// - Settlement confirmation
app.use(
paymentMiddleware(RECIPIENT, {
// Protected endpoint: requires $0.001 USDC payment
"GET /premium": {
price: "$0.0001", // Price in USD (converted to USDC)
network: "solana-devnet" // Solana devnet
},
// Another endpoint with different price
"GET /expensive": {
price: "$0.001",
network: "solana-devnet"
}
})
);
// Protected endpoints - only accessible after payment
app.get("/premium", (req, res) => {
res.json({
message: "🎉 Premium content accessed!",
data: {
secret: "This is premium content",
timestamp: new Date().toISOString()
}
});
});

Поддержка Python находится в разработке; рабочий сквозной пример доступен здесь.

ACK

Agent Commerce Kit (ACK) поддерживает протокол x402, добавляя к нему критически важные уровни для агентной экономики: верифицируемую идентификацию агентов (ACK-ID) на основе W3C DID/VC и криптографически защищённые квитанции (ACK-Pay) в виде Verifiable Credentials. Это позволяет агентам подтверждать право собственности, проходить аутентификацию автономно и формировать платёжные доказательства, готовые для целей комплаенса, — решая тем самым проблему идентификации и транзакционные барьеры, которые препятствуют участию AI-агентов в коммерции.

Диаграмма потока ACKДиаграмма потока ACK

Существует PR со сквозным примером, который ещё не влит в основную ветку, но работает. Также доступен живой пример, демонстрирующий платный доступ к изображениям, джукбокс и API для анимации изображений. Исходный код примеров вместе с Twitter-ботом, использующим API для анимации изображений в ленте, можно найти здесь.

MCPay.tech

Микроплатежи за каждый запрос для MCP (Model Context Protocol) серверов по схеме, аналогичной x402. Позволяет разработчикам монетизировать MCP-инструменты и ресурсы, требуя небольшую оплату за каждый вызов API или инструмента, — упрощая тарификацию доступа AI-агентов к премиальным источникам данных, специализированным инструментам или вычислительным ресурсам. Сайт: https://mcpay.tech/

PayAI Facilitator

Фасилитатор x402 с приоритетом Solana, оснащённый живым эхо-мерчантом для тестирования и возврата платежей. На данный момент PayAI берёт на себя все транзакционные комиссии. Сайт: https://payai.network/

A2A x402 (Google)

Инициатива по платежам между агентами, исследующая стандартизированные потоки с требованием оплаты. Поддержка Solana находится в процессе разработки; рабочий пример чата можно найти здесь

Crossmint

Crossmint — универсальная платформа для компаний и агентов, обеспечивающая интеграцию крипто-инфраструктуры, включая кошельки, онрампы, оркестрацию стейблкоинов и многое другое. Поддержка x402 на Solana в настоящее время разрабатывается и должна быть завершена к 30.10.2025. Сайт: https://www.crossmint.com/

x402scan

Обозреватель экосистемы x402, предоставляющий подробную статистику, каталог проектов и аналитику по реализациям x402. Отслеживайте объёмы транзакций, находите активных мерчантов и следите за ростом числа платных эндпоинтов в различных сетях. Сайт: https://x402scan.com/

Nexus (Thirdweb)

Thirdweb Nexus разрабатывает обёртку x402 вокруг API-ключей (в настоящее время в разработке). Сайт: https://nexus.thirdweb.com/

Нативный пример

Нативный пример без зависимостей с минимальным сервером и клиентом.

Вы можете клонировать репозиторий и запустить пример:

git clone https://github.com/Woody4618/x402-solana-examples
npm install
# Terminal 1: Start server
npm run usdc:server
# Terminal 2: Run client (requires devnet USDC)
npm run usdc:client

Обзор потока

  1. Клиент отправляет запрос к /premium.
  2. Сервер отвечает 402 с условиями оплаты: получатель, сумма.
  3. Клиент формирует транзакцию с инструкцией перевода получателю.
  4. Клиент повторяет запрос к /premium с payload транзакции.
  5. Сервер верифицирует транзакцию и отправляет её в сеть.
  6. После подтверждения сервер отвечает 200.

Альтернатива для Solana: В Solana можно реализовать вариант, при котором клиент отправляет транзакцию напрямую в сеть с инструкцией memo (вместо того чтобы отправлять её на сервер), а затем передаёт серверу только подпись транзакции для верификации. Это решает проблему разрыва соединения — если клиент отключается после оплаты, но до получения контента, он может повторить запрос с той же подписью, поскольку платёж уже подтверждён в блокчейне. Однако такой подход отклоняется от стандартного потока x402.org (который предполагает, что сервер транслирует транзакцию), поэтому в данном примере мы используем стандартный подход.

Примечание: код этого примера не прошёл аудит, не готов к использованию в производственной среде и предназначен исключительно в демонстрационных целях. Он показывает, что x402 можно реализовать без зависимостей и без использования фасилитатора. Использование фасилитатора удобно, поскольку он скрывает сложность и может брать на себя комиссии за транзакции, однако он также может стать единой точкой отказа — например, когда на кошельке фасилитатора заканчиваются средства. Пример сервера отправляет транзакции, подписанные клиентом. Возможно, вам потребуется их проверить с помощью validator.

Минимальный сервер (Express)

// x402-compliant server with USDC (SPL Token) payments
import express from "express";
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
import { TOKEN_PROGRAM_ID, getAssociatedTokenAddress } from "@solana/spl-token";
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
// Devnet USDC mint address
const USDC_MINT = new PublicKey("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU");
// Your recipient wallet address (same as SOL example)
const RECIPIENT_WALLET = new PublicKey(
"seFkxFkXEY9JGEpCyPfCWTuPZG9WK6ucf95zvKCfsRX"
);
// Derive the recipient's USDC token account (Associated Token Account)
const RECIPIENT_TOKEN_ACCOUNT = await getAssociatedTokenAddress(
USDC_MINT,
RECIPIENT_WALLET
);
// Picking a small USDC price
const PRICE_USDC = 100; // 0.0001 USDC
const app = express();
app.use(express.json());
// x402 endpoint - Quote or verify payment
app.get("/premium", async (req, res) => {
const xPaymentHeader = req.header("X-Payment");
// If client provided X-Payment header, verify and submit transaction
if (xPaymentHeader) {
try {
// Decode base64 and parse JSON (x402 standard)
const paymentData = JSON.parse(
Buffer.from(xPaymentHeader, "base64").toString("utf-8")
) as {
x402Version: number;
scheme: string;
network: string;
payload: {
serializedTransaction: string;
};
};
console.log("Received USDC payment proof from client");
console.log(` Network: ${paymentData.network}`);
// Deserialize the transaction
const txBuffer = Buffer.from(
paymentData.payload.serializedTransaction,
"base64"
);
const tx = Transaction.from(txBuffer);
console.log("Verifying SPL Token transfer instructions...");
// Step 1: Introspect and decode SPL Token transfer instruction
const instructions = tx.instructions;
let validTransfer = false;
let transferAmount = 0;
for (const ix of instructions) {
// Check if this is a Token Program instruction
if (ix.programId.equals(TOKEN_PROGRAM_ID)) {
// SPL Token Transfer instruction layout:
// [0] = instruction type (3 for Transfer)
// [1-8] = amount (u64, little-endian)
if (ix.data.length >= 9 && ix.data[0] === 3) {
// Read the amount (u64 in little-endian, starts at byte 1)
transferAmount = Number(ix.data.readBigUInt64LE(1));
// Verify accounts: [source, destination, owner]
if (ix.keys.length >= 2) {
const destAccount = ix.keys[1].pubkey;
if (
destAccount.equals(RECIPIENT_TOKEN_ACCOUNT) &&
transferAmount >= PRICE_USDC
) {
validTransfer = true;
console.log(
` ✓ Valid USDC transfer: ${transferAmount / 1000000} USDC`
);
console.log(` To: ${RECIPIENT_TOKEN_ACCOUNT.toBase58()}`);
break;
}
}
}
}
}
if (!validTransfer) {
return res.status(402).json({
error:
"Transaction does not contain valid USDC transfer to recipient with correct amount",
details:
transferAmount > 0
? `Found transfer of ${transferAmount}, expected ${PRICE_USDC}`
: "No valid token transfer instruction found"
});
}
// Step 2: Simulate the transaction BEFORE submitting
console.log("Simulating transaction...");
try {
const simulation = await connection.simulateTransaction(tx);
if (simulation.value.err) {
console.error("Simulation failed:", simulation.value.err);
return res.status(402).json({
error: "Transaction simulation failed",
details: simulation.value.err,
logs: simulation.value.logs
});
}
console.log(" ✓ Simulation successful");
} catch (simError) {
console.error("Simulation error:", simError);
return res.status(402).json({
error: "Failed to simulate transaction",
details:
simError instanceof Error ? simError.message : "Unknown error"
});
}
// Step 3: Submit the transaction (only if verified and simulated successfully)
// Note: Solana blockchain automatically rejects duplicate transaction signatures
console.log("Submitting transaction to network...");
const signature = await connection.sendRawTransaction(txBuffer, {
skipPreflight: false,
preflightCommitment: "confirmed"
});
console.log(`Transaction submitted: ${signature}`);
// Wait for confirmation
const confirmation = await connection.confirmTransaction(
signature,
"confirmed"
);
if (confirmation.value.err) {
return res.status(402).json({
error: "Transaction failed onchain",
details: confirmation.value.err
});
}
// Fetch the transaction to verify payment details
const confirmedTx = await connection.getTransaction(signature, {
commitment: "confirmed",
maxSupportedTransactionVersion: 0
});
if (!confirmedTx) {
return res.status(402).json({
error: "Could not fetch confirmed transaction"
});
}
// Verify token balance changes from transaction metadata
const postTokenBalances = confirmedTx.meta?.postTokenBalances ?? [];
const preTokenBalances = confirmedTx.meta?.preTokenBalances ?? [];
// Find the recipient's token account in the balance changes
let amountReceived = 0;
for (let i = 0; i < postTokenBalances.length; i++) {
const postBal = postTokenBalances[i];
const preBal = preTokenBalances.find(
(pre) => pre.accountIndex === postBal.accountIndex
);
// Check if this is the recipient's account
const accountKey =
confirmedTx.transaction.message.staticAccountKeys[
postBal.accountIndex
];
if (accountKey && accountKey.equals(RECIPIENT_TOKEN_ACCOUNT)) {
const postAmount = postBal.uiTokenAmount.amount;
const preAmount = preBal?.uiTokenAmount.amount ?? "0";
amountReceived = Number(postAmount) - Number(preAmount);
break;
}
}
if (amountReceived < PRICE_USDC) {
return res.status(402).json({
error: `Insufficient payment: received ${amountReceived}, expected ${PRICE_USDC}`
});
}
console.log(
`Payment verified: ${amountReceived / 1000000} USDC received`
);
console.log(
`View transaction: https://explorer.solana.com/tx/${signature}?cluster=devnet`
);
// Payment verified! Return premium content
return res.json({
data: "Premium content - USDC payment verified!",
paymentDetails: {
signature,
amount: amountReceived,
amountUSDC: amountReceived / 1000000,
recipient: RECIPIENT_TOKEN_ACCOUNT.toBase58(),
explorerUrl: `https://explorer.solana.com/tx/${signature}?cluster=devnet`
}
});
} catch (e) {
console.error("Payment verification error:", e);
return res.status(402).json({
error: "Payment verification failed",
details: e instanceof Error ? e.message : "Unknown error"
});
}
}
// No payment provided - return 402 with payment details
console.log("New USDC payment quote requested");
return res.status(402).json({
payment: {
recipientWallet: RECIPIENT_WALLET.toBase58(),
tokenAccount: RECIPIENT_TOKEN_ACCOUNT.toBase58(),
mint: USDC_MINT.toBase58(),
amount: PRICE_USDC,
amountUSDC: PRICE_USDC / 1000000,
cluster: "devnet",
message: "Send USDC to the token account"
}
});
});
app.listen(3001, () => console.log("x402 USDC server listening on :3001"));

Минимальный клиент (Node)

import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
import {
createTransferInstruction,
getOrCreateAssociatedTokenAccount,
createAssociatedTokenAccountInstruction,
getAccount
} from "@solana/spl-token";
import fetch from "node-fetch";
import { readFileSync } from "fs";
const connection = new Connection("https://api.devnet.solana.com", "confirmed");
const keypairData = JSON.parse(
readFileSync("./pay-in-usdc/client.json", "utf-8")
);
const payer = Keypair.fromSecretKey(Uint8Array.from(keypairData));
async function run() {
// 1) Request payment quote from server
const quote = await fetch("http://localhost:3001/premium");
const q = (await quote.json()) as {
payment: {
tokenAccount: string;
mint: string;
amount: number;
amountUSDC: number;
cluster: string;
};
};
if (quote.status !== 402) throw new Error("Expected 402 quote");
const recipientTokenAccount = new PublicKey(q.payment.tokenAccount);
const mint = new PublicKey(q.payment.mint);
const amount = q.payment.amount;
console.log("USDC Payment required:");
console.log(` Recipient Token Account: ${q.payment.tokenAccount}`);
console.log(` Mint (USDC): ${q.payment.mint}`);
console.log(
` Amount: ${q.payment.amountUSDC} USDC (${amount} smallest units)`
);
// 2) Get or create the payer's associated token account
console.log("\nChecking/creating associated token account...");
const payerTokenAccount = await getOrCreateAssociatedTokenAccount(
connection,
payer,
mint,
payer.publicKey
);
console.log(` Payer Token Account: ${payerTokenAccount.address.toBase58()}`);
// Check if payer has enough USDC
const balance = await connection.getTokenAccountBalance(
payerTokenAccount.address
);
console.log(` Current Balance: ${balance.value.uiAmountString} USDC`);
if (Number(balance.value.amount) < amount) {
throw new Error(
`Insufficient USDC balance. Have: ${balance.value.uiAmountString}, Need: ${q.payment.amountUSDC}`
);
}
// 3) Check if recipient token account exists, create if not
console.log("\nChecking recipient token account...");
let recipientAccountExists = false;
try {
await getAccount(connection, recipientTokenAccount);
recipientAccountExists = true;
console.log(" ✓ Recipient token account exists");
} catch (error) {
console.log(" ⚠ Recipient token account doesn't exist, will create it");
}
// 4) Create USDC transfer transaction (but DON'T submit it)
const { blockhash } = await connection.getLatestBlockhash();
const tx = new Transaction({
feePayer: payer.publicKey,
blockhash,
lastValidBlockHeight: (await connection.getLatestBlockhash())
.lastValidBlockHeight
});
// Add create account instruction if needed
if (!recipientAccountExists) {
// We need to know the recipient wallet address to create the ATA
// The server should provide this, so let's get it from the wallet address
// Usually the server will already have the token account, but to be sure for the examples
// lets create one.
const recipientWallet = new PublicKey(
"seFkxFkXEY9JGEpCyPfCWTuPZG9WK6ucf95zvKCfsRX"
);
const createAccountIx = createAssociatedTokenAccountInstruction(
payer.publicKey, // payer
recipientTokenAccount, // associated token account address
recipientWallet, // owner
mint // mint
);
tx.add(createAccountIx);
console.log(" + Added create token account instruction");
}
// Add transfer instruction
const transferIx = createTransferInstruction(
payerTokenAccount.address, // source
recipientTokenAccount, // destination
payer.publicKey, // owner
amount // amount in smallest units
);
tx.add(transferIx);
// Sign the transaction (but don't send it, the server will do that)
tx.sign(payer);
// Serialize the signed transaction
const serializedTx = tx.serialize().toString("base64");
console.log("\nTransaction created and signed (not submitted yet)");
console.log(` Instructions: ${tx.instructions.length}`);
// 4) Send X-Payment header with serialized transaction (x402 standard)
const paymentProof = {
x402Version: 1,
scheme: "exact",
network:
q.payment.cluster === "devnet" ? "solana-devnet" : "solana-mainnet",
payload: {
serializedTransaction: serializedTx
}
};
// Base64 encode the payment proof
const xPaymentHeader = Buffer.from(JSON.stringify(paymentProof)).toString(
"base64"
);
console.log(
"\nSending payment proof to server (server will submit transaction)..."
);
const paid = await fetch("http://localhost:3001/premium", {
headers: {
"X-Payment": xPaymentHeader
}
});
const result = (await paid.json()) as {
data?: string;
error?: string;
paymentDetails?: {
signature: string;
amount: number;
amountUSDC: number;
recipient: string;
explorerUrl: string;
};
};
console.log("\nServer response:");
console.log(result);
// Display explorer link if payment was successful
if (result.paymentDetails?.explorerUrl) {
console.log("\n🔗 View transaction on Solana Explorer:");
console.log(result.paymentDetails.explorerUrl);
}
}
run().catch(console.error);

Улучшения

  • Рассмотрите возможность возврата JWT после оплаты, чтобы клиенты могли на короткое время повторно использовать доступ. ACK значительно упрощает это.
  • Убедитесь, что ваши ключи не утекают, и поместите их в переменные окружения.

Is this page helpful?