Richieste di Transazione

Le richieste di transazione abilitano flussi di pagamento interattivi consentendo ai wallet di comunicare con il tuo server per comporre qualsiasi transazione Solana. Questo sblocca casi d'uso avanzati come il minting di NFT, prezzi dinamici, transazioni DeFi multi-step e logica di business personalizzata.

Panoramica

Le richieste di transazione seguono questo formato URL:

solana:<server-endpoint-url>

Il wallet effettua due richieste HTTP al tuo endpoint:

  1. Richiesta GET - Recupera le informazioni di pagamento (etichetta, icona)
  2. Richiesta POST - Ottieni la transazione da firmare

Configurazione di Base

1. Installa le Dipendenze

pnpm add @solana/pay@beta @solana/kit @solana-program/system \
@solana/kit-plugin-rpc @solana/kit-plugin-payer @solana/kit-plugin-instruction-plan

2. Crea l'Endpoint API

Configura un endpoint API che gestisce sia le richieste GET che POST:

// pages/api/pay.ts (Next.js) or similar endpoint
import { address, createNoopSigner, lamports } from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
import { createMerchantClient } from "@solana/pay";
const MERCHANT_WALLET = address(process.env.MERCHANT_WALLET);
export default async function handler(req, res) {
if (req.method === "GET") {
return handleGet(req, res);
}
if (req.method === "POST") {
return handlePost(req, res);
}
return res.status(405).json({ error: "Method not allowed" });
}
// GET request handler
async function handleGet(req, res) {
const label = "My Store";
const icon = "https://mystore.com/icon.png";
res.status(200).json({
label,
icon
});
}
// POST request handler
async function handlePost(req, res) {
try {
const { account } = req.body;
if (!account) {
return res.status(400).json({ error: "Missing account parameter" });
}
const buyerAddress = address(account);
// Create the merchant client for building the transaction
const merchant = createMerchantClient({
rpcUrl: process.env.SOLANA_RPC_URL
});
// Build transfer instruction (createNoopSigner because the wallet signs later)
const instruction = getTransferSolInstruction({
source: createNoopSigner(buyerAddress),
destination: MERCHANT_WALLET,
amount: lamports(100_000_000n) // 0.1 SOL
});
// Build a base64-encoded transaction
const transaction = merchant.pay.buildTransaction(buyerAddress, [
instruction
]);
res.status(200).json({
transaction,
message: "Thanks for your purchase!"
});
} catch (error) {
console.error("Transaction creation error:", error);
res.status(500).json({ error: "Failed to create transaction" });
}
}

3. Crea l'URL di Pagamento

Genera l'URL della richiesta di transazione:

import { encodeURL } from "@solana/pay";
// Your API endpoint
const apiUrl = "https://yoursite.com/api/pay";
// Create transaction request URL
const url = encodeURL({
link: new URL(apiUrl)
});
console.log(url.toString());
// Output: solana:https://yoursite.com/api/pay

Lato Wallet: Recupero delle Richieste di Transazione

Sul lato wallet, utilizza fetchTransaction per recuperare e firmare una transazione da un URL di richiesta di transazione:

import { createWalletClient } from "@solana/pay";
const wallet = createWalletClient({
rpcUrl: "https://api.mainnet-beta.solana.com",
payer: myWalletSigner
});
// Parse the Solana Pay URL
const parsed = wallet.pay.parseURL(url);
if ("link" in parsed) {
// This is a transaction request — fetch the transaction from the server
const transaction = await wallet.pay.fetchTransaction(
myWalletSigner.address,
parsed.link
);
// Sign and send the transaction...
}

Esempi Avanzati

Pagamento con Token SPL

Crea una transazione per il pagamento in USDC:

import { address } from "@solana/kit";
import {
fetchMint,
findAssociatedTokenPda,
getTransferCheckedInstruction,
TOKEN_PROGRAM_ADDRESS
} from "@solana-program/token";
async function createTokenPaymentInstruction(rpc, buyer, amount) {
const USDC_MINT = address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
// Get mint info for decimals
const mint = await fetchMint(rpc, USDC_MINT);
// Derive associated token accounts
const [buyerATA] = await findAssociatedTokenPda({
owner: buyer,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
mint: USDC_MINT
});
const [merchantATA] = await findAssociatedTokenPda({
owner: MERCHANT_WALLET,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
mint: USDC_MINT
});
// Calculate token amount with decimals
const tokenAmount = BigInt(Math.round(amount * 10 ** mint.data.decimals));
return getTransferCheckedInstruction({
source: buyerATA,
mint: USDC_MINT,
destination: merchantATA,
authority: buyer,
amount: tokenAmount,
decimals: mint.data.decimals
});
}

Prezzi Dinamici con Sconti

