npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

x402-agg

v0.1.0

Published

Solana payment aggregation SDK with intelligent facilitator routing and instant USDC settlement

Downloads

95

Readme

@555x402/agg

Solana payment aggregation SDK with intelligent facilitator routing and instant USDC settlement

npm version License: MIT

Overview

The AGG (Aggregator) SDK enables developers to accept Solana payments through a unified API that automatically routes to the optimal payment facilitator based on latency, fees, and health. Supports USDC and SOL with instant settlement and webhook notifications.

Features

  • Intelligent Routing: Automatically selects best facilitator (PayAI, Conduit, Internal)
  • Instant Settlement: Sub-second transaction confirmation on Solana
  • Low Fees: 0.5-1% facilitator fees + gasless
  • Webhook Notifications: HMAC-signed callbacks for payment status
  • TypeScript: Full type safety with comprehensive type definitions
  • Framework Agnostic: Works with Next.js, Express, Fastify, vanilla Node
  • Idempotent: Built-in deduplication via idempotency keys

Installation

npm install @555x402/agg
# or
pnpm add @555x402/agg
# or
yarn add @555x402/agg

Quick Start

Basic Usage

import { AggClient } from '@555x402/agg';

const client = new AggClient(
  'your-api-key',
  'https://agg.rendernet.work/pub/v1' // optional, defaults to production
);

// Verify and settle a payment
const result = await client.verifyAndSettle(
  paymentHeaderB64, // from X-Payment header
  {
    amount: 1.5,  // $1.50 USDC
    mint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), // USDC
    recipient: new PublicKey('555Tm1cfV52SrBQmnxXiHMUMrpci8miW3CkLP1Qbmtd7')
  }
);

console.log('Payment settled:', result.id);

Express.js Example

import express from 'express';
import { AggClient } from '@555x402/agg';
import { PublicKey } from '@solana/web3.js';

const app = express();
const agg = new AggClient(process.env.AGG_API_KEY!);

app.post('/premium-api', async (req, res) => {
  const paymentHeader = req.headers['x-payment'] as string;
  
  if (!paymentHeader) {
    return res.status(402).json({ 
      error: 'Payment required',
      price: { amount: 0.01, asset: 'USDC' }
    });
  }
  
  try {
    await agg.verifyAndSettle(paymentHeader, {
      amount: 0.01,
      mint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
      recipient: new PublicKey(process.env.TREASURY_WALLET!)
    });
    
    // Payment verified and settled - serve the content
    res.json({ data: 'premium content' });
  } catch (error) {
    res.status(402).json({ error: 'Payment verification failed' });
  }
});

app.listen(3000);

Next.js API Route

// app/api/protected/route.ts
import { AggClient } from '@555x402/agg';
import { PublicKey } from '@solana/web3.js';
import { NextRequest, NextResponse } from 'next/server';

const agg = new AggClient(process.env.AGG_API_KEY!);

export async function GET(request: NextRequest) {
  const paymentHeader = request.headers.get('x-payment');
  
  if (!paymentHeader) {
    return NextResponse.json(
      { error: 'Payment required', price: { amount: 0.05, asset: 'USDC' }},
      { status: 402 }
    );
  }
  
  try {
    const payment = await agg.verifyAndSettle(paymentHeader, {
      amount: 0.05,
      mint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
      recipient: new PublicKey(process.env.TREASURY_WALLET!)
    });
    
    return NextResponse.json({ 
      data: 'protected data',
      paymentId: payment.id 
    });
  } catch {
    return NextResponse.json(
      { error: 'Invalid payment' },
      { status: 402 }
    );
  }
}

API Reference

Class: AggClient

Constructor

new AggClient(apiKey: string, apiBase?: string)

Parameters:

  • apiKey (string, required): Your 555x402 API key from the dashboard
  • apiBase (string, optional): API base URL. Defaults to https://agg.rendernet.work/pub/v1

Example:

const client = new AggClient('pk_live_abc123');

// Or for devnet testing:
const devClient = new AggClient('pk_test_xyz789', 'https://agg-devnet.rendernet.work/pub/v1');

Methods

verifyAndSettle(paymentHeaderB64, expected): Promise<PaymentResult>

Verifies a payment header and settles the transaction through the optimal facilitator.

