Examples

Complete examples demonstrating various Shade402 use cases.

Express Server Example

Complete Express.js server with payment-protected endpoints:

import express from 'express';
import cors from 'cors';
import { initX402, paymentRequired, x402ErrorMiddleware } from '@shade402/express';
import { generateKeyPair } from '@shade402/core';

const app = express();
app.use(cors());
app.use(express.json());

// Generate encryption keys
const { publicKey, privateKey } = generateKeyPair();

// Initialize X402
initX402({
  paymentAddress: process.env.PAYMENT_WALLET_ADDRESS!,
  tokenMint: process.env.TOKEN_MINT!,
  network: process.env.SOLANA_NETWORK || 'solana-devnet',
  rpcUrl: process.env.SOLANA_RPC_URL,
  defaultAmount: '0.01',
  paymentTimeout: 300,
  autoVerify: true,
  encryptionPublicKey: publicKey,
  encryptionPrivateKey: privateKey,
});

// Public endpoint
app.get('/', (req, res) => {
  res.json({
    message: 'Welcome to X402 Payment API',
    endpoints: {
      free: '/api/free',
      premium: '/api/premium-data',
      analysis: '/api/premium-analysis',
    },
  });
});

// Free endpoint
app.get('/api/free', (req, res) => {
  res.json({
    message: 'This is a free endpoint',
    data: {
      timestamp: new Date().toISOString(),
      value: Math.random() * 100,
    },
  });
});

// Premium endpoint
app.get(
  '/api/premium-data',
  paymentRequired({
    amount: '0.01',
    description: 'Access to premium data endpoint',
  }),
  (req, res) => {
    res.json({
      message: 'Premium data accessed successfully',
      data: {
        timestamp: new Date().toISOString(),
        premiumValue: Math.random() * 1000,
        secretData: 'This is premium content',
        paymentId: req.payment?.paymentId,
      },
    });
  }
);

// Premium analysis endpoint
app.post(
  '/api/premium-analysis',
  paymentRequired({
    amount: '0.05',
    description: 'Premium AI analysis service',
    expiresIn: 600,
  }),
  async (req, res) => {
    const { text } = req.body;
    
    if (!text) {
      return res.status(400).json({
        error: 'Missing text field in request body',
      });
    }

    // Simulate analysis
    res.json({
      message: 'Analysis completed',
      input: text,
      analysis: {
        sentiment: 'positive',
        wordCount: text.split(' ').length,
        estimatedCost: '0.05 USDC',
        paymentId: req.payment?.paymentId,
      },
    });
  }
);

// Error handling
app.use(x402ErrorMiddleware());

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Client Example

Complete client that makes payment-protected requests:

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

async function fetchPremiumData() {
  // Load wallet (in production, load 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.get('https://api.example.com/premium-data');
    
    console.log('Data received:', response.data);
    return response.data;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  } finally {
    await client.close();
  }
}

// Usage
fetchPremiumData().then(data => {
  console.log('Success:', data);
});

Explicit Client Example

Manual payment control with explicit client:

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

async function fetchWithManualPayment() {
  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/premium-data');

    // Check if payment required
    if (client.paymentRequired(response)) {
      console.log('Payment required');
      
      // Parse payment request
      const paymentRequest = client.parsePaymentRequest(response);
      console.log('Payment amount:', paymentRequest.maxAmountRequired);
      
      // Check if expired
      if (paymentRequest.isExpired()) {
        throw new Error('Payment request expired');
      }

      // Create payment
      console.log('Creating payment...');
      const authorization = await client.createPayment(paymentRequest);
      console.log('Payment created:', authorization.transactionHash);

      // Encrypt resource if server public key available
      let encryptedResource: string | undefined;
      try {
        encryptedResource = client.encryptResource(paymentRequest.resource);
      } catch (error) {
        // Encryption optional
      }

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

    // Process response
    console.log('Data:', response.data);
    return response.data;
  } finally {
    await client.close();
  }
}

LangChain Agent Example

LangChain agent with payment tool:

import { createX402PaymentTool } from '@shade402/langchain';
import { ChatOpenAI } from '@langchain/openai';
import { AgentExecutor, createOpenAIFunctionsAgent } from 'langchain/agents';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
import { Keypair } from '@solana/web3.js';

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

  // Create payment tool
  const paymentTool = createX402PaymentTool({
    walletKeypair: wallet,
    rpcUrl: process.env.SOLANA_RPC_URL,
    maxPayment: '1.0',
    name: 'x402_payment',
    description: 'Make an X402 payment to access a paid API endpoint',
  });

  // Create prompt
  const prompt = ChatPromptTemplate.fromMessages([
    ['system', 'You are a helpful assistant that can make payments for API access when needed.'],
    ['human', '{input}'],
    new MessagesPlaceholder('agent_scratchpad'),
  ]);

  // Create agent
  const llm = new ChatOpenAI({ temperature: 0 });
  const tools = [paymentTool];

  const agent = await createOpenAIFunctionsAgent({
    llm,
    tools,
    prompt,
  });

  const executor = new AgentExecutor({
    agent,
    tools,
    verbose: true,
  });

  return executor;
}

// Usage
const agent = await createPaymentAgent();
const result = await agent.invoke({
  input: 'Fetch premium data from https://api.example.com/premium-data and summarize it',
});

console.log(result.output);

LangGraph Workflow Example

