Best Practices

Guidelines and recommendations for using Shade402 effectively and securely.

Configuration

Environment Variables

Store all sensitive configuration in environment variables:

// .env
PAYMENT_WALLET_ADDRESS=YourWalletAddress
TOKEN_MINT=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
SOLANA_NETWORK=solana-mainnet
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
WALLET_SECRET_KEY=YourBase64EncodedSecretKey

Initialize Once

Initialize X402 configuration once at application startup:

// config/x402.ts
import { initX402 } from '@shade402/express';

let initialized = false;

export function initializeX402() {
  if (initialized) return;
  
  initX402({
    paymentAddress: process.env.PAYMENT_WALLET_ADDRESS!,
    tokenMint: process.env.TOKEN_MINT!,
    network: process.env.SOLANA_NETWORK!,
    rpcUrl: process.env.SOLANA_RPC_URL,
    autoVerify: true,
  });
  
  initialized = true;
}

Default Values

Set reasonable defaults but allow overrides:

initX402({
  paymentAddress: process.env.PAYMENT_WALLET_ADDRESS!,
  tokenMint: process.env.TOKEN_MINT!,
  network: process.env.SOLANA_NETWORK || 'solana-devnet',
  defaultAmount: '0.01',
  paymentTimeout: 300, // 5 minutes
  autoVerify: process.env.NODE_ENV === 'production',
});

Payment Amounts

Set Appropriate Amounts

Choose payment amounts based on:

  • Resource value

  • Cost of providing the resource

  • Market rates

  • User experience

// Low-value resource
paymentRequired({ amount: '0.001' });

// Medium-value resource
paymentRequired({ amount: '0.01' });

// High-value resource
paymentRequired({ amount: '0.1' });

Maximum Payment Limits

Always set maximum payment limits for clients:

const client = new X402AutoClient(wallet, rpcUrl, {
  maxPaymentAmount: '1.0', // Safety limit
});

Payment Tiers

Implement different payment tiers:

// Basic tier
app.get('/api/basic', paymentRequired({ amount: '0.001' }), handler);

// Premium tier
app.get('/api/premium', paymentRequired({ amount: '0.01' }), handler);

// Enterprise tier
app.get('/api/enterprise', paymentRequired({ amount: '0.1' }), handler);

Error Handling

Comprehensive Error Handling

Handle all error types appropriately:

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

try {
  const response = await client.get(url);
} catch (error) {
  if (error instanceof PaymentRequiredError) {
    // Handle payment required
  } else if (error instanceof PaymentExpiredError) {
    // Handle expired payment
  } else if (error instanceof InsufficientFundsError) {
    // Handle insufficient funds
  } else {
    // Handle other errors
  }
}

Server-side Error Middleware

Always add error middleware:

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

// Add after all routes
app.use(x402ErrorMiddleware({
  logErrors: true,
  includeStack: process.env.NODE_ENV === 'development',
}));

Client Management

Always Close Clients

Always close clients to cleanup connections:

const client = new X402AutoClient(wallet);

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

Reuse Clients When Possible

For multiple requests, reuse the same client:

const client = new X402AutoClient(wallet);

try {
  const response1 = await client.get(url1);
  const response2 = await client.get(url2);
  const response3 = await client.get(url3);
} finally {
  await client.close();
}

Wallet Management

Load wallets securely:

// Load from environment variable
const secretKey = Buffer.from(process.env.WALLET_SECRET_KEY!, 'base64');
const wallet = Keypair.fromSecretKey(secretKey);

// Or use key management service
const wallet = await loadWalletFromKMS();

Logging and Monitoring

Log Payment Transactions

Log all payment transactions for audit:

app.get('/api/data',
  paymentRequired({ amount: '0.01' }),
  (req, res) => {
    const payment = req.payment;
    
    // Log payment
    logger.info('Payment received', {
      paymentId: payment?.paymentId,
      amount: payment?.actualAmount,
      payer: payment?.publicKey,
      transactionHash: payment?.transactionHash,
      timestamp: new Date(),
    });
    
    res.json({ data: 'Protected data' });
  }
);

