Τι είναι το x402;
Το x402 είναι ένα ανοιχτό πρωτόκολλο για πληρωμές στο διαδίκτυο. Ο κωδικός σφάλματος 402 σημαίνει «Απαιτείται Πληρωμή» και υπήρχε εδώ και καιρό στις προδιαγραφές HTTP, αλλά μόνο τώρα έγινε εφικτή η χρήση του χάρη στην ανάπτυξη των δικτύων blockchain. Πλέον το πρωτόκολλο 402 αναφέρεται στην υλοποίηση του προτύπου HTTP 402 Payment Required: ο διακομιστής απαιτεί πληρωμή πριν επιστρέψει μια προστατευμένη απόκριση. Στο Solana, αυτό υλοποιείται συνήθως ζητώντας από τον πελάτη να υποβάλει μια μικρή μεταφορά, στη συνέχεια ο διακομιστής επαληθεύει αυτό onchain και εξυπηρετεί το περιεχόμενο.
Προς το παρόν δεν είναι ακόμα σαφές ποιο από τα 402 SDK θα γίνει το πιο δημοφιλές. Έτσι, σε αυτόν τον οδηγό θα δείξουμε πώς να υλοποιήσετε το x402 χρησιμοποιώντας έναν ελάχιστο διακομιστή και πελάτη, και θα παραθέσουμε όλα τα διαθέσιμα 402 SDK με την τρέχουσα υποστήριξη Solana που περιγράφεται.
Πώς λειτουργεί;
Υπάρχουν αρκετοί τρόποι υλοποίησης του x402, που κυμαίνονται από υπερ-ελαφριές έως πλήρως διαχειριζόμενες λύσεις.
Ιδέα πρωτοκόλλου: Χρησιμοποιήστε απλό HTTP. Ένας πελάτης επισκέπτεται το URL σας → απαντάτε με 402 Payment Required και ένα αντικείμενο JSON Payment Requirements → ο πελάτης πληρώνει και επαναλαμβάνει το αίτημα με επικεφαλίδα X-PAYMENT → επαληθεύετε/διακανονίζετε → απαντάτε 200 OK. Χωρίς λογαριασμούς, χωρίς OAuth.
Διάγραμμα Ροής x402
Σημείωση: Ο διευκολυντής είναι εντελώς προαιρετικός και μπορείτε επίσης να υλοποιήσετε τη δική σας λογική επαλήθευσης με λίγες γραμμές κώδικα. Ο διευκολυντής αφαιρεί τις λεπτομέρειες ενσωμάτωσης blockchain από τον διακομιστή και τον πελάτη, καθιστώντας την υλοποίηση ευκολότερη.
Βασικά στοιχεία προδιαγραφής: Η δομή PaymentRequirements, η κεφαλίδα X-PAYMENT κωδικοποιημένη σε base64, το προαιρετικό X-PAYMENT-RESPONSE σε επιτυχία, και το (προαιρετικό) API διευκολυντή για /verify, /settle, /supported. Το τρέχον συγκεκριμένο σχήμα είναι exact (πληρωμή συγκεκριμένου ποσού). Άλλα όπως το upto προτείνονται.
Υποστήριξη Solana: Το ίδιο το πρωτόκολλο είναι αγνωστικιστικό ως προς το blockchain· στο Solana υποστηρίζει όλα τα SPL tokens. Η υποστήριξη Solana είναι διαθέσιμη ή υπό ανάπτυξη για τα περισσότερα 402 SDK.
Παρακάτω υπάρχει μια λίστα με τα διαθέσιμα 402 SDK με την τρέχουσα υποστήριξη Solana τους.
Περιπτώσεις Χρήσης
Το x402 επιτρέπει ένα ευρύ φάσμα σεναρίων μικροπληρωμών και πληρωμής ανά χρήση που δεν ήταν οικονομικά βιώσιμα πριν από το blockchain. Φανταστείτε το Netflix να πληρώνει για κάθε προβολή ή το Spotify για κάθε τραγούδι αντί για συνδρομές. Ακολουθούν μερικές πιθανές ιδέες. Αλλά ολόκληρο το φάσμα των δυνατοτήτων είναι ανοιχτό στη φαντασία:
Τεχνητή Νοημοσύνη & Εμπόριο Πρακτόρων:
- Πρόσβαση AI Agent API: Πληρωμή ανά LLM inference, δημιουργία εικόνας ή κλήση API μοντέλου AI (Δείτε παράδειγμα ACK)
- Μονετοποίηση MCP Server: Χρέωση για εργαλεία Model Context Protocol, πηγές δεδομένων και εξειδικευμένες δυνατότητες πράκτορα (Δείτε MCPay.tech)
- Πληρωμές Πράκτορα-προς-Πράκτορα: Επιτρέψτε σε αυτόνομους πράκτορες να συναλλάσσονται μεταξύ τους για υπηρεσίες και δεδομένα (Δείτε παράδειγμα a2a-x402)
- Premium Δεδομένα Εκπαίδευσης AI: Πωλήστε πρόσβαση σε επιμελημένα σύνολα δεδομένων ανά ερώτημα
Περιεχόμενο & Μέσα:
- Άρθρα με Paywall: Χρέωση μικροποσών ανά άρθρο αντί για πλήρεις συνδρομές
- Ροή Βίντεο/Ήχου: Πληρωμή ανά προβολή ή ανά λεπτό περιεχομένου
- Εικόνες Υψηλής Ανάλυσης: Ξεκλειδώστε λήψεις πλήρους ανάλυσης μετά την πληρωμή (δείτε παράδειγμα ACK) ή παράδειγμα x402 coinbase
- Πρόσβαση Premium Newsletter: Μονετοποιήστε μεμονωμένα τεύχη newsletter
Υπηρεσίες Προγραμματιστών:
- Μέτρηση API: Πληρωμή ανά κλήση RPC, ερώτημα βάσης δεδομένων ή μονάδα υπολογισμού (Δείτε παράδειγμα Corbits)
- Serverless Functions: Χρέωση για μεμονωμένες εκτελέσεις συναρτήσεων
Δεδομένα & Αναλυτικά:
- Δεδομένα Αγοράς σε Πραγματικό Χρόνο: Τιμολόγηση ανά τιμή ή ανά tick
- Πίνακες Αναλυτικών: Ξεκλειδώστε συγκεκριμένες αναφορές ή εξαγωγές δεδομένων
- Δεδομένα Αισθητήρων IoT: Μικροπληρωμές για μετρήσεις αισθητήρων από δίκτυα DePIN
Παιχνίδια & Εικονικά Αγαθά:
- Πρόσβαση σε Game Server: Πληρωμή ανά περίοδο σύνδεσης ή ανά ώρα
- Λήψεις Mod/Asset: Μονετοποιήστε περιεχόμενο δημιουργημένο από χρήστες
- Τέλη Συμμετοχής σε Τουρνουά: Αυτοματοποιημένη διανομή χρηματικού επάθλου
Διάφορα:
- Φιλτράρισμα Email/DM: Απαίτηση πληρωμής για να φτάσει κάποιος στα εισερχόμενά σας (αποτροπή spam)
- Πόροι Υπολογισμού: Πληρωμή ανά ώρα CPU, λεπτό GPU ή GB αποθήκευσης
- Πρόσβαση VPN/Proxy: Τιμολόγηση εύρους ζώνης ανά GB
- Εφάπαξ Λήψεις Αρχείων: Πωλήστε ψηφιακά αρχεία χωρίς επιβάρυνση συνδρομής (Δείτε παράδειγμα ACK)
Το βασικό πλεονέκτημα του x402 στο Solana είναι το χαμηλό κόστος συναλλαγών (κλάσματα σεντ) που καθιστά βιώσιμες τις πραγματικές μικροπληρωμές, συν την άμεση εκκαθάριση που επιτρέπει έλεγχο πρόσβασης σε πραγματικό χρόνο.
SDK και η υποστήριξή τους για Solana
Αυτή είναι μια εξελισσόμενη λίστα που θα ενημερώνεται καθώς κυκλοφορούν περισσότερα SDK ή προστίθεται υποστήριξη Solana.
| SDK / Έργο | Υποστήριξη Solana | Σημειώσεις | Τεκμηρίωση / URL |
|---|---|---|---|
| Corbits | Ναι | Βολικό SDK για 402 στο Solana | Τεκμηρίωση |
| MCPay.tech | Ναι | Πληρωμή για MCP servers σε μικροπληρωμές | Ιστότοπος |
| PayAI Facilitator | Ναι | x402 facilitator με υποστήριξη Solana | payai.network |
| Coinbase | Ναι / Python σε εξέλιξη | Η υλοποίηση αναφοράς του πρωτοκόλλου x402 από το coinbase | GitHub |
| ACK | Σε PR | Ένα πρωτόκολλο πληρωμών για πράκτορες με υποστήριξη x402 | GitHub |
| Crossmint | Υπό ανάπτυξη | Πληρωμές, πορτοφόλια· agentic finance· όχι ειδικό για x402 | crossmint.com |
| A2A x402 (Google) | Υπό ανάπτυξη | Πληρωμές πράκτορα-προς-πράκτορα με χρήση Google AI | GitHub |
| Nexus (Thirdweb) | Υπό ανάπτυξη | Περιτύλιξη x402 γύρω από κλειδιά API | Nexus |
| x402scan | Δ/Ε (Explorer) | Explorer οικοσυστήματος x402 (όχι SDK) | x402scan.com |
| Εγγενές Παράδειγμα | Ναι | Ελάχιστο παράδειγμα χωρίς εξαρτήσεις | Παραδείγματα |
Corbits
SDK με προτεραιότητα στο Solana για γρήγορη υλοποίηση ροών x402 στο Solana. Δείτε την τεκμηρίωση: https://corbits.dev/
Παράδειγμα που σας επιτρέπει να πληρώνετε για αιτήματα Solana RPC.
npm install @faremeter/payment-solana @faremeter/fetch @faremeter/info@solana/web3.js
Δημιουργήστε ένα αρχείο payer-wallet.json και τροφοδοτήστε το με λίγο USDC και λίγο SOL mainnet.
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
Αυτό θα πληρώσει για το αίτημα RPC και θα επιστρέψει το ύψος του μπλοκ πληρώνοντας μέσω του πρωτοκόλλου corbits 402.
Coinbase
Η υλοποίηση αναφοράς της Coinbase του πρωτοκόλλου x402 παρέχει βιβλιοθήκες TypeScript και παραδείγματα τόσο για ροές πελάτη όσο και διακομιστή. Το αποθετήριο περιλαμβάνει end-to-end δοκιμές που καλύπτουν 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 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()}});});
Η υποστήριξη Python βρίσκεται υπό ανάπτυξη με ένα λειτουργικό end-to-end παράδειγμα διαθέσιμο εδώ.
ACK
Το Agent Commerce Kit (ACK) υποστηρίζει το πρωτόκολλο x402 αλλά προσθέτει κρίσιμα επίπεδα για την οικονομία των πρακτόρων: επαληθεύσιμη ταυτότητα πράκτορα (ACK-ID) με χρήση W3C DIDs/VCs και κρυπτογραφικά ασφαλείς αποδείξεις (ACK-Pay) ως Verifiable Credentials. Αυτό επιτρέπει στους πράκτορες να αποδεικνύουν ιδιοκτησία, να πιστοποιούνται αυτόνομα και να δημιουργούν αποδείξεις πληρωμής έτοιμες για συμμόρφωση, αντιμετωπίζοντας την κρίση ταυτότητας και τα εμπόδια συναλλαγών που αποτρέπουν τους πράκτορες AI από τη συμμετοχή στο εμπόριο.
Διάγραμμα Ροής ACK
Υπάρχει ένα PR με παράδειγμα e2e που δεν έχει συγχωνευτεί ακόμα, αλλά λειτουργεί. Υπάρχει επίσης ένα Live Παράδειγμα που δείχνει πώς να βάλετε paywall σε εικόνες, ένα juke box και ένα API που μπορεί να κινεί εικόνες. Ο πηγαίος κώδικας για τα παραδείγματα μαζί με ένα twitter bot που χρησιμοποιεί το api για να κινεί εικόνες στο timeline βρίσκεται εδώ.
MCPay.tech
Μικροπληρωμές ανά αίτημα για MCP (Model Context Protocol) servers χρησιμοποιώντας ροές τύπου x402. Επιτρέπει στους προγραμματιστές να μονετοποιούν εργαλεία και πόρους MCP απαιτώντας μικρές πληρωμές για κάθε κλήση API ή κλήση εργαλείου, διευκολύνοντας τη χρέωση για πρόσβαση AI agent σε premium πηγές δεδομένων, εξειδικευμένα εργαλεία ή υπολογιστικούς πόρους. Ιστότοπος: https://mcpay.tech/
PayAI Facilitator
x402 facilitator με προτεραιότητα στο Solana με έναν live echo merchant για δοκιμή και επιστροφή πληρωμών. Το PayAI αναλαμβάνει προς το παρόν όλα τα τέλη συναλλαγών. Ιστότοπος: https://payai.network/
A2A x402 (Google)
Πρωτοβουλία 402 πράκτορα-προς-πράκτορα που εξερευνά τυποποιημένες ροές payment-required. Η υποστήριξη Solana βρίσκεται επί του παρόντος σε εξέλιξη και ένα λειτουργικό παράδειγμα συνομιλίας μπορεί να βρεθεί εδώ
Crossmint
Το Crossmint είναι μια all-in-one πλατφόρμα για εταιρείες και πράκτορες ώστε να ενσωματώνουν crypto rails — συμπεριλαμβανομένων πορτοφολιών, onramps, ενορχήστρωσης stablecoin και άλλων. Η υποστήριξη Solana x402 βρίσκεται επί του παρόντος υπό ανάπτυξη και αναμένεται να ολοκληρωθεί έως τις 30.10.2025. Ιστότοπος: https://www.crossmint.com/
x402scan
Explorer για το οικοσύστημα x402 που παρέχει ολοκληρωμένα στατιστικά, καταχωρίσεις έργων και αναλυτικά για υλοποιήσεις x402. Παρακολουθήστε όγκους συναλλαγών, ανακαλύψτε ενεργούς εμπόρους και παρακολουθήστε την ανάπτυξη των endpoints payment-required σε διαφορετικά δίκτυα. Ιστότοπος: https://x402scan.com/
Nexus (Thirdweb)
Το Thirdweb Nexus αναπτύσσει μια περιτύλιξη x402 γύρω από κλειδιά API (επί του παρόντος υπό ανάπτυξη). Ιστότοπος: https://nexus.thirdweb.com/
Εγγενές παράδειγμα
Ένα εγγενές παράδειγμα χωρίς εξαρτήσεις και με έναν ελάχιστο διακομιστή και πελάτη.
Μπορείτε να κλωνοποιήσετε το αποθετήριο και να εκτελέσετε το παράδειγμα:
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
Επισκόπηση Ροής
- Ο πελάτης αιτείται το
/premium. - Ο διακομιστής απαντά με 402 και όρους πληρωμής: παραλήπτης, ποσό.
- Ο πελάτης δημιουργεί μια συναλλαγή με εντολή μεταφοράς προς τον παραλήπτη.
- Ο πελάτης επαναλαμβάνει το αίτημα
/premiumμε το payload της συναλλαγής. - Ο διακομιστής επαληθεύει τη συναλλαγή και την αποστέλλει στο δίκτυο.
- Μόλις επιβεβαιωθεί, ο διακομιστής απαντά με 200.
Εναλλακτική λύση ειδικά για το Solana: Στο Solana, θα μπορούσατε να υλοποιήσετε μια παραλλαγή όπου ο client υποβάλλει την συναλλαγή απευθείας στο δίκτυο με μια εντολή memo (αντί να την στέλνει στον server), και στη συνέχεια στέλνει μόνο την υπογραφή της συναλλαγής στον server για επαλήθευση. Αυτό επιλύει το πρόβλημα απώλειας σύνδεσης—εάν ο client αποσυνδεθεί μετά την πληρωμή αλλά πριν λάβει το περιεχόμενο, μπορεί να επαναλάβει την προσπάθεια με την ίδια υπογραφή, καθώς η πληρωμή έχει ήδη επιβεβαιωθεί onchain. Ωστόσο, αυτή η προσέγγιση αποκλίνει από την τυπική ροή του x402.org (η οποία αναμένει ο server να μεταδώσει την συναλλαγή), γι' αυτό χρησιμοποιούμε την τυπική προσέγγιση σε αυτό το παράδειγμα.
Σημείωση: Ο κώδικας αυτού του παραδείγματος δεν έχει ελεγχθεί και δεν είναι έτοιμος για παραγωγή— προορίζεται αποκλειστικά για επιδεικτικούς σκοπούς. Δείχνει ότι μπορείτε να υλοποιήσετε το x402 χωρίς εξαρτήσεις και χωρίς τη χρήση facilitator. Η χρήση facilitator είναι βολική γιατί αποκρύπτει την πολυπλοκότητα και μπορεί να αναλάβει τα τέλη συναλλαγών, ωστόσο μπορεί επίσης να αποτελεί ενιαίο σημείο αποτυχίας, όταν για παράδειγμα το πορτοφόλι του facilitator εξαντλήσει τα κεφάλαιά του. Ο παραδειγματικός server υποβάλλει συναλλαγές που έχουν υπογραφεί από τον client. Ίσως χρειαστεί να τις επικυρώσετε.
Ελάχιστος Server (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"));
Ελάχιστος 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 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);
Βελτιώσεις
- Εξετάστε το ενδεχόμενο να επιστρέφετε ένα JWT μετά την πληρωμή, ώστε οι clients να μπορούν να επαναχρησιμοποιούν προσωρινά την πρόσβαση. Το ACK το καθιστά αρκετά απλό.
- Βεβαιωθείτε ότι τα κλειδιά σας δεν διαρρέουν και τοποθετήστε τα σε μεταβλητές περιβάλλοντος.
Is this page helpful?