Demande de transaction
Les demandes de transaction libèrent toute la puissance de Solana Pay en permettant des transactions dynamiques, composées par le serveur, qui peuvent gérer tout type d'opération Solana.
Contrairement aux demandes de transfert qui contiennent toutes les informations de paiement dans l'URL, les demandes de transaction utilisent des points de terminaison interactifs pour créer des transactions personnalisées basées sur des données en temps réel et une logique métier.
Cette approche transforme Solana Pay d'un simple système de paiement en une plateforme commerciale complète capable de gérer des scénarios commerciaux complexes, des prix dynamiques et des flux de transactions sophistiqués.
Comment fonctionnent les demandes de transaction
Les demandes de transaction suivent un format d'URL simple qui pointe vers le point de terminaison de votre serveur :
solana:<link>La valeur link doit être une URL vers votre point de terminaison API qui gère à la fois les requêtes GET et POST. Lorsqu'un utilisateur scanne un code QR de demande de transaction, son portefeuille initie un processus en quatre étapes :
Requête GET initiale : Récupère les informations d'affichage comme le nom de votre entreprise et le logo
Confirmation de l'utilisateur : Le portefeuille montre les informations de l'entreprise à l'utilisateur
Requête POST : Envoie la clé publique de l'utilisateur à votre point de terminaison
Réponse de transaction : Votre serveur construit une transaction personnalisée et la renvoie sous forme de chaîne encodée en base64
Le portefeuille présente ensuite cette transaction à l'utilisateur pour approbation et signature.
solana:https://myapi.com/pay?amount=100&product=premium&reference=abc123Construction du point de terminaison API
La création d'un point de terminaison de demande de transaction nécessite de gérer à la fois les requêtes GET et POST à la même URL. Voici la structure utilisant Next.js App Router :
import { NextRequest, NextResponse } from 'next/server';
// CORS headers for wallet compatibility
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
export async function OPTIONS() {
return new NextResponse(null, { status: 200, headers: corsHeaders });
}
export async function GET() {
// Implementation details below...
}
export async function POST(request: NextRequest) {
// Implementation details below...
}Gestionnaire de requêtes GET
La requête GET fournit des informations d'affichage qui aident les utilisateurs à comprendre avec quoi ils interagissent :
export async function GET() {
return NextResponse.json({
label: "Coffee Shop Demo",
icon: "https://solana.com/src/img/branding/solanaLogoMark.svg",
}, { headers: corsHeaders });
}Format de réponse :
{
"label": "Coffee Shop Demo",
"icon": "https://solana.com/src/img/branding/solanaLogoMark.svg"
}Ces informations aident l'utilisateur à comprendre avec quoi il s'apprête à interagir avant de procéder à la composition réelle de la transaction.
Gestionnaire de requêtes POST
La requête POST est là où les demandes de transaction brillent vraiment. Votre endpoint reçoit la clé publique de l'utilisateur et construit une transaction entièrement personnalisée :
export async function POST(request: NextRequest) {
// Parse user's public key from request body
const body = await request.json();
const { account } = body;
// Connect to Solana network
const connection = new Connection(clusterApiUrl("devnet"));
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
// Create transaction with user as fee payer
const transaction = new Transaction({
feePayer: new PublicKey(account),
blockhash: blockhash,
lastValidBlockHeight: lastValidBlockHeight,
});
// ========================================
// ADD YOUR CUSTOM INSTRUCTIONS HERE
// ========================================
// This is where you build your transaction logic.
// You can add any combination of instructions:
// Example 1: Simple SOL transfer
// const transferInstruction = SystemProgram.transfer({
// fromPubkey: new PublicKey(account),
// toPubkey: new PublicKey("YOUR_MERCHANT_WALLET"),
// lamports: LAMPORTS_PER_SOL * 0.01, // 0.01 SOL
// });
// transaction.add(transferInstruction);
// Serialize the transaction for the wallet
const serializedTransaction = transaction.serialize({
requireAllSignatures: false,
verifySignatures: false,
});
return NextResponse.json({
transaction: serializedTransaction.toString('base64'),
message: "Transaction created successfully", // Customize this message
}, { headers: corsHeaders });
}Capacités avancées
Transactions conditionnelles
Les demandes de transaction permettent un contrôle d'accès sophistiqué en vérifiant les conditions avant de construire les transactions. Puisque vous contrôlez l'endpoint, vous pouvez vérifier la propriété d'un NFT, l'appartenance à une liste blanche ou tout autre critère :
// Check NFT ownership before building transaction
const nfts = await metaplex.nfts().findAllByOwner({ owner: account }).run();
const hasRequiredNFT = nfts.some(nft =>
nft.collection?.address.toString() === requiredCollection
);
if (!hasRequiredNFT) {
return response.status(403).json({
error: "Access denied: Required NFT not found"
});
}
// Build transaction only for verified usersSignature partielle pour une sécurité renforcée
Pour les transactions nécessitant l'approbation d'une paire de clés administrateur ou une authentification multi-parties, Solana Pay prend en charge la signature partielle. Votre serveur peut ajouter sa signature avant d'envoyer à l'utilisateur :
const transaction = new Transaction({
feePayer: account,
blockhash,
lastValidBlockHeight,
});
// Add your instructions requiring admin signature
transaction.add(customInstruction);
// Partially sign with your admin keypair
transaction.partialSign(adminKeypair);
// Send to user for final signature
const serializedTransaction = transaction.serialize({
requireAllSignatures: false,
});Exemple
Comme exemple complet, vous pouvez utiliser l'Exemple de Minter NFT Solana de la Fondation Solana