Monitor Payment Metrics

Track payment metrics:

  • Total payments received

  • Average payment amount

  • Payment success rate

  • Failed payment attempts

  • Payment expiration rate

Error Monitoring

Monitor errors and set up alerts:

app.use(x402ErrorMiddleware({
  logErrors: true,
  onError: (error, req, res) => {
    // Send to monitoring service
    monitoringService.captureException(error, {
      tags: { type: 'x402_error' },
      extra: { path: req.path },
    });
  },
}));

Performance

Connection Pooling

Reuse Solana connections:

// Create processor once
const processor = new SolanaPaymentProcessor(rpcUrl, wallet);

// Reuse for multiple payments
for (const request of requests) {
  const tx = await processor.createPaymentTransaction(request, amount, wallet);
  // ...
}

await processor.close();

Async Operations

Use async/await properly:

// GOOD: Proper async handling
async function processPayments(requests: PaymentRequest[]) {
  const results = await Promise.all(
    requests.map(request => client.createPayment(request))
  );
  return results;
}

// BAD: Sequential processing when parallel is possible
async function processPayments(requests: PaymentRequest[]) {
  const results = [];
  for (const request of requests) {
    results.push(await client.createPayment(request));
  }
  return results;
}

Testing

Test with Devnet

Always test with devnet before production:

// Test configuration
const testConfig = {
  network: 'solana-devnet',
  rpcUrl: 'https://api.devnet.solana.com',
  tokenMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // Devnet USDC
};

Test Payment Flows

Test all payment scenarios:

  • Successful payment

  • Payment expired

  • Insufficient funds

  • Invalid payment

  • Network errors

  • Transaction failures

Integration Tests

Write integration tests for payment flows:

describe('Payment Flow', () => {
  it('should handle payment required', async () => {
    const response = await client.get(url);
    expect(client.paymentRequired(response)).toBe(true);
  });
  
  it('should create and verify payment', async () => {
    const paymentRequest = client.parsePaymentRequest(response);
    const authorization = await client.createPayment(paymentRequest);
    const retryResponse = await client.get(url, { payment: authorization });
    expect(retryResponse.status).toBe(200);
  });
});

Documentation

Code Documentation

Document payment-protected endpoints:

/**
 * Premium data endpoint
 * 
 * Requires payment: 0.01 USDC
 * Payment expiration: 5 minutes
 * 
 * @route GET /api/premium-data
 * @requires X-Payment-Authorization header
 * @returns {Object} Premium data
 */
app.get('/api/premium-data',
  paymentRequired({ amount: '0.01', expiresIn: 300 }),
  handler
);

API Documentation

Document payment requirements in API docs:

  • Payment amounts

  • Payment expiration

  • Supported tokens

  • Network requirements

  • Error responses

Code Organization

Separate Concerns

Separate payment logic from business logic:

// payment-service.ts
export class PaymentService {
  async verifyPayment(authorization: PaymentAuthorization): Promise<boolean> {
    // Payment verification logic
  }
}

// business-logic.ts
export class DataService {
  constructor(private paymentService: PaymentService) {}
  
  async getPremiumData(req: Request): Promise<Data> {
    // Business logic
  }
}

Use Middleware

Use middleware for payment verification:

// Don't mix payment and business logic
app.get('/api/data', paymentRequired({ amount: '0.01' }), handler);

// Not:
app.get('/api/data', async (req, res) => {
  // Payment logic mixed with business logic
});

Summary

Key best practices:

  1. Store sensitive config in environment variables

  2. Initialize X402 once at startup

  3. Set appropriate payment amounts

  4. Always set maximum payment limits

  5. Handle all error types

  6. Always close clients

  7. Log payment transactions

  8. Monitor payment metrics

  9. Test with devnet first

  10. Document payment requirements

  11. Separate payment and business logic

  12. Use middleware for payment verification

Next Steps

Last updated