Transaktionsanfragen ermöglichen interaktive Zahlungsabläufe, indem sie Wallets erlauben, mit Ihrem Server zu kommunizieren, um beliebige Solana-Transaktionen zu erstellen. Dies eröffnet fortgeschrittene Anwendungsfälle wie NFT-Prägung, dynamische Preisgestaltung, mehrstufige DeFi-Transaktionen und benutzerdefinierte Geschäftslogik.
Überblick
Transaktionsanfragen folgen diesem URL-Format:
solana:<server-endpoint-url>
Das Wallet sendet zwei HTTP-Anfragen an Ihren Endpunkt:
- GET-Anfrage - Zahlungsinformationen abrufen (Label, Icon)
- POST-Anfrage - Die zu signierende Transaktion erhalten
Grundlegende Einrichtung
1. Abhängigkeiten installieren
pnpm add @solana/pay@beta @solana/kit @solana-program/system \@solana/kit-plugin-rpc @solana/kit-plugin-payer @solana/kit-plugin-instruction-plan
2. API-Endpunkt erstellen
Richten Sie einen API-Endpunkt ein, der sowohl GET- als auch POST-Anfragen verarbeitet:
// pages/api/pay.ts (Next.js) or similar endpointimport { 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 handlerasync function handleGet(req, res) {const label = "My Store";const icon = "https://mystore.com/icon.png";res.status(200).json({label,icon});}// POST request handlerasync 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 transactionconst 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 transactionconst 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. Zahlungs-URL erstellen
Generieren Sie die Transaktionsanfrage-URL:
import { encodeURL } from "@solana/pay";// Your API endpointconst apiUrl = "https://yoursite.com/api/pay";// Create transaction request URLconst url = encodeURL({link: new URL(apiUrl)});console.log(url.toString());// Output: solana:https://yoursite.com/api/pay
Wallet-Seite: Transaktionsanfragen abrufen
Verwenden Sie auf der Wallet-Seite fetchTransaction, um eine Transaktion von
einer Transaktionsanfrage-URL abzurufen und zu signieren:
import { createWalletClient } from "@solana/pay";const wallet = createWalletClient({rpcUrl: "https://api.mainnet-beta.solana.com",payer: myWalletSigner});// Parse the Solana Pay URLconst parsed = wallet.pay.parseURL(url);if ("link" in parsed) {// This is a transaction request — fetch the transaction from the serverconst transaction = await wallet.pay.fetchTransaction(myWalletSigner.address,parsed.link);// Sign and send the transaction...}
Fortgeschrittene Beispiele
SPL-Token-Zahlung
Erstellen Sie eine Transaktion für USDC-Zahlungen:
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 decimalsconst mint = await fetchMint(rpc, USDC_MINT);// Derive associated token accountsconst [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 decimalsconst 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});}
Dynamische Preisgestaltung mit Rabatten
Dynamische Preisgestaltung basierend auf Kundendaten implementieren:
async function handlePost(req, res) {const { account } = req.body;const buyer = address(account);// Check if customer holds discount NFTconst hasDiscountNFT = await checkForDiscountNFT(buyer);// Calculate dynamic pricelet price = 10; // $10 base priceif (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 });}
Integration der Auftragsverwaltung
Integration in Ihr bestehendes Auftragssystem:
import { address, AccountRole, createNoopSigner, lamports } from "@solana/kit";// Enhanced POST handler with order managementasync 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 detailsconst 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 statusawait db.orders.update(orderId, {status: "processing",walletAddress: account});res.status(200).json({transaction,message: `Order ${order.id} - ${order.items.length} items`});}
Zahlungsvalidierung
Abgeschlossene Transaktionen validieren:
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 };}
Fehlerbehandlung
Robuste Fehlerbehandlung implementieren:
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 Practices
Sicherheit
- Alle Eingabeparameter validieren
- Beträge ausschließlich serverseitig berechnen
- HTTPS für alle Endpunkte verwenden
- Rate Limiting implementieren
- Sensible Daten sicher speichern
Performance
- Häufig abgerufene Daten zwischenspeichern
- Connection Pooling verwenden
- Request-Timeouts implementieren
- Webhooks für Zahlungsbestätigungen in Betracht ziehen
Benutzererfahrung
- Klare Fehlermeldungen bereitstellen
- Transaktionsfortschritt anzeigen
- Netzwerkprobleme elegant behandeln
- Wiederholung von Transaktionen unterstützen
Is this page helpful?