Solana payment aggregation SDK with intelligent facilitator routing and instant USDC settlement
npm install 555x402-agg> Solana payment aggregation SDK with intelligent facilitator routing and instant USDC settlement


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.
- ✅ 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
``bash`
npm install @555x402/aggor
pnpm add @555x402/aggor
yarn add @555x402/agg
`typescript
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);
`
`typescript
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);
`
`typescript
// 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 }
);
}
}
`
#### Constructor
`typescript`
new AggClient(apiKey: string, apiBase?: string)
Parameters:
- apiKey (string, required): Your 555x402 API key from the dashboardapiBase
- (string, optional): API base URL. Defaults to https://agg.rendernet.work/pub/v1
Example:
`typescript
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
Verifies a payment header and settles the transaction through the optimal facilitator.
Parameters:
- paymentHeaderB64 (string): Base64-encoded payment header from X-Payment HTTP headerexpected
- (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
`typescript`
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 signatureInsufficientAmountError
- - Payment amount less than expectedNetworkError
- - API unreachable or timeout
Example:
`typescriptPayment ${result.id} settled via ${result.facilitator}
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();Tx: ${result.txSignature}
console.log();`
} catch (error) {
if (error instanceof InsufficientAmountError) {
console.error('Payment too low:', error.message);
}
}
##### getStatus(paymentId): Promise
Retrieves the current status of a payment.
Parameters:
- paymentId (string): Payment order ID from verifyAndSettle response
Returns: Promise
`typescript`
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:
`typescript
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...');
}
#### 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 headersignature
- (string): Value from X-555-Signature header (format: sha256=)secret
- (string): Your webhook secret
Returns: boolean - true if signature valid, false otherwise
Example (Express):
`typescript
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:
`json`
{
"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..."
}
}
`typescript`
async function waitForSettlement(paymentId: string, maxWaitMs = 30000): Promise
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');
}
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.
`typescript
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 | Mint Address | Decimals |
|-------|--------------|----------|
| USDC (mainnet) | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v | 6 |4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU
| USDC (devnet) | | 6 |So11111111111111111111111111111111111111112
| SOL (wrapped) | | 9 |
USDC uses 6 decimals:
- $1.00 = 1000000 micro-units10000
- $0.01 = micro-units
Helper (built-in):
`typescript
import { usdToMicroUsdc, microUsdcToUsd } from '@555x402/agg/utils';
const microUnits = usdToMicroUsdc(2.50); // 2500000
const usd = microUsdcToUsd(2500000); // 2.5
`
`typescript
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' });
}
});
`typescript
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...);Payment ${payload.data.id} confirmed!
break;
case 'agg.payment.settled':
console.log();Tx: ${payload.data.txSignature}
console.log();Payment ${payload.data.id} failed
// 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();`
// Handle failure (refund, notify user, etc.)
break;
}
res.sendStatus(200);
}
);
`typescriptPayment ${result.id} initiated...
async function processPaymentWithPolling(paymentHeader: string) {
// Initiate settlement
const result = await agg.verifyAndSettle(paymentHeader, {
amount: 1.00,
mint: USDC_MINT,
recipient: TREASURY
});
console.log();Confirmed! Tx: ${status.txSignature}
// 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();`
return status;
}
if (status.status === 'failed') {
throw new Error('Payment failed');
}
}
throw new Error('Timeout waiting for confirmation');
}
`typescript`
// 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
});
`bashRequired
AGG_API_KEY=pk_live_your_api_key_here
$3
Get your API key from the 555x402 Dashboard.
Key types:
-
pk_live_* - Production keys
- pk_test_* - Devnet/testnet keys$3
- Default: 20 requests/second per API key
- Burst: 40 requests
- Contact support for higher limits
Webhook Configuration
$3
1. Go to Dashboard → Settings → Webhooks
2. Add your endpoint URL
3. Copy the webhook secret
4. Test with the webhook tester
$3
Always verify signatures:
`typescript
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$3
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
$3
`bash
npm test
`$3
Set up a test recipient wallet and API key:
`typescript
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
$3
"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)
$3
Enable detailed logging:
`typescript
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:
`typescript
import type {
PaymentResult,
PaymentStatus,
Expected,
WebhookPayload
} from '@555x402/agg';
`Changelog
$3
- Initial release
- verifyAndSettle and getStatus` methodsContributions welcome! Please read CONTRIBUTING.md first.
MIT © 555x402
- 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: support@rendernet.work
- @555x402/hyperlink - Payment link generation
- @vap/sdk - Verifiable attention protocol
- @555x402/solana402-express - Express middleware
---
Built with ❤️ for the Solana ecosystem