Parameters:

  • paymentHeaderB64 (string): Base64-encoded payment header from X-Payment HTTP header
  • expected (object):
    • amount (number): Expected payment amount in token units (e.g., 1.5 for $1.50 USDC)
    • mint (PublicKey): Token mint address (USDC, SOL, etc.)
    • recipient (PublicKey): Wallet address to receive funds

Returns: Promise

interface PaymentResult {
  id: string;              // Payment order ID
  status: string;          // 'pending' | 'confirmed' | 'failed'
  txSignature?: string;    // Solana transaction signature (if confirmed)
  facilitator?: string;    // Which facilitator processed the payment
}

Throws:

  • PaymentVerificationError - Invalid payment header or signature
  • InsufficientAmountError - Payment amount less than expected
  • NetworkError - API unreachable or timeout

Example:

try {
  const result = await client.verifyAndSettle(
    'eyJhbGc...', // from req.headers['x-payment']
    {
      amount: 2.50,
      mint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
      recipient: new PublicKey('555Tm1cfV52SrBQmnxXiHMUMrpci8miW3CkLP1Qbmtd7')
    }
  );
  
  console.log(`Payment ${result.id} settled via ${result.facilitator}`);
  console.log(`Tx: ${result.txSignature}`);
} catch (error) {
  if (error instanceof InsufficientAmountError) {
    console.error('Payment too low:', error.message);
  }
}
getStatus(paymentId): Promise<PaymentStatus>

Retrieves the current status of a payment.

Parameters:

  • paymentId (string): Payment order ID from verifyAndSettle response

Returns: Promise

interface PaymentStatus {
  id: string;
  status: 'pending' | 'confirmed' | 'failed';
  amount: number;
  mint: string;
  recipient: string;
  sender: string;
  txSignature?: string;
  createdAt: string;      // ISO 8601 timestamp
  settledAt?: string;     // ISO 8601 timestamp
}

Example:

const status = await client.getStatus('pay_abc123xyz');

if (status.status === 'confirmed') {
  console.log(`Payment confirmed in tx: ${status.txSignature}`);
} else if (status.status === 'pending') {
  console.log('Waiting for confirmation...');
}

Webhook Verification

verifyWebhookSignature(body, timestamp, signature, secret): boolean

Verifies HMAC signature of webhook payloads from the AGG service.

Parameters:

  • body (any): Raw request body (string or object)
  • timestamp (string): Value from X-555-Timestamp header
  • signature (string): Value from X-555-Signature header (format: sha256=<hex>)
  • secret (string): Your webhook secret

Returns: boolean - true if signature valid, false otherwise

Example (Express):

import { verifyWebhookSignature } from '@555x402/agg';