Implementa prezzi dinamici basati sui dati del cliente:

async function handlePost(req, res) {
const { account } = req.body;
const buyer = address(account);
// Check if customer holds discount NFT
const hasDiscountNFT = await checkForDiscountNFT(buyer);
// Calculate dynamic price
let price = 10; // $10 base price
if (hasDiscountNFT) {
price = price * 0.8; // 20% discount
}
const merchant = createMerchantClient({
rpcUrl: process.env.SOLANA_RPC_URL
});
const instruction = await createTokenPaymentInstruction(
merchant.rpc,
buyer,
price
);
const transaction = merchant.pay.buildTransaction(buyer, [instruction]);
const message = hasDiscountNFT
? `20% discount applied! Total: $${price.toFixed(2)}`
: `Total: $${price.toFixed(2)}`;
res.status(200).json({ transaction, message });
}

Integrazione Gestione Ordini

Integra con il tuo sistema di ordini esistente:

import { address, AccountRole, createNoopSigner, lamports } from "@solana/kit";
// Enhanced POST handler with order management
async function handlePost(req, res) {
const { account } = req.body;
const { orderId } = req.query;
if (!orderId) {
return res.status(400).json({ error: "Missing order ID" });
}
// Get order details
const order = await db.orders.findById(orderId);
if (!order || order.status !== "pending") {
return res.status(400).json({ error: "Invalid order" });
}
const buyer = address(account);
// Create payment instruction (createNoopSigner because the wallet signs later)
const instruction = getTransferSolInstruction({
source: createNoopSigner(buyer),
destination: MERCHANT_WALLET,
amount: lamports(BigInt(Math.round(order.total * 1e9)))
});
// Add reference for tracking (accounts is readonly, so spread into a new instruction)
const withRef = {
...instruction,
accounts: [
...(instruction.accounts ?? []),
{
address: address(order.referenceKey),
role: AccountRole.READONLY
}
]
};
const merchant = createMerchantClient({ rpcUrl: process.env.SOLANA_RPC_URL });
const transaction = merchant.pay.buildTransaction(buyer, [withRef]);
// Update order status
await db.orders.update(orderId, {
status: "processing",
walletAddress: account
});
res.status(200).json({
transaction,
message: `Order ${order.id} - ${order.items.length} items`
});
}

Validazione Pagamenti

Valida le transazioni completate:

import { address } from "@solana/kit";
import { createMerchantClient } from "@solana/pay";
const merchant = createMerchantClient({
rpcUrl: "https://api.mainnet-beta.solana.com"
});
async function validatePayment(reference, recipient, amount) {
try {
const found = await merchant.pay.findReference(reference);
// findReference only locates a signature mentioning the reference address.
// validateTransfer checks recipient, amount, and token match.
await merchant.pay.validateTransfer(found.signature, {
recipient,
amount,
reference
});
return {
success: true,
signature: found.signature
};
} catch (error) {
console.error("Payment validation error:", error);
}
return { success: false };
}

Gestione degli Errori

Implementa una gestione robusta degli errori:

async function handlePost(req, res) {
try {
const { account } = req.body;
if (!account) {
return res.status(400).json({
error: "Missing account parameter",
code: "MISSING_ACCOUNT"
});
}
let buyer;
try {
buyer = address(account);
} catch {
return res.status(400).json({
error: "Invalid account address",
code: "INVALID_ACCOUNT"
});
}
const instruction = getTransferSolInstruction({
source: createNoopSigner(buyer),
destination: MERCHANT_WALLET,
amount: lamports(100_000_000n)
});
const merchant = createMerchantClient({
rpcUrl: process.env.SOLANA_RPC_URL
});
const transaction = merchant.pay.buildTransaction(buyer, [instruction]);
res.status(200).json({
transaction,
message: "Payment ready"
});
} catch (error) {
console.error("Transaction request error:", error);
res.status(500).json({
error: "Internal server error",
code: "INTERNAL_ERROR"
});
}
}

Best Practice

Sicurezza

  • Valida tutti i parametri di input
  • Calcola gli importi solo lato server
  • Utilizza HTTPS per tutti gli endpoint
  • Implementa il rate limiting
  • Archivia i dati sensibili in modo sicuro

Performance

  • Memorizza in cache i dati utilizzati frequentemente
  • Utilizza il connection pooling
  • Implementa timeout per le richieste
  • Considera l'utilizzo di webhook per la conferma dei pagamenti

Esperienza Utente

  • Fornisci messaggi di errore chiari
  • Mostra l'avanzamento della transazione
  • Gestisci i problemi di rete in modo appropriato
  • Supporta i tentativi di ripetizione delle transazioni

Is this page helpful?

Gestito da

© 2026 Solana Foundation.
Tutti i diritti riservati.
Resta connesso