LangGraph workflow with payment nodes:

import { StateGraph } from '@langchain/langgraph';
import { fetchWithPaymentNode, PaymentState } from '@shade402/langgraph';
import { Keypair } from '@solana/web3.js';

function createPaymentWorkflow() {
  const workflow = new StateGraph<PaymentState>({
    channels: {
      api_url: { reducer: (x, y) => y ?? x },
      api_response: { reducer: (x, y) => y ?? x },
      wallet_keypair: { reducer: (x, y) => y ?? x },
      max_payment_amount: { reducer: (x, y) => y ?? x },
      result: { reducer: (x, y) => y ?? x },
    },
  });

  // Fetch API with automatic payment
  workflow.addNode('fetch_api', fetchWithPaymentNode);

  // Process response
  workflow.addNode('process', async (state: PaymentState) => {
    const data = JSON.parse(state.api_response || '{}');
    return {
      ...state,
      result: {
        success: true,
        data: data,
        paymentId: data.paymentId,
      },
    };
  });

  // Error handler
  workflow.addNode('error_handler', async (state: PaymentState) => {
    return {
      ...state,
      result: {
        success: false,
        error: state.payment_error,
      },
    };
  });

  workflow.setEntryPoint('fetch_api');

  // Route based on result
  workflow.addConditionalEdges(
    'fetch_api',
    (state: PaymentState) => {
      if (state.payment_error) return 'error';
      if (state.api_response) return 'success';
      return 'error';
    },
    {
      success: 'process',
      error: 'error_handler',
    }
  );

  return workflow.compile();
}

// Usage
const app = createPaymentWorkflow();
const wallet = Keypair.generate();

const result = await app.invoke({
  api_url: 'https://api.example.com/premium-data',
  wallet_keypair: wallet,
  max_payment_amount: '1.0',
  rpc_url: process.env.SOLANA_RPC_URL,
});

console.log(result.result);

Next.js API Route Example

Next.js API route with payment protection:

// app/api/premium-data/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { withPayment, X402HandlerContext } from '@shade402/nextjs';
import { configureX402 } from '@/lib/x402-config';

configureX402();

export const GET = withPayment(
  {
    amount: '0.01',
    description: 'Premium data access',
  },
  async (req: NextRequest, context: X402HandlerContext) => {
    const payment = context.payment;
    
    return NextResponse.json({
      data: {
        message: 'Premium data',
        timestamp: new Date().toISOString(),
        paymentId: payment?.paymentId,
      },
    });
  }
);

export const POST = withPayment(
  {
    amount: '0.05',
    description: 'Premium data write',
  },
  async (req: NextRequest, context: X402HandlerContext) => {
    const body = await req.json();
    const payment = context.payment;
    
    // Process request
    return NextResponse.json({
      success: true,
      processed: body,
      paymentId: payment?.paymentId,
    });
  }
);

Multiple Payment Tiers Example

Different payment tiers for different endpoints:

import express from 'express';
import { paymentRequired } from '@shade402/express';

const app = express();

// Free tier
app.get('/api/free', (req, res) => {
  res.json({ tier: 'free', data: 'Free data' });
});

// Basic tier
app.get('/api/basic',
  paymentRequired({ amount: '0.001', description: 'Basic tier access' }),
  (req, res) => {
    res.json({ tier: 'basic', data: 'Basic data' });
  }
);

// Premium tier
app.get('/api/premium',
  paymentRequired({ amount: '0.01', description: 'Premium tier access' }),
  (req, res) => {
    res.json({ tier: 'premium', data: 'Premium data' });
  }
);

// Enterprise tier
app.get('/api/enterprise',
  paymentRequired({ amount: '0.1', description: 'Enterprise tier access' }),
  (req, res) => {
    res.json({ tier: 'enterprise', data: 'Enterprise data' });
  }
);

Custom Verification Example

Custom payment verification logic:

import { paymentRequired, X402Request } from '@shade402/express';
import { verifyPaymentInDatabase } from './database';

app.post('/api/custom',
  paymentRequired({
    amount: '0.01',
    autoVerify: false, // Disable automatic verification
  }),
  async (req: X402Request, res) => {
    const payment = req.payment;
    
    if (!payment) {
      return res.status(402).json({ error: 'Payment required' });
    }

    // Custom verification against database
    const isVerified = await verifyPaymentInDatabase(
      payment.transactionHash
    );
    
    if (!isVerified) {
      return res.status(403).json({ error: 'Payment not verified' });
    }

    // Process request
    res.json({ success: true, paymentId: payment.paymentId });
  }
);

Error Handling Example

Comprehensive error handling:

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

async function fetchWithErrorHandling() {
  const client = new X402AutoClient(wallet, rpcUrl);
  
  try {
    const response = await client.get(url);
    return response.data;
  } catch (error) {
    if (error instanceof PaymentRequiredError) {
      console.log('Payment required:', error.paymentRequest);
      // Handle payment required
    } else if (error instanceof PaymentExpiredError) {
      console.log('Payment expired');
      // Handle expired payment
    } else if (error instanceof InsufficientFundsError) {
      console.log('Insufficient funds:', error.requiredAmount);
      // Handle insufficient funds
    } else {
      console.error('Unexpected error:', error);
      // Handle other errors
    }
    throw error;
  } finally {
    await client.close();
  }
}

Next Steps

Last updated