Wie man mit x402 auf Solana beginnt

Was ist x402?

Das x402-Protokoll ist ein offenes Protokoll für internetbasierte Zahlungen. Der 402-Fehlercode steht für „Payment Required“ (Zahlung erforderlich) und existierte bereits seit Langem in den HTTP-Spezifikationen, war jedoch erst durch den Aufstieg der Blockchain-Netzwerke praktisch nutzbar. Das 402-Protokoll beschreibt nun die Implementierung des HTTP-402-Payment-Required-Musters: Der Server verlangt eine Zahlung, bevor er eine geschützte Antwort zurückgibt. Auf Solana wird dies üblicherweise umgesetzt, indem der Client aufgefordert wird, eine kleine Überweisung durchzuführen, woraufhin der Server diese On-Chain verifiziert und den Inhalt bereitstellt.

Derzeit ist noch nicht klar, welches der 402-SDKs am weitesten verbreitet sein wird. In dieser Anleitung zeigen wir daher, wie x402 mit einem minimalen Server und Client implementiert werden kann, und listen alle verfügbaren 402-SDKs mit dem jeweils beschriebenen aktuellen Solana-Support auf.

Wie funktioniert es?

Es gibt verschiedene Möglichkeiten, x402 zu implementieren – von sehr schlank bis vollständig verwaltet.

Protokoll-Idee: Einfaches HTTP verwenden. Ein Client ruft deine URL auf → du antwortest mit 402 Payment Required und einem JSON-Payment-Requirements-Objekt → der Client zahlt und wiederholt die Anfrage mit einem X-PAYMENT-Header → du verifizierst/verrechnest → Antwort 200 OK. Keine Konten, kein OAuth.

x402 Ablaufdiagrammx402 Ablaufdiagramm

Hinweis: Der Facilitator ist vollständig optional – du kannst auch deine eigene Validierungslogik mit wenigen Codezeilen implementieren. Der Facilitator abstrahiert die Blockchain-Integrationsdetails von Server und Client, was die Implementierung erleichtert.

Wichtige Spezifikationsdetails: Die PaymentRequirements-Struktur, base64-kodierter X-PAYMENT-Header, optionaler X-PAYMENT-RESPONSE bei Erfolg sowie die (optionale) Facilitator-API für /verify, /settle, /supported. Das aktuelle konkrete Schema ist „exact“ (einen bestimmten Betrag zahlen). Andere Schemata wie upto sind vorgeschlagen.

Solana-Unterstützung: Das Protokoll selbst ist chain-agnostisch; auf Solana werden alle SPL-Token unterstützt. Solana-Unterstützung ist für die meisten 402-SDKs verfügbar oder in Entwicklung.

Weiter unten findest du eine Liste der verfügbaren 402-SDKs mit ihrem aktuellen Solana-Support.

Anwendungsfälle

x402 ermöglicht eine Vielzahl von Mikrozahlungs- und Pay-per-Use-Szenarien, die vor der Blockchain wirtschaftlich nicht realisierbar waren. Stell dir vor, Netflix zahlt für jeden einzelnen Abruf oder Spotify für jeden einzelnen Song, anstatt Abonnements zu verkaufen. Hier sind einige mögliche Ideen – doch das gesamte Spektrum der Möglichkeiten ist der Vorstellungskraft überlassen:

KI & Agent Commerce:

  • KI-Agent-API-Zugang: Zahlung pro LLM-Inferenz, Bildgenerierung oder KI-Modell-API-Aufruf (Siehe ACK-Beispiel)
  • MCP-Server-Monetarisierung: Gebühren für Model Context Protocol-Tools, Datenquellen und spezialisierte Agent-Funktionen (Siehe MCPay.tech)
  • Agent-zu-Agent-Zahlungen: Autonome Agenten können untereinander für Dienste und Daten transagieren (Siehe a2a-x402-Beispiel)
  • Premium-KI-Trainingsdaten: Zugang zu kuratierten Datensätzen auf Anfragebasis verkaufen

Inhalte & Medien:

  • Kostenpflichtige Artikel: Mikrobeträge pro Artikel statt vollständiger Abonnements berechnen
  • Video-/Audio-Streaming: Zahlung pro Abruf oder pro Inhaltsminute
  • Hochauflösende Bilder: Vollauflösende Downloads nach Zahlung freischalten (siehe ACK-Beispiel) oder x402 Coinbase-Beispiel
  • Premium-Newsletter-Zugang: Einzelne Newsletter-Ausgaben monetarisieren

Entwicklerdienste:

  • API-Messung: Zahlung pro RPC-Aufruf, Datenbankabfrage oder Compute Unit (Siehe Corbits-Beispiel)
  • Serverlose Funktionen: Gebühren für einzelne Funktionsausführungen

