
{"id":145632,"date":"2026-03-30T16:17:56","date_gmt":"2026-03-30T16:17:56","guid":{"rendered":"https:\/\/mycryptomania.com\/?p=145632"},"modified":"2026-03-30T16:17:56","modified_gmt":"2026-03-30T16:17:56","slug":"http-402-died-in-the-90s-solana-just-brought-it-back","status":"publish","type":"post","link":"https:\/\/mycryptomania.com\/?p=145632","title":{"rendered":"HTTP 402 Died in the 90s. Solana Just Brought It Back."},"content":{"rendered":"<p>Photo by <a href=\"https:\/\/unsplash.com\/@kommumikation?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\">Mika Baumeister<\/a> on\u00a0<a href=\"https:\/\/unsplash.com\/photos\/a-book-sitting-on-top-of-a-wooden-table-V1NoRoxodnM?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\">Unsplash<\/a><\/p>\n<p>In the early 1990s, the HTTP specification reserved a special status code for payments, the \u201c402 Payment Required.\u201d status. The idea? Websites could charge for content directly through the browser. Fast forward 25+ years, and 402 is still sitting there, lonely and unused, just like that gym membership you S-W-O-R-E you\u2019d\u00a0use.<\/p>\n<p>The reason behind 402 is simple: traditional payment rails are too slow and expensive for small payments. Credit card processing takes days to settle, usually costs 2\u20133% in fees, and requires users to trust merchants with sensitive payment info. For microtransactions like a $3 code review or even a 1$ virtual \u201cbuy me a coffee\u201d, it\u2019s totally impractical.<\/p>\n<h3>Solana and HTTP 402 Sitting in a Tree. K-I-S-S\u2026.<\/h3>\n<p>So we understand that traditional payment rails make micropayments economically impossible due to costs that can end up taking a hefty chunk of your hard-earned dollars. But what if we could make HTTP 402 actually\u00a0work?<\/p>\n<p>Now I can already hear the crypto bros in the back screaming \u201cCRYPTO! CRYPTO IS THE ANSWER TO ALL YOUR PROBLEMS IN LIFE!\u201d and while that is true for mostly everything, it\u2019s not going to work in our microtransactions case. A bitcoin payment can take a long time (10\u201360 minutes usually) to get mined, not to mention bitcoin fees are not that small. But\u00a0Solana\u2026<\/p>\n<p>Solana changes the game completely:<\/p>\n<p><strong>Transaction Cost<\/strong>: ~$0.00025 per transaction (yes, that\u2019s about $0.25 for 1,000 transactions)<strong>Speed<\/strong>: 400ms average confirmation time with sub-second finality (and with the upcoming Alpenglow upgrade this is going to be even\u00a0faster).<strong>Throughput<\/strong>: 3,000\u20135,000 TPS in practice (65,000 TPS theoretical max)<strong>No Intermediaries<\/strong>: True peer-to-peer payments, no hungry middleman taking a\u00a0cut.<\/p>\n<p>This means that if you charge $0.50 for an article, you will get to keep $0.4999 of it. The user gets instant access\u200a\u2014\u200ano loading screens, no \u201cprocessing payment\u201d spinners. Just pay -&gt; verify -&gt; content unlocked!<\/p>\n<p>To help facilitate the use of HTTP 402 and crypto, the wonderful champs over at Coinbase created the x402 protocol, which in Coinbase\u2019s own words\u00a0is<\/p>\n<p>A chain agnostic standard for payments on top of HTTP, leverage the existing <em>402 Payment Required<\/em> HTTP status code to indicate that a payment is required for access to the resource.<\/p>\n<p>The x402 protocol sets a standard for implementing the HTTP 402 status code using cryptocurrency payments. The concept is quite elegant: when a server wants payment for content, it responds with 402 status code and includes payment details (recipient address, amount, payment method) in the response headers and payload. The client processes the payment on-chain, then retries the request with cryptographic proof of payment. The server verifies the payment and returns the content. No payment processors, no middlemen, not even a goddamn pigeon moving your money from place to place. Just pure peer-to-peer value transfer.<\/p>\n<p>Now (there\u2019s always a now), while it\u2019s true that the x402 library does provide various implementations and facilitators for different blockchains, its Solana support is still in its infancy, and therefore in this post we are going to implement the HTTP 402 pattern ourselves <strong>based on<\/strong> the x402 protocol\u00a0\ud83d\ude4c.<\/p>\n<p>Implementing the protocol ourselves will allow us to see exactly how 402 works under the hood, and as a bonus, we won\u2019t need to deal with package dependencies, or limit ourselves to a certain supported token.<\/p>\n<p>You should think of x402 as a blueprint, and we\u2019re building a house following its\u00a0design.<\/p>\n<h3>The HTTP 402 Pattern (Inspired by\u00a0x402)<\/h3>\n<p>The x402 protocol is built around a simple request-response-retry pattern. Here\u2019s what happens under the\u00a0hood:<\/p>\n<p>1. <strong>Initial Request<\/strong>: A client (user, AI agent, etc.) makes an HTTP request to access a resource.<\/p>\n<p>2. <strong>402 Response<\/strong>: The server responds with HTTP 402 Payment Required and includes payment details in both headers and response\u00a0body:<\/p>\n<p>HTTP\/1.1 402 Payment Required<br \/>WWW-Authenticate: Solana realm=&#8221;api&#8221;<br \/>x-Payment-Address: 7xK8&#8230;q2v<br \/>x-Payment-Amount: 0.001<br \/>x-Payment-Currency: SOL<br \/>x-Content-Type: application\/json<br \/>{<br \/>  &#8220;error&#8221;: &#8220;payment_required&#8221;,<br \/>  &#8220;message&#8221;: &#8220;Payment required to access this endpoint&#8221;,<br \/>  &#8220;payment&#8221;: {<br \/>    &#8220;address&#8221;: &#8220;7xK8&#8230;q2v&#8221;,<br \/>    &#8220;amount&#8221;: 0.001,<br \/>    &#8220;currency&#8221;: &#8220;SOL&#8221;,<br \/>    &#8220;network&#8221;: &#8220;solana-devnet&#8221;<br \/>  }<br \/>}<\/p>\n<p>3. <strong>Client Processes Payment<\/strong>: The client creates and signs a blockchain transaction transferring the requested amount to the recipient address<\/p>\n<p>4. <strong>Transaction Broadcast<\/strong>: The transaction is broadcast to Solana and waits for confirmation<\/p>\n<p>5. <strong>Retry with Proof<\/strong>: The client retries the original request, this time including the transaction signature as proof of\u00a0payment:<\/p>\n<p>POST \/api\/chat<br \/>Content-Type: application\/json<br \/>Payment-Signature: E37k&#8230;x2P<br \/>{<br \/>  &#8220;message&#8221;: &#8220;Hello!&#8221;<br \/>}<\/p>\n<p>6. <strong>Server Verification<\/strong>: The server fetches the transaction from the blockchain and verifies\u00a0that:<\/p>\n<p>The transaction actually exists and was successfulThe correct amount was\u00a0sentThe payment recipient matches the server\u2019s\u00a0addressThe transaction is confirmed (not pending or\u00a0failed)<strong>**The signature hasn\u2019t been used before**<\/strong> (replay attack prevention)<\/p>\n<p>7. <strong>Content Delivery<\/strong>: If all verification passes, server responds with HTTP 200 OK and the requested content. If not, back to\u00a0402!<\/p>\n<p>For us visual bros here\u2019s a nice diagram demonstrating the whole\u00a0flow:<\/p>\n<p>\ud83d\udce3 It\u2019s important to note that while we\u2019re following the 402 pattern that x402 pioneered, our implementation is streamlined:<\/p>\n<p>We use native\u00a0SOL.We skip the facilitator and verify transactions on-chain directly.We use a simple Payment-Signature header instead of complex\u00a0payloadsWe track used signatures server-side to prevent replay\u00a0attacks<\/p>\n<p>The beauty of the 402 pattern is that it\u2019s just HTTP\u200a\u2014\u200ayou can implement it however you want, as long as you follow the basic\u00a0flow.<\/p>\n<h3>Let\u2019s Build Some\u00a0Sh*t!<\/h3>\n<p>Someone who actually build\u00a0sh*t<\/p>\n<p>Enough theory\u200a\u2014\u200atime to get our hands dirty. We\u2019re going to build a complete HTTP 402 payment system using Solana, for fun and games of\u00a0course.<\/p>\n<p><strong>So, What Are We Building?<\/strong><\/p>\n<p>We\u2019re creating a <strong>3-part system<\/strong> that demonstrates HTTP 402 in\u00a0action:<\/p>\n<p><strong>API Server<\/strong>\u200a\u2014\u200aA backend that returns 402 responses and verifies Solana\u00a0payments<strong>AI Agent<\/strong>\u200a\u2014\u200aA CLI tool that autonomously detects and handles 402\u00a0payments<strong>A surprise(?)<\/strong><\/p>\n<p>The flow is simple but powerful: The agent makes a request \u2192 API returns 402 \u2192 Agent pays autonomously \u2192 API verifies payment \u2192 Content delivered. No accounts, no credit cards, no payment processors. Just pure peer-to-peer value transfer awesomeness.<\/p>\n<p><strong>Tech Stack<\/strong><\/p>\n<p>Here\u2019s what we\u2019re working\u00a0with:<\/p>\n<p><strong>Backend<\/strong>: <a href=\"https:\/\/hono.dev\/\">Hono<\/a>\u200a\u2014\u200aA fast, lightweight web framework (think Express but\u00a0better)<strong>Blockchain<\/strong>: @solana\/kit\u200a\u2014\u200aThe latest and greatest Solana\u00a0SDK<strong>AI<\/strong>: Anthropic Claude API\u200a\u2014\u200aFor the actual AI responses (you can swap this with any\u00a0API)<strong>Language<\/strong>: TypeScript throughout\u200a\u2014\u200aBecause we\u2019re not savages\u00a0\ud83d\ude02<\/p>\n<p><strong>Project Structure<\/strong><\/p>\n<p>http-402-demo\/<br \/>\u251c\u2500\u2500 api\/                    # Backend API Server<br \/>\u2502   \u251c\u2500\u2500 src\/<br \/>\u2502   \u2502   \u2514\u2500\u2500 index.ts       # Main API with 402 logic<br \/>\u2502   \u251c\u2500\u2500 package.json<br \/>\u2502   \u2514\u2500\u2500 .env.example<br \/>\u2502<br \/>\u251c\u2500\u2500 agent\/                  # Autonomous AI Agent<br \/>\u2502   \u251c\u2500\u2500 src\/<br \/>\u2502   \u2502   \u2514\u2500\u2500 index.ts       # Agent that handles 402s<br \/>\u2502   \u251c\u2500\u2500 package.json<br \/>\u2502   \u2514\u2500\u2500 .env.example<br \/>\u2502<br \/>\u2514\u2500\u2500 README.md<\/p>\n<h4>Prerequisites<\/h4>\n<p>Before we dive in, make sure you\u00a0have:<\/p>\n<p><strong>Node.js 22+<\/strong>\u200a\u2014\u200a<a href=\"https:\/\/nodejs.org\/\">Download\u00a0here<\/a><strong>Some devnet SOL<\/strong>\u200a\u2014\u200aGet free devnet SOL from <a href=\"https:\/\/solfaucet.com\/\">solfaucet.com<\/a><strong>Anthropic API key<\/strong> (optional)\u200a\u2014\u200a<a href=\"https:\/\/console.anthropic.com\/\">Get one here<\/a> (free tier works\u00a0fine)<\/p>\n<h3>LFG!<\/h3>\n<p>let\u2019s start by cloning the\u00a0repo:<\/p>\n<p>git clone https:\/\/github.com\/jtordgeman\/http-402-starter.git<br \/>cd http-402-starter<\/p>\n<p>The starter branch has everything scaffolded\u200a\u2014\u200awe just need to fill in the 402 payment logic as we go. If you get stuck, you can always peek at the finished branch for the complete implementation.<\/p>\n<p>We kick things off with the API server\u200a\u2014\u200athe \u2665\ufe0f of our HTTP 402\u00a0system!<\/p>\n<p><strong>Building the API\u00a0Server<\/strong><\/p>\n<p>Our API has literally one job: guard content behind a paywall that only accepts Solana transactions as payment. Simple and to the\u00a0point.<\/p>\n<p>First, install dependencies and set up your environment:<\/p>\n<p>cd api<br \/>npm install<br \/>cp .env.example .env<\/p>\n<p>Open the\u00a0.env file and fill in your\u00a0values:<\/p>\n<p>PAYMENT_ADDRESS=&lt;your_solana_wallet_address&gt;   # Where payments will go<br \/>PAYMENT_AMOUNT=0.001                          # Amount in SOL<br \/>SOLANA_RPC_URL=https:\/\/api.devnet.solana.com  # Use devnet for testing<br \/>ANTHROPIC_API_KEY=&lt;your_key_here&gt;              # Optional, enables real AI responses<\/p>\n<p>Our server is built with <a href=\"https:\/\/hono.dev\/\">Hono<\/a>\u200a\u2014\u200athink Express but faster and TypeScript-first. The imports and configuration are already set up for you in the starter\u00a0project:<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/d4d8bf28cd625dfed7d0ffdaa47da1f9\/href\">https:\/\/medium.com\/media\/d4d8bf28cd625dfed7d0ffdaa47da1f9\/href<\/a><\/p>\n<p>See that createSolanaRpc call? That\u2019s the Solana kit way of connecting to the blockchain. If you\u2019re coming from Web3.js 1.x, this used to look\u00a0like:<\/p>\n<p>import { Connection } from &#8216;@solana\/web3.js&#8217;;<br \/>const connection = new Connection(&#8216;https:\/\/api.devnet.solana.com&#8217;, &#8216;confirmed&#8217;);<\/p>\n<p><strong>Step 1: Replay Attack Prevention<\/strong><\/p>\n<p>Our first order of business is to take care of security. Imagine this loophole: someone paid one of our 402 requests and now they are reusing the same transaction signature over and over again. Each Solana signature is unique per transaction, so if we store every signature we\u2019ve seen, we can instantly reject any that\u2019s been used before. Easiest way to do this in TypeScript? A set. Why is it perfect for\u00a0us?<\/p>\n<p>Set lookup O(1), making it lightning fast even with millions of\u00a0entries.Set automatically handles duplicatesIt\u2019s dead easy to use:\u00a0.has() to check,\u00a0.add() to mark\u00a0used.<\/p>\n<p>Add the processedSignatures set right after the service initialization:<\/p>\n<p>const processedSignatures = new Set&lt;string&gt;();\u26a0\ufe0f <strong>Production note<\/strong>: A Set lives in memory and resets when the server restarts. For production, store processed signatures in a database (Redis works great for\u00a0this).<\/p>\n<p><strong>Step 2: Verifying the\u00a0Payment<\/strong><\/p>\n<p>This is the most important function in the whole project. When a client claims they paid, we go directly to the Solana blockchain to verify their claim. It\u2019s not that we don\u2019t trust them, it\u2019s just that we don\u2019t trust them\u00a0\ud83d\ude42 (also, the blockchain doesn\u2019t\u00a0lie!)<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/52febba109d141c39ccc878b3716bf5a\/href\">https:\/\/medium.com\/media\/52febba109d141c39ccc878b3716bf5a\/href<\/a><\/p>\n<p>So what\u2019s going on\u00a0here?<\/p>\n<p><strong>Line 2:<\/strong> We get the instructions of the transaction from the tx\u00a0object.<strong>Line 3: <\/strong>We need to convert the expectedAmount from SOL to lamports:<strong> <\/strong>1 SOL = 1,000,000,000 lamports. It\u2019s like satoshis for Bitcoin. When we verify the transaction, we compare lamports to lamports, not SOL to\u00a0SOL.<strong>Lines 4\u201315<\/strong>: We iterate over the transaction\u2019s instructions checking if the instruction is parsed, if so we verify it\u2019s a transfer type and only then we verify that the transaction is for the correct address and amount. We used \u2265 instead of === for the lamports verification so in case the user sent more than we require, we just treat it as a \u201ctip\u201d and keep it\u00a0\ud83d\ude08If we find a transaction that fits all the details\u200a\u2014\u200awe return true, otherwise false<\/p>\n<p>That\u2019s it for our legendary verify payments method. Let\u2019s move on to the chat\u00a0route.<\/p>\n<p>legendary indeed<\/p>\n<p><strong>Step 3: The chat\u00a0Endpoint<\/strong><\/p>\n<p>This is where all the pieces come together, a.k.a the full HTTP 402 awesomeness. The chat route reads like a bouncer at a club: no ticket\/bad ticket? no entry. Only once everything checks out do you get\u00a0in.<\/p>\n<p>Update the \/api\/chat handler as\u00a0follows:<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/1dcbe20bdee7ce9dba994eba76eb108f\/href\">https:\/\/medium.com\/media\/1dcbe20bdee7ce9dba994eba76eb108f\/href<\/a><\/p>\n<p>Let\u2019s walk through\u00a0it:<\/p>\n<p><strong>Lines 2\u20133<\/strong>: We grab the Payment-Signature header and request body. The signature is how the client proves they\u00a0paid.<strong>Lines 7\u20139:<\/strong> No payment signature? call payment402Response to handle all the response\u00a0details.<strong>Lines 12\u201314: <\/strong>Before touching the blockchain, we check our processedSignatures Set. Already seen this signature? Replay attack. 400 and\u00a0goodbye.<strong>Lines 17\u201322<\/strong>: We are getting the transaction details from the blockchain by passing the signature string directly as a Signature type to the getTransaction call. We call the getTransaction\u2019s\u00a0.send() method to actually execute the RPC\u00a0call.<strong>Lines 24\u201332<\/strong>: Three sequential checks on the transaction: did the transaction exist? Did it succeed? Did it actually pay the right amount to the right address? if any of these checks fail, we respond back with a 402 response to the\u00a0client.<strong>Line 35<\/strong>: Only NOW do we mark the signature as used. After the verification passes but before actually serving content. If we did it earlier and content delivery failed, the user would lose their payment for\u00a0nothing.<strong>Lines 37\u201341<\/strong>: The payment is verified \ud83c\udf89 we can now call the generateAiResponse function to generate the response body and send back a lovely status 200 response.<\/p>\n<p>With the main method all laid out, let\u2019s add the support\u00a0methods:<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/9da2a63d3745412e0a862c0e27fe5769\/href\">https:\/\/medium.com\/media\/9da2a63d3745412e0a862c0e27fe5769\/href<\/a><\/p>\n<p>So what do we have\u00a0here?<\/p>\n<p><strong>payment402Response<\/strong> is pretty straightforward. All it does is return a 402 payment request stating the address, amount, currency and network in the body of the response. In addition to that the response also include a bunch of headers that describe the payment as\u00a0well<strong>generateAiResponse<\/strong> generates the AI response, real or mocked. The block.type === &#8220;text&#8221; check is there because the Anthropic SDK returns a union type for content blocks\u200a\u2014\u200ait could be text, a tool call, or other types\u200a\u2014\u200aso we narrow it properly instead of blindly accessing\u00a0.text.<\/p>\n<p><strong>Step 4: Test\u00a0Run<\/strong><\/p>\n<p>Let\u2019s do a quick test just to verify everything is working smoothly. Start the API\u00a0server:<\/p>\n<p>npm run dev<\/p>\n<p>Run a quick test to verify the 402 gated endpoint:<\/p>\n<p>curl -X POST http:\/\/localhost:3000\/api\/chat <br \/>  -H &#8220;Content-Type: application\/json&#8221; <br \/>  -d &#8216;{&#8220;message&#8221;: &#8220;hello&#8221;}&#8217;<\/p>\n<p>If all went well you should get back a response similar to\u00a0this:<\/p>\n<p>{<br \/>  &#8220;error&#8221;: &#8220;payment_required&#8221;,<br \/>  &#8220;message&#8221;: &#8220;Payment required to access this endpoint&#8221;,<br \/>  &#8220;payment&#8221;: {<br \/>    &#8220;address&#8221;: &#8220;YourAddress&#8230;&#8221;,<br \/>    &#8220;amount&#8221;: 0.001,<br \/>    &#8220;currency&#8221;: &#8220;SOL&#8221;,<br \/>    &#8220;network&#8221;: &#8220;solana-devnet&#8221;<br \/>  }<\/p>\n<p>Beautiful. Our API is now refusing to work for free \ud83d\udcb8. Next, it\u2019s time to build the agent that pays the\u00a0bills.<\/p>\n<p><strong>Building the Agent<\/strong>\u00a0\ud83e\udd16<\/p>\n<p>If we imagined our API as the paywall guard, the agent is that club-goer friend we all have, who knows how to talk their way in (or in our case, pay their way in). The agent\u2019s only job is to make a request, handle the 402 like a champ, and retry with payment proof. Let\u2019s build\u00a0it.<\/p>\n<p>First, just like before, install dependencies and set up your environment:<\/p>\n<p>cd agent<br \/>npm install<br \/>cp .env.example .env<\/p>\n<p>Open the\u00a0.env file and fill in your\u00a0values:<\/p>\n<p>API_URL=http:\/\/localhost:3000        # Your API server<br \/>AGENT_WALLET_KEY=your_private_key   # Agent&#8217;s Solana wallet (for autonomous payments)<br \/>SOLANA_RPC_URL=https:\/\/api.devnet.solana.com\u26a0\ufe0f <strong>Private key safety<\/strong>: Never ever EVER E-V-E-R commit your private key. The agent wallet should hold only the minimum SOL needed for payments. Think of it as a prepaid card, not your main\u00a0wallet.<\/p>\n<p><strong>Step 1: Making the\u00a0Request<\/strong><\/p>\n<p>This is the heart of the agent, the method that orchestrates the entire 402 flow. It starts by making a normal request, just like any HTTP client would. When we get a 402 back, we parse the body into this\u00a0shape:<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/5e8a6e89dd76bf02b6a8f171ec9035b1\/href\">https:\/\/medium.com\/media\/5e8a6e89dd76bf02b6a8f171ec9035b1\/href<\/a><\/p>\n<p>The API already sends all of these properties (easily verifiable, just check the API project response), we\u2019re just giving it a type so TypeScript keeps us honest. Now let\u2019s actually use it. Add the following to your src\/index.ts file:<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/e27c93150f8c54a30d5e68b86124bd21\/href\">https:\/\/medium.com\/media\/e27c93150f8c54a30d5e68b86124bd21\/href<\/a><\/p>\n<p>Before you ask, yes we use two methods to handle the request: fetchChat is a dead-simple private helper. Its one job is to fire the request and optionally include the Payment-Signature header. makeRequest is the method that owns all the\u00a0flow:<\/p>\n<p><strong>Line 13: <\/strong>we make a call to fetchChat with our message. simple and to the\u00a0point.<strong>Lines 15\u201327<\/strong>: we check if the status is 402. If it is, we destruct payment straight from the body and hand it to handlePayment, which signs and broadcasts the transaction autonomously. When we get back the response from handlePayment we retry the API call with the payment signature. If the API rejects it again, we throw with the error\u00a0message.<\/p>\n<p>The 402 flow doesn\u2019t look so scary now does\u00a0it?<\/p>\n<p><strong>Step 2: Handling the\u00a0Payment<\/strong><\/p>\n<p>Now comes the real fun part, we are finally touching those sweet sweet \ud83d\udcb8\ud83d\udcb8! When makeRequest hits a 402, it hands off to handlePayment which takes care of the payment entirely on its own. No user input, no manual signing, no readline\u00a0prompts.<\/p>\n<p>pure magic\u00a0indeed<\/p>\n<p>First, have a look at the constructor of the AIAgent\u00a0class:<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/6714d081e40d1c73e1f33fc4eccaadbb\/href\">https:\/\/medium.com\/media\/6714d081e40d1c73e1f33fc4eccaadbb\/href<\/a><\/p>\n<p>The constructor wires all the RPC related functionality we\u00a0need:<\/p>\n<p>It creates a new Solana RPC (this.rpc)It creates a WebSocket subscription so we can know when our transaction is confirmedIt creates a sendAndConfirm function using sendAndConfirmTransactionFactory, which combines the RPC and the WebSocket subscription into a single call that submits a transaction <strong>and<\/strong> waits until the network confirms it landed\u200a\u2014\u200aso when it returns, you know the payment actually went through rather than just being submitted.<\/p>\n<p>Next, add the handlePayment method to the <strong>src\/index.ts<\/strong> file as\u00a0follows:<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/f397978d421a413458e86ad804f3f594\/href\">https:\/\/medium.com\/media\/f397978d421a413458e86ad804f3f594\/href<\/a><\/p>\n<p>A few things worth calling\u00a0out:<\/p>\n<p><strong>getLatestBlockhash: <\/strong>every Solana transaction needs a blockhash to set its expiry window. This is how the network knows a transaction isn\u2019t\u00a0stale.<strong>transferInstruction: <\/strong>we use the system program\u2019s transfer instruction to move a specified amount of SOL (amount) from one wallet (the source) to another (the destination).<strong>pipe and transactionMessage<\/strong>: a Solana transaction is three things: who pays the fee, when it expires, and what it does. pipe is how @solana\/kit makes you assemble those pieces\u200a\u2014\u200astart with a blank message, add the fee payer (setTransactionMessageFeePayerSigner), set the expiry window aka blockhash (setTransactionMessageLifetimeUsingBlockhash) and finally tell it what to actually do by appending one or more instructions (appendTransactionMessageInstruction). Each step returns a new message rather than mutating the old one. Super explicit with no hidden\u00a0state.<strong>signTransactionMessageWithSigners<\/strong>\u200a\u2014\u200asigns the transaction message with the wallet\u2019s private key. Returns a fully signed transaction ready to broadcast.<strong>getSignatureFromTransaction<\/strong>\u200a\u2014\u200aextracts the signature from the signed tx. In Solana, the signature is the transaction ID\u200a\u2014\u200ayou can plug it into <a href=\"https:\/\/explorer.solana.com\/?cluster=devnet\">Solana Explorer<\/a> to inspect the tx even before it\u2019s confirmed.<strong>sendAndConfirm<\/strong>\u200a\u2014\u200abroadcasts the transaction and waits for confirmation. commitment: &#8220;confirmed&#8221; means roughly 2\/3 of validators have seen it\u200a\u2014\u200afast enough for our use case and more reliable than processed.<em>\ud83e\udd16<\/em> <strong>The agent wallet needs devnet SOL.<\/strong> Grab some from <a href=\"https:\/\/solfaucet.com\/\">solfaucet.com<\/a> or using the Solana CLI\u2019s airdrop method before running the\u00a0demo.<\/p>\n<p><strong>Step 3: Test\u00a0Run<\/strong><\/p>\n<p>Start your API server first (in a separate terminal):<\/p>\n<p>cd api &amp;&amp; npm run dev<\/p>\n<p>Then run the\u00a0agent:<\/p>\n<p>cd agent &amp;&amp; npm start<\/p>\n<p>And if everything went well you should see this awesomeness printed\u00a0out:<\/p>\n<p>\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550<br \/>\ud83e\udd16 HTTP 402 Autonomous Agent Demo<br \/>\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550<br \/>\ud83d\udccd API: http:\/\/localhost:3000<br \/>\ud83c\udf10 Network: https:\/\/api.devnet.solana.com<br \/>\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550<br \/>\u2705 Agent wallet loaded<br \/>402 response received {<br \/>  error: &#8216;payment_required&#8217;,<br \/>  message: &#8216;Payment required to access this endpoint&#8217;,<br \/>  payment: {<br \/>    address: &#8216;5b9r8&#8230;KDSM&#8217;,<br \/>    amount: 0.001,<br \/>    currency: &#8216;SOL&#8217;,<br \/>    network: &#8216;solana-devnet&#8217;<br \/>  }<br \/>}<\/p>\n<p>\ud83d\udcb0 Paying 0.001 SOL autonomously&#8230;<br \/>\u2705 Payment confirmed: 3i5Ak&#8230;tX8Qx<br \/>Agent response {<br \/>  success: true,<br \/>  response: &#8216;Mock AI response to: &#8220;Explain HTTP 402 in one sentence&#8221;&#8216;,<br \/>  payment: {<br \/>    signature: &#8216;3i5Akiq&#8230;X8Qx&#8217;,<br \/>    verified: true<br \/>  }<br \/>}<br \/>\u2705 Demo completed successfully!<\/p>\n<p>\ud83d\udd90\ud83c\udfa4 we finished the whole 402 process, no prompts, no manual steps. The agent detects the 402 response, pays up and retry the request with the proof of payment (signature).<\/p>\n<h3>A 402\u00a0MCP?<\/h3>\n<p>So we built a CLI agent that handles 402s autonomously. But I have a feeling a thought just crossed your mind: what if Claude Code itself could handle HTTP 402 payments natively? Well, that\u2019s exactly what MCP (Model Context Protocol) makes possible.<\/p>\n<p>We already know how to handle payment from our CLI agent, so what will happen if we use the same logic in a MCP tool?\u00a0\ud83e\udd14<\/p>\n<p><strong>The 402 MCP\u00a0Server<\/strong><\/p>\n<p>The entirety of the MCP server can be found in the <strong>\/mcp-server\/src\/index.ts <\/strong>file:<\/p>\n<p><a href=\"https:\/\/medium.com\/media\/efbd6b31d6839e3dfa156a314848af42\/href\">https:\/\/medium.com\/media\/efbd6b31d6839e3dfa156a314848af42\/href<\/a><\/p>\n<p>Few things to point\u00a0out:<\/p>\n<p>The handlePayment function is identical to what we wrote for the agent. The only real difference is the wrapper. Instead of a class method calling it on a 402, it\u2019s the MCP tool doing the same\u00a0thing.<strong>registerTool takes the schema separately from the handler.<\/strong> The first argument is the tool name, the second is metadata + inputSchema (a Zod object), and the third is the async handler. This separation is intentional\u200a\u2014\u200ait lets MCP clients inspect the tool&#8217;s shape without executing it.<strong>solToLamports uses string arithmetic, not floats.<\/strong> 0.001 * 1_000_000_000 in floating point isn\u2019t always exact. We split the decimal string, pad to 9 digits, and do pure BigInt\u00a0math.We use Solana\u2019s native system program to create the transfer instruction (getTransferSolInstruction<em>)<\/em><\/p>\n<p><strong>Running the MCP\u00a0server<\/strong><\/p>\n<p>First, install dependencies and set up your environment:<\/p>\n<p>cd mcp-server<br \/>npm install<br \/>cp .env.example .env<\/p>\n<p>Make sure you update the\u00a0.env file with your\u00a0values:<\/p>\n<p>AGENT_WALLET_KEY=&lt;Your agent wallet private key&gt;<br \/>SOLANA_RPC_URL=https:\/\/api.devnet.solana.com<\/p>\n<p>For Claude to be able to use the server we must add a\u00a0.mcp.json file describing the MCP first. When we run claude in our project main folder it will pick up that file and register the MCP for\u00a0use.<\/p>\n<p>Create a\u00a0.mcp.json file in the root of the project (same level as agent and api folders) and make sure to update the agent wallet key as\u00a0well:<\/p>\n<p>{<br \/>  &#8220;mcpServers&#8221;: {<br \/>    &#8220;http-402-payments&#8221;: {<br \/>      &#8220;command&#8221;: &#8220;node&#8221;,<br \/>      &#8220;args&#8221;: [&#8220;.\/mcp-server\/dist\/index.js&#8221;],<br \/>      &#8220;env&#8221;: {<br \/>        &#8220;AGENT_WALLET_KEY&#8221;: &#8220;YOUR_BASE58_PRIVATE_KEY&#8221;,<br \/>        &#8220;SOLANA_RPC_URL&#8221;: &#8220;https:\/\/api.devnet.solana.com&#8221;<br \/>      }<br \/>    }<br \/>  }<br \/>}<\/p>\n<p>Build the server (npm run build inside mcp-server\/), then launch Claude Code from your project\u00a0root.<\/p>\n<p><strong>Seeing it in\u00a0Action<\/strong><\/p>\n<p>With the API server running and the MCP server loaded, you can just ask Claude Code naturally:<\/p>\n<p>Call my payment-gated API at <a href=\"http:\/\/localhost:3000\/api\/chat\">http:\/\/localhost:3000\/api\/chat<\/a> with the message\u00a0\u2018Hello\u2019<\/p>\n<p>Claude Code will call make_paid_request, get the 402, pay autonomously, and retry\u200a\u2014\u200awithout you writing any orchestration code. Here&#8217;s what that looks\u00a0like:<\/p>\n<p>Look Ma! I can pay on my\u00a0own!<\/p>\n<p>There we go, Claude picked up our MCP and actually paid for its own transaction using 402\u00a0\ud83c\udf89\ud83c\udf89\ud83c\udf89<\/p>\n<p>If we look at the bigger picture, HTTP 402 and MCP is a natural fit. MCP tools are how AI agents interact with the world. Payment-gated APIs are how that world charges for access. The two were always going to meet, we\u2019re just doing it a bit\u00a0early.<\/p>\n<h3>Wrapping Up<\/h3>\n<p>HTTP 402 has been sitting in the spec since the early 1990s. The reason it never took off is that the payment layer was always the hard part\u200a\u2014\u200acredit cards have friction, subscriptions need accounts, micropayments had no good infrastructure. Solana changes that. And AI agents make it worth building, they don\u2019t need onboarding flows, they don\u2019t forget to cancel subscriptions, they just need to pay for what they use, automatically, in the moment they need\u00a0it.<\/p>\n<p>In this post we demonstrated a payment-gated API, an agent that handles the flow end-to-end, and an MCP server that gives Claude Code the same ability with zero orchestration code.<\/p>\n<p>There\u2019s still more to explore of course. The code in this post is nowhere near production grade, and we didn\u2019t even touch on the web frontend side as wiring 402 into a React app with wallets like Phantom or Solflare is a whole different flow. If you\u2019d like to see a follow-up post on that, let me know in the comments.<\/p>\n<p><a href=\"https:\/\/medium.com\/coinmonks\/http-402-died-in-the-90s-solana-just-brought-it-back-a67384dd08c4\">HTTP 402 Died in the 90s. Solana Just Brought It Back.<\/a> was originally published in <a href=\"https:\/\/medium.com\/coinmonks\">Coinmonks<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>","protected":false},"excerpt":{"rendered":"<p>Photo by Mika Baumeister on\u00a0Unsplash In the early 1990s, the HTTP specification reserved a special status code for payments, the \u201c402 Payment Required.\u201d status. The idea? Websites could charge for content directly through the browser. Fast forward 25+ years, and 402 is still sitting there, lonely and unused, just like that gym membership you S-W-O-R-E [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":145633,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-145632","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-interesting"],"_links":{"self":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/145632"}],"collection":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=145632"}],"version-history":[{"count":0,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/145632\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/media\/145633"}],"wp:attachment":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=145632"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=145632"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=145632"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}