Dans le monde d'aujourd'hui, les reçus sont essentiels pour valider les transactions et conserver la preuve des achats. Qu'il s'agisse d'une grande banque ou d'un petit magasin de bord de route, les reçus aident les entreprises et les particuliers à s'organiser et à suivre leurs dépenses. Mais voilà le problème : la plupart ne fournissent pas de reçus et s'appuient sur des explorateurs pour vérifier les détails des transactions. Et si vous n'aviez pas à vous y fier ? Imaginez à quel point il serait plus pratique pour vos utilisateurs de générer des reçus directement, sans avoir à vérifier leurs portefeuilles. des dApps Si vous créez une dApp basée sur le paiement sur Rootstock, cet article vous montrera comment créer un générateur de reçus simple mais efficace à l'aide de l'API Rootstock et d'une seule méthode RPC (Remote Procedure Call). Cette approche facilite le processus et garantit que vos enregistrements de transactions sont précis et faciles d'accès. Apprenons les étapes et les outils nécessaires pour créer une expérience de génération de reçus fluide. Prérequis Vous devez avoir un nœud installé sur votre appareil connaissances en Javascript Framework Js installé de votre choix Éditeur de code, par exemple VScode J'utiliserai React Typescript et TailwindCSS pour le style Outils et technologies Clé API de Rootstock : pour interagir avec RPC Web3js : pour générer un QR code que les utilisateurs peuvent scanner et obtenir leur reçu QRCode React : pour générer le reçu en PDF Jspdf Installer, importer les packages et créer le composant fonctionnel Installez toutes les dépendances à l’aide de cette commande : npm i web3js jspdf qrcode.react Créez un nouveau fichier ou utilisez App.jsx Importez les packages dans le fichier avec les éléments suivants : import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react"; Initialiser le composant fonctionnel const TransactionReceipt = () => { /......./ } export default TransactionReceipt; Gestion d'état et installation Web3 L'extrait de code ici gérera l'état et la fonctionnalité de récupération et d'affichage des détails de transaction à l'aide du hook useState, de Web3js, de Rootstock RPC et de l'API. const [transactionId, setTransactionId] = useState(""); interface TransactionDetails { transactionHash: string; from: string; to: string; cumulativeGasUsed: number; blockNumber: number; contractAddress?: string; } const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); const [error, setError] = useState(""); const web3 = new Web3( `https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}` ); : Gestion de l'État : Cette ligne initialise une variable d'état . Cette variable d'état sera responsable du hachage de transaction saisi que d'autres variables et fonctions exploiteront pour générer le reçu. const [transactionId, setTransactionId] = useState(""); transactionId : Cette ligne initialise une variable d'état avec une valeur et fournit une fonction pour mettre à jour sa valeur. L'état peut contenir soit un objet , soit . const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); transactionDetails null setTransactionDetails TransactionDetails null : Cette ligne initialise une variable d'état avec une chaîne vide et fournit une fonction pour mettre à jour sa valeur. const [error, setError] = useState(""); error setError : Interface TypeScript : cela définit une interface TypeScript pour la structure de l'objet de détails de transaction. Il comprend des propriétés telles que , , , , et une facultative. interface TransactionDetails transactionHash from to cumulativeGasUsed blockNumber contractAddress : Initialisation Web3 : Cette ligne initialise Web3js, en se connectant au point de terminaison RPC du réseau de test Rootstock. L'URL du point de terminaison inclut une clé API qui est récupérée à partir des variables d'environnement à l'aide de . const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}); import.meta.env.VITE_API_KEY Fonction permettant de récupérer les détails de la transaction Le code ici récupérera les détails de la transaction à l'aide d'une fonction asynchrone de Rootstock avec la méthode web3.js. const fetchTransactionDetails = async () => { try { setError(""); setTransactionDetails(null); const receipt = await web3.eth.getTransactionReceipt(transactionId); if (!receipt) { throw new Error("Transaction not found!"); } setTransactionDetails(receipt); } catch (err) { if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } } }; : Configuration de la gestion des erreurs : Cette structure est utilisée pour gérer les erreurs qui pourraient survenir lors de l'exécution de la fonction. try { ... } catch (err) { ... } : Réinitialiser l'état setError("");: Ceci efface tous les messages d'erreur précédents en définissant l'état sur une chaîne vide. error setTransactionDetails(null);: Cela efface tous les détails de transaction précédents en définissant l'état sur . transactionDetails null : Récupérer le reçu de transaction ;: Cette ligne utilise la méthode web3js pour récupérer le reçu de transaction pour le transactionId saisi. const receipt = await web3.eth.getTransactionReceipt(transactionId) : Vérification de réception : Si le reçu n'est pas trouvé (c'est-à-dire que le reçu est ou ), une erreur est générée avec le message "Transaction not found!". if (!receipt) { throw new Error("Transaction not found!"); } null undefined : Définir les détails de la transaction : Si le reçu est trouvé, il met à jour l'état avec le reçu récupéré. setTransactionDetails(receipt) transactionDetails : Gestion des erreurs : Ce bloc intercepte toutes les erreurs qui se produisent pendant l'exécution du bloc . catch (err) { ... } try : Si l'erreur détectée est une instance de la classe Error, elle définit l'état sur le message d'erreur. Sinon, elle définit l'état sur un message d'erreur générique "Une erreur inconnue s'est produite". if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } error error Fonctions pour générer un reçu PDF Ici, le package Jspdf sera utilisé pour générer le PDF contenant les détails de la transaction. const generatePDF = () => { if (!transactionDetails) return; const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress, } = transactionDetails; const pdf = new jsPDF(); pdf.setFontSize(16); pdf.text("Transaction Receipt", 10, 10); pdf.setFontSize(12); pdf.text(`Transaction Hash: ${transactionHash}`, 10, 20); pdf.text(`From: ${from}`, 10, 30); pdf.text(`Contract Address: ${contractAddress}`, 10, 40); pdf.text(`To: ${to}`, 10, 40); pdf.text(`Cumulative Gas Used: ${cumulativeGasUsed}`, 10, 50); pdf.text(`Block Number: ${blockNumber}`, 10, 60); pdf.save("Transaction_Receipt.pdf"); }; : Vérifiez les détails de la transaction : Ceci vérifie si est ou . Si c'est le cas, la fonction retourne plus tôt que prévu et ne fait rien. if (!transactionDetails) return; transactionDetails null undefined : Détails de la transaction de déstructuration : Cela déstructure l'objet pour extraire les propriétés individuelles pour un accès plus facile. const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails; transactionDetails : Créer un document PDF : Ceci crée une nouvelle instance de la classe jsPDF, qui représente un document PDF. const pdf = new jsPDF() : Définir la taille de la police et ajouter un titre : Ceci définit la taille de police du titre sur 16. pdf.setFontSize(16) : Ceci ajoute le titre "Reçu de transaction" aux coordonnées (10, 10) dans le document PDF. pdf.text("Transaction Receipt", 10, 10); : Ajouter les détails de la transaction au PDF : Ceci définit la taille de la police à 12 pour le reste du texte. pdf.setFontSize(12); : ${transactionHash} : Ceci ajoute le hachage de transaction aux coordonnées (10, 20). pdf.text(Transaction Hash , 10, 20); : Ceci ajoute l'adresse de l'expéditeur aux coordonnées (10, 30). pdf.text(From: ${from}, 10, 30); : Ceci ajoute l'adresse du contrat aux coordonnées (10, 40). Remarque : cette ligne doit être corrigée pour éviter tout chevauchement de texte. pdf.text(Contract Address: ${contractAddress}, 10, 40); : Ceci ajoute l'adresse du destinataire aux coordonnées (10, 50). pdf.text(To: ${to}, 10, 50); pdf.text(Gaz cumulé utilisé : ${cumulativeGasUsed} : Ceci ajoute le gaz cumulé utilisé aux coordonnées (10, 60). , 10, 60); : Ceci ajoute le numéro de bloc aux coordonnées (10, 70). pdf.text(Block Number: ${blockNumber}, 10, 70); : Enregistrer le document PDF pdf.save("Transaction_Receipt.pdf");: Cela enregistrera le document PDF avec le nom de fichier "Transaction_Receipt.pdf". L'interface utilisateur Ici, vous restituerez ces composants fonctionnels sous forme d'interface utilisateur aux utilisateurs. Ce code a déjà inclus le style à l'aide de Tailwindcss return ( <div className="p-8 font-sans bg-gray-100 min-h-screen"> <div className="max-w-3xl m-auto bg-white p-6 rounded-lg shadow-lg"> <h1 className="text-3xl font-bold mb-6 text-center text-blue-600"> Transaction Receipt Generator </h1> <div className="mb-6"> <div className="flex"> <input type="text" id="transactionId" value={transactionId} onChange={(e) => setTransactionId(e.target.value)} placeholder="Enter transaction hash" className="border p-2 w-full rounded-l-lg" /> <button onClick={fetchTransactionDetails} className="p-2 bg-blue-500 text-white rounded-r-lg" > Fetch Details </button> </div> </div> {error && ( <p className="text-red-500 mt-4 text-center">Error: {error}</p> )} {transactionDetails && ( <div className="mt-6 flex flex-row gap-8"> <div className="w-2/3"> <h2 className="text-2xl font-semibold mb-4 text-center"> Transaction Details </h2> <div className="bg-gray-50 p-4 rounded-lg shadow-inner w-[460px]"> <p> <strong>Transaction Hash:</strong>{" "} {`${transactionDetails.transactionHash.slice( 0, 6 )}...${transactionDetails.transactionHash.slice(-6)}`} </p> <p> <strong>From:</strong> {transactionDetails.from} </p> <p> <strong>Contract Address:</strong>{" "} {transactionDetails.contractAddress} </p> <p> <strong>To:</strong> {transactionDetails.to} </p> <p> <strong>Cumulative Gas Used:</strong>{" "} {transactionDetails.cumulativeGasUsed.toString()} </p> <p> <strong>Block Number:</strong>{" "} {transactionDetails.blockNumber.toString()} </p> </div> <button onClick={generatePDF} className="mt-6 w-full p-3 bg-green-500 text-white rounded-lg" > Download PDF Receipt </button> </div> <div className="w-1/2 text-center"> <h3 className="text-xl font-semibold mb-4">QR Code</h3> <QRCodeSVG value={`Transaction Hash: ${ transactionDetails.transactionHash }, From: ${transactionDetails.from}, To: ${transactionDetails.to}, Contract Address: ${transactionDetails.contractAddress}, Cumulative Gas Used: ${transactionDetails.cumulativeGasUsed.toString()}, Block Number: ${transactionDetails.blockNumber.toString()}`} size={200} className="mx-auto" /> </div> </div> )} </div> </div> Pour le générateur de code QR, la bibliothèque qrcode.react a été utilisée et les détails de la transaction ont été cryptés dans le code QR SVG. Base de code finale et sortie Si vous suivez les étapes, votre base de code devrait ressembler à ceci : import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react"; const TransactionReceipt = () => { const [transactionId, setTransactionId] = useState(""); interface TransactionDetails { transactionHash: string; from: string; to: string; cumulativeGasUsed: number; blockNumber: number; contractAddress?: string; } const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); const [error, setError] = useState(""); const web3 = new Web3( `https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}` ); const fetchTransactionDetails = async () => { try { setError(""); setTransactionDetails(null); const receipt = await web3.eth.getTransactionReceipt(transactionId); if (!receipt) { throw new Error("Transaction not found!"); } setTransactionDetails(receipt); } catch (err) { if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } } }; const generatePDF = () => { if (!transactionDetails) return; const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress, } = transactionDetails; const pdf = new jsPDF(); pdf.setFontSize(16); pdf.text("Transaction Receipt", 10, 10); pdf.setFontSize(12); pdf.text(`Transaction Hash: ${transactionHash}`, 10, 20); pdf.text(`From: ${from}`, 10, 30); pdf.text(`Contract Address: ${contractAddress}`, 10, 40); pdf.text(`To: ${to}`, 10, 40); pdf.text(`Cumulative Gas Used: ${cumulativeGasUsed}`, 10, 50); pdf.text(`Block Number: ${blockNumber}`, 10, 60); pdf.save("Transaction_Receipt.pdf"); }; return ( <div className="p-8 font-sans bg-gray-100 min-h-screen"> <div className="max-w-3xl m-auto bg-white p-6 rounded-lg shadow-lg"> <h1 className="text-3xl font-bold mb-6 text-center text-blue-600"> Transaction Receipt Generator </h1> <div className="mb-6"> <div className="flex"> <input type="text" id="transactionId" value={transactionId} onChange={(e) => setTransactionId(e.target.value)} placeholder="Enter transaction hash" className="border p-2 w-full rounded-l-lg" /> <button onClick={fetchTransactionDetails} className="p-2 bg-blue-500 text-white rounded-r-lg" > Fetch Details </button> </div> </div> {error && ( <p className="text-red-500 mt-4 text-center">Error: {error}</p> )} {transactionDetails && ( <div className="mt-6 flex flex-row gap-8"> <div className="w-2/3"> <h2 className="text-2xl font-semibold mb-4 text-center"> Transaction Details </h2> <div className="bg-gray-50 p-4 rounded-lg shadow-inner w-[460px]"> <p> <strong>Transaction Hash:</strong>{" "} {`${transactionDetails.transactionHash.slice( 0, 6 )}...${transactionDetails.transactionHash.slice(-6)}`} </p> <p> <strong>From:</strong> {transactionDetails.from} </p> <p> <strong>Contract Address:</strong>{" "} {transactionDetails.contractAddress} </p> <p> <strong>To:</strong> {transactionDetails.to} </p> <p> <strong>Cumulative Gas Used:</strong>{" "} {transactionDetails.cumulativeGasUsed.toString()} </p> <p> <strong>Block Number:</strong>{" "} {transactionDetails.blockNumber.toString()} </p> </div> <button onClick={generatePDF} className="mt-6 w-full p-3 bg-green-500 text-white rounded-lg" > Download PDF Receipt </button> </div> <div className="w-1/2 text-center"> <h3 className="text-xl font-semibold mb-4">QR Code</h3> <QRCodeSVG value={`Transaction Hash: ${ transactionDetails.transactionHash }, From: ${transactionDetails.from}, To: ${transactionDetails.to}, Contract Address: ${transactionDetails.contractAddress}, Cumulative Gas Used: ${transactionDetails.cumulativeGasUsed.toString()}, Block Number: ${transactionDetails.blockNumber.toString()}`} size={200} className="mx-auto" /> </div> </div> )} </div> </div> ); }; export default TransactionReceipt; Ensuite, importez le et restituez-le dans votre fichier TransactionReceipt App.tsx Démo https://youtu.be/Xwkl9pu8UiM?embedable=true Conclusion Dans cet article, vous avez pu créer un générateur de reçus dans un code PDF ou QR à l'aide de la clé API Rootstock et de la méthode RPC. J'espère donc voir cette fonctionnalité intégrée dans votre prochain projet d'application décentralisée.