Skip to main content
This example demonstrates how to make x402 payments using native SOL (Solana’s lamports) instead of SPL tokens like USDC. This is useful when you want to accept SOL directly without requiring token transfers. Source: GitHub › scripts/solana-example/sol-payment.ts

SOL vs USDC Payments

Use SOL when:
  • You want to accept Solana’s native currency
  • You’re building applications that work with SOL directly
  • You want to simplify payment flows (no token accounts needed)
  • Your use case doesn’t require stablecoin payments
Use USDC when:
  • You need stable value (not affected by SOL price volatility)
  • You’re building payment systems that require price stability
  • You’re integrating with traditional payment systems

Full Example

import "dotenv/config";
import { logResponse } from "../logger";
import { Keypair } from "@solana/web3.js";
import { createLocalWallet } from "@faremeter/wallet-solana";
import { createPaymentHandler } from "@faremeter/x-solana-settlement";
import { wrap as wrapFetch } from "@faremeter/fetch";
import fs from "fs";

const { PAYER_KEYPAIR_PATH } = process.env;

if (!PAYER_KEYPAIR_PATH) {
  throw new Error("PAYER_KEYPAIR_PATH must be set in your environment");
}

const keypair = Keypair.fromSecretKey(
  Uint8Array.from(JSON.parse(fs.readFileSync(PAYER_KEYPAIR_PATH, "utf-8"))),
);

const wallet = await createLocalWallet("devnet", keypair);

const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(wallet)],
});

const req = await fetchWithPayer("http://127.0.0.1:3000/protected");
await logResponse(req);

Step-by-Step Breakdown

1. Load Keypair

const keypair = Keypair.fromSecretKey(
  Uint8Array.from(JSON.parse(fs.readFileSync(PAYER_KEYPAIR_PATH, "utf-8"))),
);
Load your Solana keypair from a JSON file. This keypair controls the SOL you’ll use for payments.

2. Create Wallet

const wallet = await createLocalWallet("devnet", keypair);
Create a wallet interface from your keypair. This wallet will sign transactions that transfer SOL.

3. Create Payment Handler (No Mint)

const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(wallet)],
});
Key difference: Notice that createPaymentHandler is called without a mint parameter. When no mint is provided, the handler uses native SOL for payments.

4. Make Payment

const req = await fetchWithPayer("http://127.0.0.1:3000/protected");
The payment flow works identically to token payments, but transfers SOL (lamports) instead.

SOL vs Token Payments

import { createLocalWallet } from "@faremeter/wallet-solana";
import { createPaymentHandler } from "@faremeter/x-solana-settlement";

const wallet = await createLocalWallet("devnet", keypair);

// No mint specified = native SOL
const fetchWithPayer = wrapFetch(fetch, {
  handlers: [createPaymentHandler(wallet)],
});
Key Differences:
  • SOL: No token accounts needed, simpler setup, but price volatility
  • USDC: Requires token accounts, stable value, but more complex setup

SOL Payment Details

When paying with SOL:
  • Transactions transfer lamports (1 SOL = 1,000,000,000 lamports)
  • No associated token accounts needed
  • Uses SystemProgram.transfer instruction
  • Payment amounts are specified in lamports

Environment Variables

  • PAYER_KEYPAIR_PATH: Path to your Solana keypair JSON file

Funding Your Wallet

Make sure your wallet has sufficient SOL for:
  • Payment amount (specified by the merchant)
  • Transaction fees (~0.000005 SOL per transaction)
You can get devnet SOL from:
solana airdrop 1 <your-wallet-address> --url devnet