app.post('/webhooks/agg', express.json(), (req, res) => {
  const timestamp = req.headers['x-555-timestamp'] as string;
  const signature = req.headers['x-555-signature'] as string;
  const secret = process.env.WEBHOOK_SECRET!;
  
  if (!verifyWebhookSignature(req.body, timestamp, signature, secret)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process webhook
  const { event, data } = req.body;
  
  if (event === 'agg.payment.settled') {
    console.log(`Payment ${data.id} settled to ${data.to}`);
    // Update your database, send confirmation email, etc.
  }
  
  res.sendStatus(200);
});

Webhook Payload Format:

{
  "event": "agg.payment.pending | agg.payment.settled | agg.payment.failed",
  "data": {
    "id": "pay_abc123",
    "status": "settled",
    "amount": 1500000,
    "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "to": "555Tm1cfV52SrBQmnxXiHMUMrpci8miW3CkLP1Qbmtd7",
    "from": "sender_wallet_address",
    "txSignature": "4vJ9J..."
  }
}

Advanced Usage

Polling for Settlement

async function waitForSettlement(paymentId: string, maxWaitMs = 30000): Promise<PaymentStatus> {
  const start = Date.now();
  
  while (Date.now() - start < maxWaitMs) {
    const status = await client.getStatus(paymentId);
    
    if (status.status === 'confirmed') {
      return status;
    }
    
    if (status.status === 'failed') {
      throw new Error('Payment failed');
    }
    
    await new Promise(r => setTimeout(r, 1000)); // Poll every second
  }
  
  throw new Error('Settlement timeout');
}

Custom Facilitator Selection

The AGG router automatically selects the optimal facilitator, but you can influence routing by registering preferred facilitators for your recipient wallet.

Contact support to configure facilitator preferences.

Error Handling

import { AggClient, PaymentError } from '@555x402/agg';

try {
  await client.verifyAndSettle(...);
} catch (error) {
  if (error instanceof PaymentError) {
    switch (error.code) {
      case 'INVALID_SIGNATURE':
        // Payment header signature doesn't match
        break;
      case 'INSUFFICIENT_AMOUNT':
        // Payment amount < expected
        break;
      case 'INVALID_RECIPIENT':
        // Recipient mismatch
        break;
      case 'FACILITATOR_UNAVAILABLE':
        // All facilitators down (rare)
        break;
      case 'NETWORK_ERROR':
        // Solana RPC or API unreachable
        break;
    }
  }
}

Token Support

Supported Tokens

| Token | Mint Address | Decimals | |-------|--------------|----------| | USDC (mainnet) | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v | 6 | | USDC (devnet) | 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU | 6 | | SOL (wrapped) | So11111111111111111111111111111111111111112 | 9 |

Amount Conversion

USDC uses 6 decimals:

  • $1.00 = 1000000 micro-units
  • $0.01 = 10000 micro-units

Helper (built-in):

import { usdToMicroUsdc, microUsdcToUsd } from '@555x402/agg/utils';

const microUnits = usdToMicroUsdc(2.50);  // 2500000
const usd = microUsdcToUsd(2500000);      // 2.5

Examples

1. Pay-Per-Request API

import { AggClient } from '@555x402/agg';
import { PublicKey } from '@solana/web3.js';

const agg = new AggClient(process.env.AGG_API_KEY!);
const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');

app.get('/api/premium-data', async (req, res) => {
  const payment = req.headers['x-payment'] as string;
  
  if (!payment) {
    return res.status(402).json({
      error: 'Payment required',
      price: { amount: 0.10, asset: 'USDC' }
    });
  }
  
  try {
    const result = await agg.verifyAndSettle(payment, {
      amount: 0.10,
      mint: USDC_MINT,
      recipient: new PublicKey(process.env.TREASURY!)
    });
    
    // Log for analytics
    console.log(`Payment ${result.id} from ${result.sender}`);
    
    // Serve premium content
    const data = await fetchPremiumData();
    res.json({ data });
  } catch (error) {
    res.status(402).json({ error: 'Invalid payment' });
  }
});

2. Webhook Handler (Async Settlement Notification)

import express from 'express';
import { verifyWebhookSignature } from '@555x402/agg';

const app = express();

app.post('/webhooks/payments', 
  express.raw({ type: 'application/json' }), 
  async (req, res) => {
    const signature = req.headers['x-555-signature'] as string;
    const timestamp = req.headers['x-555-timestamp'] as string;
    const body = req.body.toString();
    
    // Verify signature
    if (!verifyWebhookSignature(
      body, 
      timestamp, 
      signature, 
      process.env.WEBHOOK_SECRET!
    )) {
      return res.status(401).send('Invalid signature');
    }
    
    const payload = JSON.parse(body);
    
    // Handle events
    switch (payload.event) {
      case 'agg.payment.pending':
        console.log(`Payment ${payload.data.id} pending...`);
        break;
        
      case 'agg.payment.settled':
        console.log(`Payment ${payload.data.id} confirmed!`);
        console.log(`Tx: ${payload.data.txSignature}`);
        
        // Update your database
        await db.payments.update({
          where: { id: payload.data.id },
          data: { 
            status: 'settled',
            txSignature: payload.data.txSignature 
          }
        });
        
        // Send confirmation email
        await sendEmail(payload.data.to, 'Payment received!');
        break;
        
      case 'agg.payment.failed':
        console.error(`Payment ${payload.data.id} failed`);
        // Handle failure (refund, notify user, etc.)
        break;
    }
    
    res.sendStatus(200);
  }
);

3. Polling Pattern (No Webhooks)

async function processPaymentWithPolling(paymentHeader: string) {
  // Initiate settlement
  const result = await agg.verifyAndSettle(paymentHeader, {
    amount: 1.00,
    mint: USDC_MINT,
    recipient: TREASURY
  });
  
  console.log(`Payment ${result.id} initiated...`);
  
  // Poll for confirmation
  for (let i = 0; i < 30; i++) {
    await new Promise(r => setTimeout(r, 1000)); // Wait 1s
    
    const status = await agg.getStatus(result.id);
    
    if (status.status === 'confirmed') {
      console.log(`Confirmed! Tx: ${status.txSignature}`);
      return status;
    }
    
    if (status.status === 'failed') {
      throw new Error('Payment failed');
    }
  }
  
  throw new Error('Timeout waiting for confirmation');
}

4. Username-Based Payments

// Pay to a username instead of wallet address
const result = await agg.verifyAndSettle(paymentHeader, {
  amount: 5.00,
  mint: USDC_MINT,
  recipient: '@alice'  // AGG resolves username to wallet
});

Configuration

Environment Variables

# Required
AGG_API_KEY=pk_live_your_api_key_here

# Optional
AGG_API_BASE=https://agg.rendernet.work/pub/v1  # defaults to prod
WEBHOOK_SECRET=whsec_your_webhook_secret         # for webhook verification

API Keys

Get your API key from the 555x402 Dashboard.

Key types:

  • pk_live_* - Production keys
  • pk_test_* - Devnet/testnet keys

Rate Limits

  • Default: 20 requests/second per API key
  • Burst: 40 requests
  • Contact support for higher limits

Webhook Configuration

Setting Up Webhooks

  1. Go to Dashboard → Settings → Webhooks
  2. Add your endpoint URL
  3. Copy the webhook secret
  4. Test with the webhook tester

Webhook Security

Always verify signatures:

import crypto from 'crypto';

function verifyWebhook(body: string, timestamp: string, signature: string, secret: string): boolean {
  const sig = signature.replace(/^sha256=/, '');
  const hash = crypto.createHash('sha256').update(body).digest();
  const mac = crypto.createHmac('sha256', secret)
    .update(timestamp)
    .update('.')
    .update(hash)
    .digest('hex');
  
  try {
    return crypto.timingSafeEqual(
      Buffer.from(mac, 'hex'),
      Buffer.from(sig, 'hex')
    );
  } catch {
    return false;
  }
}

Replay attack protection:

  • Check X-555-Timestamp is within 5 minutes of current time
  • Store processed webhook IDs to prevent duplicates

Webhook Retry Logic

Failed webhooks are retried with exponential backoff:

  • Attempt 1: Immediate
  • Attempt 2: 60s later
  • Attempt 3: 5min later
  • Attempt 4: 15min later
  • Attempt 5: 1hr later
  • Attempt 6: 6hr later
  • After 6 attempts: moved to dead-letter queue

Testing

Unit Tests

npm test

Integration Tests

Set up a test recipient wallet and API key:

import { AggClient } from '@555x402/agg';

describe('AGG Integration', () => {
  const client = new AggClient(
    process.env.AGG_TEST_API_KEY!,
    'https://agg-devnet.rendernet.work/pub/v1'
  );
  
  it('should settle USDC payment', async () => {
    const result = await client.verifyAndSettle(mockPaymentHeader, {
      amount: 0.01,
      mint: DEVNET_USDC_MINT,
      recipient: TEST_WALLET
    });
    
    expect(result.status).toBe('pending');
    expect(result.id).toBeDefined();
  });
});

Troubleshooting

Common Errors

"Invalid API key"

  • Verify your API key is correct
  • Check you're using the right environment (live vs test keys)

"Payment verification failed"

  • Ensure payment header is base64-encoded
  • Check the signature matches the payment data
  • Verify sender has sufficient balance

"No healthy facilitator available"

  • Rare; indicates all payment facilitators are down
  • Retry after 30 seconds
  • Check status page

"Amount mismatch"

  • Payment amount doesn't match expected amount
  • Ensure you're using correct decimal places (USDC = 6, SOL = 9)

Debug Mode

Enable detailed logging:

const client = new AggClient(apiKey, apiBase, { debug: true });

This logs all API requests/responses to console.

Type Definitions

Full TypeScript definitions are included. Import types:

import type { 
  PaymentResult, 
  PaymentStatus, 
  Expected,
  WebhookPayload 
} from '@555x402/agg';

Changelog

v0.1.0 (2025-11-11)

  • Initial release
  • verifyAndSettle and getStatus methods
  • Webhook signature verification
  • TypeScript support

Contributing

Contributions welcome! Please read CONTRIBUTING.md first.

License

MIT © 555x402

Support

  • Documentation: https://docs.rendernet.work
  • API Reference: https://docs.rendernet.work/api/agg
  • Issues: https://github.com/Render-Network-OS/555x402-agg-sdk/issues
  • Discord: https://discord.gg/555x402
  • Email: [email protected]

Related Packages


Built with ❤️ for the Solana ecosystem