Verification
API reference for verifying payments on the Solana blockchain.
verifyPayment()
Section titled “verifyPayment()”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 verifyexpectedRecipient(PublicKey): Expected recipient addressexpectedAmount(bigint): Expected amount in atomic unitsexpectedMint(PublicKey): Expected token mint (USDC)
Returns: Promise<VerificationResult>
VerificationResult
Section titled “VerificationResult”interface VerificationResult { isValid: boolean; invalidReason: string | null; txHash?: string; amount?: string; from?: string; to?: string;}Properties:
isValid(boolean): Whether verification succeededinvalidReason(string | null): Reason for failure (if any)txHash(string, optional): Transaction hashamount(string, optional): Amount transferredfrom(string, optional): Sender addressto(string, optional): Recipient address
Verification Checks
Section titled “Verification Checks”The verification process performs these checks:
1. Replay Protection
Section titled “1. Replay Protection”if (isSignatureUsed(signature)) { return { isValid: false, invalidReason: "Payment already processed", };}2. Transaction Existence
Section titled “2. Transaction Existence”const tx = await connection.getTransaction(signature);if (!tx) { return { isValid: false, invalidReason: "Transaction not found", };}3. Transaction Success
Section titled “3. Transaction Success”if (tx.meta?.err) { return { isValid: false, invalidReason: `Transaction failed: ${JSON.stringify(tx.meta.err)}`, };}4. Token Transfer
Section titled “4. Token Transfer”const transfer = await findTokenTransfer(tx);if (!transfer.found) { return { isValid: false, invalidReason: "No token transfer instruction found", };}5. Recipient Verification
Section titled “5. Recipient Verification”if (transfer.destinationOwner !== expectedRecipient.toBase58()) { return { isValid: false, invalidReason: `Wrong recipient: expected ${expectedRecipient}, got ${transfer.destinationOwner}`, };}6. Amount Verification
Section titled “6. Amount Verification”const transferAmount = BigInt(transfer.amount);if (transferAmount < expectedAmount) { return { isValid: false, invalidReason: `Insufficient amount: expected ${expectedAmount}, got ${transferAmount}`, };}7. Token Verification
Section titled “7. Token Verification”if (transfer.mint !== expectedMint.toBase58()) { return { isValid: false, invalidReason: `Wrong token: expected ${expectedMint}, got ${transfer.mint}`, };}Complete Example
Section titled “Complete Example”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; }}
// Usageawait verifyUserPayment("5XzT4qW3...", "FcxKSp7YxqYXdq...", "0.10");Error Handling
Section titled “Error Handling”Try-Catch
Section titled “Try-Catch”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);}Using with x402 Client
Section titled “Using with x402 Client”The x402 client automatically verifies payments:
import { x402 } from "x402test";
// Automatic verificationconst response = await x402(url) .withPayment("0.01") .expectPaymentSettled() // This calls verifyPayment() .execute();
// Payment is verified on-chainconsole.log("Verified signature:", response.payment?.signature);Common Failure Reasons
Section titled “Common Failure Reasons”Transaction Not Found
Section titled “Transaction Not Found”"Transaction not found or not confirmed"Causes:
- Transaction hasn’t confirmed yet
- Invalid signature
- Wrong network (devnet vs mainnet)
Insufficient Amount
Section titled “Insufficient Amount”"Insufficient amount: expected 100000, got 50000"Causes:
- Client paid less than required
- Wrong decimal conversion
Wrong Recipient
Section titled “Wrong Recipient”"Wrong recipient: expected FcxK..., got EPjF..."Causes:
- Payment sent to wrong address
- Configuration mismatch
Payment Already Processed
Section titled “Payment Already Processed”"Payment already processed"Causes:
- Replay attack attempt
- Accidental duplicate request
Wrong Token
Section titled “Wrong Token”"Wrong token: expected EPjF... (USDC), got 4zMMC... (SOL)"Causes:
- Paid with wrong SPL token
- Wrong mint address in config
Replay Protection Functions
Section titled “Replay Protection Functions”isSignatureUsed()
Section titled “isSignatureUsed()”Checks if a signature has been used.
import { isSignatureUsed } from "x402test";
if (isSignatureUsed(signature)) { console.log("Signature already used");}markSignatureUsed()
Section titled “markSignatureUsed()”Marks a signature as used.
import { markSignatureUsed } from "x402test";
markSignatureUsed(signature, endpoint, amount);getSignatureInfo()
Section titled “getSignatureInfo()”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);}getSignatureStats()
Section titled “getSignatureStats()”Gets statistics about all signatures.
import { getSignatureStats } from "x402test";
const stats = getSignatureStats();console.log("Total signatures:", stats.total);console.log("Signatures:", stats.signatures);resetSignatures()
Section titled “resetSignatures()”Clears all signature records (for testing).
import { resetSignatures } from "x402test";
resetSignatures();console.log("All signatures cleared");Next Steps
Section titled “Next Steps”- Wallets - Wallet management
- Replay Protection - Deep dive
- Examples - Complete examples