Daten & Analysen:

  • Echtzeit-Marktdaten: Preisfeeds pro Kurs oder pro Tick
  • Analyse-Dashboards: Bestimmte Berichte oder Datenexporte freischalten
  • IoT-Sensordaten: Mikrozahlungen für Sensorwerte aus DePIN-Netzwerken

Gaming & virtuelle Güter:

  • Game-Server-Zugang: Zahlung pro Sitzung oder pro Stunde
  • Mod-/Asset-Downloads: Nutzergenerierten Content monetarisieren
  • Turnier-Teilnahmegebühren: Automatisierte Preispool-Verteilung

Sonstiges:

  • E-Mail-/DM-Filterung: Zahlung verlangen, um deinen Posteingang zu erreichen (Spam-Prävention)
  • Rechenressourcen: Zahlung pro CPU-Stunde, GPU-Minute oder Speicher-GB
  • VPN-/Proxy-Zugang: Bandbreitenpreise pro GB
  • Einmalige Datei-Downloads: Digitale Dateien ohne Abonnement-Overhead verkaufen (Siehe ACK-Beispiel)

Der entscheidende Vorteil von x402 auf Solana sind niedrige Transaktionskosten (Bruchteile eines Cents), die echte Mikrozahlungen ermöglichen, sowie sofortige Abwicklung für Echtzeit-Zugangskontrollen.

SDKs und ihr Solana-Support

Dies ist eine sich weiterentwickelnde Liste, die aktualisiert wird, sobald weitere SDKs veröffentlicht oder Solana-Unterstützung hinzugefügt wird.

SDK / ProjektSolana-UnterstützungHinweiseDocs / URL
CorbitsJaPraktisches SDK für 402 auf SolanaDocs
MCPay.techJaZahlung für MCP-Server in MikrozahlungenWebsite
PayAI FacilitatorJax402-Facilitator mit Solana-Unterstützungpayai.network
CoinbaseJa / Python in ArbeitDie Coinbase-Referenzimplementierung des x402-ProtokollsGitHub
ACKIm PREin Agent-Zahlungsprotokoll mit x402-UnterstützungGitHub
CrossmintIn EntwicklungZahlungen, Wallets, agentisches Finance; nicht x402-spezifischcrossmint.com
A2A x402 (Google)In EntwicklungAgent-zu-Agent-Zahlungen mit Google AIGitHub
Nexus (Thirdweb)In Entwicklungx402-Wrapper um API-SchlüsselNexus
x402scanK. A. (Explorer)x402-Ökosystem-Explorer (kein SDK)x402scan.com
Natives BeispielJaMinimales Beispiel ohne AbhängigkeitenBeispiele

Corbits

Solana-first SDK zur schnellen Implementierung von x402-Flows auf Solana. Siehe die Docs: https://corbits.dev/

Beispiel, mit dem du für Solana-RPC-Anfragen bezahlen kannst.

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

Erstelle eine payer-wallet.json und lade sie mit etwas USDC und etwas Mainnet-SOL auf.

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

Damit wird für die RPC-Anfrage bezahlt und die Blockhöhe mithilfe des corbits-402-Protokolls zurückgegeben.

Coinbase

Coinbases Referenzimplementierung des x402-Protokolls bietet TypeScript-Bibliotheken und Beispiele für Client- und Server-Flows. Das Repository enthält End-to-End-Tests für 6 verschiedene SVM-Szenarien (Solana Virtual Machine). Die Implementierung umfasst Zahlungsverifizierung, Beleggenerierung und Fehlerbehandlung.

Wichtige Funktionen:

  • TypeScript-Client- und Server-Implementierungen
  • Hilfsprogramme zur Zahlungsverifizierung
  • Unterstützung mehrerer Zahlungsschemata (exakter Betrag, Höchstbetrag)
  • Testsuite mit Solana-Transaktionsbeispielen
  • Trennung zwischen Protokolllogik und Geschäftslogik

Ein einfach nutzbares Beispiel mit einem minimalen Server und Client findest du hier.

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()
}
});
});

Die Python-Unterstützung befindet sich in Entwicklung; ein funktionierendes End-to-End-Beispiel ist hier verfügbar.

ACK

Das Agent Commerce Kit (ACK) unterstützt das x402-Protokoll und ergänzt es um kritische Schichten für die Agent-Wirtschaft: verifizierbare Agent-Identität (ACK-ID) mittels W3C-DIDs/VCs sowie kryptografisch gesicherte Belege (ACK-Pay) als Verifiable Credentials. Dies ermöglicht es Agenten, Eigentümerschaft nachzuweisen, sich autonom zu authentifizieren und compliance-fähige Zahlungsnachweise zu generieren – und adressiert damit die Identitätskrise und Transaktionsbarrieren, die KI-Agenten an der Teilnahme am Handel hindern.

ACK AblaufdiagrammACK Ablaufdiagramm

