A vevő a fiókjában le szeretné tölteni a számláit, a könyvelő pedig havonta egy mappában kéri őket. Ehhez a PDF-eket a saját tárhelyeden tartod. Így nem kell minden letöltésnél a Számlázz.hu-t hívni, és a számla akkor is elérhető, ha a Számlázz.hu épp karbantart.
A kassza/storage modul s3FetchStorage() adaptere bármilyen S3-kompatibilis tárhellyel működik, és csak fetch-et használ, AWS SDK nem kell hozzá.
A függvény rendelésszám alapján megkeresi a bizonylatot, és a PDF-et a bizonylat keltéhez tartozó év és hónap mappájába menti, például szamlak/2026/09/WEB-2026-128.pdf. A díjbekérő, a sztornó és a többi bizonylattípus külön almappába kerül.
lib/szamla-pdf.ts
import type { InvoiceDocumentType, Kassza } from 'kassza'import { type InvoiceDocumentKind, invoicePdfKey, type StorageAdapter, type StoredFile, storePdf,} from 'kassza/storage'import { kassza as alapKliens } from '@/lib/kassza'import { tarhely as alapTarhely } from '@/lib/tarhely'const MAPPAK: Partial<Record<InvoiceDocumentType, InvoiceDocumentKind>> = { invoice: 'invoice', proforma: 'proforma', advance: 'advance', final: 'final', corrective: 'corrective', reversal: 'storno', deliveryNote: 'deliveryNote',}export async function szamlaPdfMentese( orderNumber: string, kliens: Kassza = alapKliens, tarhely: StorageAdapter = alapTarhely,): Promise<StoredFile | null> { const szamla = await kliens.invoices.find({ orderNumber }, { includePdf: true }) if (!szamla) return null const pdf = szamla.pdf ?? (await kliens.invoices.getPdf(szamla.header.number)).pdf const kulcs = invoicePdfKey({ number: szamla.header.number, type: MAPPAK[szamla.header.type], date: szamla.header.issueDate, }) return storePdf(tarhely, kulcs, pdf)}
A storePdf() a feltöltés előtt ellenőrzi, hogy a tartalom valóban PDF-e, és StorageError-t dob, ha nem.
PDF mentése tárhelyre
A számla PDF-je dátum szerinti kulccsal egy memóriás tárhelyre.
Egy route, két metódussal. A POST-ot a saját rendszered hívja a számlázás után, például egy háttérfeladatból. A GET-et a bejelentkezett vevő hívja, és egy 5 percig érvényes, aláírt letöltési linkre irányítja át. A bejelentkezettVevo(), a pdfKulcsMentese() és a pdfKulcsBetoltese() a saját függvényeid, az adminKeres() a Nevezés díjbekérővel recept belső token ellenőrzése.
app/api/szamlak/[rendelesszam]/pdf/route.ts
import { isSzamlazzError } from 'kassza'import { isStorageError } from 'kassza/storage'import { adminKeres } from '@/lib/admin'import { bejelentkezettVevo } from '@/lib/auth'import { pdfKulcsBetoltese, pdfKulcsMentese } from '@/lib/rendelesek'import { szamlaPdfMentese } from '@/lib/szamla-pdf'import { tarhely } from '@/lib/tarhely'type Parameterek = { params: Promise<{ rendelesszam: string }> }const LINK_ERVENYESSEGE_MP = 300export async function POST(request: Request, { params }: Parameterek) { if (!adminKeres(request)) return new Response('Nincs jogosultság', { status: 401 }) const { rendelesszam } = await params try { const fajl = await szamlaPdfMentese(rendelesszam) if (!fajl) return Response.json({ hiba: 'Nincs ilyen számla.' }, { status: 404 }) await pdfKulcsMentese(rendelesszam, fajl.key) return Response.json({ kulcs: fajl.key, meret: fajl.size }) } catch (error) { if (isStorageError(error)) return Response.json({ hiba: error.message }, { status: 502 }) if (isSzamlazzError(error)) { return Response.json({ hiba: error.message, tipp: error.hint }, { status: 502 }) } throw error }}export async function GET(request: Request, { params }: Parameterek) { const vevo = await bejelentkezettVevo(request) if (!vevo) return new Response('Jelentkezz be', { status: 401 }) const { rendelesszam } = await params const kulcs = await pdfKulcsBetoltese(vevo.id, rendelesszam) if (!kulcs) return new Response('Nincs ilyen számla', { status: 404 }) const url = await tarhely.getUrl(kulcs, { expiresInSeconds: LINK_ERVENYESSEGE_MP }) return Response.redirect(url, 302)}
A pdfKulcsBetoltese() csak akkor adjon vissza kulcsot, ha a rendelés az adott vevőé. Enélkül bárki letölthetné más számláját, ha kitalálja a rendelésszámot.