Czym jest x402?
x402 to otwarty protokół do płatności natywnych dla internetu. Kod błędu 402 oznacza „Wymagana płatność“ i od dawna istniał w specyfikacji HTTP, jednak dopiero teraz jego praktyczne zastosowanie stało się możliwe dzięki rozwojowi sieci blockchain. Protokół 402 odnosi się do implementacji wzorca HTTP 402 Payment Required: serwer wymaga płatności przed zwróceniem chronionej odpowiedzi. Na Solanie jest to zazwyczaj realizowane poprzez poproszenie klienta o wykonanie małego przelewu, po czym serwer weryfikuje go onchain i udostępnia treść.
W tej chwili nie jest jeszcze jasne, który z pakietów SDK dla protokołu 402 okaże się najpopularniejszy. Dlatego w tym przewodniku pokażemy, jak wdrożyć x402 przy użyciu minimalnego serwera i klienta, oraz wymienimy wszystkie dostępne pakiety SDK dla protokołu 402 wraz z opisem ich aktualnego wsparcia dla Solany.
Jak to działa?
Istnieje kilka sposobów implementacji x402 — od bardzo uproszczonych po w pełni zarządzane.
Idea protokołu: Użyj zwykłego HTTP. Klient odpytuje Twój URL → odpowiadasz kodem 402 Payment Required z obiektem JSON zawierającym wymagania płatności → klient dokonuje płatności i ponawia żądanie z nagłówkiem X-PAYMENT → weryfikujesz/rozliczasz → odpowiadasz 200 OK. Bez kont, bez OAuth.
Diagram przepływu x402
Uwaga: Fasilitator jest całkowicie opcjonalny — możesz też zaimplementować własną logikę walidacji za pomocą kilku linii kodu. Fasilitator ukrywa szczegóły integracji z blockchainem po stronie serwera i klienta, co ułatwia implementację.
Elementy specyfikacji, które warto znać: Struktura PaymentRequirements, nagłówek X-PAYMENT zakodowany w base64, opcjonalny nagłówek X-PAYMENT-RESPONSE przy sukcesie oraz opcjonalne API fasilitatora dla /verify, /settle, /supported. Aktualnym konkretnym schematem jest exact (zapłać określoną kwotę). Inne, takie jak upto, są w fazie propozycji.
Wsparcie dla Solany: Sam protokół jest niezależny od sieci blockchain; na Solanie obsługuje wszystkie tokeny SPL. Wsparcie dla Solany jest dostępne lub w trakcie opracowania dla większości pakietów SDK protokołu 402.
Poniżej znajduje się lista dostępnych pakietów SDK dla protokołu 402 wraz z ich aktualnym wsparciem dla Solany.
Przypadki użycia
x402 umożliwia szeroki zakres scenariuszy mikropłatności i płatności za użycie, które wcześniej nie były ekonomicznie wykonalne bez blockchainu. Wyobraź sobie Netflix płacący za każde obejrzenie lub Spotify płacący za każdą piosenkę zamiast pobierać opłaty za subskrypcje. Oto kilka możliwych pomysłów. Jednak cały wachlarz możliwości jest otwarty na wyobraźnię:
AI i handel agentowy:
- Dostęp do API agentów AI: Płatność za każde wywołanie modelu LLM, generowanie obrazów lub wywołanie API modelu AI (zob. przykład ACK)
- Monetyzacja serwerów MCP: Pobieranie opłat za narzędzia Model Context Protocol, źródła danych i wyspecjalizowane możliwości agentów (zob. MCPay.tech)
- Płatności agent-do-agenta: Umożliwienie autonomicznym agentom dokonywania transakcji między sobą za usługi i dane (zob. przykład a2a-x402)
- Dane treningowe premium dla AI: Sprzedaż dostępu do wyselekcjonowanych zbiorów danych w modelu płatności za zapytanie
Treści i media:
- Artykuły z paywallem: Pobieranie mikropłatności za artykuł zamiast pełnych subskrypcji
- Streaming wideo/audio: Płatność za obejrzenie lub za minutę treści
- Obrazy w wysokiej rozdzielczości: Odblokowanie pobrań w pełnej rozdzielczości po dokonaniu płatności (zob. przykład ACK) lub przykład x402 coinbase
- Dostęp do newsletterów premium: Monetyzacja poszczególnych wydań newslettera
Usługi deweloperskie:
- Mierzenie zużycia API: Płatność za wywołanie RPC, zapytanie do bazy danych lub jednostkę obliczeniową (zob. przykład Corbits)
- Funkcje bezserwerowe: Pobieranie opłat za poszczególne wywołania funkcji
Dane i analityka:
- Dane rynkowe w czasie rzeczywistym: Cenniki per-kwotowanie lub per-tick
- Pulpity analityczne: Odblokowanie określonych raportów lub eksportów danych
- Dane z czujników IoT: Mikropłatności za odczyty czujników z sieci DePIN
Gry i dobra wirtualne:
- Dostęp do serwerów gier: Płatność za sesję lub za godzinę
- Pobieranie modów/zasobów: Monetyzacja treści tworzonych przez użytkowników
- Opłaty za udział w turniejach: Automatyczna dystrybucja puli nagród
Różne:
- Filtrowanie e-maili/DM: Wymaganie płatności w celu dotarcia do Twojej skrzynki odbiorczej (zapobieganie spamowi)
- Zasoby obliczeniowe: Płatność za godzinę CPU, minutę GPU lub GB pamięci masowej
- Dostęp do VPN/Proxy: Cennik za GB przepustowości
- Jednorazowe pobieranie plików: Sprzedaż plików cyfrowych bez kosztów subskrypcji (zob. przykład ACK)
Kluczową zaletą x402 na Solanie są niskie koszty transakcji (ułamki centa), co sprawia, że prawdziwe mikropłatności stają się opłacalne, oraz natychmiastowe rozliczenia umożliwiające kontrolę dostępu w czasie rzeczywistym.
Pakiety SDK i ich wsparcie dla Solany
To jest rozwijająca się lista, która będzie aktualizowana w miarę wydawania nowych pakietów SDK lub dodawania wsparcia dla Solany.
| SDK / Projekt | Wsparcie dla Solany | Uwagi | Dokumentacja / URL |
|---|---|---|---|
| Corbits | Tak | Wygodny pakiet SDK dla protokołu 402 na Solanie | Dokumentacja |
| MCPay.tech | Tak | Płatności za serwery MCP w mikropłatnościach | Strona |
| PayAI Facilitator | Tak | Fasilitator x402 ze wsparciem dla Solany | payai.network |
| Coinbase | Tak / Python w trakcie realizacji | Referencyjna implementacja protokołu x402 firmy Coinbase | GitHub |
| ACK | W PR | Protokół płatności agentowych ze wsparciem x402 | GitHub |
| Crossmint | W trakcie opracowania | Płatności, portfele; finanse agentowe; nie dedykowane dla x402 | crossmint.com |
| A2A x402 (Google) | W trakcie opracowania | Płatności agent-do-agenta z wykorzystaniem Google AI | GitHub |
| Nexus (Thirdweb) | W trakcie opracowania | Opakowanie x402 wokół kluczy API | Nexus |
| x402scan | N/D (Eksplorator) | Eksplorator ekosystemu x402 (nie jest pakietem SDK) | x402scan.com |
| Przykład natywny | Tak | Minimalny przykład bez zależności | Przykłady |
Corbits
Pakiet SDK stawiający Solanę na pierwszym miejscu, umożliwiający szybkie wdrażanie przepływów x402 na Solanie. Zobacz dokumentację: https://corbits.dev/
Przykład umożliwiający płatność za żądania RPC na Solanie.
npm install @faremeter/payment-solana @faremeter/fetch @faremeter/info@solana/web3.js
Utwórz plik payer-wallet.json i zasil go pewną ilością USDC oraz 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 fileconst 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 interfaceconst wallet = {network,publicKey: keypair.publicKey,updateTransaction: async (tx: VersionedTransaction) => {tx.sign([keypair]);return tx;}};// Setup payment handlerconst handler = createPaymentHandler(wallet, usdcMint, connection);const fetchWithPayer = wrap(fetch, { handlers: [handler] });// Call the API - payment happens automaticallyconst 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
To wywołanie opłaci żądanie RPC i zwróci wysokość bloku, korzystając z protokołu corbits 402.
Coinbase
Referencyjna implementacja Coinbase protokołu x402 dostarcza biblioteki TypeScript oraz przykłady zarówno dla przepływów klienta, jak i serwera. Repozytorium zawiera testy end-to-end obejmujące 6 różnych scenariuszy SVM (Solana Virtual Machine). Implementacja obejmuje weryfikację płatności, generowanie potwierdzeń oraz obsługę błędów.
Kluczowe funkcje:
- Implementacje klienta i serwera w TypeScript
- Narzędzia do weryfikacji płatności
- Wsparcie dla wielu schematów płatności (dokładna kwota, kwota do wysokości)
- Zestaw testów z przykładami transakcji na Solanie
- Rozdzielenie logiki protokołu od logiki biznesowej
Łatwy w użyciu przykład z minimalnym serwerem i klientem znajdziesz tutaj.
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 confirmationapp.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 paymentapp.get("/premium", (req, res) => {res.json({message: "🎉 Premium content accessed!",data: {secret: "This is premium content",timestamp: new Date().toISOString()}});});
Wsparcie dla Pythona jest w trakcie opracowania — działający przykład end-to-end jest dostępny tutaj.
ACK
Agent Commerce Kit (ACK) obsługuje protokół x402, ale dodaje krytyczne warstwy dla gospodarki agentowej: weryfikowalną tożsamość agenta (ACK-ID) przy użyciu W3C DID/VC oraz kryptograficznie bezpieczne potwierdzenia (ACK-Pay) w postaci Weryfikowalnych Poświadczeń. Umożliwia to agentom udowodnienie własności, autonomiczne uwierzytelnianie oraz generowanie gotowych do celów compliance dowodów płatności, rozwiązując kryzys tożsamości i bariery transakcyjne uniemożliwiające agentom AI uczestnictwo w handlu.
Diagram przepływu ACK
Istnieje PR z przykładem e2e, który nie został jeszcze scalony, ale działa. Dostępny jest również Live Example pokazujący, jak zabezpieczyć obrazy paywallem, stworzyć szafę grającą oraz API animujące obrazy. Kod źródłowy przykładów wraz z botem na Twitterze używającym API do animowania obrazów na osi czasu można znaleźć tutaj.
MCPay.tech
Mikropłatności za żądanie dla serwerów MCP (Model Context Protocol) przy użyciu przepływów zbliżonych do x402. Umożliwia deweloperom monetyzację narzędzi i zasobów MCP poprzez wymaganie małych płatności za każde wywołanie API lub narzędzia, ułatwiając pobieranie opłat za dostęp agentów AI do premium źródeł danych, wyspecjalizowanych narzędzi lub zasobów obliczeniowych. Strona: https://mcpay.tech/
PayAI Facilitator
Fasilitator x402 stawiający Solanę na pierwszym miejscu, z działającym echo merchantem do testowania i zwrotu płatności. PayAI przejmuje obecnie wszystkie opłaty transakcyjne. Strona: https://payai.network/
A2A x402 (Google)
Inicjatywa płatności agent-do-agenta badająca ustandaryzowane przepływy z wymaganą płatnością. Wsparcie dla Solany jest obecnie w trakcie realizacji, a działający przykład czatu można znaleźć tutaj
Crossmint
Crossmint to kompleksowa platforma dla firm i agentów umożliwiająca integrację infrastruktury krypto — w tym portfeli, onrampów, orkiestracji stablecoinów i wiele więcej. Wsparcie dla x402 na Solanie jest obecnie w trakcie realizacji i ma zostać ukończone do 30.10.2025. Strona: https://www.crossmint.com/
x402scan
Eksplorator ekosystemu x402 dostarczający kompleksowe statystyki, listingi projektów i analitykę dla implementacji x402. Śledź wolumeny transakcji, odkrywaj aktywnych merchantów i monitoruj wzrost liczby endpointów z wymaganą płatnością w różnych sieciach. Strona: https://x402scan.com/
Nexus (Thirdweb)
Thirdweb Nexus tworzy opakowanie x402 wokół kluczy API (obecnie w trakcie opracowania). Strona: https://nexus.thirdweb.com/
Przykład natywny
Natywny przykład bez zależności z minimalnym serwerem i klientem.
Możesz sklonować repozytorium i uruchomić przykład:
git clone https://github.com/Woody4618/x402-solana-examplesnpm install# Terminal 1: Start servernpm run usdc:server# Terminal 2: Run client (requires devnet USDC)npm run usdc:client
Przegląd przepływu
- Klient wysyła żądanie do
/premium. - Serwer odpowiada kodem 402 z warunkami płatności: adresem odbiorcy i kwotą.
- Klient tworzy transakcję z instrukcją przelewu do odbiorcy.
- Klient ponawia żądanie do
/premiumz ładunkiem transakcji. - Serwer weryfikuje transakcję i przesyła ją do sieci.
- Po potwierdzeniu serwer odpowiada kodem 200.
Alternatywa specyficzna dla Solany: Na Solanie można zaimplementować wariant, w którym klient przesyła transakcję bezpośrednio do sieci z instrukcją memo (zamiast wysyłać ją do serwera), a następnie wysyła jedynie sygnaturę transakcji do serwera w celu weryfikacji. Rozwiązuje to problem utraty połączenia — jeśli klient rozłączy się po dokonaniu płatności, ale przed otrzymaniem treści, może ponowić próbę z tą samą sygnaturą, ponieważ płatność jest już potwierdzona onchain. Jednak to podejście odbiega od standardowego przepływu x402.org (który zakłada, że serwer rozgłasza transakcję), dlatego w tym przykładzie stosujemy podejście standardowe.
Uwaga: Kod tego przykładu nie został poddany audytowi, nie jest gotowy do użytku produkcyjnego i służy wyłącznie celom demonstracyjnym. Pokazuje, że można zaimplementować x402 bez zależności i bez użycia facilitatora. Korzystanie z facilitatora jest wygodne, ponieważ ukrywa złożoność i może przejąć opłaty transakcyjne, jednak może też stanowić pojedy punkt awarii — na przykład gdy portfel facilitatora wyczerpie środki. Przykładowy serwer przesyła transakcje podpisane przez klienta. Być może będziesz musiał je zwalidować.
Minimalny serwer (Express)
// x402-compliant server with USDC (SPL Token) paymentsimport 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 addressconst 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 priceconst PRICE_USDC = 100; // 0.0001 USDCconst app = express();app.use(express.json());// x402 endpoint - Quote or verify paymentapp.get("/premium", async (req, res) => {const xPaymentHeader = req.header("X-Payment");// If client provided X-Payment header, verify and submit transactionif (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 transactionconst 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 instructionconst instructions = tx.instructions;let validTransfer = false;let transferAmount = 0;for (const ix of instructions) {// Check if this is a Token Program instructionif (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 submittingconsole.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 signaturesconsole.log("Submitting transaction to network...");const signature = await connection.sendRawTransaction(txBuffer, {skipPreflight: false,preflightCommitment: "confirmed"});console.log(`Transaction submitted: ${signature}`);// Wait for confirmationconst 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 detailsconst 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 metadataconst postTokenBalances = confirmedTx.meta?.postTokenBalances ?? [];const preTokenBalances = confirmedTx.meta?.preTokenBalances ?? [];// Find the recipient's token account in the balance changeslet 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 accountconst 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 contentreturn 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 detailsconsole.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"));
Minimalny klient (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 serverconst 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 accountconsole.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 USDCconst 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 notconsole.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 neededif (!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, // payerrecipientTokenAccount, // associated token account addressrecipientWallet, // ownermint // mint);tx.add(createAccountIx);console.log(" + Added create token account instruction");}// Add transfer instructionconst transferIx = createTransferInstruction(payerTokenAccount.address, // sourcerecipientTokenAccount, // destinationpayer.publicKey, // owneramount // 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 transactionconst 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 proofconst 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 successfulif (result.paymentDetails?.explorerUrl) {console.log("\n🔗 View transaction on Solana Explorer:");console.log(result.paymentDetails.explorerUrl);}}run().catch(console.error);
Ulepszenia
- Rozważ zwracanie JWT po dokonaniu płatności, aby klienci mogli krótkotrwale ponownie wykorzystywać dostęp. ACK znacznie to ułatwia.
- Upewnij się, że Twoje klucze nie wyciekają i przechowuj je w zmiennych środowiskowych.
Is this page helpful?