Es gibt einen PR mit einem E2E-Beispiel, der noch nicht gemergt wurde, aber funktioniert. Außerdem gibt es ein Live-Beispiel, das zeigt, wie man Bilder, eine Jukebox und eine API zum Animieren von Bildern kostenpflichtig macht. Den Quellcode der Beispiele sowie einen Twitter-Bot, der die API nutzt, um Bilder im Zeitstrahl zu animieren, findest du hier.

MCPay.tech

Pay-per-Request-Mikrozahlungen für MCP-Server (Model Context Protocol) mit x402-ähnlichen Flows. Ermöglicht Entwicklern die Monetarisierung von MCP-Tools und -Ressourcen durch kleine Zahlungen pro API-Aufruf oder Tool-Aufruf – so lässt sich der KI-Agent-Zugang zu Premium-Datenquellen, spezialisierten Tools oder Rechenressourcen einfach in Rechnung stellen. Website: https://mcpay.tech/

PayAI Facilitator

Solana-first-x402-Facilitator mit einem Live-Echo-Merchant zum Testen und Rückerstatten von Zahlungen. PayAI übernimmt derzeit alle Transaktions-Fee. Website: https://payai.network/

A2A x402 (Google)

Agent-zu-Agent-402-Initiative zur Erforschung standardisierter Payment-Required-Flows. Die Solana-Unterstützung befindet sich derzeit in Entwicklung; ein funktionierendes Chat-Beispiel findest du hier

Crossmint

Crossmint ist eine All-in-One-Plattform für Unternehmen und Agenten zur Integration von Krypto-Infrastruktur – einschließlich Wallets, Onramps, Stablecoin-Orchestrierung und mehr. Die Solana-x402-Unterstützung befindet sich derzeit in Entwicklung und soll bis zum 30.10.2025 abgeschlossen sein. Website: https://www.crossmint.com/

x402scan

Explorer für das x402-Ökosystem mit umfassenden Statistiken, Projektlisten und Analysen zu x402-Implementierungen. Verfolge Transaktionsvolumen, entdecke aktive Händler und beobachte das Wachstum von Payment-Required-Endpunkten in verschiedenen Netzwerken. Website: https://x402scan.com/

Nexus (Thirdweb)

Thirdweb Nexus entwickelt einen x402-Wrapper um API-Schlüssel (derzeit in Entwicklung). Website: https://nexus.thirdweb.com/

Natives Beispiel

Ein natives Beispiel ohne Abhängigkeiten und mit einem minimalen Server und Client.

Du kannst das Repository klonen und das Beispiel ausführen:

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

Flow-Übersicht

  1. Client ruft /premium auf.
  2. Server antwortet mit 402 und den Zahlungsbedingungen: Empfänger, Betrag.
  3. Client erstellt eine Transaktion mit einer Überweisungsanweisung an den Empfänger.
  4. Client wiederholt die Anfrage an /premium mit dem Transaktions-Payload.
  5. Server verifiziert die Transaktion und sendet sie an das Netzwerk.
  6. Sobald sie bestätigt ist, antwortet der Server mit 200.

Solana-spezifische Alternative: Auf Solana könnte man eine Variante implementieren, bei der der Client die Transaktion direkt mit einer Memo-Anweisung an das Netzwerk übermittelt (anstatt sie an den Server zu senden) und anschließend nur die Transaktionssignatur zur Verifizierung an den Server schickt. Dies löst das Problem bei Verbindungsabbrüchen – wenn der Client nach der Zahlung, aber vor dem Empfang des Inhalts die Verbindung verliert, kann er es mit derselben Signatur erneut versuchen, da die Zahlung bereits onchain bestätigt ist. Dieser Ansatz weicht jedoch vom x402.org-Standardablauf ab (der erwartet, dass der Server die Transaktion überträgt), weshalb wir in diesem Beispiel den Standardansatz verwenden.

Hinweis: Der Code dieses Beispiels wurde nicht geprüft, ist nicht produktionsreif und dient ausschließlich zu Demonstrationszwecken. Er zeigt, dass x402 ohne Abhängigkeiten und ohne den Einsatz eines Facilitators implementiert werden kann. Die Nutzung eines Facilitators ist praktisch, da er Komplexität verbirgt und Transaktionsgebühren übernehmen kann – er kann jedoch auch ein Single Point of Failure sein, etwa wenn das Facilitator-Wallet keine ausreichenden Mittel mehr hat. Der Beispiel-Server übermittelt vom Client signierte Transaktionen. Diese müssen möglicherweise validiert werden.

Minimaler Server (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"));

Minimaler Client (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);

Verbesserungen

  • Erwägen Sie, nach der Zahlung ein JWT zurückzugeben, damit Clients den Zugang kurzzeitig wiederverwenden können. ACK macht das sehr einfach.
  • Stellen Sie sicher, dass Ihre Schlüssel nicht nach außen gelangen, und speichern Sie sie in Umgebungsvariablen.

Is this page helpful?

Inhaltsverzeichnis

Seite bearbeiten
© 2026 Solana Foundation. Alle Rechte vorbehalten.