Skip to content

Basic Payment

The most basic x402test usage - make a request that requires payment.

  1. Solana validator running:

    Terminal window
    solana-test-validator
  2. x402test server running:

    Terminal window
    npx x402test start

Create 01-simple-payment.ts:

import { x402 } from "x402test";
async function example() {
console.log("Example 1: Simple Payment\n");
try {
// Make a request to a payment-protected endpoint
const response = await x402("http://localhost:4402/api/data")
.withPayment({ amount: "0.01" }) // Willing to pay up to 0.01 USDC
.expectStatus(200) // Expect success
.execute();
console.log("✔ Payment successful!");
console.log("Response:", JSON.stringify(response.body, null, 2));
console.log("Payment signature:", response.payment?.signature);
} catch (error) {
console.error("✘ Payment failed:", error);
process.exit(1);
}
}
example();
Terminal window
npx tsx 01-simple-payment.ts
Example 1: Simple Payment
✔ Payment successful!
Response: {
"method": "GET",
"path": "/api/data",
"data": {
"message": "Your data here"
}
}
Payment signature: 5XzT4qW3Hk2p7vN...

The client makes a GET request to /api/data:

GET /api/data HTTP/1.1
Host: localhost:4402

Server returns payment requirements:

HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"x402Version": 1,
"accepts": [{
"scheme": "solanaTransferChecked",
"network": "solana-devnet",
"maxAmountRequired": "10000",
"resource": "http://localhost:4402/api/data",
"payTo": "FcxKSp7YxqYXdq...",
"asset": "EPjFWdd5AufqSSqeM2..."
}]
}

The client automatically:

  • Creates a USDC transfer transaction
  • Signs with test wallet
  • Submits to Solana blockchain
  • Waits for confirmation

Client retries with X-PAYMENT header:

GET /api/data HTTP/1.1
Host: localhost:4402
X-PAYMENT: eyJ4NDAyVmVyc2lvbiI6...

Server verifies:

  • Transaction exists and succeeded
  • Amount matches requirement
  • Recipient is correct
  • Token is USDC
  • Signature not already used

Server returns protected content:

HTTP/1.1 200 OK
Content-Type: application/json
{
"method": "GET",
"path": "/api/data",
"data": {
"message": "Your data here"
}
}

Specifies maximum amount willing to pay:

.withPayment({ amount: "0.01" }) // Object form
.withPayment("0.01") // String form

Asserts response status code:

.expectStatus(200) // Expect 200 OK

Throws error if status doesn’t match.

Executes the request:

const response = await request.execute();

Returns X402Response<T> with:

  • status: HTTP status code
  • body: Response body
  • payment: Payment details (if payment was made)
const response = await x402("http://localhost:4402/api/data")
.withPayment("0.01")
.execute();
// Manually check status
if (response.status === 200) {
console.log("Success:", response.body);
} else {
console.error("Failed:", response.status);
}
const response = await x402("http://localhost:4402/api/data")
.withPayment("0.01")
.expectStatus(200)
.expectPaymentSettled()
.expectBody((body) => body.data !== undefined)
.execute();
try {
const response = await x402(url)
.withPayment("0.01")
.expectStatus(200)
.execute();
console.log("Success:", response.body);
} catch (error) {
if (error.message.includes("less than server required")) {
console.error("Payment amount too low");
} else {
console.error("Request failed:", error.message);
}
}

Cause: Server not running

Solution:

Terminal window
npx x402test start

Cause: Solana validator not running or transaction not confirmed

Solution:

Terminal window
solana-test-validator

Cause: Test wallet depleted

Solution:

Terminal window
rm .x402test-wallets.json
npx x402test init