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 dApp-ova 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.
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
- Web3js : za interakciju s RPC-om
- QRCode React : za generiranje QR koda koji korisnici mogu skenirati i dobiti račun
- Jspdf : za generiranje računa u PDF-ove
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 :
-
const [transactionId, setTransactionId] = useState("");
: Ovaj redak inicijalizira varijablu stanjatransactionId
. Ova varijabla stanja bit će odgovorna za uneseni hash transakcije koji će druge varijable i funkcije iskoristiti za generiranje računa. -
const [transactionDetails, setTransactionDetails] = useState<TransactionDetails | null>(null);
: Ovaj redak inicijalizira varijablu stanjatransactionDetails
snull
vrijednošću i pruža funkcijusetTransactionDetails
za ažuriranje njezine vrijednosti. Stanje može sadržavati objektTransactionDetails
ilinull
. -
const [error, setError] = useState("");
: Ovaj redak inicijaliziraerror
varijable stanja s praznim nizom i pruža funkcijusetError
za ažuriranje njezine vrijednosti.
-
TypeScript sučelje :
-
interface TransactionDetails
: Ovo definira TypeScript sučelje za strukturu objekta detalja transakcije. Uključuje svojstva kao što sutransactionHash
,from
,to
,cumulativeGasUsed
,blockNumber
i izbornucontractAddress
.
-
Web3 inicijalizacija :
-
const web3 = new Web3(https://rpc.testnet.rootstock.io/${import.meta.env.VITE_API_KEY});
: 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ćuimport.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 :
-
try { ... } catch (err) { ... }
: Ova se struktura koristi za rješavanje bilo kakvih pogrešaka koje se mogu pojaviti tijekom izvođenja funkcije.
-
- Ponovno stanje :
- setError("");: Ovo briše sve prethodne poruke o pogrešci postavljanjem stanja
error
na prazan niz. - setTransactionDetails(null);: Ovo briše sve prethodne detalje transakcije postavljanjem stanja
transactionDetails
nanull
.
- setError("");: Ovo briše sve prethodne poruke o pogrešci postavljanjem stanja
- Dohvati potvrdu o transakciji :
-
const receipt = await web3.eth.getTransactionReceipt(transactionId)
;: Ovaj redak koristi metodu web3js za dohvaćanje potvrde o transakciji za uneseni transakcijskiId.
-
- Provjeri primitak :
-
if (!receipt) { throw new Error("Transaction not found!"); }
: Ako potvrda nije pronađena (tj. potvrda jenull
iliundefined
), pojavljuje se pogreška s porukom "Transakcija nije pronađena!".
-
- Postavite detalje transakcije :
-
setTransactionDetails(receipt)
: Ako je potvrda pronađena, ažurira stanjetransactionDetails
s dohvaćenom potvrdom.
-
- Rješavanje grešaka :
-
catch (err) { ... }
: Ovaj blok hvata sve pogreške koje se dogode tijekom izvođenja blokatry
. -
if (err instanceof Error) { setError(err.message); } else { setError("An unknown error occurred"); }
: Ako je uhvaćena pogreška instanca klase Pogreška, ona postavlja stanjeerror
na poruku o pogrešci. U suprotnom, postavlja stanjeerror
na generičku poruku o pogrešci "Došlo je do nepoznate pogreške".
-
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 :
if (!transactionDetails) return;
: Ovim se provjerava je litransactionDetails
null
iliundefined
. Ako jest, funkcija se rano vraća i ne radi ništa.
- Pojedinosti o transakciji uništenja :
const { transactionHash, from, to, cumulativeGasUsed, blockNumber, contractAddress } = transactionDetails;
: Ovo destrukturira objekttransactionDetails
kako bi se izdvojila pojedinačna svojstva radi lakšeg pristupa.
- Napravite PDF dokument :
const pdf = new jsPDF()
: Ovo stvara novu instancu klase jsPDF, koja predstavlja PDF dokument.
- Postavite veličinu fonta i dodajte naslov :
pdf.setFontSize(16)
: Ovo postavlja veličinu fonta naslova na 16.pdf.text("Transaction Receipt", 10, 10);
: Ovo dodaje naslov "Potvrda o transakciji" na koordinatama (10, 10) u PDF dokumentu.
- Dodavanje pojedinosti o transakciji u PDF :
pdf.setFontSize(12);
: Ovo postavlja veličinu fonta na 12 za ostatak teksta.pdf.text(Transaction Hash
: ${transactionHash}, 10, 20);
: Ovo dodaje hash transakcije na koordinate (10, 20).pdf.text(From: ${from}, 10, 30);
: Ovo dodaje adresu pošiljatelja na koordinate (10, 30).pdf.text(Contract Address: ${contractAddress}, 10, 40);
: Ovo dodaje adresu ugovora na koordinate (10, 40). Napomena: ovaj redak treba ispraviti kako bi se izbjeglo preklapanje teksta.pdf.text(To: ${to}, 10, 50);
: Ovo dodaje adresu primatelja na koordinate (10, 50).pdf.text(Kumulativno iskorišteni plin: ${cumulativeGasUsed}
, 10, 60);
: Ovo dodaje kumulativni plin korišten na koordinatama (10, 60).pdf.text(Block Number: ${blockNumber}, 10, 70);
: Ovo dodaje broj bloka na koordinate (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 TransactionReceipt
i renderirajte ga u svojoj datoteci App.tsx
Demo
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.