Solana Integration Guide

This tutorial shows how to integrate T402 payments with Solana using SPL tokens.

Overview

Solana uses SPL tokens for USDT/USDC transfers. The T402 Solana implementation supports:

  • SPL Token transfers (USDT, USDC)
  • Ed25519 signatures
  • Recent blockhash based replay protection

Prerequisites

  • Node.js 18+ or Python 3.10+
  • Solana wallet with devnet SOL and tokens
  • Basic understanding of Solana concepts

Network Identifiers

NetworkCAIP-2 Identifier
Solana Mainnetsolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
Solana Devnetsolana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1
Solana Testnetsolana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z

Token Addresses

TokenNetworkMint Address
USDCMainnetEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
USDTMainnetEs9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
USDCDevnet4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU

Install Dependencies

npm install @t402/svm @t402/core @solana/web3.js @solana/spl-token

Client Setup

Create a client that can sign Solana payment authorizations.

import { t402Client } from '@t402/core/client';
import { ExactSvmScheme, toClientSvmSigner } from '@t402/svm';
import { Keypair, Connection } from '@solana/web3.js';
import bs58 from 'bs58';
 
// Create connection
const connection = new Connection('https://api.mainnet-beta.solana.com');
 
// Create keypair from secret key
const secretKey = bs58.decode('your_base58_private_key');
const keypair = Keypair.fromSecretKey(secretKey);
 
// Create Solana signer
const svmSigner = toClientSvmSigner({
  keypair,
  connection,
});
 
// Create t402 client
const client = new t402Client()
  .register('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', new ExactSvmScheme(svmSigner));

Server Setup

Configure your server to accept Solana payments.

import express from 'express';
import { paymentMiddleware } from '@t402/express';
import { t402ResourceServer, createFacilitatorClient } from '@t402/core/server';
import { registerExactSvmServerScheme } from '@t402/svm/exact/server';
 
const app = express();
 
// Create facilitator client
const facilitator = createFacilitatorClient({
  url: 'https://facilitator.t402.io',
});
 
// Create t402 server
const server = new t402ResourceServer(facilitator);
registerExactSvmServerScheme(server, {});
 
// Define routes with Solana payment
const routes = {
  '/api/premium/*': {
    accepts: {
      scheme: 'exact',
      network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
      payTo: 'YourSolanaAddress',
      price: '$0.01',
    },
    description: 'Premium API access',
  },
};
 
// Apply middleware
app.use(paymentMiddleware(routes, server));
 
app.get('/api/premium/data', (req, res) => {
  res.json({ data: 'Premium content' });
});
 
app.listen(3000);

Make Payment Requests

import { wrapFetchWithPayment } from '@t402/fetch';
 
const fetchWithPay = wrapFetchWithPayment(fetch, client);
 
// Automatic payment handling
const response = await fetchWithPay('https://api.example.com/api/premium/data', {
  method: 'GET',
});
 
const data = await response.json();
console.log(data);

Solana-Specific Concepts

SPL Token Transfers

Solana uses SPL tokens for USDT/USDC. The payment flow:

  1. Client creates and signs SPL token transfer instruction
  2. Transaction includes recent blockhash for validity
  3. Facilitator submits transaction to network
  4. SPL program processes the transfer

Associated Token Accounts

Each wallet needs an Associated Token Account (ATA) for each token:

import { getAssociatedTokenAddress } from '@solana/spl-token';
 
// Get ATA for USDC
const ata = await getAssociatedTokenAddress(
  new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), // USDC mint
  walletPublicKey,
);

The facilitator automatically creates ATAs if they don’t exist, but this adds a small fee.

Transaction Priority

For faster confirmations, add priority fees:

import { ComputeBudgetProgram } from '@solana/web3.js';
 
// Add priority fee instruction
const priorityFeeIx = ComputeBudgetProgram.setComputeUnitPrice({
  microLamports: 1000, // Priority fee in micro-lamports
});

Blockhash Validity

Transactions are valid for ~60 seconds:

// Get recent blockhash
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
 
// Transaction is valid until lastValidBlockHeight

Testing on Devnet

Get devnet SOL from the Solana Faucet.

  1. Configure for devnet:
const connection = new Connection('https://api.devnet.solana.com');
 
const routes = {
  '/api/test/*': {
    accepts: {
      scheme: 'exact',
      network: 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
      payTo: 'YourDevnetAddress',
      price: '$0.001',
    },
  },
};
  1. Use devnet wallet and tokens

  2. Verify transactions on explorer.solana.com

Phantom Wallet Integration

For browser-based applications:

// Check if Phantom is available
if ('solana' in window) {
  const phantom = window.solana;
 
  if (phantom.isPhantom) {
    // Connect to Phantom
    const response = await phantom.connect();
    const publicKey = response.publicKey;
 
    // Create signer from Phantom
    const signer = toClientSvmSigner({
      publicKey,
      signTransaction: (tx) => phantom.signTransaction(tx),
    });
 
    const client = new t402Client()
      .register('solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', new ExactSvmScheme(signer));
  }
}

Troubleshooting

Common Issues

IssueSolution
Blockhash expiredRetry with fresh blockhash
Insufficient SOLAdd SOL for transaction fees
Missing ATACreate ATA before transfer
Signature verification failedEnsure keypair matches public key

RPC Errors

CodeDescription
-32002Transaction simulation failed
-32003Transaction already processed
-32005Node unhealthy