Skip to content

Verification

API reference for verifying payments on the Solana blockchain.

Verifies a payment transaction on-chain.

import { verifyPayment, getUsdcMint } from "x402test";
import { PublicKey } from "@solana/web3.js";
const result = await verifyPayment(
signature,
new PublicKey(recipientAddress),
BigInt(expectedAmount),
getUsdcMint()
);
if (result.isValid) {
console.log("Payment verified!");
} else {
console.error("Verification failed:", result.invalidReason);
}

Parameters:

  • signature (string): Transaction signature to verify
  • expectedRecipient (PublicKey): Expected recipient address
  • expectedAmount (bigint): Expected amount in atomic units
  • expectedMint (PublicKey): Expected token mint (USDC)

Returns: Promise<VerificationResult>

interface VerificationResult {
isValid: boolean;
invalidReason: string | null;
txHash?: string;
amount?: string;
from?: string;
to?: string;
}

Properties:

  • isValid (boolean): Whether verification succeeded
  • invalidReason (string | null): Reason for failure (if any)
  • txHash (string, optional): Transaction hash
  • amount (string, optional): Amount transferred
  • from (string, optional): Sender address
  • to (string, optional): Recipient address

The verification process performs these checks:

if (isSignatureUsed(signature)) {
return {
isValid: false,
invalidReason: "Payment already processed",
};
}
const tx = await connection.getTransaction(signature);
if (!tx) {
return {
isValid: false,
invalidReason: "Transaction not found",
};
}
if (tx.meta?.err) {
return {
isValid: false,
invalidReason: `Transaction failed: ${JSON.stringify(tx.meta.err)}`,
};
}
const transfer = await findTokenTransfer(tx);
if (!transfer.found) {
return {
isValid: false,
invalidReason: "No token transfer instruction found",
};
}
if (transfer.destinationOwner !== expectedRecipient.toBase58()) {
return {
isValid: false,
invalidReason: `Wrong recipient: expected ${expectedRecipient}, got ${transfer.destinationOwner}`,
};
}
const transferAmount = BigInt(transfer.amount);
if (transferAmount < expectedAmount) {
return {
isValid: false,
invalidReason: `Insufficient amount: expected ${expectedAmount}, got ${transferAmount}`,
};
}
if (transfer.mint !== expectedMint.toBase58()) {
return {
isValid: false,
invalidReason: `Wrong token: expected ${expectedMint}, got ${transfer.mint}`,
};
}
import { verifyPayment, getUsdcMint } from "x402test";
import { PublicKey } from "@solana/web3.js";
async function verifyUserPayment(
signature: string,
recipientAddress: string,
expectedUsdcAmount: string
) {
// Convert USDC to atomic units
const atomicAmount = BigInt(parseFloat(expectedUsdcAmount) * 1e6);
// Verify payment
const result = await verifyPayment(
signature,
new PublicKey(recipientAddress),
atomicAmount,
getUsdcMint()
);
if (result.isValid) {
console.log("✓ Payment verified");
console.log(" From:", result.from);
console.log(" To:", result.to);
console.log(" Amount:", result.amount);
console.log(" TX:", result.txHash);
return true;
} else {
console.error("✗ Verification failed");
console.error(" Reason:", result.invalidReason);
return false;
}
}
// Usage
await verifyUserPayment("5XzT4qW3...", "FcxKSp7YxqYXdq...", "0.10");
try {
const result = await verifyPayment(signature, recipient, amount, mint);
if (!result.isValid) {
// Handle verification failure
console.error(result.invalidReason);
}
} catch (error) {
// Handle unexpected errors
console.error("Verification error:", error.message);
}

The x402 client automatically verifies payments:

import { x402 } from "x402test";
// Automatic verification
const response = await x402(url)
.withPayment("0.01")
.expectPaymentSettled() // This calls verifyPayment()
.execute();
// Payment is verified on-chain
console.log("Verified signature:", response.payment?.signature);
"Transaction not found or not confirmed"

Causes:

  • Transaction hasn’t confirmed yet
  • Invalid signature
  • Wrong network (devnet vs mainnet)
"Insufficient amount: expected 100000, got 50000"

Causes:

  • Client paid less than required
  • Wrong decimal conversion
"Wrong recipient: expected FcxK..., got EPjF..."

Causes:

  • Payment sent to wrong address
  • Configuration mismatch
"Payment already processed"

Causes:

  • Replay attack attempt
  • Accidental duplicate request
"Wrong token: expected EPjF... (USDC), got 4zMMC... (SOL)"

Causes:

  • Paid with wrong SPL token
  • Wrong mint address in config

Checks if a signature has been used.

import { isSignatureUsed } from "x402test";
if (isSignatureUsed(signature)) {
console.log("Signature already used");
}

Marks a signature as used.

import { markSignatureUsed } from "x402test";
markSignatureUsed(signature, endpoint, amount);

Gets information about a used signature.

import { getSignatureInfo } from "x402test";
const info = getSignatureInfo(signature);
if (info) {
console.log("Used at:", new Date(info.usedAt));
console.log("Endpoint:", info.endpoint);
console.log("Amount:", info.amount);
}

Gets statistics about all signatures.

import { getSignatureStats } from "x402test";
const stats = getSignatureStats();
console.log("Total signatures:", stats.total);
console.log("Signatures:", stats.signatures);

Clears all signature records (for testing).

import { resetSignatures } from "x402test";
resetSignatures();
console.log("All signatures cleared");