Client Usage

The @shade402/client package provides HTTP clients for making X402 payment requests. It offers two client types: explicit (manual control) and automatic (automatic payment handling).

Installation

pnpm add @shade402/client @shade402/core @solana/web3.js

Client Types

Explicit Client

The X402Client provides manual control over the payment flow. You explicitly check for 402 responses, create payments, and retry requests.

Automatic Client

The X402AutoClient automatically handles the payment flow. When it receives a 402 response, it automatically creates a payment and retries the request.

Explicit Client

Basic Usage

import { X402Client } from '@shade402/client';
import { Keypair } from '@solana/web3.js';

const wallet = Keypair.generate();
const client = new X402Client(wallet, process.env.SOLANA_RPC_URL);

try {
  // Make initial request
  let response = await client.get('https://api.example.com/data');

  // Check if payment required
  if (client.paymentRequired(response)) {
    // Parse payment request
    const paymentRequest = client.parsePaymentRequest(response);

    // Create payment
    const authorization = await client.createPayment(paymentRequest);

    // Retry with payment
    response = await client.get('https://api.example.com/data', {
      payment: authorization,
    });
  }

  // Process response
  console.log(response.data);
} finally {
  // Always close the client
  await client.close();
}

HTTP Methods

The explicit client supports all HTTP methods:

// GET request
const getResponse = await client.get(url, options);

// POST request
const postResponse = await client.post(url, data, options);

// PUT request
const putResponse = await client.put(url, data, options);

// DELETE request
const deleteResponse = await client.delete(url, options);

// Generic request
const response = await client.request('GET', url, options);

Options

Request options include standard Axios options plus:

interface RequestOptions {
  payment?: PaymentAuthorization;  // Payment authorization for retry
  encryptedResource?: string;       // Encrypted resource field
  // ... standard Axios options
}

Local Development

For local development with localhost URLs, enable allowLocal:

const client = new X402Client(
  wallet,
  process.env.SOLANA_RPC_URL,
  undefined,
  true // allowLocal - only for development!
);

Automatic Client

Basic Usage

import { X402AutoClient } from '@shade402/client';
import { Keypair } from '@solana/web3.js';

const wallet = Keypair.generate();
const client = new X402AutoClient(wallet, process.env.SOLANA_RPC_URL, {
  maxPaymentAmount: '1.0', // Safety limit
  autoRetry: true,
});

try {
  // Payment is handled automatically
  const response = await client.get('https://api.example.com/data');
  console.log(response.data);
} finally {
  await client.close();
}

Configuration Options

interface X402AutoClientOptions {
  maxRetries?: number;        // Maximum retry attempts (default: 1)
  autoRetry?: boolean;        // Auto-retry after payment (default: true)
  maxPaymentAmount?: string;  // Maximum payment amount (safety limit)
  allowLocal?: boolean;       // Allow localhost URLs (development only)
}

HTTP Methods

// GET request
const getResponse = await client.get(url, options);

// POST request
const postResponse = await client.post(url, data, options);

// PUT request
const putResponse = await client.put(url, data, options);

// DELETE request
const deleteResponse = await client.delete(url, options);

// Generic fetch
const response = await client.fetch(url, {
  method: 'GET',
  // ... options
});

Error Handling

The automatic client throws errors for payment issues:

import { PaymentRequiredError, InsufficientFundsError } from '@shade402/core';

try {
  const response = await client.get(url);
} catch (error) {
  if (error instanceof PaymentRequiredError) {
    // Payment required but autoRetry is false
    console.log('Payment required:', error.paymentRequest);
  } else if (error instanceof InsufficientFundsError) {
    console.log('Insufficient funds');
  } else {
    console.error('Other error:', error);
  }
}

Resource Encryption

Both clients support resource encryption:

// Explicit client
const client = new X402Client(wallet);

// Get 402 response (includes server public key)
let response = await client.get(url);
const paymentRequest = client.parsePaymentRequest(response);

// Encrypt resource
const encryptedResource = client.encryptResource(paymentRequest.resource);

// Retry with encrypted resource
response = await client.get(url, {
  payment: authorization,
  encryptedResource: encryptedResource,
});

// Automatic client handles encryption automatically
const autoClient = new X402AutoClient(wallet);
const response = await autoClient.get(url); // Encryption handled automatically

Payment Amount Control

You can specify a custom payment amount:

// Explicit client
const paymentRequest = client.parsePaymentRequest(response);
const authorization = await client.createPayment(
  paymentRequest,
  '0.05' // Custom amount (must be <= max_amount_required)
);

// Automatic client uses maxPaymentAmount as safety limit
const client = new X402AutoClient(wallet, rpcUrl, {
  maxPaymentAmount: '1.0', // Won't pay more than 1.0 tokens
});

URL Validation

The client validates URLs to prevent SSRF attacks:

  • Only allows http:// and https:// schemes

  • Blocks localhost and private IPs (unless allowLocal is enabled)

  • Validates URL format

For local development, enable allowLocal:

const client = new X402Client(wallet, rpcUrl, undefined, true);

Cleanup

Always close the client when done to cleanup connections:

try {
  // Use client
} finally {
  await client.close();
}

Complete Example

import { X402AutoClient } from '@shade402/client';
import { Keypair } from '@solana/web3.js';

async function fetchPremiumData() {
  // Load wallet from secure storage
  const wallet = Keypair.fromSecretKey(
    Buffer.from(process.env.WALLET_SECRET_KEY!, 'base64')
  );

  // Create client
  const client = new X402AutoClient(wallet, process.env.SOLANA_RPC_URL, {
    maxPaymentAmount: '1.0',
    autoRetry: true,
  });

  try {
    // Make request - payment handled automatically
    const response = await client.post(
      'https://api.example.com/premium-analysis',
      { text: 'Analyze this text' },
      {
        headers: {
          'Content-Type': 'application/json',
        },
      }
    );

    return response.data;
  } catch (error) {
    console.error('Error fetching data:', error);
    throw error;
  } finally {
    await client.close();
  }
}

Best Practices

  1. Always close the client when done

  2. Use maximum payment amounts as safety limits

  3. Store wallet keys securely (never in code)

  4. Use allowLocal only for local development

  5. Handle errors appropriately

  6. Monitor payment amounts and transactions

  7. Use the automatic client for simpler workflows

  8. Use the explicit client for fine-grained control

Next Steps

Last updated