U današnjem svijetu, računi su ključni za potvrđivanje transakcija i čuvanje dokaza o kupnji. Bilo da se radi o velikoj banci ili maloj trgovini uz cestu, računi pomažu tvrtkama i pojedincima da ostanu organizirani i prate svoju potrošnju. Ali evo u čemu je stvar: većina ne daje potvrde i oslanja se na istraživače za provjeru pojedinosti o transakciji. Što ako se ne morate oslanjati na to? Zamislite koliko bi praktičnije bilo za vaše korisnike da generiraju račune izravno, bez potrebe da provjeravaju svoje novčanike. dApp-ova Ako na Rootstocku gradite dApp temeljen na plaćanju, ovaj će vam članak pokazati kako stvoriti jednostavan, ali učinkovit generator računa pomoću Rootstock API-ja i samo jedne RPC (Remote Procedure Call) metode. Ovaj pristup olakšava proces i osigurava da su vaši zapisi o transakcijama točni i laki za pristup. Naučimo korake i alate potrebne za stvaranje glatkog iskustva generiranja računa. Preduvjeti Morate imati instaliran čvor na svom uređaju poznavanje Javascripta Instaliran Js framework po vašem izboru Uređivač koda, npr. VScode Koristit ću React Typescript i TailwindCSS za stiliziranje Alat i tehnologije API ključ podloge : za interakciju s RPC-om Web3js : za generiranje QR koda koji korisnici mogu skenirati i dobiti račun QRCode React : za generiranje računa u PDF-ove Jspdf Instalirajte, uvezite pakete i izradite funkcionalnu komponentu Instalirajte sve ovisnosti pomoću ove naredbe: npm i web3js jspdf qrcode.react Napravite novu datoteku ili upotrijebite App.jsx Uvezite pakete u datoteku sa sljedećim: import { useState } from "react"; import Web3 from "web3"; import { jsPDF } from "jspdf"; import { QRCodeSVG } from "qrcode.react"; Inicijalizirajte funkcionalnu komponentu const TransactionReceipt = () => { /......./ } export default TransactionReceipt; Upravljanje stanjem i Web3 Intilaizacija Isječak koda koji se nalazi ovdje upravljat će stanjem i funkcionalnošću za dohvaćanje i prikazivanje detalja transakcije pomoću useState kuke, Web3js, Rootstock RPC i API-ja. 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}` ); : Upravljanje državom : Ovaj redak inicijalizira varijablu stanja . Ova varijabla stanja bit će odgovorna za uneseni hash transakcije koji će druge varijable i funkcije iskoristiti za generiranje računa. const [transactionId, setTransactionId] = useState(""); transactionId : Ovaj redak inicijalizira varijablu stanja s vrijednošću i pruža funkciju za ažuriranje njezine vrijednosti. Stanje može sadržavati objekt ili . const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null); transactionDetails null setTransactionDetails TransactionDetails null : Ovaj redak inicijalizira varijable stanja s praznim nizom i pruža funkciju za ažuriranje njezine vrijednosti. const [error, setError] = useState(""); error setError : TypeScript sučelje : Ovo definira TypeScript sučelje za strukturu objekta detalja transakcije. Uključuje svojstva kao što su , , , , i izbornu . interface TransactionDetails transactionHash from to cumulativeGasUsed blockNumber contractAddress : Web3 inicijalizacija : Ovaj redak inicijalizira Web3js, povezujući se s RPC krajnjom točkom na Rootstock testnet. URL krajnje točke uključuje API ključ koji se dohvaća iz varijabli okruženja pomoću . const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY}); import.meta.env.VITE_API_KEY Funkcija za dohvaćanje pojedinosti o transakciji Kod ovdje će dohvatiti detalje transakcije pomoću asinkrone funkcije iz Rootstocka s metodom 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"); } } }; : Postavljanje rukovanja pogreškama : Ova se struktura koristi za rješavanje bilo kakvih pogrešaka koje se mogu pojaviti tijekom izvođenja funkcije. try { ... } catch (err) { ... } : Ponovno stanje setError("");: Ovo briše sve prethodne poruke o pogrešci postavljanjem stanja na prazan niz. error setTransactionDetails(null);: Ovo briše sve prethodne detalje transakcije postavljanjem stanja na . transactionDetails null : Dohvati potvrdu o transakciji ;: Ovaj redak koristi metodu web3js za dohvaćanje potvrde o transakciji za uneseni transakcijskiId. const receipt = await web3.eth.getTransactionReceipt(transactionId) : Provjeri primitak : Ako potvrda nije pronađena (tj. potvrda je ili ), pojavljuje se pogreška s porukom "Transakcija nije pronađena!". if (!receipt) { throw new Error("Transaction not found!"); } null undefined : Postavite detalje transakcije : Ako je potvrda pronađena, ažurira stanje s dohvaćenom potvrdom. setTransactionDetails(receipt) transactionDetails : Rješavanje grešaka : Ovaj blok hvata sve pogreške koje se dogode tijekom izvođenja bloka . catch (err) { ... } try : Ako je uhvaćena pogreška instanca klase Pogreška, ona postavlja stanje na poruku o pogrešci. U suprotnom, postavlja stanje na generičku poruku o pogrešci "Došlo je do nepoznate pogreške". if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); } error error Funkcije za generiranje PDF-a računa Ovdje će se paket Jspdf koristiti za generiranje PDF-a koji sadrži detalje transakcije. 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"); }; : Provjerite pojedinosti transakcije : Ovim se provjerava je li ili . Ako jest, funkcija se rano vraća i ne radi ništa. if (!transactionDetails) return; transactionDetails null undefined : Pojedinosti o transakciji uništenja : Ovo destrukturira objekt kako bi se izdvojila pojedinačna svojstva radi lakšeg pristupa. const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails; transactionDetails : Napravite PDF dokument : Ovo stvara novu instancu klase jsPDF, koja predstavlja PDF dokument. const pdf = new jsPDF() : Postavite veličinu fonta i dodajte naslov : Ovo postavlja veličinu fonta naslova na 16. pdf.setFontSize(16) : Ovo dodaje naslov "Potvrda o transakciji" na koordinatama (10, 10) u PDF dokumentu. pdf.text("Transaction Receipt", 10, 10); : Dodavanje pojedinosti o transakciji u PDF : Ovo postavlja veličinu fonta na 12 za ostatak teksta. pdf.setFontSize(12); : ${transactionHash} : Ovo dodaje hash transakcije na koordinate (10, 20). pdf.text(Transaction Hash , 10, 20); : Ovo dodaje adresu pošiljatelja na koordinate (10, 30). pdf.text(From: ${from}, 10, 30); : Ovo dodaje adresu ugovora na koordinate (10, 40). Napomena: ovaj redak treba ispraviti kako bi se izbjeglo preklapanje teksta. pdf.text(Contract Address: ${contractAddress}, 10, 40); : Ovo dodaje adresu primatelja na koordinate (10, 50). pdf.text(To: ${to}, 10, 50); pdf.text(Kumulativno iskorišteni plin: ${cumulativeGasUsed} : Ovo dodaje kumulativni plin korišten na koordinatama (10, 60). , 10, 60); : Ovo dodaje broj bloka na koordinate (10, 70). pdf.text(Block Number: ${blockNumber}, 10, 70); : Spremi PDF dokument pdf.save("Transaction_Receipt.pdf");: Ovo će spremiti PDF dokument s nazivom datoteke "Transaction_Receipt.pdf" . Korisničko sučelje Ovdje ćete korisnicima prikazati te funkcionalne komponente kao korisničko sučelje. Ovaj kod već je uključio stil korištenjem Tailwindcss-a 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> Za generator QR koda korištena je biblioteka qrcode.react, a detalji transakcije šifrirani su u QR kod SVG. Konačna baza koda i izlaz Ako slijedite korak, vaša bi baza koda trebala izgledati ovako: 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; Zatim uvezite i renderirajte ga u svojoj datoteci TransactionReceipt App.tsx Demo https://youtu.be/Xwkl9pu8UiM?embedable=true Zaključak U ovom ste članku uspjeli izgraditi generator računa u PDF ili QR kod pomoću Rootstock API ključa i RPC metode. Nadam se da ću u vašem sljedećem dApp projektu vidjeti ovu značajku.