En el món actual, els rebuts són crucials per validar les transaccions i conservar el comprovant de les compres. Tant si es tracta d'un gran banc com d'una petita botiga a la carretera, els rebuts ajuden les empreses i els particulars a organitzar-se i fer un seguiment de les seves despeses. Però aquí està la cosa: la majoria de no proporcionen rebuts i confien en els exploradors per verificar els detalls de la transacció. I si no haguessis de confiar en això? Imagineu-vos com de més convenient seria per als vostres usuaris generar rebuts directament, sense necessitat de revisar les seves carteres. les dApps Si esteu creant una dApp basada en pagaments a Rootstock, aquest article us mostrarà com crear un generador de rebuts senzill però eficaç mitjançant l'API Rootstock i només un mètode RPC (Remote Procedure Call). Aquest enfocament facilita el procés i garanteix que els registres de transaccions siguin precisos i de fàcil accés. Aprenem els passos i les eines necessàries per crear una experiència de generació de rebuts sense problemes. Requisits previs Heu de tenir el node instal·lat al vostre dispositiu coneixements en Javascript S'ha instal·lat el marc Js que escolliu Editor de codi, per exemple, VScode Faré servir React Typescript i TailwindCSS per a l'estil Eina i Tecnologies Clau de l'API d'arreel : per interactuar amb RPC Web3js : per generar un codi QR perquè els usuaris puguin escanejar i obtenir el seu rebut QRCode React : per generar el rebut en PDF Jspdf Instal·leu, importeu els paquets i creeu el component funcional Instal·leu totes les dependències mitjançant aquesta comanda: npm i web3js jspdf qrcode.react Creeu un fitxer nou o utilitzeu App.jsx Importeu els paquets al fitxer amb el següent: import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react"; Inicialitzar el component funcional const TransactionReceipt = () => { /......./ } export default TransactionReceipt; Gestió de l'Estat i Iniciació Web3 El fragment de codi aquí gestionarà l'estat i la funcionalitat per obtenir i mostrar els detalls de la transacció mitjançant useState hook, Web3js, Rootstock RPC i 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}` ); : Gestió estatal : Aquesta línia inicialitza una variable d'estat . Aquesta variable d'estat serà responsable del hash de transacció introduït que altres variables i funcions aprofitaran per generar el rebut. const [transactionId, setTransactionId] = useState(""); transactionId : Aquesta línia inicialitza una variable d'estat amb un valor i proporciona una funció per actualitzar-ne el valor. L'estat pot contenir un objecte o . const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); transactionDetails null setTransactionDetails TransactionDetails null : Aquesta línia inicialitza un de variable d'estat amb una cadena buida i proporciona una funció per actualitzar-ne el valor. const [error, setError] = useState(""); error setError : Interfície TypeScript : defineix una interfície TypeScript per a l'estructura de l'objecte de detalls de transacció. Inclou propietats com , , , , i un opcional. interface TransactionDetails transactionHash from to cumulativeGasUsed blockNumber contractAddress : Inicialització Web3 : Aquesta línia inicialitza Web3js, connectant-se al punt final RPC a la xarxa de prova de Rootstock. L'URL del punt final inclou una clau d'API que es recupera de les variables d'entorn mitjançant . const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}); import.meta.env.VITE_API_KEY Funció per obtenir els detalls de la transacció El codi aquí obtindrà els detalls de la transacció mitjançant una funció asíncrona de Rootstock amb el mètode 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"); } } }; : Configuració de gestió d'errors : aquesta estructura s'utilitza per gestionar qualsevol error que es pugui produir durant l'execució de la funció. try { ... } catch (err) { ... } : Restableix l'estat setError("");: Això esborra qualsevol missatge d'error anterior establint l'estat en una cadena buida. error setTransactionDetails(null);: això esborra qualsevol detall de transacció anterior establint l'estat a . transactionDetails null : Obteniu el rebut de la transacció ;: Aquesta línia utilitza el mètode web3js per obtenir el rebut de transacció per a l'identificador de transacció introduït. const receipt = await web3.eth.getTransactionReceipt(transactionId) : Comprovar el rebut : Si no es troba el rebut (és a dir, el rebut és o ), es genera un error amb el missatge "Transacció no trobada!". if (!receipt) { throw new Error("Transaction not found!"); } null undefined : Estableix els detalls de la transacció : si es troba el rebut, actualitza l'estat amb el rebut obtingut. setTransactionDetails(receipt) transactionDetails : Gestió d'errors : aquest bloc detecta qualsevol error que es produeixi durant l'execució del bloc . catch (err) { ... } try : si l'error detectat és una instància de la classe Error, estableix l'estat al missatge de l'error. En cas contrari, estableix l'estat en un missatge d'error genèric "S'ha produït un error desconegut". if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } error error Funcions per generar PDF de rebut Aquí s'utilitzarà el paquet Jspdf per generar el PDF que conté els detalls de la transacció. 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"); }; : Comproveu els detalls de la transacció : Això comprova si és o . Si és així, la funció torna aviat i no fa res. if (!transactionDetails) return; transactionDetails null undefined : Detalls de la transacció de desestructuració : Això desestructura l'objecte per extreure propietats individuals per facilitar l'accés. const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails; transactionDetails : Crea un document PDF : Això crea una nova instància de la classe jsPDF, que representa un document PDF. const pdf = new jsPDF() : Estableix la mida de la lletra i afegiu el títol : Això estableix la mida de la lletra de l'encapçalament a 16. pdf.setFontSize(16) : Això afegeix el títol "Rebut de transacció" a les coordenades (10, 10) al document PDF. pdf.text("Transaction Receipt", 10, 10); : Afegeix els detalls de la transacció al PDF : Això estableix la mida de la lletra en 12 per a la resta del text. pdf.setFontSize(12); : ${transactionHash} : Això afegeix el hash de la transacció a les coordenades (10, 20). pdf.text(Transaction Hash , 10, 20); : Això afegeix l'adreça del remitent a les coordenades (10, 30). pdf.text(From: ${from}, 10, 30); : Això afegeix l'adreça del contracte a les coordenades (10, 40). Nota: aquesta línia s'ha de corregir per evitar la superposició de text. pdf.text(Contract Address: ${contractAddress}, 10, 40); : Això afegeix l'adreça del destinatari a les coordenades (10, 50). pdf.text(To: ${to}, 10, 50); pdf.text(Gas acumulat utilitzat: ${cumulativeGasUsed} : Això afegeix el gas acumulat utilitzat a les coordenades (10, 60). , 10, 60); : Això afegeix el número de bloc a les coordenades (10, 70). pdf.text(Block Number: ${blockNumber}, 10, 70); : Desa el document PDF pdf.save("Transaction_Receipt.pdf");: Això desarà el document PDF amb el nom de fitxer "Transaction_Receipt.pdf" . La interfície d'usuari Aquí presentareu aquests components funcionals com a interfície d'usuari als usuaris. Aquest codi ja ha inclòs l'estil amb 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> Per al generador de codis QR, es va utilitzar la biblioteca qrcode.react i els detalls de la transacció es van xifrar amb el codi QR SVG. Base de codi final i sortida Si seguiu el pas, la vostra base de codi hauria de ser així: 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; A continuació, importeu el i representeu-lo al vostre fitxer TransactionReceipt App.tsx Demostració https://youtu.be/Xwkl9pu8UiM?embeble=true Conclusió En aquest article, heu pogut crear un generador de rebuts en un codi PDF o QR mitjançant el mètode Rootstock API Key i RPC. Així que en el vostre proper projecte dApp, espero veure-hi aquesta característica.